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