001 /** 002 * Copyright 2010-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.codehaus.mojo.license; 017 018 import java.io.File; 019 import java.io.FileOutputStream; 020 import java.io.IOException; 021 import java.util.ArrayList; 022 import java.util.Collection; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.SortedMap; 026 import java.util.SortedSet; 027 028 import org.apache.commons.collections.CollectionUtils; 029 import org.apache.commons.collections.MapUtils; 030 import org.apache.maven.artifact.repository.ArtifactRepository; 031 import org.apache.maven.plugin.MojoFailureException; 032 import org.apache.maven.plugin.logging.Log; 033 import org.apache.maven.project.MavenProject; 034 import org.apache.maven.project.ProjectBuildingException; 035 import org.codehaus.mojo.license.model.LicenseMap; 036 037 /** 038 * Goal to generate the third-party license file. 039 * <p/> 040 * This file contains a list of the dependencies and their licenses. Each dependency and it's license is displayed on a 041 * single line in the format <br/> 042 * 043 * <pre> 044 * (<license-name>) <project-name> <groupId>:<artifactId>:<version> - <project-url> 045 * </pre> 046 * 047 * It will also copy it in the class-path (says add the generated directory as a resource of the build). 048 * 049 * @author tchemit <chemit@codelutin.com> 050 * @goal add-third-party 051 * @phase generate-resources 052 * @requiresDependencyResolution test 053 * @requiresProject true 054 * @since 1.0 055 */ 056 public class AddThirdPartyMojo extends AbstractAddThirdPartyMojo implements MavenProjectDependenciesConfigurator { 057 058 /** 059 * Local Repository. 060 * 061 * @parameter expression="${localRepository}" 062 * @required 063 * @readonly 064 * @since 1.0.0 065 */ 066 protected ArtifactRepository localRepository; 067 068 /** 069 * Remote repositories used for the project. 070 * 071 * @parameter expression="${project.remoteArtifactRepositories}" 072 * @required 073 * @readonly 074 * @since 1.0.0 075 */ 076 protected List remoteRepositories; 077 078 /** 079 * Deploy the third party missing file in maven repository. 080 * 081 * @parameter expression="${license.deployMissingFile}" default-value="true" 082 * @since 1.0 083 */ 084 protected boolean deployMissingFile; 085 086 /** 087 * Load from repositories third party missing files. 088 * 089 * @parameter expression="${license.useRepositoryMissingFiles}" default-value="true" 090 * @since 1.0 091 */ 092 protected boolean useRepositoryMissingFiles; 093 094 /** 095 * dependencies tool. 096 * 097 * @component 098 * @readonly 099 * @since 1.0 100 */ 101 private DependenciesTool dependenciesTool; 102 103 /** 104 * Controls if THIRD-PARTY.properties gets created or not 105 * 106 * @parameter expression="${license.doGenerateMissing}" default-value="false" 107 */ 108 private boolean doGenerateMissing; 109 110 @Override 111 protected boolean checkPackaging() { 112 return rejectPackaging("pom"); 113 } 114 115 @Override 116 protected SortedMap<String, MavenProject> loadDependencies() { 117 getLog().info("dependencies tool=" + dependenciesTool.getClass()); 118 119 return dependenciesTool.loadProjectDependencies(getProject(), this, localRepository, remoteRepositories, 120 getArtifactCache()); 121 } 122 123 @Override 124 protected SortedProperties createUnsafeMapping() throws ProjectBuildingException, IOException, 125 ThirdPartyToolException { 126 127 SortedProperties unsafeMappings = getThridPartyTool().loadUnsafeMapping(getLicenseMap(), getArtifactCache(), 128 getEncoding(), getMissingFile()); 129 130 SortedSet<MavenProject> unsafeDependencies = getUnsafeDependencies(); 131 132 getLog().debug("1.0"); 133 if (CollectionUtils.isNotEmpty(unsafeDependencies)) { 134 135 // there is some unresolved license 136 137 getLog().debug("2.0"); 138 if (isUseRepositoryMissingFiles()) { 139 140 // try to load missing third party files from dependencies 141 142 Collection<MavenProject> projects = new ArrayList<MavenProject>(getProjectDependencies().values()); 143 projects.remove(getProject()); 144 projects.removeAll(unsafeDependencies); 145 146 getLog().debug("2.1"); 147 SortedProperties resolvedUnsafeMapping = new SortedProperties("UTF-8");// getThridPartyTool().loadThirdPartyDescriptorsForUnsafeMapping( 148 // getEncoding(), projects, unsafeDependencies, getLicenseMap(), localRepository, 149 // remoteRepositories); 150 getLog().debug("2.2"); 151 152 // push back resolved unsafe mappings 153 unsafeMappings.putAll(resolvedUnsafeMapping); 154 155 } 156 } 157 if (isVerbose()) { 158 getLog().info("found " + unsafeMappings.size() + " unsafe mappings"); 159 } 160 161 // compute if missing file should be (re)-generate 162 boolean generateMissingfile = doGenerateMissing 163 && computeDoGenerateMissingFile(unsafeMappings, unsafeDependencies); 164 165 setDoGenerateMissing(generateMissingfile); 166 167 if (generateMissingfile && isVerbose()) { 168 StringBuilder sb = new StringBuilder(); 169 sb.append("Will use from missing file "); 170 sb.append(unsafeMappings.size()); 171 sb.append(" dependencies :"); 172 for (Map.Entry<Object, Object> entry : unsafeMappings.entrySet()) { 173 String id = (String) entry.getKey(); 174 String license = (String) entry.getValue(); 175 sb.append("\n - ").append(id).append(" - ").append(license); 176 } 177 getLog().info(sb.toString()); 178 } else { 179 if (isUseMissingFile() && !unsafeMappings.isEmpty()) { 180 getLog().debug("Missing file " + getMissingFile() + " is up-to-date."); 181 } 182 } 183 getLog().info("4"); 184 return unsafeMappings; 185 } 186 187 /** 188 * @param unsafeMappings 189 * the unsafe mapping coming from the missing file 190 * @param unsafeDependencies 191 * the unsafe dependencies from the project 192 * @return {@code true} if missing ifle should be (re-)generated, {@code false} otherwise 193 * @throws IOException 194 * if any IO problem 195 * @since 1.0 196 */ 197 protected boolean computeDoGenerateMissingFile(SortedProperties unsafeMappings, 198 SortedSet<MavenProject> unsafeDependencies) throws IOException { 199 200 if (!isUseMissingFile()) { 201 202 // never use the missing file 203 return false; 204 } 205 206 if (isForce()) { 207 208 // the mapping for missing file is not empty, regenerate it 209 return !CollectionUtils.isEmpty(unsafeMappings.keySet()); 210 } 211 212 if (!CollectionUtils.isEmpty(unsafeDependencies)) { 213 214 // there is some unsafe dependencies from the project, must 215 // regenerate missing file 216 return true; 217 } 218 219 File missingFile = getMissingFile(); 220 221 if (!missingFile.exists()) { 222 223 // the missing file does not exists, this happens when 224 // using remote missing file from dependencies 225 return true; 226 } 227 228 // check if the missing file has changed 229 SortedProperties oldUnsafeMappings = new SortedProperties(getEncoding()); 230 oldUnsafeMappings.load(missingFile); 231 return !unsafeMappings.equals(oldUnsafeMappings); 232 } 233 234 @Override 235 protected boolean checkSkip() { 236 if (!isDoGenerate() && !isDoGenerateBundle() && !isDoGenerateMissing()) { 237 238 getLog().info("All files are up to date, skip goal execution."); 239 return false; 240 } 241 return true; 242 } 243 244 @Override 245 protected void doAction() throws Exception { 246 boolean unsafe = checkUnsafeDependencies(); 247 248 writeThirdPartyFile(); 249 250 if (isDoGenerateMissing()) { 251 252 writeMissingFile(); 253 } 254 255 if (unsafe && isFailIfWarning()) { 256 throw new MojoFailureException("There is some dependencies with no license, please fill the file " 257 + getMissingFile()); 258 } 259 260 if (!unsafe && isUseMissingFile() && MapUtils.isEmpty(getUnsafeMappings()) && getMissingFile().exists()) { 261 262 // there is no missing dependencies, but still a missing file, delete it 263 getLog().debug("There is no dependency to put in missing file, delete it at " + getMissingFile()); 264 FileUtil.deleteFile(getMissingFile()); 265 } 266 267 if (!unsafe && isDeployMissingFile() && MapUtils.isNotEmpty(getUnsafeMappings())) { 268 269 // can deploy missing file 270 File file = getMissingFile(); 271 272 getLog().debug("Will deploy third party file from " + file); 273 getThridPartyTool().attachThirdPartyDescriptor(getProject(), file); 274 } 275 276 addResourceDir(getOutputDirectory(), "**/*.txt"); 277 } 278 279 protected void writeMissingFile() throws IOException { 280 281 Log log = getLog(); 282 LicenseMap licenseMap = getLicenseMap(); 283 File file = getMissingFile(); 284 285 FileUtil.createDirectoryIfNecessary(file.getParentFile()); 286 log.info("Regenerate missing license file " + file); 287 288 FileOutputStream writer = new FileOutputStream(file); 289 try { 290 StringBuilder sb = new StringBuilder(" Generated by " + getClass().getName()); 291 List<String> licenses = new ArrayList<String>(licenseMap.keySet()); 292 licenses.remove(LicenseMap.getUnknownLicenseMessage()); 293 if (!licenses.isEmpty()) { 294 sb.append("\n-------------------------------------------------------------------------------"); 295 sb.append("\n Already used licenses in project :"); 296 for (String license : licenses) { 297 sb.append("\n - ").append(license); 298 } 299 } 300 sb.append("\n-------------------------------------------------------------------------------"); 301 sb.append("\n Please fill the missing licenses for dependencies :\n\n"); 302 getUnsafeMappings().store(writer, sb.toString()); 303 } finally { 304 writer.close(); 305 } 306 } 307 308 public boolean isDoGenerateMissing() { 309 return doGenerateMissing; 310 } 311 312 public void setDoGenerateMissing(boolean doGenerateMissing) { 313 this.doGenerateMissing = doGenerateMissing; 314 } 315 316 public ArtifactRepository getLocalRepository() { 317 return localRepository; 318 } 319 320 public List getRemoteRepositories() { 321 return remoteRepositories; 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 public boolean isIncludeTransitiveDependencies() { 329 return includeTransitiveDependencies; 330 } 331 332 // /** 333 // * {@inheritDoc} 334 // */ 335 // public List<String> getExcludedScopes() 336 // { 337 // return Arrays.asList( Artifact.SCOPE_SYSTEM ); 338 // } 339 340 public boolean isDeployMissingFile() { 341 return deployMissingFile; 342 } 343 344 public boolean isUseRepositoryMissingFiles() { 345 return useRepositoryMissingFiles; 346 } 347 348 }