diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml index c9705fe7..2fabab6c 100644 --- a/maven-plugin/pom.xml +++ b/maven-plugin/pom.xml @@ -159,6 +159,12 @@ dom4j + + + org.projectlombok + lombok + + net.lingala.zip4j @@ -226,7 +232,35 @@ + + + src/test/resources + true + + integration-test/invokeIT/pom.xml + + + + src/test/resources + false + + integration-test/invokeIT/pom.xml + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + + + + test-jar + + + + org.apache.maven.plugins maven-plugin-plugin diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/AbstractTask.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AbstractTask.java index b7e19161..272849fe 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/AbstractTask.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AbstractTask.java @@ -1,5 +1,6 @@ package org.openmrs.maven.plugins; +import lombok.Getter; import org.apache.commons.lang.StringUtils; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; @@ -12,14 +13,15 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.settings.Settings; +import org.openmrs.maven.plugins.git.DefaultGitHelper; +import org.openmrs.maven.plugins.git.GitHelper; import org.openmrs.maven.plugins.model.Server; import org.openmrs.maven.plugins.utility.ConfigurationInstaller; import org.openmrs.maven.plugins.utility.DefaultJira; import org.openmrs.maven.plugins.utility.DistroHelper; -import org.openmrs.maven.plugins.git.DefaultGitHelper; -import org.openmrs.maven.plugins.git.GitHelper; import org.openmrs.maven.plugins.utility.DockerHelper; import org.openmrs.maven.plugins.utility.Jira; +import org.openmrs.maven.plugins.utility.MavenEnvironment; import org.openmrs.maven.plugins.utility.ModuleInstaller; import org.openmrs.maven.plugins.utility.NodeHelper; import org.openmrs.maven.plugins.utility.OwaHelper; @@ -140,10 +142,17 @@ public abstract class AbstractTask extends AbstractMojo { */ DockerHelper dockerHelper; + /** + * provides access to the current Maven environment + */ + @Getter + private MavenEnvironment mavenEnvironment; + public AbstractTask() { } public AbstractTask(AbstractTask other) { + this.mavenEnvironment = other.mavenEnvironment; this.mavenProject = other.mavenProject; this.mavenSession = other.mavenSession; this.wizard = other.wizard; @@ -155,6 +164,7 @@ public AbstractTask(AbstractTask other) { this.distroHelper = other.distroHelper; this.owaHelper = other.owaHelper; this.spaInstaller = other.spaInstaller; + this.configurationInstaller = other.configurationInstaller; this.gitHelper = other.gitHelper; this.dockerHelper = other.dockerHelper; this.settings = other.settings; @@ -166,6 +176,16 @@ public AbstractTask(AbstractTask other) { } public void initTask() { + if (mavenEnvironment == null) { + mavenEnvironment = new MavenEnvironment(); + mavenEnvironment.setMavenProject(mavenProject); + mavenEnvironment.setMavenSession(mavenSession); + mavenEnvironment.setSettings(settings); + mavenEnvironment.setArtifactMetadataSource(artifactMetadataSource); + mavenEnvironment.setArtifactFactory(artifactFactory); + mavenEnvironment.setPluginManager(pluginManager); + mavenEnvironment.setWizard(wizard); + } if (jira == null) { jira = new DefaultJira(); } diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/InvokeMethod.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/InvokeMethod.java new file mode 100644 index 00000000..bf21c5b9 --- /dev/null +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/InvokeMethod.java @@ -0,0 +1,93 @@ +package org.openmrs.maven.plugins; + +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.openmrs.maven.plugins.utility.MavenEnvironment; +import org.openmrs.maven.plugins.utility.Wizard; + +import java.lang.reflect.Method; + +/** + * The purpose is this Mojo is to support testing of helper utilities that are used by the various goals. This will: + * instantiate a new object of type className + * if this class has a setter property for a MavenExecution, this will be set + * it will then invoke the given testMethod on the testClass + */ +@Mojo(name = InvokeMethod.NAME) +@Getter @Setter +public class InvokeMethod extends AbstractTask { + + public static final String NAME = "invoke-method"; + + @Parameter(property = "className") + String className; + + @Parameter(property = "methodName") + String methodName; + + public void executeTask() throws MojoExecutionException { + + if (StringUtils.isBlank(className) || StringUtils.isBlank(methodName)) { + throw new MojoExecutionException("You must supply both a className and methodName parameter"); + } + + Wizard wizard = getMavenEnvironment().getWizard(); + + wizard.showMessage("Invoking: " + className + ":" + methodName); + + Class clazz; + try { + clazz = getClass().getClassLoader().loadClass(className); + } + catch (ClassNotFoundException e) { + throw new MojoExecutionException("Unable to load class", e); + } + wizard.showMessage("Class " + clazz.getName() + " loaded successfully"); + + Object instance; + try { + instance = clazz.getConstructor().newInstance(); + } catch (Exception e) { + throw new MojoExecutionException("Unable to instantiate class with " + getClass(), e); + } + wizard.showMessage("New instance of " + clazz.getName() + " instantiated"); + + Method setterMethod = null; + try { + setterMethod = clazz.getMethod("set" + MavenEnvironment.class.getSimpleName(), MavenEnvironment.class); + } + catch (NoSuchMethodException ignored) { + } + + if (setterMethod != null) { + try { + setterMethod.invoke(instance, getMavenEnvironment()); + wizard.showMessage("Instance populated with maven environment"); + } + catch (Exception e) { + throw new MojoExecutionException("Error executing method: " + methodName, e); + } + } + + Method method; + try { + method = clazz.getMethod(methodName); + } + catch (NoSuchMethodException e) { + throw new MojoExecutionException("Unable to find method: " + methodName, e); + } + wizard.showMessage("Got test method: " + method.getName()); + + try { + method.invoke(instance); + } + catch (Exception e) { + throw new MojoExecutionException("Error executing method: " + methodName, e); + } + wizard.showMessage("Method: " + method.getName() + " invoked successfully"); + } +} diff --git a/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractMavenIT.java b/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractMavenIT.java new file mode 100644 index 00000000..492edbce --- /dev/null +++ b/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractMavenIT.java @@ -0,0 +1,55 @@ +package org.openmrs.maven.plugins; + + +import lombok.Getter; +import lombok.Setter; +import org.junit.Before; +import org.openmrs.maven.plugins.utility.MavenEnvironment; + +import java.io.File; + +@Getter @Setter +public abstract class AbstractMavenIT extends AbstractSdkIT { + + MavenEnvironment mavenEnvironment = null; + + @Override + @Before + public void setup() throws Exception { + super.setup(); + mavenEnvironment = null; + } + + @Override + void addTestResources() throws Exception { + includePomFile("invokeIT", "pom.xml"); + } + + protected void executeTest(MavenTestFunction testFunction) throws Exception { + StackTraceElement invoker = Thread.currentThread().getStackTrace()[2]; + String className = invoker.getClassName(); + String testMethod = invoker.getMethodName(); + if (mavenEnvironment == null) { + addTaskParam("className", className); + addTaskParam("methodName", testMethod); + addTaskParam(BATCH_ANSWERS, getAnswers()); + addTaskParam("testMode", "true"); + String plugin = resolveSdkArtifact(); + verifier.executeGoal(plugin + ":" + InvokeMethod.NAME); + } + else { + testFunction.executeTest(); + } + } + + protected File getMavenTestDirectory() { + return new File(mavenEnvironment.getMavenProject().getBuild().getDirectory()); + } + + /** + * Simple interface that encapsulates a test that should be evaluated by tests that use this Mojo + */ + public interface MavenTestFunction { + void executeTest() throws Exception; + } +} diff --git a/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractSdkIT.java b/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractSdkIT.java index a9f8bbea..36a2f397 100644 --- a/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractSdkIT.java +++ b/maven-plugin/src/test/java/org/openmrs/maven/plugins/AbstractSdkIT.java @@ -83,25 +83,26 @@ public String resolveSdkArtifact() throws MojoExecutionException { return sdk.get("groupId")+":"+sdk.get("artifactId")+":"+sdk.get("version"); } - void includeTestResource(String fileName) throws Exception { - File source = getTestFile(TEST_DIRECTORY, fileName); - File target = new File(testDirectory, fileName); - if (source.isDirectory()) { - FileUtils.copyDirectory(source, testDirectory); - } - else { - FileUtils.copyFile(source, target); + void includeDistroPropertiesFile(String... paths) throws Exception { + Path sourcePath = testDirectoryPath.resolve(TEST_DIRECTORY); + for (String path : paths) { + sourcePath = sourcePath.resolve(path); } + Path targetPath = testDirectoryPath.resolve(DistroProperties.DISTRO_FILE_NAME); + FileUtils.copyFile(sourcePath.toFile(), targetPath.toFile()); } - void includeDistroPropertiesFile(String fileName) throws Exception { - File source = getTestFile(TEST_DIRECTORY, fileName); - File target = new File(testDirectory, DistroProperties.DISTRO_FILE_NAME); - FileUtils.copyFile(source, target); + void includePomFile(String... paths) throws Exception { + Path sourcePath = testDirectoryPath.resolve(TEST_DIRECTORY); + for (String path : paths) { + sourcePath = sourcePath.resolve(path); + } + Path targetPath = testDirectoryPath.resolve("pom.xml"); + FileUtils.copyFile(sourcePath.toFile(), targetPath.toFile()); } void addTestResources() throws Exception { - includeTestResource("pom.xml"); + includePomFile("pom.xml"); includeDistroPropertiesFile(DistroProperties.DISTRO_FILE_NAME); } diff --git a/maven-plugin/src/test/java/org/openmrs/maven/plugins/BuildIT.java b/maven-plugin/src/test/java/org/openmrs/maven/plugins/BuildIT.java index aecce120..f6e79d7e 100644 --- a/maven-plugin/src/test/java/org/openmrs/maven/plugins/BuildIT.java +++ b/maven-plugin/src/test/java/org/openmrs/maven/plugins/BuildIT.java @@ -1,5 +1,6 @@ package org.openmrs.maven.plugins; +import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.Test; import org.openmrs.maven.plugins.model.Project; @@ -13,7 +14,7 @@ public class BuildIT extends AbstractSdkIT { private String serverId; void addTestResources() throws Exception { - includeTestResource("buildIT"); + FileUtils.copyDirectory(getTestFile(TEST_DIRECTORY, "buildIT"), testDirectory); } @Before diff --git a/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/ArtifactHelperIT.java b/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/ArtifactHelperIT.java new file mode 100644 index 00000000..854c3fbe --- /dev/null +++ b/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/ArtifactHelperIT.java @@ -0,0 +1,48 @@ +package org.openmrs.maven.plugins.utility; + + +import lombok.Getter; +import lombok.Setter; +import org.apache.maven.it.VerificationException; +import org.junit.Test; +import org.openmrs.maven.plugins.AbstractMavenIT; +import org.openmrs.maven.plugins.model.Artifact; + +import java.io.File; + +import static org.junit.Assert.assertTrue; + +@Getter @Setter +public class ArtifactHelperIT extends AbstractMavenIT { + + @Test + public void test_downloadModuleWithDefaultName() throws Exception { + executeTest(() -> { + ArtifactHelper artifactHelper = new ArtifactHelper(getMavenEnvironment()); + Artifact artifact = new Artifact("idgen-omod", "4.14.0", "org.openmrs.module", "jar"); + artifactHelper.downloadArtifact(artifact, getMavenTestDirectory(), null); + File expectedFile = new File(getMavenTestDirectory(), "idgen-omod-4.14.0.jar"); + assertTrue(expectedFile.exists()); + }); + } + + @Test + public void downloadModuleWithSpecificName() throws Exception { + executeTest(() -> { + ArtifactHelper artifactHelper = new ArtifactHelper(getMavenEnvironment()); + Artifact artifact = new Artifact("idgen-omod", "4.14.0", "org.openmrs.module", "jar"); + artifactHelper.downloadArtifact(artifact, getMavenTestDirectory(), "idgen.omod"); + File expectedFile = new File(getMavenTestDirectory(), "idgen.omod"); + assertTrue(expectedFile.exists()); + }); + } + + @Test(expected = VerificationException.class) + public void downloadModuleThatDoesNotExist() throws Exception { + executeTest(() -> { + ArtifactHelper artifactHelper = new ArtifactHelper(getMavenEnvironment()); + Artifact artifact = new Artifact("idgen-omod", "4.0.0", "org.openmrs.module", "jar"); + artifactHelper.downloadArtifact(artifact, getMavenTestDirectory(), "idgen.omod"); + }); + } +} diff --git a/maven-plugin/src/test/resources/integration-test/invokeIT/pom.xml b/maven-plugin/src/test/resources/integration-test/invokeIT/pom.xml new file mode 100644 index 00000000..7e6f9327 --- /dev/null +++ b/maven-plugin/src/test/resources/integration-test/invokeIT/pom.xml @@ -0,0 +1,78 @@ + + 4.0.0 + + org.openmrs + openmrs-sdk-integration-test-project + 1.0 + pom + + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + ${project.groupId} + ${project.artifactId} + ${project.version} + tests + test-jar + compile + + + org.apache.maven.shared + maven-verifier + 1.7.2 + compile + + + junit + junit + 4.12 + jar + compile + + + org.hamcrest + hamcrest-core + 2.2 + compile + + + org.hamcrest + hamcrest-library + 2.2 + compile + + + org.mockito + mockito-core + 3.9.0 + compile + + + + + + + + + openmrs-repo + OpenMRS repository + https://mavenrepo.openmrs.org/nexus/content/repositories/public + + + + diff --git a/pom.xml b/pom.xml index 075485a5..4e704731 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ UTF-8 3.20.0 2.13.4.2 + 1.18.26 @@ -466,6 +467,13 @@ guava 32.1.2-jre + + + + org.projectlombok + lombok + ${lombokVersion} + diff --git a/sdk-commons/pom.xml b/sdk-commons/pom.xml index 6baf6266..60133553 100644 --- a/sdk-commons/pom.xml +++ b/sdk-commons/pom.xml @@ -86,6 +86,11 @@ mockito-core test + + org.projectlombok + lombok + provided + diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/ArtifactHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/ArtifactHelper.java new file mode 100644 index 00000000..039f2dc2 --- /dev/null +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/ArtifactHelper.java @@ -0,0 +1,76 @@ +package org.openmrs.maven.plugins.utility; + +import org.apache.commons.lang.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.openmrs.maven.plugins.model.Artifact; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.twdata.maven.mojoexecutor.MojoExecutor; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; +import static org.twdata.maven.mojoexecutor.MojoExecutor.element; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment; +import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; +import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.name; +import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; +import static org.twdata.maven.mojoexecutor.MojoExecutor.version; + +/** + * The purpose of this class is to handle all interactions with Maven that require retrieving artifacts from the Maven repository + */ +public class ArtifactHelper { + + private static final Logger log = LoggerFactory.getLogger(ArtifactHelper.class); + + final MavenEnvironment mavenEnvironment; + + public ArtifactHelper(MavenEnvironment mavenEnvironment) { + this.mavenEnvironment = mavenEnvironment; + } + + public File downloadArtifact(Artifact artifact, File directory) throws MojoExecutionException { + return downloadArtifact(artifact, directory, null); + } + + /** + * Downloads the given artifact to the given directory with the given fileName. If fileName is null, it will use the maven default. + * @param artifact the artifact to download + * @param directory the directory into which to download the artifact + * @param fileName the name of the file to save the artifact to (optional, if null will use the maven default) + * @return the downloaded File + * @throws MojoExecutionException + */ + public File downloadArtifact(Artifact artifact, File directory, String fileName) throws MojoExecutionException { + if (StringUtils.isBlank(fileName)) { + fileName = artifact.getArtifactId() + "-" + artifact.getVersion() + "." + artifact.getType(); + } + artifact.setDestFileName(fileName); + List artifactItems = new ArrayList<>(); + MojoExecutor.Element element = artifact.toElement(directory.getAbsolutePath()); + artifactItems.add(element); + executeMojo( + plugin( + groupId(SDKConstants.DEPENDENCY_PLUGIN_GROUP_ID), + artifactId(SDKConstants.DEPENDENCY_PLUGIN_ARTIFACT_ID), + version(SDKConstants.DEPENDENCY_PLUGIN_VERSION) + ), + goal("copy"), + configuration( + element(name("artifactItems"), artifactItems.toArray(new MojoExecutor.Element[0])) + ), + executionEnvironment( + mavenEnvironment.getMavenProject(), + mavenEnvironment.getMavenSession(), + mavenEnvironment.getPluginManager() + ) + ); + return new File(directory, artifact.getDestFileName()); + } +} diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/MavenEnvironment.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/MavenEnvironment.java new file mode 100644 index 00000000..9a06fbae --- /dev/null +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/MavenEnvironment.java @@ -0,0 +1,23 @@ +package org.openmrs.maven.plugins.utility; + +import lombok.Data; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.BuildPluginManager; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; + +/** + * Component that allows access to the Maven components set within the current execution environment + */ +@Data +public class MavenEnvironment { + private MavenProject mavenProject; + private MavenSession mavenSession; + private Settings settings; + private ArtifactMetadataSource artifactMetadataSource; + private ArtifactFactory artifactFactory; + private BuildPluginManager pluginManager; + private Wizard wizard; +}