diff --git a/archetype-submodule-owa/pom.xml b/archetype-submodule-owa/pom.xml
new file mode 100644
index 00000000..480c4a9c
--- /dev/null
+++ b/archetype-submodule-owa/pom.xml
@@ -0,0 +1,38 @@
+
+
+
+ openmrs-sdk
+ org.openmrs.maven
+ 3.4.8-SNAPSHOT
+
+ 4.0.0
+
+ org.openmrs.maven.archetypes
+ openmrs-sdk-archetype-submodule-owa
+ 3.4.8-SNAPSHOT
+ maven-archetype
+
+ OpenMRS OWA Submodule Maven Archetype
+
+
+
+
+ org.apache.maven.archetype
+ archetype-packaging
+ 2.2
+
+
+
+
+
+
+ maven-archetype-plugin
+ 2.2
+
+
+
+
+
+
\ No newline at end of file
diff --git a/archetype-submodule-owa/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetype-submodule-owa/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644
index 00000000..272399d6
--- /dev/null
+++ b/archetype-submodule-owa/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+ assembly.xml
+
+
+
+
+
diff --git a/archetype-submodule-owa/src/main/resources/archetype-resources/assembly.xml b/archetype-submodule-owa/src/main/resources/archetype-resources/assembly.xml
new file mode 100644
index 00000000..f10fba9b
--- /dev/null
+++ b/archetype-submodule-owa/src/main/resources/archetype-resources/assembly.xml
@@ -0,0 +1,21 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+
+ false
+ dist
+
+ zip
+
+
+
+ dist
+ .
+
+ **/*
+ *
+
+
+
+
\ No newline at end of file
diff --git a/archetype-submodule-owa/src/main/resources/archetype-resources/pom.xml b/archetype-submodule-owa/src/main/resources/archetype-resources/pom.xml
new file mode 100644
index 00000000..f15742fe
--- /dev/null
+++ b/archetype-submodule-owa/src/main/resources/archetype-resources/pom.xml
@@ -0,0 +1,76 @@
+
+
+ 4.0.0
+
+ ${moduleArtifactId}-owa
+ ${groupId}
+ ${version}
+ pom
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ 1.0
+
+
+
+ install node and npm
+
+ install-node-and-npm
+
+ validate
+
+ v5.3.0
+ 3.9.6
+
+
+
+
+ npm install
+
+ npm
+
+ generate-resources
+
+ install
+
+
+
+
+ npm build
+
+ npm
+
+ generate-resources
+
+ run build:prod
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ zip-owa
+
+ single
+
+ package
+
+
+ assembly.xml
+
+
+
+
+
+
+
+
+
+
diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddFeature.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddFeature.java
new file mode 100644
index 00000000..dbbeed6f
--- /dev/null
+++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/AddFeature.java
@@ -0,0 +1,90 @@
+package org.openmrs.maven.plugins;
+
+import edu.emory.mathcs.backport.java.util.Arrays;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.SystemStreamLog;
+import org.openmrs.maven.plugins.utility.OwaHelper;
+import org.openmrs.maven.plugins.utility.Project;
+import org.openmrs.maven.plugins.utility.SDKConstants;
+import org.openmrs.maven.plugins.utility.XmlHelper;
+
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+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.plugin;
+import static org.twdata.maven.mojoexecutor.MojoExecutor.version;
+
+/**
+ * @goal add-feature
+ * @requiresProject false
+ */
+public class AddFeature extends AbstractTask {
+
+ public static final String OPEN_WEB_APP = "Open Web App";
+ public static final String[] OPTIONS = {OPEN_WEB_APP};
+
+ /**
+ * feature user wants to add
+ *
+ * @parameter expression="${feature}"
+ */
+ private String feature;
+
+ @Override
+ public void executeTask() throws MojoExecutionException, MojoFailureException {
+ if (Project.hasProject(new File(System.getProperty("user.dir")))) {
+ feature = wizard.promptForMissingValueWithOptions("What feature would you like to add?", feature, "feature", Arrays.asList(OPTIONS), null, null);
+ if(feature.equals(OPEN_WEB_APP) || feature.equals("owa")){
+ addOwaSubmodule();
+ } else {
+ throw new IllegalArgumentException("Adding feature "+feature+" is not available. Available features: "+OPTIONS);
+ }
+ } else {
+ throw new IllegalArgumentException("No project found in this directory. Please enter project's main directory and run this command again");
+ }
+ }
+
+ private void addOwaSubmodule() throws MojoExecutionException {
+ //apply changes to config.xml and main pom.xml
+ wizard.showMessage("Modifying pom.xml files...");
+ new XmlHelper().modifyXml(new File(mavenProject.getBasedir(), "omod"+File.separator+"pom.xml"), "archetype-submodule-owa/omod.pom.xml");
+ new XmlHelper().modifyXml(new File(mavenProject.getBasedir(), "omod"+File.separator+"src"+File.separator+"main"+File.separator+"resources"+File.separator+"config.xml"), "archetype-submodule-owa/config.xml");
+
+ //run archetype to create skeleton configuration for maven owa submodule
+ Properties properties = new Properties();
+ properties.setProperty("artifactId", "owa");
+ properties.setProperty("moduleArtifactId", mavenProject.getArtifactId());
+ properties.setProperty("groupId", mavenProject.getGroupId());
+ properties.setProperty("package", "owa");
+ properties.setProperty("version", mavenProject.getVersion());
+ mavenSession.getExecutionProperties().putAll(properties);
+
+ wizard.showMessage("Creating OWA submodule...");
+ executeMojo(
+ plugin(
+ groupId(SDKConstants.PLUGIN_ARCHETYPE_GROUP_ID),
+ artifactId(SDKConstants.PLUGIN_ARCHETYPE_ARTIFACT_ID),
+ version(SDKConstants.PLUGIN_ARCHETYPE_VERSION)
+ ),
+ goal("generate"), configuration(
+ element("interactiveMode", "false"),
+ element("archetypeArtifactId", "openmrs-sdk-archetype-submodule-owa"),
+ element("archetypeGroupId", "org.openmrs.maven.archetypes"),
+ element("archetypeVersion", SDKConstants.getSDKInfo().getVersion())
+ ),
+ executionEnvironment(mavenProject, mavenSession, pluginManager));
+
+ new OwaHelper(mavenSession, mavenProject, pluginManager, wizard)
+ .setInstallationDir(new File(mavenProject.getBasedir(),"owa"))
+ .createOwaProject();
+ }
+}
diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java
index 4b140db3..45537150 100644
--- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java
+++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/OwaHelper.java
@@ -6,7 +6,6 @@
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
-import org.openmrs.maven.plugins.utility.Wizard;
import org.twdata.maven.mojoexecutor.MojoExecutor;
import java.io.File;
@@ -31,12 +30,20 @@ public class OwaHelper {
private MavenSession session;
+ private File installationDir;
+
private MavenProject mavenProject;
private BuildPluginManager pluginManager;
private final Wizard wizard;
+ //enable chaining
+ public OwaHelper setInstallationDir(File installationDir) {
+ this.installationDir = installationDir;
+ return this;
+ }
+
public OwaHelper(MavenSession session, MavenProject mavenProject, BuildPluginManager pluginManager, Wizard wizard) {
this.session = session;
this.mavenProject = mavenProject;
@@ -45,7 +52,7 @@ public OwaHelper(MavenSession session, MavenProject mavenProject, BuildPluginMan
}
public void createOwaProject() throws MojoExecutionException {
- File owaDir = prepareOwaDir();
+ File owaDir = installationDir != null ? installationDir : prepareOwaDir();
wizard.showMessage("Creating OWA project in " + owaDir.getAbsolutePath() + "...\n");
@@ -66,7 +73,7 @@ private File prepareOwaDir() {
File owaDir = new File(System.getProperty("user.dir"));
boolean pathCorrect = false;
do {
- String owaDirValue = wizard.promptForValueIfMissingWithDefault("Please specify a directory for OWA project (a relative or absolute path)", null, null, null);
+ String owaDirValue = wizard.promptForValueIfMissingWithDefault("Please specify a directory for OWA project (a relative or absolute path)", owaDir.getName(), null, null);
File owaDirPath = new File(owaDirValue);
if (owaDirPath.isAbsolute()) {
owaDir = owaDirPath;
diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java
index db005702..44199c93 100644
--- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java
+++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/SDKConstants.java
@@ -27,6 +27,10 @@ public class SDKConstants {
public static final String PLUGIN_DEPENDENCIES_GROUP_ID = "org.apache.maven.plugins";
public static final String PLUGIN_DEPENDENCIES_ARTIFACT_ID = "maven-dependency-plugin";
public static final String PLUGIN_DEPENDENCIES_VERSION = "2.8";
+ // archetype plugin
+ public static final String PLUGIN_ARCHETYPE_GROUP_ID = "org.apache.maven.plugins";
+ public static final String PLUGIN_ARCHETYPE_ARTIFACT_ID = "maven-archetype-plugin";
+ public static final String PLUGIN_ARCHETYPE_VERSION = "2.4";
//docker plugin
public static final String PLUGIN_DOCKER_ARTIFACT_ID = "openmrs-sdk-docker-maven-plugin";
// release plugin
diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/XmlHelper.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/XmlHelper.java
new file mode 100644
index 00000000..76dccc68
--- /dev/null
+++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/utility/XmlHelper.java
@@ -0,0 +1,96 @@
+package org.openmrs.maven.plugins.utility;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.ReaderFactory;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.XMLWriter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringWriter;
+
+public class XmlHelper {
+
+ public void modifyXml(File targetfile, String modificationsPath) throws MojoExecutionException {
+
+ StringWriter writer = new StringWriter();
+
+ try (
+ InputStream modificationsStream = getClass().getClassLoader().getResourceAsStream(modificationsPath)
+ ){
+ Document targetDocument = getDocument(targetfile);
+ Document modificationsDocument = getDocument(modificationsStream);
+
+ Element targetRoot = targetDocument.getRootElement();
+ Element modificationsRoot = modificationsDocument.getRootElement();
+
+ if(!targetRoot.getName().equals(modificationsRoot.getName())){
+ throw new IllegalArgumentException(
+ "Target file and modifications file have differing root elements - target:"+targetRoot.getName()+", root: "+modificationsRoot.getName()
+ );
+ }
+
+ applyModifications(modificationsRoot, targetRoot);
+
+ XMLWriter xmlWriter = new XMLWriter( writer );
+ xmlWriter.write( targetDocument );
+
+ FileUtils.fileWrite(targetfile, writer.toString());
+
+ } catch (IOException | DocumentException e) {
+ throw new MojoExecutionException("Failed to create owa submodule", e);
+ }
+ }
+
+ protected void applyModifications(Element modificationsRoot, Element targetRoot) {
+ for(Object object : modificationsRoot.elements()){
+ if(object instanceof Element){
+ Element modificationElement = (Element) object;
+ String copy = modificationElement.attributeValue("sdk-copy");
+ if("true".equals(copy)){
+ addModification(targetRoot, modificationElement);
+ } else {
+ Element targetElement = targetRoot.element(modificationElement.getName());
+ if(targetElement == null) {
+ targetElement = targetRoot.addElement(modificationElement.getName());
+ }
+ applyModifications(modificationElement, targetElement);
+ }
+ }
+ }
+ }
+
+ protected void addChildren(Element modificationsRoot, Element targetRoot){
+ for(Object object : modificationsRoot.elements()){
+ if(object instanceof Element){
+ addModification(targetRoot, (Element) object);
+ }
+ }
+ }
+
+ protected void addModification(Element targetRoot, Element modificationElement) {
+ Element targetElement = targetRoot.addElement(modificationElement.getName());
+ targetElement.setText(modificationElement.getText());
+ //add children values
+ addChildren(modificationElement, targetElement);
+ }
+
+ protected Document getDocument(File file) throws IOException, DocumentException {
+ return getDocument(new FileInputStream(file));
+ }
+
+ protected Document getDocument(InputStream stream) throws IOException, DocumentException {
+ Reader targetReader = ReaderFactory.newXmlReader(stream);
+ Document doc = new SAXReader().read(targetReader);
+ IOUtils.closeQuietly(stream);
+ return doc;
+ }
+}
diff --git a/maven-plugin/src/main/resources/archetype-submodule-owa/config.xml b/maven-plugin/src/main/resources/archetype-submodule-owa/config.xml
new file mode 100644
index 00000000..57fb6342
--- /dev/null
+++ b/maven-plugin/src/main/resources/archetype-submodule-owa/config.xml
@@ -0,0 +1,6 @@
+
+
+
+ org.openmrs.module.owa
+
+
\ No newline at end of file
diff --git a/maven-plugin/src/main/resources/archetype-submodule-owa/omod.pom.xml b/maven-plugin/src/main/resources/archetype-submodule-owa/omod.pom.xml
new file mode 100644
index 00000000..9b9fe1b0
--- /dev/null
+++ b/maven-plugin/src/main/resources/archetype-submodule-owa/omod.pom.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ ${project.parent.groupId}
+ ${project.parent.artifactId}-owa
+ ${project.parent.version}
+ zip
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ include-owa
+ process-resources
+
+ copy
+
+
+
+
+ ${project.parent.groupId}
+ ${project.parent.artifactId}-owa
+ ${project.parent.version}
+ zip
+ ${project.parent.artifactId}.owa
+
+
+ ${project.build.directory}/classes/web/module/owas
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/maven-plugin/src/main/resources/archetype-submodule-owa/owa.gitignore b/maven-plugin/src/main/resources/archetype-submodule-owa/owa.gitignore
new file mode 100644
index 00000000..089485fb
--- /dev/null
+++ b/maven-plugin/src/main/resources/archetype-submodule-owa/owa.gitignore
@@ -0,0 +1,8 @@
+### npm owa ###
+owa/node_modules
+owa/dist
+owa/config.json
+owa/node
+owa/*.zip
+owa/etc
+###########
\ No newline at end of file
diff --git a/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/XmlHelperTest.java b/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/XmlHelperTest.java
new file mode 100644
index 00000000..7946883b
--- /dev/null
+++ b/maven-plugin/src/test/java/org/openmrs/maven/plugins/utility/XmlHelperTest.java
@@ -0,0 +1,66 @@
+package org.openmrs.maven.plugins.utility;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentFactory;
+import org.dom4j.Element;
+import org.dom4j.tree.DefaultElement;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+public class XmlHelperTest {
+
+ private XmlHelper helper = new XmlHelper();
+
+ @Test
+ public void testApplyChanges(){
+ Element modificationsRoot = new DefaultElement("project");
+ Element targetRoot = new DefaultElement("project");
+
+ Document target = DocumentFactory.getInstance().createDocument(targetRoot);
+ Document modifications = DocumentFactory.getInstance().createDocument(modificationsRoot);
+
+ Element targetDependencies = target.getRootElement().addElement("dependencies");
+
+ Element dependency = modifications.getRootElement()
+ .addElement("dependencies")
+ .addElement("dependency").addAttribute("sdk-copy", "true");
+
+ dependency.addElement("groupId").setText("org.openmrs.module");
+ dependency.addElement("artifactId").setText("metadtamapiing");
+ dependency.addElement("version").setText("2345");
+
+ assertThat(target.getRootElement().element("dependencies").element("dependency"), is(nullValue()));
+
+ helper.applyModifications(modifications.getRootElement(), target.getRootElement());
+
+ assertThat(target.getRootElement().element("dependencies").element("dependency"), is(notNullValue()));
+ assertThat(target.getRootElement().element("dependencies").element("dependency").element("groupId").getText(), is("org.openmrs.module"));
+ }
+
+ @Test
+ public void testApplyChanges2(){
+ Element modificationsRoot = new DefaultElement("project");
+ Element targetRoot = new DefaultElement("project");
+
+ Document target = DocumentFactory.getInstance().createDocument(targetRoot);
+ Document modifications = DocumentFactory.getInstance().createDocument(modificationsRoot);
+
+ Element dependency = modifications.getRootElement()
+ .addElement("dependencies")
+ .addElement("dependency").addAttribute("sdk-copy", "true");
+
+ dependency.addElement("groupId").setText("org.openmrs.module");
+ dependency.addElement("artifactId").setText("metadtamapiing");
+ dependency.addElement("version").setText("2345");
+
+ assertThat(target.getRootElement().element("dependencies"), is(nullValue()));
+
+ helper.applyModifications(modifications.getRootElement(), target.getRootElement());
+
+ assertThat(target.getRootElement().element("dependencies").element("dependency"), is(notNullValue()));
+ }
+}
diff --git a/pom.xml b/pom.xml
index 67df9b10..7c3604ea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,7 @@
archetype-module-refapp
archetype-module-platform
+ archetype-submodule-owa
docker-maven-plugin
maven-plugin
integration-tests