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