View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.javadoc;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.util.List;
26  
27  import org.apache.maven.archiver.MavenArchiveConfiguration;
28  import org.apache.maven.archiver.MavenArchiver;
29  import org.apache.maven.artifact.DependencyResolutionRequiredException;
30  import org.apache.maven.artifact.handler.ArtifactHandler;
31  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
32  import org.apache.maven.doxia.tools.SiteTool;
33  import org.apache.maven.model.Resource;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugins.annotations.LifecyclePhase;
36  import org.apache.maven.plugins.annotations.Mojo;
37  import org.apache.maven.plugins.annotations.Parameter;
38  import org.apache.maven.plugins.annotations.ResolutionScope;
39  import org.apache.maven.plugins.javadoc.resolver.ResourceResolver;
40  import org.apache.maven.project.MavenProjectHelper;
41  import org.apache.maven.project.ProjectBuilder;
42  import org.apache.maven.reporting.MavenReportException;
43  import org.apache.maven.toolchain.ToolchainManager;
44  import org.codehaus.plexus.archiver.ArchiverException;
45  import org.codehaus.plexus.archiver.jar.JarArchiver;
46  import org.codehaus.plexus.archiver.jar.ManifestException;
47  import org.codehaus.plexus.archiver.manager.ArchiverManager;
48  import org.eclipse.aether.RepositorySystem;
49  
50  /**
51   * Bundles the Javadoc documentation for <code>main Java code</code> in an <b>NON aggregator</b> project into
52   * a jar using the standard <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/man/javadoc.html">
53   * Javadoc Tool</a>.
54   *
55   * @since 2.0
56   */
57  @Mojo(
58          name = "jar",
59          defaultPhase = LifecyclePhase.PACKAGE,
60          requiresDependencyResolution = ResolutionScope.COMPILE,
61          threadSafe = true)
62  public class JavadocJarMojo extends AbstractJavadocMojo {
63      /**
64       * Includes all generated Javadoc files
65       */
66      private static final String[] DEFAULT_INCLUDES = new String[] {"**/**"};
67  
68      /**
69       * Excludes all processing files.
70       *
71       * @see AbstractJavadocMojo#DEBUG_JAVADOC_SCRIPT_NAME
72       * @see AbstractJavadocMojo#OPTIONS_FILE_NAME
73       * @see AbstractJavadocMojo#PACKAGES_FILE_NAME
74       * @see AbstractJavadocMojo#ARGFILE_FILE_NAME
75       * @see AbstractJavadocMojo#FILES_FILE_NAME
76       */
77      private static final String[] DEFAULT_EXCLUDES = new String[] {
78          DEBUG_JAVADOC_SCRIPT_NAME, OPTIONS_FILE_NAME, PACKAGES_FILE_NAME, ARGFILE_FILE_NAME, FILES_FILE_NAME
79      };
80  
81      // ----------------------------------------------------------------------
82      // Mojo components
83      // ----------------------------------------------------------------------
84  
85      /**
86       * Used for attaching the artifact in the project.
87       */
88      private MavenProjectHelper projectHelper;
89  
90      /**
91       * The Jar archiver.
92       *
93       * @since 2.5
94       */
95      private JarArchiver jarArchiver;
96  
97      // CHECKSTYLE_OFF: ParameterNumber
98      @Inject
99      public JavadocJarMojo(
100             MavenProjectHelper projectHelper,
101             JarArchiver jarArchiver,
102             SiteTool siteTool,
103             ArchiverManager archiverManager,
104             ResourceResolver resourceResolver,
105             RepositorySystem repoSystem,
106             ArtifactHandlerManager artifactHandlerManager,
107             ProjectBuilder mavenProjectBuilder,
108             ToolchainManager toolchainManager) {
109         super(
110                 siteTool,
111                 archiverManager,
112                 resourceResolver,
113                 repoSystem,
114                 artifactHandlerManager,
115                 mavenProjectBuilder,
116                 toolchainManager);
117         this.projectHelper = projectHelper;
118         this.jarArchiver = jarArchiver;
119     }
120     // CHECKSTYLE_ON: ParameterNumber
121 
122     // ----------------------------------------------------------------------
123     // Mojo Parameters
124     // ----------------------------------------------------------------------
125 
126     /**
127      * Specifies the directory where the generated jar file will be put.
128      */
129     @Parameter(property = "project.build.directory")
130     private String jarOutputDirectory;
131 
132     /**
133      * Specifies the filename that will be used for the generated jar file. Please note that <code>-javadoc</code>
134      * or <code>-test-javadoc</code> will be appended to the file name.
135      */
136     @Parameter(property = "project.build.finalName")
137     private String finalName;
138 
139     /**
140      * Specifies whether to attach the generated artifact to the project helper.
141      * <br/>
142      */
143     @Parameter(property = "attach", defaultValue = "true")
144     private boolean attach;
145 
146     /**
147      * The archive configuration to use.
148      * See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven Archiver Reference</a>.
149      *
150      * @since 2.5
151      */
152     @Parameter
153     private MavenArchiveConfiguration archive = new JavadocArchiveConfiguration();
154 
155     /**
156      * Path to the default MANIFEST file to use. It will be used if
157      * <code>useDefaultManifestFile</code> is set to <code>true</code>.
158      *
159      * @since 2.5
160      */
161     @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/MANIFEST.MF", required = true, readonly = true)
162     private File defaultManifestFile;
163 
164     /**
165      * Set this to <code>true</code> to enable the use of the <code>defaultManifestFile</code>.
166      * <br/>
167      *
168      * @since 2.5
169      */
170     @Parameter(defaultValue = "false")
171     private boolean useDefaultManifestFile;
172 
173     /**
174      * @since 2.10
175      */
176     @Parameter(property = "maven.javadoc.classifier", defaultValue = "javadoc", required = true)
177     private String classifier;
178 
179     /** {@inheritDoc} */
180     @Override
181     protected void doExecute() throws MojoExecutionException {
182         if (skip) {
183             getLog().info("Skipping javadoc generation");
184             return;
185         }
186 
187         if (!isAggregator() || !"pom".equalsIgnoreCase(project.getPackaging())) {
188             ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler();
189             if (!"java".equals(artifactHandler.getLanguage())) {
190                 getLog().info("Not executing Javadoc as the project is not a Java classpath-capable package");
191                 return;
192             }
193         }
194 
195         try {
196             executeReport(SiteTool.DEFAULT_LOCALE);
197         } catch (MavenReportException e) {
198             failOnError("MavenReportException: Error while generating Javadoc", e);
199         } catch (RuntimeException e) {
200             failOnError("RuntimeException: Error while generating Javadoc", e);
201         }
202 
203         File javadocOutputDirectory = new File(getPluginReportOutputDirectory());
204         if (javadocOutputDirectory.exists()) {
205             try {
206                 File outputFile = generateArchive(javadocOutputDirectory, finalName + "-" + getClassifier() + ".jar");
207 
208                 if (!attach) {
209                     getLog().info("NOT adding javadoc to attached artifacts list.");
210                 } else {
211                     // TODO: these introduced dependencies on the project are going to become problematic - can we
212                     // export it
213                     //  through metadata instead?
214                     projectHelper.attachArtifact(project, "javadoc", getClassifier(), outputFile);
215                 }
216             } catch (ArchiverException e) {
217                 failOnError("ArchiverException: Error while creating archive", e);
218             } catch (IOException e) {
219                 failOnError("IOException: Error while creating archive", e);
220             } catch (RuntimeException e) {
221                 failOnError("RuntimeException: Error while creating archive", e);
222             }
223         } else {
224             getLog().info("No Javadoc in project. Archive not created.");
225         }
226     }
227 
228     // ----------------------------------------------------------------------
229     // Protected methods
230     // ----------------------------------------------------------------------
231 
232     /**
233      * @return the wanted classifier, i.e. <code>javadoc</code> or <code>test-javadoc</code>
234      */
235     protected String getClassifier() {
236         return classifier;
237     }
238 
239     // ----------------------------------------------------------------------
240     // private methods
241     // ----------------------------------------------------------------------
242 
243     /**
244      * Method that creates the jar file
245      *
246      * @param javadocFiles the directory where the generated jar file will be put
247      * @param jarFileName the filename of the generated jar file
248      * @return a File object that contains the generated jar file
249      * @throws ArchiverException {@link ArchiverException}
250      * @throws IOException {@link IOException}
251      */
252     private File generateArchive(File javadocFiles, String jarFileName) throws ArchiverException, IOException {
253         MavenArchiver archiver = new MavenArchiver();
254         archiver.setArchiver(jarArchiver);
255         archiver.setCreatedBy("Maven Javadoc Plugin", "org.apache.maven.plugins", "maven-javadoc-plugin");
256 
257         // configure for Reproducible Builds based on outputTimestamp value
258         archiver.configureReproducibleBuild(outputTimestamp);
259 
260         if (javadocFiles.exists()) {
261             archiver.getArchiver().addDirectory(javadocFiles, DEFAULT_INCLUDES, DEFAULT_EXCLUDES);
262         }
263 
264         List<Resource> resources = project.getBuild().getResources();
265 
266         for (Resource r : resources) {
267             if (r.getDirectory().endsWith("maven-shared-archive-resources")) {
268                 archiver.getArchiver().addDirectory(new File(r.getDirectory()));
269             }
270         }
271 
272         if (useDefaultManifestFile && defaultManifestFile.exists() && archive.getManifestFile() == null) {
273             getLog().info("Adding existing MANIFEST to archive. Found under: " + defaultManifestFile.getPath());
274             archive.setManifestFile(defaultManifestFile);
275         }
276 
277         File outputFile = new File(jarOutputDirectory, jarFileName);
278 
279         // Why do we do this?
280         if (outputFile.exists()) {
281             outputFile.delete();
282         }
283         archiver.setOutputFile(outputFile);
284 
285         try {
286             archiver.createArchive(session, project, archive);
287         } catch (ManifestException | DependencyResolutionRequiredException e) {
288             throw new ArchiverException("Error creating Javadoc archive: " + e.getMessage(), e);
289         }
290 
291         return outputFile;
292     }
293 }