diff --git a/maven/pom.xml b/maven/pom.xml index d8d7c723eb9fc..962fe0f502789 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -27,6 +27,11 @@ shamrock-maven-plugin maven-plugin + + + ${project.version} + + @@ -37,9 +42,7 @@ org.apache.maven maven-core - - - + org.apache.maven.plugin-tools maven-plugin-annotations @@ -65,7 +68,141 @@ org.jboss.shamrock shamrock-development-mode + + + + jline + jline + 2.14.6 + + + + + org.freemarker + freemarker + 2.3.28 + + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.7 + + + + + junit + junit + test + + + org.assertj + assertj-core + 3.11.1 + test + + + org.apache.maven.shared + maven-invoker + 3.0.1 + test + + + + + src/main/resources + true + + *.ftl + + + + + + org.codehaus.plexus + plexus-component-metadata + 1.7.1 + + ${basedir}/target/filtered-resources/META-INF/plexus + + + + + generate-metadata + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5.2 + + shamrock + true + + + + help-goal + + helpmojo + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.1.0 + + ${project.build.directory}/it + true + src/it/settings.xml + ${project.build.directory}/local-repo + verify + true + ${skipTests} + true + invoker.properties + + + + integration-tests + + install + run + verify + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + ${maven.home} + ${settings.localRepository} + + + + + + \ No newline at end of file diff --git a/maven/src/it/settings.xml b/maven/src/it/settings.xml new file mode 100644 index 0000000000000..2d90068bb65a7 --- /dev/null +++ b/maven/src/it/settings.xml @@ -0,0 +1,35 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/maven/src/it/setup-on-existing-pom-it/invoker.properties b/maven/src/it/setup-on-existing-pom-it/invoker.properties new file mode 100644 index 0000000000000..7eadca9f7e5ee --- /dev/null +++ b/maven/src/it/setup-on-existing-pom-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals=${project.groupId}:${project.artifactId}:${project.version}:create diff --git a/maven/src/it/setup-on-existing-pom-it/pom.xml b/maven/src/it/setup-on-existing-pom-it/pom.xml new file mode 100644 index 0000000000000..cf1865f996e43 --- /dev/null +++ b/maven/src/it/setup-on-existing-pom-it/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + org.acme + shamrock-setup-demo + 0.1-SNAPSHOT + jar + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + UTF-8 + + + + junit + junit + 4.12 + test + + + diff --git a/maven/src/it/setup-on-existing-pom-it/verify.groovy b/maven/src/it/setup-on-existing-pom-it/verify.groovy new file mode 100644 index 0000000000000..4dfddb1e06564 --- /dev/null +++ b/maven/src/it/setup-on-existing-pom-it/verify.groovy @@ -0,0 +1,6 @@ +import org.jboss.shamrock.maven.it.SetupVerifier + +String base = basedir +File pomFile = new File(base, "pom.xml") + +SetupVerifier.verifySetup(pomFile) diff --git a/maven/src/it/setup-on-min-pom-it/invoker.properties b/maven/src/it/setup-on-min-pom-it/invoker.properties new file mode 100644 index 0000000000000..7eadca9f7e5ee --- /dev/null +++ b/maven/src/it/setup-on-min-pom-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals=${project.groupId}:${project.artifactId}:${project.version}:create diff --git a/maven/src/it/setup-on-min-pom-it/pom.xml b/maven/src/it/setup-on-min-pom-it/pom.xml new file mode 100644 index 0000000000000..c1812e524ac72 --- /dev/null +++ b/maven/src/it/setup-on-min-pom-it/pom.xml @@ -0,0 +1,25 @@ + + + + + 4.0.0 + org.acme + shamrock-setup-demo + 0.1-SNAPSHOT + diff --git a/maven/src/it/setup-on-min-pom-it/verify.groovy b/maven/src/it/setup-on-min-pom-it/verify.groovy new file mode 100644 index 0000000000000..4dfddb1e06564 --- /dev/null +++ b/maven/src/it/setup-on-min-pom-it/verify.groovy @@ -0,0 +1,6 @@ +import org.jboss.shamrock.maven.it.SetupVerifier + +String base = basedir +File pomFile = new File(base, "pom.xml") + +SetupVerifier.verifySetup(pomFile) diff --git a/maven/src/it/setup-with-custom-shamrock-version-it/invoker.properties b/maven/src/it/setup-with-custom-shamrock-version-it/invoker.properties new file mode 100644 index 0000000000000..3ef60fed3b388 --- /dev/null +++ b/maven/src/it/setup-with-custom-shamrock-version-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals=${project.groupId}:${project.artifactId}:${project.version}:create -DshamrockVersion=0.0.0 diff --git a/maven/src/it/setup-with-custom-shamrock-version-it/pom.xml b/maven/src/it/setup-with-custom-shamrock-version-it/pom.xml new file mode 100644 index 0000000000000..9be7221ebf23c --- /dev/null +++ b/maven/src/it/setup-with-custom-shamrock-version-it/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + org.acme + shamrock-setup-demo + 0.1-SNAPSHOT + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + UTF-8 + + + + junit + junit + 4.12 + test + + + diff --git a/maven/src/it/setup-with-custom-shamrock-version-it/verify.groovy b/maven/src/it/setup-with-custom-shamrock-version-it/verify.groovy new file mode 100644 index 0000000000000..944cff7d9180d --- /dev/null +++ b/maven/src/it/setup-with-custom-shamrock-version-it/verify.groovy @@ -0,0 +1,6 @@ +import org.jboss.shamrock.maven.it.SetupVerifier + +String base = basedir +File pomFile = new File(base, "pom.xml") + +SetupVerifier.verifySetupWithVersion(pomFile) diff --git a/maven/src/main/java/org/jboss/shamrock/maven/AddExtensionMojo.java b/maven/src/main/java/org/jboss/shamrock/maven/AddExtensionMojo.java new file mode 100644 index 0000000000000..9cfdbe2c163a9 --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/AddExtensionMojo.java @@ -0,0 +1,50 @@ +package org.jboss.shamrock.maven; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Writer; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import static org.jboss.shamrock.maven.components.dependencies.Extensions.addExtensions; + +@Mojo(name = "add-extension", requiresProject = true) +public class AddExtensionMojo extends AbstractMojo { + + /** + * The Maven project which will define and configure the shamrock-maven-plugin + */ + @Parameter(defaultValue = "${project}") + protected MavenProject project; + + @Parameter(property = "extensions") + private List extensions; + + + @Override + public void execute() throws MojoExecutionException { + Model model = project.getOriginalModel().clone(); + if (addExtensions(model, extensions, getLog())) { + File pomFile = project.getFile(); + save(pomFile, model); + } + } + + private void save(File pomFile, Model model) throws MojoExecutionException { + MavenXpp3Writer xpp3Writer = new MavenXpp3Writer(); + try (FileWriter pomFileWriter = new FileWriter(pomFile)) { + xpp3Writer.write(pomFileWriter, model); + pomFileWriter.flush(); + } catch (IOException e) { + throw new MojoExecutionException("Unable to write the pom.xml file", e); + } + } + +} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/CreateProjectMojo.java b/maven/src/main/java/org/jboss/shamrock/maven/CreateProjectMojo.java new file mode 100644 index 0000000000000..4f70cf45ac3fa --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/CreateProjectMojo.java @@ -0,0 +1,311 @@ +/* + * + * Copyright (c) 2016-2018 Red Hat, Inc. + * + * Red Hat licenses this file to you 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 org.jboss.shamrock.maven; + +import org.apache.maven.model.*; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.model.io.xpp3.MavenXpp3Writer; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.jboss.shamrock.maven.components.Prompter; +import org.jboss.shamrock.maven.components.SetupTemplates; +import org.jboss.shamrock.maven.utilities.MojoUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; + +import static org.jboss.shamrock.maven.components.dependencies.Extensions.addExtensions; +import static org.jboss.shamrock.maven.utilities.MojoUtils.configuration; +import static org.jboss.shamrock.maven.utilities.MojoUtils.plugin; + +/** + * This goal helps in setting up Shamrock Maven project with shamrock-maven-plugin, with sensible defaults + */ +@Mojo(name = "create", requiresProject = false) +public class CreateProjectMojo extends AbstractMojo { + + private static final String JAVA_EXTENSION = ".java"; + public static final String VERSION_PROP = "shamrock-version"; + public static final String PLUGIN_VERSION_PROPERTY_NAME = "shamrock.version"; + public static final String PLUGIN_VERSION_PROPERTY = "${" + PLUGIN_VERSION_PROPERTY_NAME + "}"; + public static final String PLUGIN_GROUPID = "org.jboss.shamrock"; + public static final String PLUGIN_ARTIFACTID = "shamrock-maven-plugin"; + public static final String PLUGIN_KEY = PLUGIN_GROUPID + ":" + PLUGIN_ARTIFACTID; + + /** + * The Maven project which will define and configure the shamrock-maven-plugin + */ + @Parameter(defaultValue = "${project}") + protected MavenProject project; + + @Parameter(property = "projectGroupId") + private String projectGroupId; + + @Parameter(property = "projectArtifactId") + private String projectArtifactId; + + @Parameter(property = "projectVersion", defaultValue = "1.0-SNAPSHOT") + private String projectVersion; + + @Parameter(property = "shamrockVersion") + private String shamrockVersion; + + @Parameter(property = "path", defaultValue = "/hello") + protected String path; + + @Parameter(property = "className") + private String className; + + @Parameter(property = "root", defaultValue = "/app") + private String root; + + @Parameter(property = "extensions") + private List extensions; + + @Component + private Prompter prompter; + + @Component + private SetupTemplates templates; + + @Override + public void execute() throws MojoExecutionException { + getLog().info("Executing..."); + File pomFile = project.getFile(); + + Model model; + //Create pom.xml if not + if (pomFile == null || !pomFile.isFile()) { + pomFile = createPomFileFromUserInputs(); + } + + //We should get cloned of the OriginalModel, as project.getModel will return effective model + model = project.getOriginalModel().clone(); + + createDirectories(); + templates.generate(project, root, path, className, getLog()); + Optional maybe = MojoUtils.hasPlugin(project, PLUGIN_KEY); + if (maybe.isPresent()) { + return; + } + + // The plugin is not configured, add it. + addVersionProperty(model); + addMainPluginConfig(model); + addExtensions(model, extensions, getLog()); + addNativeProfile(model); + save(pomFile, model); + } + + private void addNativeProfile(Model model) { + Profile profile = new Profile(); + profile.setId("native"); + BuildBase buildBase = new BuildBase(); + Plugin plg = plugin(PLUGIN_GROUPID, PLUGIN_ARTIFACTID, PLUGIN_VERSION_PROPERTY); + PluginExecution exec = new PluginExecution(); + exec.addGoal("native-image"); + MojoUtils.Element element = new MojoUtils.Element("enableHttpUrlHandler", "true"); + exec.setConfiguration(configuration(element)); + plg.addExecution(exec); + buildBase.addPlugin(plg); + profile.setBuild(buildBase); + model.addProfile(profile); + } + + private void addMainPluginConfig(Model model) { + Plugin plugin = plugin(PLUGIN_GROUPID, PLUGIN_ARTIFACTID, PLUGIN_VERSION_PROPERTY); + if (isParentPom(model)) { + addPluginManagementSection(model, plugin); + //strip the shamrockVersion off + plugin = plugin(PLUGIN_GROUPID, PLUGIN_ARTIFACTID); + } else { + plugin = plugin(PLUGIN_GROUPID, PLUGIN_ARTIFACTID, PLUGIN_VERSION_PROPERTY); + } + PluginExecution pluginExec = new PluginExecution(); + pluginExec.addGoal("build"); + plugin.addExecution(pluginExec); + Build build = createBuildSectionIfRequired(model); + build.getPlugins().add(plugin); + } + + private void addVersionProperty(Model model) { + //Set a property at maven project level for Shamrock maven plugin versions + shamrockVersion = shamrockVersion == null ? MojoUtils.getVersion(VERSION_PROP) : shamrockVersion; + model.getProperties().putIfAbsent(PLUGIN_VERSION_PROPERTY_NAME, shamrockVersion); + } + + private Build createBuildSectionIfRequired(Model model) { + Build build = model.getBuild(); + if (build == null) { + build = new Build(); + model.setBuild(build); + } + if (build.getPlugins() == null) { + build.setPlugins(new ArrayList<>()); + } + return build; + } + + private void addPluginManagementSection(Model model, Plugin plugin) { + if (model.getBuild().getPluginManagement() != null) { + if (model.getBuild().getPluginManagement().getPlugins() == null) { + model.getBuild().getPluginManagement().setPlugins(new ArrayList<>()); + } + model.getBuild().getPluginManagement().getPlugins().add(plugin); + } + } + + private File createPomFileFromUserInputs() throws MojoExecutionException { + Model model; + String workingdDir = System.getProperty("user.dir"); + File pomFile = new File(workingdDir, "pom.xml"); + try { + + if (projectGroupId == null) { + projectGroupId = prompter.promptWithDefaultValue("Set the project groupId", + "io.jboss.shamrock.sample"); + } + + // If the user does not specify the artifactId, we switch to the interactive mode. + if (projectArtifactId == null) { + projectArtifactId = prompter.promptWithDefaultValue("Set the project artifactId", + "my-shamrock-project"); + + // Ask for version only if we asked for the artifactId + projectVersion = prompter.promptWithDefaultValue("Set the project version", "1.0-SNAPSHOT"); + + // Ask for maven version if not set + if (shamrockVersion == null) { + shamrockVersion = prompter.promptWithDefaultValue("Set the Shamrock version", + MojoUtils.getVersion(VERSION_PROP)); + } + + if (className == null) { + className = prompter.promptWithDefaultValue("Set the resource class name", + projectGroupId.replace("-", ".").replace("_", ".") + + ".HelloResource"); + + if (className != null && className.endsWith(JAVA_EXTENSION)) { + className = className.substring(0, className.length() - JAVA_EXTENSION.length()); + } + } + + if (root == null) { + root = prompter.promptWithDefaultValue("Set the application root ", + "/app"); + if (!root.startsWith("/")) { + root = "/" + root; + } + } + + if (path == null) { + path = prompter.promptWithDefaultValue("Set the resource path ", + "/hello"); + if (!path.startsWith("/")) { + path = "/" + path; + } + } + } + + // Create directory if the current one is not empty. + File wkDir = new File(workingdDir); + String[] children = wkDir.list(); + if (children != null && children.length != 0) { + // Need to generate directory + File sub = new File(wkDir, projectArtifactId); + sub.mkdirs(); + getLog().info("Directory " + projectArtifactId + " created"); + // This updates the project pom file but also the base directory. + pomFile = new File(sub, "pom.xml"); + project.setFile(pomFile); + } + + + Map context = new HashMap<>(); + context.put("mProjectGroupId", projectGroupId); + context.put("mProjectArtifactId", projectArtifactId); + context.put("mProjectVersion", projectVersion); + context.put("shamrockVersion", shamrockVersion != null ? shamrockVersion : MojoUtils.getVersion(VERSION_PROP)); + + context.put("className", className); + context.put("root", root); + context.put("path", path); + + templates.createNewProjectPomFile(context, pomFile); + + //The project should be recreated and set with right model + MavenXpp3Reader xpp3Reader = new MavenXpp3Reader(); + + model = xpp3Reader.read(new FileInputStream(pomFile)); + } catch (Exception e) { + throw new MojoExecutionException("Error while setup of shamrock-maven-plugin", e); + } + + project = new MavenProject(model); + project.setFile(pomFile); + project.setPomFile(pomFile); + project.setOriginalModel(model); // the current model is the original model as well + + addExtensions(model, extensions, getLog()); + save(pomFile, model); + return pomFile; + } + + private void save(File pomFile, Model model) throws MojoExecutionException { + MavenXpp3Writer xpp3Writer = new MavenXpp3Writer(); + try (FileWriter pomFileWriter = new FileWriter(pomFile)) { + xpp3Writer.write(pomFileWriter, model); + pomFileWriter.flush(); + } catch (IOException e) { + throw new MojoExecutionException("Unable to write the pom.xml file", e); + } + } + + private void createDirectories() { + File base = project.getBasedir(); + File source = new File(base, "src/main/java"); + File resources = new File(base, "src/main/resources"); + File test = new File(base, "src/test/java"); + + String prefix = "Creation of "; + if (!source.isDirectory()) { + boolean res = source.mkdirs(); + getLog().debug(prefix + source.getAbsolutePath() + " : " + res); + } + if (!resources.isDirectory()) { + boolean res = resources.mkdirs(); + getLog().debug(prefix + resources.getAbsolutePath() + " : " + res); + } + if (!test.isDirectory()) { + boolean res = test.mkdirs(); + getLog().debug(prefix + test.getAbsolutePath() + " : " + res); + } + } + + private boolean isParentPom(Model model) { + return "pom".equals(model.getPackaging()); + } + +} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/ListExtensionsMojo.java b/maven/src/main/java/org/jboss/shamrock/maven/ListExtensionsMojo.java new file mode 100644 index 0000000000000..dad4b4afeb6b5 --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/ListExtensionsMojo.java @@ -0,0 +1,20 @@ +package org.jboss.shamrock.maven; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.Mojo; +import org.jboss.shamrock.maven.components.dependencies.Extensions; + +@Mojo(name = "list-extensions", requiresProject = false) +public class ListExtensionsMojo extends AbstractMojo { + + @Override + public void execute() { + getLog().info("Available extensions:"); + Extensions.get().stream() + .sorted((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())) + .forEach(ext -> getLog().info("\t * " + ext.getName() + " (" + ext.getGroupId() + ":" + ext.getArtifactId() + ")")); + + getLog().info("\nAdd an extension to your project by adding the dependency to your " + + "project or use `mvn shamrock:add-extension -Dextensions=\"name\"`"); + } +} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/components/Prompter.java b/maven/src/main/java/org/jboss/shamrock/maven/components/Prompter.java new file mode 100644 index 0000000000000..f3c4c8bbb7361 --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/components/Prompter.java @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2016-2018 Red Hat, Inc. + * + * Red Hat licenses this file to you 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 org.jboss.shamrock.maven.components; + +import jline.console.ConsoleReader; +import org.apache.commons.lang3.StringUtils; +import org.codehaus.plexus.component.annotations.Component; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Objects; + +/** + * Prompt implementation. + * + * @author Clement Escoffier + */ +@Component(role = Prompter.class, instantiationStrategy = "per-lookup") +public class Prompter { + + private final ConsoleReader console; + + public Prompter() throws IOException { + this.console = new ConsoleReader(); + console.setHistoryEnabled(false); + console.setExpandEvents(false); + } + + public Prompter(InputStream in, OutputStream out) throws IOException { + this.console = new ConsoleReader(in, out); + console.setHistoryEnabled(false); + console.setExpandEvents(false); + } + + public ConsoleReader getConsole() { + return console; + } + + public String prompt(final String message, final Character mask) throws IOException { + Objects.requireNonNull(message); + + final String prompt = String.format("%s: ", message); + String value; + do { + value = console.readLine(prompt, mask); + } + while (StringUtils.isBlank(value)); + return value; + } + + + public String prompt(final String message) throws IOException { + Objects.requireNonNull(message); + return prompt(message, null); + } + + public String promptWithDefaultValue(final String message, final String defaultValue) throws IOException { + Objects.requireNonNull(message); + Objects.requireNonNull(defaultValue); + + final String prompt = String.format("%s [%s]: ", message, defaultValue); + String value = console.readLine(prompt); + if (StringUtils.isBlank(value)) { + return defaultValue; + } + return value; + } + +} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/components/SetupTemplates.java b/maven/src/main/java/org/jboss/shamrock/maven/components/SetupTemplates.java new file mode 100644 index 0000000000000..5911b3a2422a7 --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/components/SetupTemplates.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. + * + * Red Hat licenses this file to you 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 org.jboss.shamrock.maven.components; + +import com.google.common.base.Strings; +import freemarker.cache.ClassTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; + +import java.io.File; +import java.io.FileWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; + +/** + * Prompt implementation. + * + * @author Clement Escoffier + */ +@Component(role = SetupTemplates.class, instantiationStrategy = "singleton") +public class SetupTemplates { + + private static final Configuration cfg; + private static final String JAVA_EXTENSION = ".java"; + + static { + cfg = new Configuration(Configuration.VERSION_2_3_23); + cfg.setTemplateLoader(new ClassTemplateLoader(SetupTemplates.class, "/")); + } + + public void createNewProjectPomFile(Map context, File pomFile) throws MojoExecutionException { + try { + Template temp = cfg.getTemplate("templates/pom-template.ftl"); + Writer out = new FileWriter(pomFile); + temp.process(context, out); + } catch (Exception e) { + throw new MojoExecutionException("Unable to generate pom.xml", e); + } + } + + public void generate(MavenProject project, String rootPath, String path, String className, Log log) throws MojoExecutionException { + if (Strings.isNullOrEmpty(className)) { + return; + } + log.info("Creating resource " + className); + + File root = new File(project.getBasedir(), "src/main/java"); + File testRoot = new File(project.getBasedir(), "src/test/java"); + + String packageName = null; + if (className.endsWith(JAVA_EXTENSION)) { + className = className.substring(0, className.length() - JAVA_EXTENSION.length()); + } + + if (className.contains(".")) { + int idx = className.lastIndexOf('.'); + packageName = className.substring(0, idx); + className = className.substring(idx + 1); + } + + if (packageName != null) { + File packageDir = new File(root, packageName.replace('.', '/')); + if (!packageDir.exists()) { + packageDir.mkdirs(); + log.info("Creating directory " + packageDir.getAbsolutePath()); + } + root = packageDir; + + File testPackageDir = new File(testRoot, packageName.replace('.', '/')); + if (!testPackageDir.exists()) { + testPackageDir.mkdirs(); + log.info("Creating directory " + packageDir.getAbsolutePath()); + } + testRoot = testPackageDir; + } + + File classFile = new File(root, className + JAVA_EXTENSION); + File testClassFile = new File(testRoot, className + "Test" + JAVA_EXTENSION); + Map context = new HashMap<>(); + context.put("classname", className); + context.put("root_prefix", rootPath); + context.put("path", path); + if (packageName != null) { + context.put("packageName", packageName); + } + try { + Template temp = cfg.getTemplate("templates/resource-template.ftl"); + Writer out = new FileWriter(classFile); + temp.process(context, out); + } catch (Exception e) { + throw new MojoExecutionException("Unable to generate resource code", e); + } + + // Generate test resources. + try { + Template temp = cfg.getTemplate("templates/test-resource-template.ftl"); + Writer out = new FileWriter(testClassFile); + temp.process(context, out); + } catch (Exception e) { + throw new MojoExecutionException("Unable to generate test code", e); + } + + // Generate application. + File appClassFile = new File(root, "ShamrockApplication.java"); + try { + Template temp = cfg.getTemplate("templates/application-template.ftl"); + Writer out = new FileWriter(appClassFile); + temp.process(context, out); + } catch (Exception e) { + throw new MojoExecutionException("Unable to generate Application class", e); + } + + } + +} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/components/dependencies/Extension.java b/maven/src/main/java/org/jboss/shamrock/maven/components/dependencies/Extension.java new file mode 100644 index 0000000000000..676fc4789811a --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/components/dependencies/Extension.java @@ -0,0 +1,149 @@ +/* + * + * Copyright (c) 2016-2018 Red Hat, Inc. + * + * Red Hat licenses this file to you 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 org.jboss.shamrock.maven.components.dependencies; + +import org.apache.maven.model.Dependency; +import org.jboss.shamrock.maven.CreateProjectMojo; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Clement Escoffier + */ +public class Extension { + + private String artifactId; + private String groupId; + private String scope; + private String version = CreateProjectMojo.PLUGIN_VERSION_PROPERTY; + + private String type; + private String classifier; + + private String name; + private String[] labels; + + public Extension() { + // Use by mapper. + } + + + public String getArtifactId() { + return artifactId; + } + + public Extension setArtifactId(String artifactId) { + this.artifactId = artifactId; + return this; + } + + public String getGroupId() { + return groupId; + } + + public Extension setGroupId(String groupId) { + this.groupId = groupId; + return this; + } + + public String getScope() { + return scope; + } + + public Extension setScope(String scope) { + this.scope = scope; + return this; + } + + public String getVersion() { + return version; + } + + public Extension setVersion(String version) { + this.version = version; + return this; + } + + public String getType() { + return type; + } + + public Extension setType(String type) { + this.type = type; + return this; + } + + public String getClassifier() { + return classifier; + } + + public Extension setClassifier(String classifier) { + this.classifier = classifier; + return this; + } + + public String[] getLabels() { + return labels; + } + + public Extension setLabels(String[] labels) { + this.labels = labels; + return this; + } + + public String getName() { + return name; + } + + public Extension setName(String name) { + this.name = name; + return this; + } + + public List labels() { + List list = new ArrayList<>(); + if (labels != null) { + list.addAll(Stream.of(labels).map(String::toLowerCase).collect(Collectors.toList())); + } + list.add(artifactId.toLowerCase()); + return list; + } + + public Dependency toDependency() { + Dependency dependency = new Dependency(); + dependency.setGroupId(groupId); + dependency.setArtifactId(artifactId); + if (scope != null && !scope.isEmpty()) { + dependency.setScope(scope); + } + if (classifier != null && !classifier.isEmpty()) { + dependency.setClassifier(classifier); + } + if (version != null && ! version.isEmpty()) { + dependency.setVersion(version); + } + return dependency; + } + + public String toCoordinates() { + return getGroupId() + ":" + getArtifactId(); + } +} + diff --git a/maven/src/main/java/org/jboss/shamrock/maven/components/dependencies/Extensions.java b/maven/src/main/java/org/jboss/shamrock/maven/components/dependencies/Extensions.java new file mode 100644 index 0000000000000..b25b419b71319 --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/components/dependencies/Extensions.java @@ -0,0 +1,116 @@ +/* + * + * Copyright (c) 2016-2018 Red Hat, Inc. + * + * Red Hat licenses this file to you 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 org.jboss.shamrock.maven.components.dependencies; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.plugin.logging.Log; +import org.jboss.shamrock.maven.utilities.MojoUtils; + +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Optional; + +/** + * @author Clement Escoffier + */ +public class Extensions { + + private Extensions() { + // avoid direct instantiation + } + + public static List get() { + ObjectMapper mapper = new ObjectMapper() + .enable(JsonParser.Feature.ALLOW_COMMENTS) + .enable(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS); + URL url = Extensions.class.getClassLoader().getResource("extensions.json"); + try { + return mapper.readValue(url, new TypeReference>() { + // Do nothing. + }); + } catch (IOException e) { + throw new RuntimeException("Unable to load the extensions.json file", e); + } + } + + public static Dependency parse(String dependency, Log log) { + Dependency res = new Dependency(); + String[] segments = dependency.split(":"); + if (segments.length >= 2) { + res.setGroupId(segments[0]); + res.setArtifactId(segments[1]); + if (segments.length >= 3 && !segments[2].isEmpty()) { + res.setVersion(segments[2]); + } + if (segments.length >= 4) { + res.setClassifier(segments[3]); + } + return res; + } else { + log.warn("Invalid dependency description '" + dependency + "'"); + return null; + } + } + + public static boolean addExtensions(Model model, List extensions, Log log) { + if (extensions == null || extensions.isEmpty()) { + return false; + } + + boolean updated = false; + List exts = Extensions.get(); + for (String dependency : extensions) { + Optional optional = exts.stream() + .filter(d ->{ + boolean hasTag = d.labels().contains(dependency.trim().toLowerCase()); + boolean machName = d.getName().toLowerCase().contains(dependency.trim().toLowerCase()); + return hasTag || machName; + }) + .findAny(); + + if (optional.isPresent()) { + if (! MojoUtils.hasDependency(model, optional.get().getGroupId(), optional.get().getArtifactId())) { + log.info("Adding extension " + optional.get().toCoordinates()); + model.addDependency(optional.get().toDependency()); + updated = true; + } else { + log.info("Extension already present - skipping"); + } + + } else if (dependency.contains(":")) { + // Add it as a dependency + // groupId:artifactId:version:classifier + Dependency parsed = Extensions.parse(dependency, log); + if (parsed != null) { + log.info("Adding dependency " + parsed.getManagementKey()); + model.addDependency(parsed); + updated = true; + } + } else { + log.warn("Cannot find a dependency matching '" + dependency + "'"); + } + } + + return updated; + } + +} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/utilities/MojoUtils.java b/maven/src/main/java/org/jboss/shamrock/maven/utilities/MojoUtils.java new file mode 100644 index 0000000000000..6ae7001a7eada --- /dev/null +++ b/maven/src/main/java/org/jboss/shamrock/maven/utilities/MojoUtils.java @@ -0,0 +1,223 @@ +/* + * + * Copyright (c) 2016-2018 Red Hat, Inc. + * + * Red Hat licenses this file to you 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 org.jboss.shamrock.maven.utilities; + + +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + +/** + * @author kameshs + */ +public class MojoUtils { + + private static final Properties properties = new Properties(); + + static { + loadProperties(); + } + + private MojoUtils() { + // Avoid direct instantiation + } + + /** + * Checks whether or not the given project has a plugin with the given key. The key is given using the + * "groupId:artifactId" syntax. + * + * @param project the project + * @param pluginKey the plugin + * @return an Optional completed if the plugin is found. + */ + public static Optional hasPlugin(MavenProject project, String pluginKey) { + Optional optPlugin = project.getBuildPlugins().stream() + .filter(plugin -> pluginKey.equals(plugin.getKey())) + .findFirst(); + + if (!optPlugin.isPresent() && project.getPluginManagement() != null) { + optPlugin = project.getPluginManagement().getPlugins().stream() + .filter(plugin -> pluginKey.equals(plugin.getKey())) + .findFirst(); + } + return optPlugin; + } + + /** + * Checks whether the project has the dependency + * + * @param model - the project to check existence of dependency + * @param groupId - the dependency groupId + * @param artifactId - the dependency artifactId + * @return true if the project has the dependency + */ + public static boolean hasDependency(Model model, String groupId, String artifactId) { + return model.getDependencies().stream() + .anyMatch(d -> groupId.equals(d.getGroupId()) + && artifactId.equals(d.getArtifactId())); + } + + private static void loadProperties() { + URL url = MojoUtils.class.getClassLoader().getResource("shamrock-maven-plugin.properties"); + Objects.requireNonNull(url); + try (InputStream in = url.openStream()) { + properties.load(in); + } catch (IOException e) { + throw new IllegalStateException("Invalid packaging of the shamrock-maven-plugin, the shamrock-maven-plugin" + + ".properties file cannot be read", e); + } + } + + public static String getVersion(String key) { + return properties.getProperty(key); + } + + /** + * Builds the configuration for the goal using Elements + * + * @param elements A list of elements for the configuration section + * @return The elements transformed into the Maven-native XML format + */ + public static Xpp3Dom configuration(Element... elements) { + Xpp3Dom dom = new Xpp3Dom("configuration"); + for (Element e : elements) { + dom.addChild(e.toDom()); + } + return dom; + } + + /** + * Defines the plugin without its version or extensions. + * + * @param groupId The group id + * @param artifactId The artifact id + * @return The plugin instance + */ + public static Plugin plugin(String groupId, String artifactId) { + return plugin(groupId, artifactId, null); + } + + /** + * Defines a plugin without extensions. + * + * @param groupId The group id + * @param artifactId The artifact id + * @param version The plugin version + * @return The plugin instance + */ + public static Plugin plugin(String groupId, String artifactId, String version) { + return plugin(groupId, artifactId, version, Collections.emptyList()); + } + + /** + * Defines a plugin. + * + * @param groupId The group id + * @param artifactId The artifact id + * @param version The plugin version + * @param dependencies The plugin extensions + * @return The plugin instance + */ + public static Plugin plugin(String groupId, String artifactId, String version, List dependencies) { + Plugin plugin = new Plugin(); + plugin.setArtifactId(artifactId); + plugin.setGroupId(groupId); + plugin.setVersion(version); + plugin.setDependencies(dependencies); + return plugin; + } + + /** + * Element wrapper class for configuration elements + */ + public static class Element { + private final Element[] children; + private final String name; + private final String text; + private final Attributes attributes; + + public Element(String name, Element... children) { + this(name, null, new Attributes(), children); + } + + public Element(String name, Attributes attributes, Element... children) { + this(name, null, attributes, children); + } + + public Element(String name, String text, Element... children) { + this.name = name; + this.text = text; + this.children = children; + this.attributes = new Attributes(); + } + + public Element(String name, String text, Attributes attributes, Element... children) { + this.name = name; + this.text = text; + this.children = children; + this.attributes = attributes; + } + + public Xpp3Dom toDom() { + Xpp3Dom dom = new Xpp3Dom(name); + if (text != null) { + dom.setValue(text); + } + for (Element e : children) { + dom.addChild(e.toDom()); + } + for(Attribute attribute : attributes.attributes) { + dom.setAttribute(attribute.name, attribute.value); + } + + return dom; + } + } + + /** + * Collection of attributes wrapper class + */ + public static class Attributes { + private List attributes; + + public Attributes(Attribute ... attributes) { + this.attributes = Arrays.asList(attributes); + } + } + + /** + * Attribute wrapper class + */ + public static class Attribute { + private final String name; + private final String value; + + public Attribute(String name, String value) { + this.name = name; + this.value = value; + } + } + +} diff --git a/maven/src/main/resources/extensions.json b/maven/src/main/resources/extensions.json new file mode 100644 index 0000000000000..765fa398b3f64 --- /dev/null +++ b/maven/src/main/resources/extensions.json @@ -0,0 +1,185 @@ +[ + { + "name": "Agroal", + "labels": [ + "agroal", + "database-connection-pool" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-agroal-deployment" + }, + { + "name": "Arc", + "labels": [ + "arc", + "cdi", + "dependency-injection", + "di" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-arc-deployment" + }, + { + "name": "Bean Validation", + "labels": [ + "bean-validation", + "validation" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-bean-validation" + }, + { + "name": "Fault Tolerance", + "labels": [ + "fault-tolerance", + "microprofile-fault-tolerance", + "circuit-breaker", + "bulkhead" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-fault-tolerance-deployment" + }, + { + "name": "Health", + "labels": [ + "health-check", + "health", + "microprofile-health", + "microprofile-health-check" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-health-deployment" + }, + { + "name": "JaxRS", + "labels": [ + "jaxrs", + "resteasy", + "web", + "rest" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-jaxrs-deployment" + }, + { + "name": "JPA", + "labels": [ + "jpa", + "hibernate" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-jpa-deployment" + }, + { + "name": "Logging", + "labels": [ + "log", + "logging", + "slf4j", + "log4j" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-logging-deployment" + }, + { + "name": "Metrics", + "labels": [ + "metrics", + "metric", + "prometheus", + "monitoring" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-metrics-deployment" + }, + { + "name": "OpenAPI", + "labels": [ + "openapi", + "open-api" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-openapi-deployment" + }, + { + "name": "Open Tracing", + "labels": [ + "open-tracing", + "tracing", + "distributed-tracing", + "jaeger" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-opentracing-deployment" + }, + { + "name": "Reactive Streams", + "labels": [ + "reactive-streams", + "microprofile-reactive-streams" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-reactive-streams-operators-deployment" + }, + { + "name": "Rest Client", + "labels": [ + "rest-client", + "web-client", + "microprofile-rest-client" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-rest-client-deployment" + }, + { + "name": "Scheduler", + "labels": [ + "scheduler", + "tasks", + "periodic-tasks" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-scheduler-deployment" + }, + { + "name": "Transactions", + "labels": [ + "transactions", + "transaction", + "tx", + "txs" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-transactions-deployment" + }, + { + "name": "Servlet", + "labels": [ + "servlet", + "undertow" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-undertow-deployment" + }, + { + "name": "Eclipse Vert.x", + "labels": [ + "eclipse-vert.x", + "vertx", + "vert.x" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-vertx-deployment" + }, + { + "name": "Web Sockets", + "labels": [ + "websocket", + "websockets", + "web-socket", + "web-sockets" + ], + "groupId": "org.jboss.shamrock", + "artifactId": "shamrock-websockets-deployment" + } +] diff --git a/maven/src/main/resources/shamrock-maven-plugin.properties b/maven/src/main/resources/shamrock-maven-plugin.properties new file mode 100644 index 0000000000000..d8ad9fa51d556 --- /dev/null +++ b/maven/src/main/resources/shamrock-maven-plugin.properties @@ -0,0 +1,20 @@ +# +# +# Copyright (c) 2016-2018 Red Hat, Inc. +# +# Red Hat licenses this file to you 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. +# +maven-compiler-plugin-version=3.8.0 +maven-jar-plugin-version=3.1.0 +maven-resources-plugin=3.1.0 +shamrock-version=${shamrock.version} diff --git a/maven/src/main/resources/templates/application-template.ftl b/maven/src/main/resources/templates/application-template.ftl new file mode 100644 index 0000000000000..09816500361e0 --- /dev/null +++ b/maven/src/main/resources/templates/application-template.ftl @@ -0,0 +1,11 @@ +<#if packageName??> +package ${packageName}; + + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +@ApplicationPath("${root_prefix}") +public class ShamrockApplication extends Application { + +} diff --git a/maven/src/main/resources/templates/pom-template.ftl b/maven/src/main/resources/templates/pom-template.ftl new file mode 100644 index 0000000000000..2bcf2f5fbbccf --- /dev/null +++ b/maven/src/main/resources/templates/pom-template.ftl @@ -0,0 +1,93 @@ + + 4.0.0 + ${mProjectGroupId} + ${mProjectArtifactId} + ${mProjectVersion} + + UTF-8 + UTF-8 + 1.8 + 1.8 + + ${shamrockVersion} + + + + + org.jboss.shamrock + shamrock-jaxrs-deployment + provided + ${r"${shamrock.version}"} + + + org.jboss.shamrock + shamrock-arc-deployment + provided + ${r"${shamrock.version}"} + + + org.jboss.shamrock + shamrock-logging-deployment + provided + ${r"${shamrock.version}"} + + + + + org.jboss.shamrock + shamrock-junit + ${r"${shamrock.version}"} + test + + + io.rest-assured + rest-assured + 3.2.0 + test + + + + + + + org.jboss.shamrock + shamrock-maven-plugin + ${r"${shamrock.version}"} + + + + build + + + + + + + + + + native + + + + org.jboss.shamrock + shamrock-maven-plugin + ${r"${shamrock.version}"} + + + + native-image + + + true + + + + + + + + + + diff --git a/maven/src/main/resources/templates/resource-template.ftl b/maven/src/main/resources/templates/resource-template.ftl new file mode 100644 index 0000000000000..35040f41fb6f5 --- /dev/null +++ b/maven/src/main/resources/templates/resource-template.ftl @@ -0,0 +1,18 @@ +<#if packageName??> +package ${packageName}; + + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("${path}") +public class ${classname} { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "hello"; + } +} diff --git a/maven/src/main/resources/templates/test-resource-template.ftl b/maven/src/main/resources/templates/test-resource-template.ftl new file mode 100644 index 0000000000000..d966c20d63d99 --- /dev/null +++ b/maven/src/main/resources/templates/test-resource-template.ftl @@ -0,0 +1,24 @@ +<#if packageName??> +package ${packageName}; + + +import org.jboss.shamrock.test.ShamrockTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@RunWith(ShamrockTest.class) +public class ${classname}Test { + + @Test + public void testHelloEndpoint() { + given() + .when().get("${root_prefix}${path}") + .then() + .statusCode(200) + .body(is("hello")); + } + +} diff --git a/maven/src/test/java/org/jboss/shamrock/maven/it/CreateProjectMojoIT.java b/maven/src/test/java/org/jboss/shamrock/maven/it/CreateProjectMojoIT.java new file mode 100644 index 0000000000000..fdd979b09641d --- /dev/null +++ b/maven/src/test/java/org/jboss/shamrock/maven/it/CreateProjectMojoIT.java @@ -0,0 +1,166 @@ +package org.jboss.shamrock.maven.it; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.shared.invoker.*; +import org.jboss.shamrock.maven.CreateProjectMojo; +import org.junit.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.Collections; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Clement Escoffier + */ +public class CreateProjectMojoIT extends MojoTestBase { + + private Invoker invoker; + private File testDir; + + private void init(File root) { + invoker = new DefaultInvoker(); + invoker.setWorkingDirectory(root); + String repo = System.getProperty("maven.repo"); + if (repo == null) { + repo = new File(System.getProperty("user.home"), ".m2/repository").getAbsolutePath(); + } + invoker.setLocalRepositoryDirectory(new File(repo)); + installPluginToLocalRepository(invoker.getLocalRepositoryDirectory()); + } + + @Test + public void testProjectGenerationFromScratch() throws MavenInvocationException, FileNotFoundException { + testDir = initEmptyProject("projects/project-generation"); + assertThat(testDir).isDirectory(); + init(testDir); + Properties properties = new Properties(); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + setup(properties); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(new File(testDir, "src/main/java")).isDirectory(); + } + + @Test + public void testProjectGenerationFromMinimalPom() throws Exception { + testDir = initProject("projects/simple-pom-it", "projects/project-generation-from-empty-pom"); + assertThat(testDir).isDirectory(); + init(testDir); + setup(new Properties()); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains(CreateProjectMojo.PLUGIN_ARTIFACTID, CreateProjectMojo.PLUGIN_VERSION_PROPERTY, CreateProjectMojo.PLUGIN_GROUPID); + assertThat(new File(testDir, "src/main/java")).isDirectory(); + } + + @Test + public void testProjectGenerationFromScratchWithResource() throws Exception { + testDir = initEmptyProject("projects/project-generation-with-resource"); + assertThat(testDir).isDirectory(); + init(testDir); + Properties properties = new Properties(); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + properties.put("className", "org.acme.MyResource.java"); + setup(properties); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(new File(testDir, "src/main/java")).isDirectory(); + assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/ShamrockApplication.java")).isFile(); + } + + @Test + public void testProjectGenerationFromMinimalPomWithResource() throws Exception { + testDir = initProject("projects/simple-pom-it", "projects/project-generation-from-empty-pom-with-resource"); + assertThat(testDir).isDirectory(); + init(testDir); + Properties properties = new Properties(); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + properties.put("className", "org.acme.MyResource.java"); + setup(properties); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains("shamrock.version"); + assertThat(new File(testDir, "src/main/java")).isDirectory(); + assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/ShamrockApplication.java")).isFile(); + } + + @Test + public void testProjectGenerationFromScratchWithExtensions() throws Exception { + testDir = initEmptyProject("projects/project-generation-with-resources-and-extension"); + assertThat(testDir).isDirectory(); + init(testDir); + Properties properties = new Properties(); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + properties.put("className", "org.acme.MyResource"); + properties.put("extensions", "web,metrics,missing"); + + setup(properties); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(new File(testDir, "src/main/java")).isDirectory(); + assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/ShamrockApplication.java")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains("shamrock-jaxrs-deployment", "shamrock-metrics-deployment").doesNotContain("missing"); + } + + @Test + public void testProjectGenerationFromScratchWithCustomDependencies() throws Exception { + testDir = initEmptyProject("projects/project-generation-with-resource-and-custom-deps"); + assertThat(testDir).isDirectory(); + init(testDir); + Properties properties = new Properties(); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + properties.put("className", "org.acme.MyResource"); + properties.put("extensions", "commons-io:commons-io:2.5"); + setup(properties); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/ShamrockApplication.java")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains("commons-io"); + } + + @Test + public void testProjectGenerationFromMinimalPomWithDependencies() throws Exception { + testDir = initProject("projects/simple-pom-it", + "projects/project-generation-from-minimal-pom-with-extensions"); + assertThat(testDir).isDirectory(); + init(testDir); + Properties properties = new Properties(); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + properties.put("className", "org.acme.MyResource"); + properties.put("extensions", "commons-io:commons-io:2.5"); + setup(properties); + assertThat(new File(testDir, "pom.xml")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); + assertThat(new File(testDir, "src/main/java/org/acme/ShamrockApplication.java")).isFile(); + assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) + .contains("commons-io"); + } + + private void setup(Properties params) throws MavenInvocationException, FileNotFoundException { + InvocationRequest request = new DefaultInvocationRequest(); + request.setBatchMode(true); + request.setGoals(Collections.singletonList( + CreateProjectMojo.PLUGIN_KEY + ":" + MojoTestBase.VERSION + ":create" + )); + request.setProperties(params); + getEnv().forEach(request::addShellEnvironment); + PrintStreamLogger logger = new PrintStreamLogger(new PrintStream(new FileOutputStream("build-create.log")), + InvokerLogger.DEBUG); + invoker.setLogger(logger); + invoker.execute(request); + } + +} diff --git a/maven/src/test/java/org/jboss/shamrock/maven/it/JarVerifier.java b/maven/src/test/java/org/jboss/shamrock/maven/it/JarVerifier.java new file mode 100644 index 0000000000000..2a3aaada95fdb --- /dev/null +++ b/maven/src/test/java/org/jboss/shamrock/maven/it/JarVerifier.java @@ -0,0 +1,59 @@ +package org.jboss.shamrock.maven.it; + +import sun.tools.jar.resources.jar; + +import java.io.*; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; + +import static org.assertj.core.api.Assertions.assertThat; + +class JarVerifier { + + private File jarFile; + + JarVerifier(File jarFile) { + this.jarFile = jarFile; + } + + void assertThatJarIsCreated() { + assertThat(jarFile).isNotNull(); + assertThat(jarFile).isFile(); + } + + void assertThatJarHasManifest() throws Exception { + try (JarFile jf = new JarFile(jarFile)){ + Manifest manifest = jf.getManifest(); + assertThat(manifest).isNotNull(); + } + } + + void assertThatFileIsContained(String file) throws Exception { + try (JarFile jf = new JarFile(jarFile)){ + assertThat(jf.getJarEntry(file)).isNotNull(); + } + } + + void assertThatFileIsNotContained(String file) throws Exception { + try (JarFile jf = new JarFile(jarFile)){ + assertThat(jf.getJarEntry(file)).isNull(); + } + } + + void assertThatFileContains(String path, String[] lines) throws Exception { + try (JarFile jf = new JarFile(jarFile)) { + ZipEntry entry = jf.getEntry(path); + assertThat(entry).isNotNull(); + String content = read(jf.getInputStream(entry)); + assertThat(content).containsSubsequence(lines); + } + } + + private static String read(InputStream input) throws IOException { + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) { + return buffer.lines().collect(Collectors.joining("\n")); + } + } +} diff --git a/maven/src/test/java/org/jboss/shamrock/maven/it/MojoTestBase.java b/maven/src/test/java/org/jboss/shamrock/maven/it/MojoTestBase.java new file mode 100644 index 0000000000000..70376883f8a0d --- /dev/null +++ b/maven/src/test/java/org/jboss/shamrock/maven/it/MojoTestBase.java @@ -0,0 +1,153 @@ +package org.jboss.shamrock.maven.it; + + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.apache.maven.shared.utils.StringUtils; +import org.jboss.shamrock.maven.CreateProjectMojo; +import org.jboss.shamrock.maven.utilities.MojoUtils; +import org.junit.BeforeClass; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MojoTestBase { + static String VERSION; + private static ImmutableMap VARIABLES; + + @BeforeClass + public static void init() { + VERSION = MojoUtils.getVersion(CreateProjectMojo.VERSION_PROP); + assertThat(VERSION).isNotNull(); + + VARIABLES = ImmutableMap.of( + "@project.groupId@", CreateProjectMojo.PLUGIN_GROUPID, + "@project.artifactId@", CreateProjectMojo.PLUGIN_ARTIFACTID, + "@project.version@", VERSION); + } + + boolean isCoverage() { + return System.getProperty("coverage") != null; + } + + + static File initProject(String name) { + return initProject(name, name); + } + + static File initEmptyProject(String name) { + File tc = new File("target/test-classes/" + name); + if (tc.isDirectory()) { + boolean delete = tc.delete(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, "test-classes deleted? " + delete); + } + boolean mkdirs = tc.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, "test-classes created? " + mkdirs); + return tc; + } + + public static File initProject(String name, String output) { + File tc = new File("target/test-classes"); + if (!tc.isDirectory()) { + boolean mkdirs = tc.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, "test-classes created? " + mkdirs); + } + + File in = new File("src/test/resources", name); + if (!in.isDirectory()) { + throw new RuntimeException("Cannot find directory: " + in.getAbsolutePath()); + } + + File out = new File(tc, output); + if (out.isDirectory()) { + FileUtils.deleteQuietly(out); + } + boolean mkdirs = out.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, out.getAbsolutePath() + " created? " + mkdirs); + try { + System.out.println("Copying " + in.getAbsolutePath() + " to " + out.getParentFile().getAbsolutePath()); + org.codehaus.plexus.util.FileUtils.copyDirectoryStructure(in, out); + } catch (IOException e) { + throw new RuntimeException("Cannot copy project resources", e); + } + return out; + } + + static void installPluginToLocalRepository(File local) { + File repo = new File(local, CreateProjectMojo.PLUGIN_GROUPID.replace(".", "/") + "/" + + CreateProjectMojo.PLUGIN_ARTIFACTID + "/" + MojoTestBase.VERSION); + if (!repo.isDirectory()) { + boolean mkdirs = repo.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, repo.getAbsolutePath() + " created? " + mkdirs); + } + + File plugin = new File("target", CreateProjectMojo.PLUGIN_ARTIFACTID + "-" + MojoTestBase.VERSION + ".jar"); + + try { + FileUtils.copyFileToDirectory(plugin, repo); + String installedPomName = CreateProjectMojo.PLUGIN_ARTIFACTID + "-" + MojoTestBase.VERSION + ".pom"; + FileUtils.copyFile(new File("pom.xml"), new File(repo, installedPomName)); + } catch (IOException e) { + throw new RuntimeException("Cannot copy the plugin jar, or the pom file, to the local repository", e); + } + } + + static void installJarToLocalRepository(String local, String name, File jar) { + File repo = new File(local, "org/acme/" + name + "/1.0"); + if (!repo.isDirectory()) { + boolean mkdirs = repo.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, repo.getAbsolutePath() + " created? " + mkdirs); + } + + try { + FileUtils.copyFileToDirectory(jar, repo); + String installedPomName = name + "-1.0.pom"; + FileUtils.write(new File(repo, installedPomName), "\n" + + " 4.0.0\n" + + " org.acme\n" + + " " + name + "\n" + + " 1.0\n" + + "", "UTF-8"); + } catch (IOException e) { + throw new RuntimeException("Cannot copy the jar, or the pom file, to the local repository", e); + } + } + + static void prepareProject(File testDir) throws IOException { + File pom = new File(testDir, "pom.xml"); + assertThat(pom).isFile(); + filter(pom, VARIABLES); + } + + static void filter(File input, Map variables) throws IOException { + assertThat(input).isFile(); + String data = FileUtils.readFileToString(input, "UTF-8"); + + for (Map.Entry token : variables.entrySet()) { + String value = String.valueOf(token.getValue()); + data = StringUtils.replace(data, token.getKey(), value); + } + FileUtils.write(input, data, "UTF-8"); + } + + Map getEnv() { + String opts = System.getProperty("mavenOpts"); + Map env = new HashMap<>(); + if (opts != null) { + env.put("MAVEN_OPTS", opts); + } + return env; + } +} diff --git a/maven/src/test/java/org/jboss/shamrock/maven/it/SetupVerifier.java b/maven/src/test/java/org/jboss/shamrock/maven/it/SetupVerifier.java new file mode 100644 index 0000000000000..cd762535660c4 --- /dev/null +++ b/maven/src/test/java/org/jboss/shamrock/maven/it/SetupVerifier.java @@ -0,0 +1,97 @@ +package org.jboss.shamrock.maven.it; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Profile; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.jboss.shamrock.maven.CreateProjectMojo; +import org.jboss.shamrock.maven.utilities.MojoUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Optional; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.jboss.shamrock.maven.CreateProjectMojo.PLUGIN_VERSION_PROPERTY_NAME; +import static org.junit.Assert.*; + +public class SetupVerifier { + + public static void assertThatJarExists(File archive) throws Exception { + JarVerifier jarVerifier = new JarVerifier(archive); + jarVerifier.assertThatJarIsCreated(); + jarVerifier.assertThatJarHasManifest(); + } + + public static void assertThatJarContainsFile(File archive, String file) throws Exception { + JarVerifier jarVerifier = new JarVerifier(archive); + jarVerifier.assertThatFileIsContained(file); + } + + public static void assertThatJarDoesNotContainFile(File archive, String file) throws Exception { + JarVerifier jarVerifier = new JarVerifier(archive); + jarVerifier.assertThatFileIsNotContained(file); + } + + public static void assertThatJarContainsFileWithContent(File archive, String path, String... lines) throws Exception { + JarVerifier jarVerifier = new JarVerifier(archive); + jarVerifier.assertThatFileContains(path, lines); + } + + public static void verifySetup(File pomFile) throws Exception { + assertNotNull("Unable to find pom.xml", pomFile); + MavenXpp3Reader xpp3Reader = new MavenXpp3Reader(); + Model model = xpp3Reader.read(new FileInputStream(pomFile)); + + MavenProject project = new MavenProject(model); + + Optional maybe = MojoUtils.hasPlugin(project, CreateProjectMojo.PLUGIN_KEY); + assertThat(maybe).isNotEmpty(); + + //Check if the properties have been set correctly + Properties properties = model.getProperties(); + assertThat(properties.containsKey(PLUGIN_VERSION_PROPERTY_NAME)).isTrue(); + + // Check plugin is set + Plugin plugin = maybe.orElseThrow(() -> new AssertionError("Plugin expected")); + assertThat(plugin).isNotNull().satisfies(p -> { + assertThat(p.getArtifactId()).isEqualTo(CreateProjectMojo.PLUGIN_ARTIFACTID); + assertThat(p.getGroupId()).isEqualTo(CreateProjectMojo.PLUGIN_GROUPID); + assertThat(p.getVersion()).isEqualTo(CreateProjectMojo.PLUGIN_VERSION_PROPERTY); + }); + + // Check build execution Configuration + assertThat(plugin.getExecutions()).hasSize(1).allSatisfy(execution -> { + assertThat(execution.getGoals()).containsExactly("build"); + assertThat(execution.getConfiguration()).isNull(); + }); + + + // Check profile + assertThat(model.getProfiles()).hasSize(1); + Profile profile = model.getProfiles().get(0); + assertThat(profile.getId()).isEqualTo("native"); + Plugin actual = profile.getBuild().getPluginsAsMap().get(CreateProjectMojo.PLUGIN_KEY); + assertThat(actual).isNotNull(); + assertThat(actual.getExecutions()).hasSize(1).allSatisfy(exec -> { + assertThat(exec.getGoals()).containsExactly("native-image"); + assertThat(exec.getConfiguration()).isInstanceOf(Xpp3Dom.class) + .satisfies(o -> assertThat(o.toString()).contains("enableHttpUrlHandler")); + }); + } + + public static void verifySetupWithVersion(File pomFile) throws Exception { + MavenXpp3Reader xpp3Reader = new MavenXpp3Reader(); + Model model = xpp3Reader.read(new FileInputStream(pomFile)); + + MavenProject project = new MavenProject(model); + Properties projectProps = project.getProperties(); + assertNotNull(projectProps); + assertFalse(projectProps.isEmpty()); + assertEquals(projectProps.getProperty("shamrock.version"), "0.0.0"); + } + +} diff --git a/maven/src/test/resources/projects/simple-pom-it/pom.xml b/maven/src/test/resources/projects/simple-pom-it/pom.xml new file mode 100644 index 0000000000000..6e05400d24d45 --- /dev/null +++ b/maven/src/test/resources/projects/simple-pom-it/pom.xml @@ -0,0 +1,9 @@ + + + + 4.0.0 + io.acme.it + acme-empty-pom + 0.0.1.BUILD-SNAPSHOT +