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  import java.util.Locale;
25  
26  import org.apache.maven.archiver.MavenArchiveConfiguration;
27  import org.apache.maven.archiver.MavenArchiver;
28  import org.apache.maven.artifact.DependencyResolutionRequiredException;
29  import org.apache.maven.artifact.handler.ArtifactHandler;
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 JavadocJar 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 destination directory where javadoc saves the generated HTML files.
99       * @see <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/man/javadoc.html#standard-doclet-options">Doclet option d</a>.
100      * @deprecated
101      */
102     @Deprecated
103     @Parameter(property = "destDir")
104     private File destDir;
105 
106     /**
107      * Specifies the directory where the generated jar file will be put.
108      */
109     @Parameter(property = "project.build.directory")
110     private String jarOutputDirectory;
111 
112     /**
113      * Specifies the filename that will be used for the generated jar file. Please note that <code>-javadoc</code>
114      * or <code>-test-javadoc</code> will be appended to the file name.
115      */
116     @Parameter(property = "project.build.finalName")
117     private String finalName;
118 
119     /**
120      * Specifies whether to attach the generated artifact to the project helper.
121      * <br/>
122      */
123     @Parameter(property = "attach", defaultValue = "true")
124     private boolean attach;
125 
126     /**
127      * The archive configuration to use.
128      * See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven Archiver Reference</a>.
129      *
130      * @since 2.5
131      */
132     @Parameter
133     private MavenArchiveConfiguration archive = new JavadocArchiveConfiguration();
134 
135     /**
136      * Path to the default MANIFEST file to use. It will be used if
137      * <code>useDefaultManifestFile</code> is set to <code>true</code>.
138      *
139      * @since 2.5
140      */
141     @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/MANIFEST.MF", required = true, readonly = true)
142     private File defaultManifestFile;
143 
144     /**
145      * Set this to <code>true</code> to enable the use of the <code>defaultManifestFile</code>.
146      * <br/>
147      *
148      * @since 2.5
149      */
150     @Parameter(defaultValue = "false")
151     private boolean useDefaultManifestFile;
152 
153     /**
154      * @since 2.10
155      */
156     @Parameter(property = "maven.javadoc.classifier", defaultValue = "javadoc", required = true)
157     private String classifier;
158 
159     /** {@inheritDoc} */
160     @Override
161     public void doExecute() throws MojoExecutionException {
162         if (skip) {
163             getLog().info("Skipping javadoc generation");
164             return;
165         }
166 
167         File innerDestDir = this.destDir;
168         if (innerDestDir == null) {
169             innerDestDir = new File(getOutputDirectory());
170         }
171 
172         if (!isAggregator() || !"pom".equalsIgnoreCase(project.getPackaging())) {
173             ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler();
174             if (!"java".equals(artifactHandler.getLanguage())) {
175                 getLog().info("Not executing Javadoc as the project is not a Java classpath-capable package");
176                 return;
177             }
178         }
179 
180         try {
181             executeReport(Locale.getDefault());
182         } catch (MavenReportException e) {
183             failOnError("MavenReportException: Error while generating Javadoc", e);
184         } catch (RuntimeException e) {
185             failOnError("RuntimeException: Error while generating Javadoc", e);
186         }
187 
188         if (innerDestDir.exists()) {
189             try {
190                 File outputFile = generateArchive(innerDestDir, finalName + "-" + getClassifier() + ".jar");
191 
192                 if (!attach) {
193                     getLog().info("NOT adding javadoc to attached artifacts list.");
194                 } else {
195                     // TODO: these introduced dependencies on the project are going to become problematic - can we
196                     // export it
197                     //  through metadata instead?
198                     projectHelper.attachArtifact(project, "javadoc", getClassifier(), outputFile);
199                 }
200             } catch (ArchiverException e) {
201                 failOnError("ArchiverException: Error while creating archive", e);
202             } catch (IOException e) {
203                 failOnError("IOException: Error while creating archive", e);
204             } catch (RuntimeException e) {
205                 failOnError("RuntimeException: Error while creating archive", e);
206             }
207         }
208     }
209 
210     // ----------------------------------------------------------------------
211     // Protected methods
212     // ----------------------------------------------------------------------
213 
214     /**
215      * @return the wanted classifier, i.e. <code>javadoc</code> or <code>test-javadoc</code>
216      */
217     protected String getClassifier() {
218         return classifier;
219     }
220 
221     // ----------------------------------------------------------------------
222     // private methods
223     // ----------------------------------------------------------------------
224 
225     /**
226      * Method that creates the jar file
227      *
228      * @param javadocFiles the directory where the generated jar file will be put
229      * @param jarFileName the filename of the generated jar file
230      * @return a File object that contains the generated jar file
231      * @throws ArchiverException {@link ArchiverException}
232      * @throws IOException {@link IOException}
233      */
234     private File generateArchive(File javadocFiles, String jarFileName) throws ArchiverException, IOException {
235         File javadocJar = new File(jarOutputDirectory, jarFileName);
236 
237         if (javadocJar.exists()) {
238             javadocJar.delete();
239         }
240 
241         MavenArchiver archiver = new MavenArchiver();
242         archiver.setCreatedBy("Maven Javadoc Plugin", "org.apache.maven.plugins", "maven-javadoc-plugin");
243         archiver.setArchiver(jarArchiver);
244         archiver.setOutputFile(javadocJar);
245 
246         // configure for Reproducible Builds based on outputTimestamp value
247         archiver.configureReproducibleBuild(outputTimestamp);
248 
249         if (!javadocFiles.exists()) {
250             getLog().warn("JAR will be empty - no content was marked for inclusion!");
251         } else {
252             archiver.getArchiver().addDirectory(javadocFiles, DEFAULT_INCLUDES, DEFAULT_EXCLUDES);
253         }
254 
255         List<Resource> resources = project.getBuild().getResources();
256 
257         for (Resource r : resources) {
258             if (r.getDirectory().endsWith("maven-shared-archive-resources")) {
259                 archiver.getArchiver().addDirectory(new File(r.getDirectory()));
260             }
261         }
262 
263         if (useDefaultManifestFile && defaultManifestFile.exists() && archive.getManifestFile() == null) {
264             getLog().info("Adding existing MANIFEST to archive. Found under: " + defaultManifestFile.getPath());
265             archive.setManifestFile(defaultManifestFile);
266         }
267 
268         try {
269             archiver.createArchive(session, project, archive);
270         } catch (ManifestException e) {
271             throw new ArchiverException("ManifestException: " + e.getMessage(), e);
272         } catch (DependencyResolutionRequiredException e) {
273             throw new ArchiverException("DependencyResolutionRequiredException: " + e.getMessage(), e);
274         }
275 
276         return javadocJar;
277     }
278 }