001 /*
002 * #%L
003 * License Maven Plugin
004 *
005 * $Id: AddThirdPartyMojo.java 14420 2011-08-11 10:00:24Z tchemit $
006 * $HeadURL: http://svn.codehaus.org/mojo/tags/license-maven-plugin-1.0/src/main/java/org/codehaus/mojo/license/AddThirdPartyMojo.java $
007 * %%
008 * Copyright (C) 2008 - 2011 CodeLutin, Codehaus, Tony Chemit
009 * %%
010 * This program is free software: you can redistribute it and/or modify
011 * it under the terms of the GNU Lesser General Public License as
012 * published by the Free Software Foundation, either version 3 of the
013 * License, or (at your option) any later version.
014 *
015 * This program is distributed in the hope that it will be useful,
016 * but WITHOUT ANY WARRANTY; without even the implied warranty of
017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
018 * GNU General Lesser Public License for more details.
019 *
020 * You should have received a copy of the GNU General Lesser Public
021 * License along with this program. If not, see
022 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
023 * #L%
024 */
025
026 package org.codehaus.mojo.license;
027
028 import java.io.File;
029 import java.io.FileOutputStream;
030 import java.io.IOException;
031 import java.util.ArrayList;
032 import java.util.Collection;
033 import java.util.List;
034 import java.util.Map;
035 import java.util.SortedMap;
036 import java.util.SortedSet;
037
038 import org.apache.commons.collections.CollectionUtils;
039 import org.apache.commons.collections.MapUtils;
040 import org.apache.maven.artifact.repository.ArtifactRepository;
041 import org.apache.maven.plugin.MojoFailureException;
042 import org.apache.maven.plugin.logging.Log;
043 import org.apache.maven.project.MavenProject;
044 import org.apache.maven.project.ProjectBuildingException;
045 import org.codehaus.mojo.license.model.LicenseMap;
046
047 /**
048 * Goal to generate the third-party license file.
049 * <p/>
050 * This file contains a list of the dependencies and their licenses. Each dependency and it's license is displayed on a
051 * single line in the format <br/>
052 *
053 * <pre>
054 * (<license-name>) <project-name> <groupId>:<artifactId>:<version> - <project-url>
055 * </pre>
056 *
057 * It will also copy it in the class-path (says add the generated directory as a resource of the build).
058 *
059 * @author tchemit <chemit@codelutin.com>
060 * @goal add-third-party
061 * @phase generate-resources
062 * @requiresDependencyResolution test
063 * @requiresProject true
064 * @since 1.0
065 */
066 public class AddThirdPartyMojo extends AbstractAddThirdPartyMojo implements MavenProjectDependenciesConfigurator {
067
068 /**
069 * Local Repository.
070 *
071 * @parameter expression="${localRepository}"
072 * @required
073 * @readonly
074 * @since 1.0.0
075 */
076 protected ArtifactRepository localRepository;
077
078 /**
079 * Remote repositories used for the project.
080 *
081 * @parameter expression="${project.remoteArtifactRepositories}"
082 * @required
083 * @readonly
084 * @since 1.0.0
085 */
086 protected List remoteRepositories;
087
088 /**
089 * Deploy the third party missing file in maven repository.
090 *
091 * @parameter expression="${license.deployMissingFile}" default-value="true"
092 * @since 1.0
093 */
094 protected boolean deployMissingFile;
095
096 /**
097 * Load from repositories third party missing files.
098 *
099 * @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 }