View Javadoc

1   /**
2    * Copyright 2010-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.codehaus.mojo.license;
17  
18  import java.io.File;
19  import java.io.FileOutputStream;
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.SortedMap;
26  import java.util.SortedSet;
27  
28  import org.apache.commons.collections.CollectionUtils;
29  import org.apache.commons.collections.MapUtils;
30  import org.apache.maven.artifact.repository.ArtifactRepository;
31  import org.apache.maven.plugin.MojoFailureException;
32  import org.apache.maven.plugin.logging.Log;
33  import org.apache.maven.project.MavenProject;
34  import org.apache.maven.project.ProjectBuildingException;
35  import org.codehaus.mojo.license.model.LicenseMap;
36  
37  /**
38   * Goal to generate the third-party license file.
39   * <p/>
40   * This file contains a list of the dependencies and their licenses. Each dependency and it's license is displayed on a
41   * single line in the format <br/>
42   *
43   * <pre>
44   *   (&lt;license-name&gt;) &lt;project-name&gt; &lt;groupId&gt;:&lt;artifactId&gt;:&lt;version&gt; - &lt;project-url&gt;
45   * </pre>
46   *
47   * It will also copy it in the class-path (says add the generated directory as a resource of the build).
48   *
49   * @author tchemit <chemit@codelutin.com>
50   * @goal add-third-party
51   * @phase generate-resources
52   * @requiresDependencyResolution test
53   * @requiresProject true
54   * @since 1.0
55   */
56  public class AddThirdPartyMojo extends AbstractAddThirdPartyMojo implements MavenProjectDependenciesConfigurator {
57  
58      /**
59       * Local Repository.
60       *
61       * @parameter expression="${localRepository}"
62       * @required
63       * @readonly
64       * @since 1.0.0
65       */
66      protected ArtifactRepository localRepository;
67  
68      /**
69       * Remote repositories used for the project.
70       *
71       * @parameter expression="${project.remoteArtifactRepositories}"
72       * @required
73       * @readonly
74       * @since 1.0.0
75       */
76      protected List remoteRepositories;
77  
78      /**
79       * Deploy the third party missing file in maven repository.
80       *
81       * @parameter expression="${license.deployMissingFile}" default-value="true"
82       * @since 1.0
83       */
84      protected boolean deployMissingFile;
85  
86      /**
87       * Load from repositories third party missing files.
88       *
89       * @parameter expression="${license.useRepositoryMissingFiles}" default-value="true"
90       * @since 1.0
91       */
92      protected boolean useRepositoryMissingFiles;
93  
94      /**
95       * dependencies tool.
96       *
97       * @component
98       * @readonly
99       * @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 }