/* * Copyright (c) EMC Corporation. All rights reserved. */ package radl.maven; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.InstantiationStrategy; 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.project.MavenProject; import radl.common.io.IO; import radl.core.cli.Arguments; import radl.java.extraction.FromJavaRadlExtractor; /** * Maven plugin for extracting RADL file from the Java server code. */ @Mojo(name = "radlFromCode", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE, instantiationStrategy = InstantiationStrategy.KEEP_ALIVE) public class RadlFromCodePlugin extends AbstractMojo implements MavenConfig { private static final String MSG = "RADL is extracted from code and saved at: %s"; private static final String RADL_CORE_ARTIFACT_ID = "radl-core"; /** * The enclosing project. */ @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; /** * The enclosing plugin dependencies. */ @Parameter(readonly = true, defaultValue = "${plugin.artifacts}") private List<Artifact> pluginDependencies; /** * The classpath scope which is used for the compilation of the RADL extraction from code. * Available values are: <pre>runtime</pre>, <pre>compile</pre>, <pre>test</pre> and <pre>system</pre>. * Defaults to <pre>runtime</pre>. */ @Parameter(property = CLASSPATH_SCOPE, defaultValue = CLASSPATH_SCOPE_DEFAULT) protected String classpathScope; /** * The sub-directory of the project build directory into which RADL is generated. * Defaults to <pre>target/radl</pre>. */ @Parameter(property = DOCS_DIR, defaultValue = DOCS_DIR_DEFAULT) private File docsDir; /** * The name of the service that the RADL files describe. Defaults to the project's name. */ @Parameter(property = SERVICE_NAME, defaultValue = SERVICE_NAME_DEFAULT) private String serviceName; /** * The source code file directory to scan for RADL extraction. Defaults to the project's java src dir. */ @Parameter(property = SRC_SET_DIR, defaultValue = SRC_SET_DIR_DEFAULT) private File srcDir; /** * The argument file containing argument properties for the RADL extraction. If it is not specified, * the plugin will create a temporary argument file with the minimal configuration. If it is specified, * other parameters in this plugin will be merged into the argument file. */ @Parameter(property = "argumentFile") private File argumentFile; /** * The additional configuration file containing configuration properties for the RADL extraction. */ @Parameter(property = "configurationFile") private File configurationFile; @Override public void execute() throws MojoExecutionException, MojoFailureException { FromJavaRadlExtractor radlFromJavaExtractor = new FromJavaRadlExtractor(); File tempArgumentsFile = null; try { tempArgumentsFile = generateProjectArgumentsFile(); radlFromJavaExtractor.run( new Arguments(new String[] { "@" + tempArgumentsFile.getAbsolutePath() })); getLog().info(String.format(MSG, docsDir)); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { IO.delete(tempArgumentsFile); } } private File generateProjectArgumentsFile() throws IOException { String radlFilePath = getRadlFile(); Properties properties = initArgumentProperties(); mergeMavenPluginProperties(radlFilePath, properties); File tempArgumentsFile = writePropertiesToFile(properties); return tempArgumentsFile; } private File writePropertiesToFile(Properties properties) throws IOException { File tempArgumentsFile = createNewArgumentFile(); try (PrintWriter writer = new PrintWriter(tempArgumentsFile, "UTF8")) { properties.store(writer, ""); } getLog().info("RADL generation argument file: " + tempArgumentsFile); return tempArgumentsFile; } private void mergeMavenPluginProperties(String radlFilePath, Properties properties) { getLog().info("[RADL Extraction - Service Name] " + serviceName); properties.setProperty("service.name", serviceName); getLog().info("[RADL Extraction - SRC Dir] " + srcDir); properties.setProperty("base.dir", srcDir.getAbsolutePath()); getLog().info("[RADL Extraction - RADL File] " + radlFilePath); properties.setProperty("radl.file", radlFilePath); String classpath = buildClasspath(); getLog().info("[RADL Extraction - Classpath] " + classpath); properties.setProperty("classpath", classpath); if (configurationFile != null && configurationFile.exists()) { getLog().info("[RADL Extraction - configuration] " + configurationFile); properties.setProperty("configuration.file", configurationFile.getAbsolutePath()); } } private Properties initArgumentProperties() throws IOException { Properties properties = new Properties(); // load from origin if (argumentFile != null && argumentFile.exists()) { try (FileInputStream source = new FileInputStream(argumentFile)) { properties.load(source); } } return properties; } private String getRadlFile() { File radlFile = new File(getDocsDir(), serviceName + ".radl"); if (radlFile.exists()) { radlFile.delete(); } return radlFile.getAbsolutePath(); } private File createNewArgumentFile() throws IOException { File tempArgumentsFile = new File(getDocsDir(), "extract-radl.properties"); if (tempArgumentsFile.exists()) { tempArgumentsFile.delete(); } tempArgumentsFile.createNewFile(); return tempArgumentsFile; } @SuppressWarnings("unchecked") protected void collectProjectArtifactsAndClasspath(List<Artifact> artifacts, List<File> theClasspathFiles) { if ("compile".equals(classpathScope)) { artifacts.addAll(project.getCompileArtifacts()); theClasspathFiles.add(new File(project.getBuild().getOutputDirectory())); } else if ("test".equals(classpathScope)) { artifacts.addAll(project.getTestArtifacts()); theClasspathFiles.add(new File(project.getBuild().getTestOutputDirectory())); theClasspathFiles.add(new File(project.getBuild().getOutputDirectory())); } else if ("runtime".equals(classpathScope)) { artifacts.addAll(project.getRuntimeArtifacts()); theClasspathFiles.add(new File(project.getBuild().getOutputDirectory())); } else if ("system".equals(classpathScope)) { artifacts.addAll(project.getSystemArtifacts()); } else { throw new IllegalStateException("Invalid classpath scope: " + classpathScope); } } protected String buildClasspath() { List<Artifact> artifacts = new ArrayList<>(); List<File> theClasspathFiles = new ArrayList<>(); collectProjectArtifactsAndClasspath(artifacts, theClasspathFiles); StringBuilder paths = new StringBuilder(); for (File classpathFile : theClasspathFiles) { getLog().info("Adding project build directory to classpath: " + classpathFile.getName()); paths.append(classpathFile.getAbsolutePath()).append(File.pathSeparator); } for (Artifact artifact : artifacts) { getLog().info("Adding project dependency artifact to classpath: " + artifact.getArtifactId()); paths.append(artifact.getFile().getAbsolutePath()).append(File.pathSeparator); } if (pluginDependencies != null) { for (Artifact artifact : pluginDependencies) { if (RADL_CORE_ARTIFACT_ID.equals(artifact.getArtifactId())) { getLog().info("Adding project plugin dependency artifact to classpath: " + artifact.getArtifactId()); paths.append(artifact.getFile().getAbsolutePath()).append(File.pathSeparator); } } } return paths.toString(); } private File getDocsDir() { if (!docsDir.exists()) { docsDir.mkdir(); } return docsDir; } }