br.com.anteros.restdoc.maven.plugin.AnterosRestDocMojo.java Source code

Java tutorial

Introduction

Here is the source code for br.com.anteros.restdoc.maven.plugin.AnterosRestDocMojo.java

Source

/*******************************************************************************
 *  Copyright 2017 Anteros Tecnologia
 *   
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0
 *   
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *******************************************************************************/
package br.com.anteros.restdoc.maven.plugin;

import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.ANTEROS_JSON;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.ANTEROS_JSONC_JAVADOC;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.CLASSPATH_OPTION;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.DOCLET_OPTION;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.ITEMS_ADOC;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.PROTECTED_OPTION;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.SOURCEPATH_OPTION;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.SPRING_WEB_CONTROLLER;
import static br.com.anteros.restdoc.maven.plugin.AnterosRestConstants.TARGET;
import static br.com.anteros.restdoc.maven.plugin.util.JavadocUtil.isNotEmpty;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.plugins.javadoc.AbstractJavadocMojo;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReportException;
import org.apache.maven.shared.artifact.filter.PatternExcludesArtifactFilter;
import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
import org.asciidoctor.maven.AsciidoctorMojo;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.util.FileUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

import br.com.anteros.core.utils.ResourceUtils;
import br.com.anteros.restdoc.maven.plugin.doclet.AnterosRestDoclet;
import br.com.anteros.restdoc.maven.plugin.doclet.model.ClassDescriptor;
import br.com.anteros.restdoc.maven.plugin.util.ResourceResolver;
import br.com.anteros.restdoc.maven.plugin.util.SourceResolverConfig;

/**
 * 
 * @author Edson Martins
 * @author Eduardo Albertini
 *
 */
@Mojo(name = "generate", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.COMPILE, requiresDependencyCollection = ResolutionScope.COMPILE)
public class AnterosRestDocMojo extends AsciidoctorMojo {

    /**
     * Diretrio de sada do arquivo gerado (.html, .pdf, etc.)
     */
    @Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)
    private File outputDirectory;

    /**
     * Projeto maven
     */
    @Parameter(defaultValue = "${project}")
    private MavenProject project = null;

    /**
     * Nome do projeto
     */
    @Parameter(defaultValue = "${project.name}", required = true)
    private String projectDisplayName;

    /**
     * Diretrios com os cdigos fonte
     */
    @Parameter(defaultValue = "${project.compileSourceRoots}")
    private List<String> compileSourceRoots;

    /**
     * Diretrios com os cdigos fonte compilados
     */
    @Parameter(defaultValue = "${project.compileClasspathElements}")
    private List<String> classpathElements;

    /**
     * Nome dos pacotes para procurar os controllers
     */
    @Parameter(required = true)
    private List<String> packageScanEndpoints = new ArrayList<String>();

    /**
     * Base para gerar as URL de execuo dos exemplos
     */
    @Parameter(required = true)
    private String urlBase;

    /**
     * Verifica se deve incluir o cdigo fonte das dependncias
     */
    @Parameter(defaultValue = "false")
    private boolean includeDependencySources;

    /**
     * Diretrio para fazer cache do cdigo fonte
     */
    @Parameter(defaultValue = "${project.build.directory}/distro-javadoc-sources")
    private File sourceDependencyCacheDir;

    /**
     * Lista para incluir o cdigo fonte das dependncias. Exemplo:
     * <code>org.apache.maven:*</code>
     *
     * @see #includeDependencySources
     */
    @Parameter
    private List<String> dependencySourceIncludes;

    /**
     * Lista para excluir o cdigo fonte das dependncias. Exemplo:
     * <code>org.apache.maven.shared:*</code>
     *
     * @see #includeDependencySources
     */
    @Parameter
    private List<String> dependencySourceExcludes;

    /**
     * Repositrio local
     */
    @Parameter(property = "localRepository")
    private ArtifactRepository localRepository;

    /**
     * Repositrios remoto
     */
    @Parameter(property = "project.remoteArtifactRepositories")
    private List<ArtifactRepository> remoteRepositories;

    /**
     * Verifica se deve incluir o cdigo fonte das dependncias transitivas
     */
    @Parameter(defaultValue = "false")
    private boolean includeTransitiveDependencySources;

    /**
     * Archiver manager
     */
    @Component
    private ArchiverManager archiverManager;

    /**
     * Factory for creating artifact objects
     */
    @Component
    private ArtifactFactory factory;

    /**
     * Used to resolve artifacts of aggregated modules
     */
    @Component
    private ArtifactMetadataSource artifactMetadataSource;

    /**
     * Used for resolving artifacts
     */
    @Component
    private ArtifactResolver resolver;

    /**
     * 
     */
    @Parameter(property = "reactorProjects", readonly = true)
    private List<MavenProject> reactorProjects;

    public void execute() throws MojoExecutionException, MojoFailureException {

        /**
         * Se no informar o nome do documento .adoc principal copiamos o padro
         * para a pasta sourceDirectory.
         */
        if (sourceDocumentName == null) {
            sourceDocumentName = "index.adoc";
            try {
                InputStream openStream = ResourceUtils.getResourceAsStream("template_index.adoc");
                if (!sourceDirectory.exists())
                    sourceDirectory.mkdirs();

                FileOutputStream fos = new FileOutputStream(
                        new File(sourceDirectory + File.separator + sourceDocumentName));
                br.com.anteros.core.utils.IOUtils.copy(openStream, fos);
                fos.flush();
                fos.close();
                openStream.close();
            } catch (IOException e) {
                throw new MojoExecutionException(
                        "No foi informado o nome do documento principal para gerar a documentao e no foi encontrado o padro.",
                        e);
            }
        }

        List<String> dependencySourcePaths = null;
        if (includeDependencySources) {
            try {
                dependencySourcePaths = getDependencySourcePaths();
            } catch (MavenReportException e1) {
                throw new MojoExecutionException("Ocorreu um erro obtendo source code das dependncias.", e1);
            }
        }

        String temporaryDirectoryJson = rootDir + File.separator + TARGET + File.separator + ANTEROS_JSONC_JAVADOC;

        /**
         * Monta a lista de parmetros que sero usados para ler a
         * documentao(javadoc).
         */
        List<String> parameters = new ArrayList<String>();

        parameters.add(PROTECTED_OPTION);
        parameters.add(SOURCEPATH_OPTION);

        StringBuilder sbSourcesPath = new StringBuilder();
        sbSourcesPath.append(getSourcesPath());
        if (dependencySourcePaths != null) {
            for (String ds : dependencySourcePaths) {
                sbSourcesPath.append(File.pathSeparator);
                sbSourcesPath.append(ds);
            }
        }
        sbSourcesPath.append(File.pathSeparator).append(temporaryDirectoryJson);
        parameters.add(sbSourcesPath.toString());

        if (packageScanEndpoints != null) {
            for (String p : packageScanEndpoints) {
                if (p.contains("*"))
                    throw new MojoExecutionException("Pacotes no podem conter asteriscos(*) no nome " + p);
                parameters.add(p);
            }
        }
        parameters.add(SPRING_WEB_CONTROLLER);
        parameters.add(DOCLET_OPTION);
        parameters.add(AnterosRestDoclet.class.getName());
        parameters.add(CLASSPATH_OPTION);
        parameters.add(getClassPath());

        /**
         * Executa o javadoc para obter os comentrios referente as classes que
         * sero geradas na documentao da API REST usando um Doclet
         * customizado (AnterosRestDoclet)
         */
        com.sun.tools.javadoc.Main.execute(parameters.toArray(new String[] {}));

        /**
         * L o arquivo JSON contendo informaes sobre os endpoints lidos com o
         * javadoc via AnterosRestDoclet(Doclet customizado).
         */
        File temporaryJsonPath = new File(temporaryDirectoryJson);
        if (!temporaryJsonPath.exists())
            temporaryJsonPath.mkdirs();

        File file = new File(temporaryJsonPath + File.separator + ANTEROS_JSON);
        FileInputStream fis;
        try {

            fis = new FileInputStream(file);
            /**
             * Le os dados do JSON gerado a partir do javadoc.
             */
            ObjectMapper mapper = new ObjectMapper();
            ClassDescriptor[] classDescriptors = mapper.readValue(fis, ClassDescriptor[].class);

            /**
             * Gera o arquivo de items da documentao.
             */
            String filePath = "";
            if (sourceDirectory != null) {
                filePath = sourceDirectory.getAbsolutePath();
            } else if (sourceDocumentName != null) {
                filePath = sourceDocumentName.substring(0, sourceDocumentName.lastIndexOf(File.separator));
            }

            File itemsFile = new File(filePath, ITEMS_ADOC);

            Writer writer = new FileWriter(itemsFile);
            SnippetGenerator.generate(urlBase, writer, classDescriptors);
            writer.flush();
            writer.close();

            fis.close();

            /**
             * Altera o CSS default do plugin para o tema azul
             */

            if (!attributes.containsKey("stylesheet")) {
                File stylesheetFile = new File(filePath, "asciidoc_stylesheet.css");

                try {
                    InputStream openStream = ResourceUtils.getResourceAsStream("asciidoc_stylesheet.css");
                    FileOutputStream fos = new FileOutputStream(stylesheetFile);
                    br.com.anteros.core.utils.IOUtils.copy(openStream, fos);
                    fos.flush();
                    fos.close();
                    openStream.close();
                } catch (IOException e) {
                    throw new MojoExecutionException("No foi possvel copiar o template padro de CSS.", e);
                }

                this.attributes.put("stylesheet", stylesheetFile.getAbsolutePath());
            }

            /**
             * Executa gerao dos arquivos da documentao a partir dos
             * arquivos .adoc (asciidoc)
             */
            super.execute();

        } catch (Exception e) {
            e.printStackTrace();
            throw new MojoExecutionException("No foi possvel gerar a documentao da API Rest.", e);
        }
    }

    protected StringBuilder getSourcesPath() {
        StringBuilder classPath = new StringBuilder();
        boolean appendDelimiter = false;

        for (String s : compileSourceRoots) {
            if (appendDelimiter)
                classPath.append(":");
            classPath.append(s);
            appendDelimiter = true;
        }
        return classPath;
    }

    protected String getClassPath() {
        StringBuilder classPath;
        boolean appendDelimiter;
        classPath = new StringBuilder();
        appendDelimiter = false;

        for (String s : classpathElements) {
            if (appendDelimiter)
                classPath.append(":");
            classPath.append(s);
            appendDelimiter = true;
        }
        return classPath.toString();
    }

    public List<String> getPackageScanEndpoints() {
        return packageScanEndpoints;
    }

    /**
     * Resolve dependency sources so they can be included directly in the
     * javadoc process. To customize this, override
     * {@link AbstractJavadocMojo#configureDependencySourceResolution(SourceResolverConfig)}.
     */
    protected final List<String> getDependencySourcePaths() throws MavenReportException {
        try {
            if (sourceDependencyCacheDir.exists()) {
                FileUtils.forceDelete(sourceDependencyCacheDir);
                sourceDependencyCacheDir.mkdirs();
            }
        } catch (IOException e) {
            throw new MavenReportException(
                    "Failed to delete cache directory: " + sourceDependencyCacheDir + "\nReason: " + e.getMessage(),
                    e);
        }

        final SourceResolverConfig config = getDependencySourceResolverConfig();

        final AndArtifactFilter andFilter = new AndArtifactFilter();

        final List<String> dependencyIncludes = dependencySourceIncludes;
        final List<String> dependencyExcludes = dependencySourceExcludes;

        if (!includeTransitiveDependencySources || isNotEmpty(dependencyIncludes)
                || isNotEmpty(dependencyExcludes)) {
            if (!includeTransitiveDependencySources) {
                andFilter.add(createDependencyArtifactFilter());
            }

            if (isNotEmpty(dependencyIncludes)) {
                andFilter.add(new PatternIncludesArtifactFilter(dependencyIncludes, false));
            }

            if (isNotEmpty(dependencyExcludes)) {
                andFilter.add(new PatternExcludesArtifactFilter(dependencyExcludes, false));
            }

            config.withFilter(andFilter);
        }

        try {
            return ResourceResolver.resolveDependencySourcePaths(config);
        } catch (final ArtifactResolutionException e) {
            throw new MavenReportException(
                    "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e);
        } catch (final ArtifactNotFoundException e) {
            throw new MavenReportException(
                    "Failed to resolve one or more javadoc source/resource artifacts:\n\n" + e.getMessage(), e);
        }
    }

    /**
     * Construct a SourceResolverConfig for resolving dependency sources and
     * resources in a consistent way, so it can be reused for both source and
     * resource resolution.
     *
     */
    private SourceResolverConfig getDependencySourceResolverConfig() {
        return configureDependencySourceResolution(
                new SourceResolverConfig(getLog(), project, localRepository, sourceDependencyCacheDir, resolver,
                        factory, artifactMetadataSource, archiverManager).withReactorProjects(reactorProjects));
    }

    /**
     * Override this method to customize the configuration for resolving
     * dependency sources. The default behavior enables the resolution of
     * -sources jar files.
     */
    protected SourceResolverConfig configureDependencySourceResolution(final SourceResolverConfig config) {
        return config.withCompileSources();
    }

    /**
     * Returns a ArtifactFilter that only includes direct dependencies of this
     * project (verified via groupId and artifactId).
     *
     * @return
     */
    private ArtifactFilter createDependencyArtifactFilter() {
        Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();

        List<String> artifactPatterns = new ArrayList<String>(dependencyArtifacts.size());
        for (Artifact artifact : dependencyArtifacts) {
            artifactPatterns.add(artifact.getGroupId() + ":" + artifact.getArtifactId());
        }

        return new IncludesArtifactFilter(artifactPatterns);
    }

}