View Javadoc

1   /*
2    * #%L
3    * License Maven Plugin
4    *
5    * $Id: AddThirdPartyMojo.java 14420 2011-08-11 10:00:24Z tchemit $
6    * $HeadURL: http://svn.codehaus.org/mojo/tags/license-maven-plugin-1.0/src/main/java/org/codehaus/mojo/license/AddThirdPartyMojo.java $
7    * %%
8    * Copyright (C) 2008 - 2011 CodeLutin, Codehaus, Tony Chemit
9    * %%
10   * This program is free software: you can redistribute it and/or modify
11   * it under the terms of the GNU Lesser General Public License as
12   * published by the Free Software Foundation, either version 3 of the
13   * License, or (at your option) any later version.
14   *
15   * This program is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   * GNU General Lesser Public License for more details.
19   *
20   * You should have received a copy of the GNU General Lesser Public
21   * License along with this program.  If not, see
22   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
23   * #L%
24   */
25  
26  package org.codehaus.mojo.license;
27  
28  import java.io.File;
29  import java.io.FileOutputStream;
30  import java.io.IOException;
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.SortedMap;
36  import java.util.SortedSet;
37  
38  import org.apache.commons.collections.CollectionUtils;
39  import org.apache.commons.collections.MapUtils;
40  import org.apache.maven.artifact.repository.ArtifactRepository;
41  import org.apache.maven.plugin.MojoFailureException;
42  import org.apache.maven.plugin.logging.Log;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.maven.project.ProjectBuildingException;
45  import org.codehaus.mojo.license.model.LicenseMap;
46  
47  /**
48   * Goal to generate the third-party license file.
49   * <p/>
50   * This file contains a list of the dependencies and their licenses. Each dependency and it's license is displayed on a
51   * single line in the format <br/>
52   *
53   * <pre>
54   *   (&lt;license-name&gt;) &lt;project-name&gt; &lt;groupId&gt;:&lt;artifactId&gt;:&lt;version&gt; - &lt;project-url&gt;
55   * </pre>
56   *
57   * It will also copy it in the class-path (says add the generated directory as a resource of the build).
58   *
59   * @author tchemit <chemit@codelutin.com>
60   * @goal add-third-party
61   * @phase generate-resources
62   * @requiresDependencyResolution test
63   * @requiresProject true
64   * @since 1.0
65   */
66  public class AddThirdPartyMojo extends AbstractAddThirdPartyMojo implements MavenProjectDependenciesConfigurator {
67  
68      /**
69       * Local Repository.
70       *
71       * @parameter expression="${localRepository}"
72       * @required
73       * @readonly
74       * @since 1.0.0
75       */
76      protected ArtifactRepository localRepository;
77  
78      /**
79       * Remote repositories used for the project.
80       *
81       * @parameter expression="${project.remoteArtifactRepositories}"
82       * @required
83       * @readonly
84       * @since 1.0.0
85       */
86      protected List remoteRepositories;
87  
88      /**
89       * Deploy the third party missing file in maven repository.
90       *
91       * @parameter expression="${license.deployMissingFile}" default-value="true"
92       * @since 1.0
93       */
94      protected boolean deployMissingFile;
95  
96      /**
97       * Load from repositories third party missing files.
98       *
99       * @parameter expression="${license.useRepositoryMissingFiles}" default-value="true"
100      * @since 1.0
101      */
102     protected boolean useRepositoryMissingFiles;
103 
104     /**
105      * dependencies tool.
106      *
107      * @component
108      * @readonly
109      * @since 1.0
110      */
111     private DependenciesTool dependenciesTool;
112 
113     /**
114      * Controls if THIRD-PARTY.properties gets created or not
115      *
116      * @parameter expression="${license.doGenerateMissing}" default-value="false"
117      */
118     private boolean doGenerateMissing;
119 
120     @Override
121     protected boolean checkPackaging() {
122         return rejectPackaging("pom");
123     }
124 
125     @Override
126     protected SortedMap<String, MavenProject> loadDependencies() {
127         return dependenciesTool.loadProjectDependencies(getProject(), this, localRepository, remoteRepositories,
128                 getArtifactCache());
129     }
130 
131     @Override
132     protected SortedProperties createUnsafeMapping() throws ProjectBuildingException, IOException,
133             ThirdPartyToolException {
134 
135         SortedProperties unsafeMappings = getThridPartyTool().loadUnsafeMapping(getLicenseMap(), getArtifactCache(),
136                 getEncoding(), getMissingFile());
137 
138         SortedSet<MavenProject> unsafeDependencies = getUnsafeDependencies();
139 
140         if (CollectionUtils.isNotEmpty(unsafeDependencies)) {
141 
142             // there is some unresolved license
143 
144             if (isUseRepositoryMissingFiles()) {
145 
146                 // try to load missing third party files from dependencies
147 
148                 Collection<MavenProject> projects = new ArrayList<MavenProject>(getProjectDependencies().values());
149                 projects.remove(getProject());
150                 projects.removeAll(unsafeDependencies);
151 
152                 SortedProperties resolvedUnsafeMapping = getThridPartyTool().loadThirdPartyDescriptorsForUnsafeMapping(
153                         getEncoding(), projects, unsafeDependencies, getLicenseMap(), localRepository,
154                         remoteRepositories);
155 
156                 // push back resolved unsafe mappings
157                 unsafeMappings.putAll(resolvedUnsafeMapping);
158 
159             }
160         }
161         if (isVerbose()) {
162             getLog().info("found " + unsafeMappings.size() + " unsafe mappings");
163         }
164 
165         // compute if missing file should be (re)-generate
166         boolean generateMissingfile = doGenerateMissing
167                 && computeDoGenerateMissingFile(unsafeMappings, unsafeDependencies);
168 
169         setDoGenerateMissing(generateMissingfile);
170 
171         if (generateMissingfile && isVerbose()) {
172             StringBuilder sb = new StringBuilder();
173             sb.append("Will use from missing file ");
174             sb.append(unsafeMappings.size());
175             sb.append(" dependencies :");
176             for (Map.Entry<Object, Object> entry : unsafeMappings.entrySet()) {
177                 String id = (String) entry.getKey();
178                 String license = (String) entry.getValue();
179                 sb.append("\n - ").append(id).append(" - ").append(license);
180             }
181             getLog().info(sb.toString());
182         } else {
183             if (isUseMissingFile() && !unsafeMappings.isEmpty()) {
184                 getLog().debug("Missing file " + getMissingFile() + " is up-to-date.");
185             }
186         }
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 }