/amps-maven-plugin/src/main/java/com/atlassian/maven/plugins/amps/wadl/RestDocsGenerator.java
Java | 262 lines | 203 code | 25 blank | 34 comment | 9 complexity | 341f64866df6b08cb7e6abeb74eaef5c MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
- package com.atlassian.maven.plugins.amps.wadl;
- import com.atlassian.maven.plugins.amps.MavenContext;
- import com.atlassian.maven.plugins.amps.util.MojoExecutorWrapper;
- import com.atlassian.maven.plugins.amps.util.PluginXmlUtils;
- import com.atlassian.maven.plugins.amps.util.PluginXmlUtils.RESTModuleInfo;
- import com.google.common.annotations.VisibleForTesting;
- import org.apache.maven.artifact.DependencyResolutionRequiredException;
- import org.apache.maven.model.Plugin;
- import org.apache.maven.plugin.MojoExecutionException;
- import org.apache.maven.plugin.logging.Log;
- import org.apache.maven.project.MavenProject;
- import org.twdata.maven.mojoexecutor.MojoExecutor.Element;
- import javax.annotation.Nullable;
- import javax.annotation.ParametersAreNonnullByDefault;
- import java.io.File;
- import java.io.IOException;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Set;
- import java.util.function.Function;
- import static com.atlassian.maven.plugins.amps.util.FileUtils.fixWindowsSlashes;
- import static java.lang.String.format;
- import static java.lang.String.join;
- import static java.lang.Thread.currentThread;
- import static java.nio.charset.StandardCharsets.UTF_8;
- import static java.util.Arrays.stream;
- import static java.util.Objects.requireNonNull;
- import static java.util.function.Function.identity;
- import static java.util.regex.Matcher.quoteReplacement;
- import static java.util.stream.Collectors.joining;
- import static java.util.stream.Collectors.toList;
- import static org.apache.commons.io.FileUtils.writeStringToFile;
- import static org.apache.commons.io.IOUtils.resourceToString;
- import static org.apache.commons.lang3.StringUtils.replace;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.element;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.name;
- /**
- * Generates WADL documentation for P2 plugin REST modules.
- *
- * @since 8.1.2 was previously in the {@code MavenGoals} class
- */
- @ParametersAreNonnullByDefault
- public class RestDocsGenerator {
- private final MavenContext mavenContext;
- private final MojoExecutorWrapper mojoExecutorWrapper;
- /**
- * Constructor.
- *
- * @param mavenContext the Maven context
- * @param mojoExecutorWrapper executes Mojos
- */
- public RestDocsGenerator(final MavenContext mavenContext, final MojoExecutorWrapper mojoExecutorWrapper) {
- this.mavenContext = requireNonNull(mavenContext);
- this.mojoExecutorWrapper = requireNonNull(mojoExecutorWrapper);
- }
- private Log log() {
- return mavenContext.getLog();
- }
- private MavenProject mavenProject() {
- return mavenContext.getProject();
- }
- /**
- * Generates <a href="https://en.wikipedia.org/wiki/Web_Application_Description_Language">WADL</a> documentation for
- * the REST modules in the current plugin project.
- * <p>
- * Works by invoking the Maven JavaDoc plugin, using the custom doclet
- * {@code com.sun.jersey.wadl.resourcedoc.ResourceDocletJSON}, the source for which can be found in
- * <a href="https://bitbucket.org/atlassian/atlassian-jersey-restdoc/>atlassian/atlassian-jersey-restdoc</a>
- *
- * @param jacksonModules any custom Jackson serializer modules to be used; a colon-separated list of their fully
- * qualified class names, each implementing {@code org.codehaus.jackson.map.Module}; see REST-267
- * @throws MojoExecutionException if the operation fails
- */
- public void generateRestDocs(@Nullable final String jacksonModules) throws MojoExecutionException {
- final List<RESTModuleInfo> restModules = PluginXmlUtils.getRestModules(mavenContext);
- if (restModules.isEmpty()) {
- log().info("No REST modules found, skipping WADL doc generation");
- return;
- }
- withoutGlobalJavadocPlugin(() -> invokeJavadocPluginWithCustomDoclet(restModules, jacksonModules));
- generateApplicationXmlFiles();
- }
- private void invokeJavadocPluginWithCustomDoclet(
- final List<RESTModuleInfo> restModules, @Nullable final String jacksonModules) {
- try {
- mojoExecutorWrapper.executeWithMergedConfig(
- mavenContext.getPlugin("org.apache.maven.plugins", "maven-javadoc-plugin"),
- goal("javadoc"),
- configuration(
- element(name("maxmemory"), "1024m"),
- element(name("sourcepath"), getPackagesPath(restModules)),
- element(name("doclet"), "com.atlassian.jersey.wadl.doclet.ResourceDocletJSON"),
- element(name("docletPath"), join(File.pathSeparator, getDocletPathElements())),
- element(name("docletArtifacts"),
- element(name("docletArtifact"),
- element(name("groupId"), "com.atlassian.plugins.rest"),
- element(name("artifactId"), "atlassian-rest-doclet"),
- element(name("version"), "6.1.9")
- ),
- element(name("docletArtifact"),
- // Contains the class named in the "doclet" element above
- element(name("groupId"), "com.atlassian.jersey"),
- element(name("artifactId"), "atlassian-jersey-restdoc"),
- element(name("version"), "2.0.0")
- ),
- element(name("docletArtifact"),
- // The Xerces version used by the above doclet
- element(name("groupId"), "xerces"),
- element(name("artifactId"), "xercesImpl"),
- element(name("version"), "2.12.1")
- ),
- element(name("docletArtifact"),
- element(name("groupId"), "commons-lang"),
- element(name("artifactId"), "commons-lang"),
- element(name("version"), "2.6")
- ),
- element(name("docletArtifact"),
- element(name("groupId"), "com.sun.istack"),
- element(name("artifactId"), "istack-commons-runtime"),
- element(name("version"), "3.0.11")
- )
- ),
- element(name("outputDirectory")),
- element(name("additionalOptions"), getAdditionalOptions(jacksonModules)),
- element(name("useStandardDocletOptions"), "false")
- ),
- mavenContext.getExecutionEnvironment()
- );
- } catch (MojoExecutionException e) {
- log().warn("Could not generate REST documentation. Please verify that Atlassian REST is a " +
- "'provided' scope dependency of the plugin.", e);
- }
- }
- private Element[] getAdditionalOptions(@Nullable final String jacksonModules) {
- final String resourcedocPath = fixWindowsSlashes(
- mavenProject().getBuild().getOutputDirectory() + File.separator + "resourcedoc.xml");
- final List<Element> additionalOptions = new ArrayList<>();
- additionalOptions.add(element(name("additionalOption"), "-output \"" + resourcedocPath + "\""));
- if (jacksonModules != null) {
- additionalOptions.add(element(name("additionalOption"), " -modules \"" + jacksonModules + "\""));
- }
- return additionalOptions.toArray(new Element[0]);
- }
- private Set<String> getDocletPathElements() throws MojoExecutionException {
- try {
- final Set<String> docletPathElements = new LinkedHashSet<>();
- docletPathElements.add(mavenProject().getBuild().getOutputDirectory());
- docletPathElements.addAll(mavenProject().getCompileClasspathElements());
- docletPathElements.addAll(mavenProject().getRuntimeClasspathElements());
- //noinspection deprecation -- no suggested replacement
- docletPathElements.addAll(mavenProject().getSystemClasspathElements());
- // AMPS-663: add plugin execution classes to doclet path
- docletPathElements.addAll(getPluginClasspathElements());
- return docletPathElements;
- } catch (final DependencyResolutionRequiredException e) {
- throw new MojoExecutionException("Dependencies must be resolved", e);
- }
- }
- @VisibleForTesting
- static List<String> getPluginClasspathElements() {
- final URL[] pluginUrls = ((URLClassLoader) currentThread().getContextClassLoader()).getURLs();
- return stream(pluginUrls)
- .map(URL::getFile)
- .map(File::new)
- .map(File::getPath)
- .collect(toList());
- }
- private String getPackagesPath(final Collection<RESTModuleInfo> restModules) {
- if (anyRestModuleHasNoExplicitPackages(restModules)) {
- final String sourceDirectory = mavenProject().getBuild().getSourceDirectory();
- log().info(format("Scanning all of %s for REST resources", sourceDirectory)); // fixes AMPS-1152
- return sourceDirectory;
- }
- return restModules.stream()
- .flatMap(module -> module.getPackagesToScan().stream())
- .map(this::getAbsoluteDirectory)
- .distinct()
- .collect(joining(File.pathSeparator));
- }
- private boolean anyRestModuleHasNoExplicitPackages(final Collection<RESTModuleInfo> restModules) {
- return restModules.stream()
- .map(RESTModuleInfo::getPackagesToScan)
- .anyMatch(List::isEmpty);
- }
- private String getAbsoluteDirectory(final String packageName) {
- final String relativePackageDirectory = packageName.replaceAll("\\.", quoteReplacement(File.separator));
- return mavenProject().getBuild().getSourceDirectory() + File.separator + relativePackageDirectory;
- }
- private void generateApplicationXmlFiles() throws MojoExecutionException {
- try {
- // application-doc.xml
- final PluginXmlUtils.PluginInfo pluginInfo = PluginXmlUtils.getPluginInfo(mavenContext);
- generateApplicationXmlFile("application-doc.xml", template -> {
- final String intermediate = replace(template, "${rest.doc.title}", pluginInfo.getName());
- return replace(intermediate, "${rest.doc.description}", pluginInfo.getDescription());
- });
- // application-grammars.xml
- generateApplicationXmlFile("application-grammars.xml", identity());
- } catch (Exception e) {
- throw new MojoExecutionException("Error writing REST application XML files", e);
- }
- }
- private void generateApplicationXmlFile(final String filename, final Function<String, String> fileContentTransformer)
- throws IOException {
- final File outputFile = new File(mavenProject().getBuild().getOutputDirectory(), filename);
- if (!outputFile.exists()) {
- final String defaultFileContent = resourceToString(filename, UTF_8, getClass().getClassLoader());
- final String transformedFileContent = fileContentTransformer.apply(defaultFileContent);
- writeStringToFile(outputFile, transformedFileContent, UTF_8);
- log().info("Wrote " + outputFile.getAbsolutePath());
- }
- }
- private interface CustomJavadocAction {
- void invoke() throws MojoExecutionException;
- }
- /**
- * AMPSDEV-127: 'generate-rest-docs' fails with JDK8 - invalid flag: -Xdoclint:all
- * Root cause: ResourceDocletJSON doclet does not support option doclint
- * Solution: Temporarily remove global javadoc configuration (remove doclint)
- */
- private void withoutGlobalJavadocPlugin(final CustomJavadocAction javadocAction) throws MojoExecutionException {
- final Plugin globalJavadoc = mavenProject().getPlugin("org.apache.maven.plugins:maven-javadoc-plugin");
- if (globalJavadoc != null) {
- mavenProject().getBuild().removePlugin(globalJavadoc);
- }
- javadocAction.invoke();
- if (globalJavadoc != null) {
- mavenProject().getBuild().addPlugin(globalJavadoc);
- }
- }
- }