Java tutorial
/******************************************************************************* * 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); } }