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 
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 }