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 return dependenciesTool.loadProjectDependencies(getProject(), this, localRepository, remoteRepositories, 118 getArtifactCache()); 119 } 120 121 @Override 122 protected SortedProperties createUnsafeMapping() throws ProjectBuildingException, IOException, 123 ThirdPartyToolException { 124 125 SortedProperties unsafeMappings = getThridPartyTool().loadUnsafeMapping(getLicenseMap(), getArtifactCache(), 126 getEncoding(), getMissingFile()); 127 128 SortedSet<MavenProject> unsafeDependencies = getUnsafeDependencies(); 129 130 getLog().debug("1.0"); 131 if (CollectionUtils.isNotEmpty(unsafeDependencies)) { 132 133 // there is some unresolved license 134 135 getLog().debug("2.0"); 136 if (isUseRepositoryMissingFiles()) { 137 138 // try to load missing third party files from dependencies 139 140 Collection<MavenProject> projects = new ArrayList<MavenProject>(getProjectDependencies().values()); 141 projects.remove(getProject()); 142 projects.removeAll(unsafeDependencies); 143 144 getLog().debug("2.1"); 145 146 SortedProperties resolvedUnsafeMapping = new SortedProperties("UTF-8"); 147 // The next few lines attempt to download groupid--artifactid--third-party.properties for every 148 // dependency in the tree 149 150 // getThridPartyTool().loadThirdPartyDescriptorsForUnsafeMapping( 151 // getEncoding(), projects, unsafeDependencies, getLicenseMap(), localRepository, 152 // remoteRepositories); 153 getLog().debug("2.2"); 154 155 // push back resolved unsafe mappings 156 unsafeMappings.putAll(resolvedUnsafeMapping); 157 158 } 159 } 160 if (isVerbose()) { 161 getLog().info("found " + unsafeMappings.size() + " unsafe mappings"); 162 } 163 164 // compute if missing file should be (re)-generate 165 boolean generateMissingfile = doGenerateMissing 166 && computeDoGenerateMissingFile(unsafeMappings, unsafeDependencies); 167 168 setDoGenerateMissing(generateMissingfile); 169 170 if (generateMissingfile && isVerbose()) { 171 StringBuilder sb = new StringBuilder(); 172 sb.append("Will use from missing file "); 173 sb.append(unsafeMappings.size()); 174 sb.append(" dependencies :"); 175 for (Map.Entry<Object, Object> entry : unsafeMappings.entrySet()) { 176 String id = (String) entry.getKey(); 177 String license = (String) entry.getValue(); 178 sb.append("\n - ").append(id).append(" - ").append(license); 179 } 180 getLog().info(sb.toString()); 181 } else { 182 if (isUseMissingFile() && !unsafeMappings.isEmpty()) { 183 getLog().debug("Missing file " + getMissingFile() + " is up-to-date."); 184 } 185 } 186 getLog().debug("4"); 187 return unsafeMappings; 188 } 189 190 /** 191 * @param unsafeMappings 192 * the unsafe mapping coming from the missing file 193 * @param unsafeDependencies 194 * the unsafe dependencies from the project 195 * @return {@code true} if missing ifle should be (re-)generated, {@code false} otherwise 196 * @throws IOException 197 * if any IO problem 198 * @since 1.0 199 */ 200 protected boolean computeDoGenerateMissingFile(SortedProperties unsafeMappings, 201 SortedSet<MavenProject> unsafeDependencies) throws IOException { 202 203 if (!isUseMissingFile()) { 204 205 // never use the missing file 206 return false; 207 } 208 209 if (isForce()) { 210 211 // the mapping for missing file is not empty, regenerate it 212 return !CollectionUtils.isEmpty(unsafeMappings.keySet()); 213 } 214 215 if (!CollectionUtils.isEmpty(unsafeDependencies)) { 216 217 // there is some unsafe dependencies from the project, must 218 // regenerate missing file 219 return true; 220 } 221 222 File missingFile = getMissingFile(); 223 224 if (!missingFile.exists()) { 225 226 // the missing file does not exists, this happens when 227 // using remote missing file from dependencies 228 return true; 229 } 230 231 // check if the missing file has changed 232 SortedProperties oldUnsafeMappings = new SortedProperties(getEncoding()); 233 oldUnsafeMappings.load(missingFile); 234 return !unsafeMappings.equals(oldUnsafeMappings); 235 } 236 237 @Override 238 protected boolean checkSkip() { 239 if (!isDoGenerate() && !isDoGenerateBundle() && !isDoGenerateMissing()) { 240 241 getLog().info("All files are up to date, skip goal execution."); 242 return false; 243 } 244 return true; 245 } 246 247 @Override 248 protected void doAction() throws Exception { 249 boolean unsafe = checkUnsafeDependencies(); 250 251 writeThirdPartyFile(); 252 253 if (isDoGenerateMissing()) { 254 255 writeMissingFile(); 256 } 257 258 if (unsafe && isFailIfWarning()) { 259 throw new MojoFailureException("There is some dependencies with no license, please fill the file " 260 + getMissingFile()); 261 } 262 263 if (!unsafe && isUseMissingFile() && MapUtils.isEmpty(getUnsafeMappings()) && getMissingFile().exists()) { 264 265 // there is no missing dependencies, but still a missing file, delete it 266 getLog().debug("There is no dependency to put in missing file, delete it at " + getMissingFile()); 267 FileUtil.deleteFile(getMissingFile()); 268 } 269 270 if (!unsafe && isDeployMissingFile() && MapUtils.isNotEmpty(getUnsafeMappings())) { 271 272 // can deploy missing file 273 File file = getMissingFile(); 274 275 getLog().debug("Will deploy third party file from " + file); 276 getThridPartyTool().attachThirdPartyDescriptor(getProject(), file); 277 } 278 279 addResourceDir(getOutputDirectory(), "**/*.txt"); 280 } 281 282 protected void writeMissingFile() throws IOException { 283 284 Log log = getLog(); 285 LicenseMap licenseMap = getLicenseMap(); 286 File file = getMissingFile(); 287 288 FileUtil.createDirectoryIfNecessary(file.getParentFile()); 289 log.info("Regenerate missing license file " + file); 290 291 FileOutputStream writer = new FileOutputStream(file); 292 try { 293 StringBuilder sb = new StringBuilder(" Generated by " + getClass().getName()); 294 List<String> licenses = new ArrayList<String>(licenseMap.keySet()); 295 licenses.remove(LicenseMap.getUnknownLicenseMessage()); 296 if (!licenses.isEmpty()) { 297 sb.append("\n-------------------------------------------------------------------------------"); 298 sb.append("\n Already used licenses in project :"); 299 for (String license : licenses) { 300 sb.append("\n - ").append(license); 301 } 302 } 303 sb.append("\n-------------------------------------------------------------------------------"); 304 sb.append("\n Please fill the missing licenses for dependencies :\n\n"); 305 getUnsafeMappings().store(writer, sb.toString()); 306 } finally { 307 writer.close(); 308 } 309 } 310 311 public boolean isDoGenerateMissing() { 312 return doGenerateMissing; 313 } 314 315 public void setDoGenerateMissing(boolean doGenerateMissing) { 316 this.doGenerateMissing = doGenerateMissing; 317 } 318 319 public ArtifactRepository getLocalRepository() { 320 return localRepository; 321 } 322 323 public List getRemoteRepositories() { 324 return remoteRepositories; 325 } 326 327 /** 328 * {@inheritDoc} 329 */ 330 @Override 331 public boolean isIncludeTransitiveDependencies() { 332 return includeTransitiveDependencies; 333 } 334 335 // /** 336 // * {@inheritDoc} 337 // */ 338 // public List<String> getExcludedScopes() 339 // { 340 // return Arrays.asList( Artifact.SCOPE_SYSTEM ); 341 // } 342 343 public boolean isDeployMissingFile() { 344 return deployMissingFile; 345 } 346 347 public boolean isUseRepositoryMissingFiles() { 348 return useRepositoryMissingFiles; 349 } 350 351 }