001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2020-2024 Andres Almiray. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.kordamp.maven.checker; 019 020import org.apache.maven.model.Dependency; 021import org.apache.maven.model.Model; 022import org.apache.maven.project.MavenProject; 023 024import java.util.ArrayList; 025import java.util.List; 026 027import static java.lang.System.lineSeparator; 028 029/** 030 * Checks if a POM file is a minimal BOM file. 031 * <p> 032 * The following blocks are required: 033 * <ul> 034 * <li><dependencyManagement></li> 035 * </ul> 036 * <p> 037 * The following blocks are forbidden: 038 * <ul> 039 * <li><build></li> 040 * <li><reporting></li> 041 * <li><dependencies></li> 042 * <li><repositories></li> 043 * <li><pluginRepositories></li> 044 * <li><profiles></li> 045 * <li><modules></li> 046 * </ul> 047 * 048 * @author Andres Almiray 049 * @since 1.0.0 050 */ 051public class BomChecker { 052 public static class Configuration { 053 private boolean failOnError; 054 055 public boolean isFailOnError() { 056 return failOnError; 057 } 058 059 /** 060 * Sets the value for {@code failOnError}. 061 * 062 * @param failOnError if {@code true} fails the build when an error is encountered. 063 */ 064 public Configuration withFailOnError(boolean failOnError) { 065 this.failOnError = failOnError; 066 return this; 067 } 068 } 069 070 /** 071 * Checks the resolved model of the given MaveProject for compliance. 072 * 073 * @param log the logger to use. 074 * @param project the project to be checked. 075 * @param configuration configuration required for inspection. 076 * @throws PomCheckException if the POM is invalid 077 */ 078 public static void check(Logger log, MavenProject project, Configuration configuration) throws PomCheckException { 079 Model model = project.getOriginalModel(); 080 081 List<String> errors = new ArrayList<>(); 082 083 // 1. is it packaged as 'pom'? 084 log.debug("Checking <packaging>"); 085 if (!"pom".equals(model.getPackaging())) { 086 errors.add("The value of <packaging> must be 'pom'."); 087 } 088 089 log.debug("Checking <dependencyManagement>"); 090 // 2. must have a <dependencyManagement> block 091 if (null != model.getDependencyManagement()) { 092 List<Dependency> dependencies = model.getDependencyManagement().getDependencies(); 093 if (dependencies == null || dependencies.isEmpty()) { 094 errors.add("No dependencies have been defined in <dependencyManagement>."); 095 } 096 } else { 097 errors.add("No <dependencyManagement> block has been defined."); 098 } 099 100 log.debug("Checking <build>"); 101 if (null != model.getBuild()) { 102 errors.add("The <build> block should not be present."); 103 } 104 105 log.debug("Checking <reporting>"); 106 if (null != model.getReporting()) { 107 errors.add("The <reporting> block should not be present."); 108 } 109 110 log.debug("Checking <dependencies>"); 111 if (null != model.getDependencies() && !model.getDependencies().isEmpty()) { 112 errors.add("The <dependencies> block should not be present."); 113 } 114 115 log.debug("Checking <repositories>"); 116 if (null != model.getRepositories() && !model.getRepositories().isEmpty()) { 117 errors.add("The <repositories> block should not be present."); 118 } 119 120 log.debug("Checking <pluginRepositories>"); 121 if (null != model.getPluginRepositories() && !model.getPluginRepositories().isEmpty()) { 122 errors.add("The <pluginRepositories> block should not be present."); 123 } 124 125 log.debug("Checking <profiles>"); 126 if (null != model.getProfiles() && !model.getProfiles().isEmpty()) { 127 errors.add("The <profiles> block should not be present."); 128 } 129 130 log.debug("Checking <modules>"); 131 if (null != model.getModules() && !model.getModules().isEmpty()) { 132 errors.add("The <modules> block should not be present."); 133 } 134 135 if (!errors.isEmpty()) { 136 StringBuilder b = new StringBuilder(lineSeparator()) 137 .append("The POM file") 138 .append(lineSeparator()) 139 .append(project.getFile().getAbsolutePath()) 140 .append(lineSeparator()) 141 .append("is not a valid BOM due to the following reasons:") 142 .append(lineSeparator()); 143 for (String s : errors) { 144 b.append(" * ").append(s).append(lineSeparator()); 145 } 146 147 if (configuration.isFailOnError()) { 148 throw new PomCheckException(b.toString()); 149 } else { 150 log.warn(b.toString()); 151 } 152 } else { 153 log.info("BOM {} passes all checks.", project.getFile().getAbsolutePath()); 154 } 155 } 156}