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         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 = getThirdPartyTool().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             getThirdPartyTool().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 }