From 89a43457f406b8c90f5d81e0ca6e37ceab49e55d Mon Sep 17 00:00:00 2001 From: Brian Long Date: Thu, 24 Mar 2022 20:20:36 +0100 Subject: [PATCH 1/3] major refactoring with testing; not done --- pom.xml | 16 + src/it/download-app/pom.xml | 14 +- src/it/translate-app/pom.xml | 11 +- src/it/unpack-app/pom.xml | 14 +- .../aps/modeling/ApsAppAccessibleGoal.java | 38 --- .../maven/aps/modeling/UnpackAppGoal.java | 186 ----------- .../{ => goal}/ApsAddressibleGoal.java | 28 +- .../modeling/goal/ApsAppAccessibleGoal.java | 57 ++++ .../maven/aps/modeling/goal/ApsAppGoal.java | 10 + .../aps/modeling/{ => goal}/ApsInfoGoal.java | 4 +- .../modeling/{ => goal}/DeployAppGoal.java | 15 +- .../aps/modeling/goal/DisablableGoal.java | 29 ++ .../modeling/{ => goal}/DownloadAppGoal.java | 26 +- .../aps/modeling/{ => goal}/PackAppGoal.java | 17 +- .../modeling/{ => goal}/TranslateAppGoal.java | 50 ++- .../aps/modeling/goal/UnpackAppGoal.java | 302 ++++++++++++++++++ .../ApsAppJsonTranslator.java | 19 +- .../{ => translator}/ApsAppTranslator.java | 60 +++- .../{ => translator}/ApsFileTranslator.java | 2 +- .../ApsFormJsonTranslator.java | 4 +- .../ApsProcessBpmnTranslator.java | 17 +- .../ApsProcessJsonTranslator.java | 16 +- .../util/ApsModelArrayNodeSorter.java | 22 ++ .../modeling/util/ApsModelListMapSorter.java | 23 ++ .../modeling/util/ArrayNodeObjectSorter.java | 82 +++++ .../maven/aps/modeling/{ => util}/Index.java | 10 +- .../aps/modeling/util/ListMapSorter.java | 70 ++++ .../aps/modeling/util/MapComparator.java | 49 +++ .../aps/modeling/{ => util}/ModelUtil.java | 73 +++-- .../modeling/util/ObjectNodeComparator.java | 58 ++++ .../modeling/{ => xml}/CDATAFlexSection.java | 2 +- .../{ => xml}/DomNamespaceContext.java | 2 +- 32 files changed, 939 insertions(+), 387 deletions(-) delete mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/ApsAppAccessibleGoal.java delete mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/UnpackAppGoal.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => goal}/ApsAddressibleGoal.java (65%) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppAccessibleGoal.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppGoal.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => goal}/ApsInfoGoal.java (85%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => goal}/DeployAppGoal.java (89%) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/goal/DisablableGoal.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => goal}/DownloadAppGoal.java (61%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => goal}/PackAppGoal.java (89%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => goal}/TranslateAppGoal.java (59%) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => translator}/ApsAppJsonTranslator.java (86%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => translator}/ApsAppTranslator.java (82%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => translator}/ApsFileTranslator.java (76%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => translator}/ApsFormJsonTranslator.java (91%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => translator}/ApsProcessBpmnTranslator.java (95%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => translator}/ApsProcessJsonTranslator.java (92%) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelArrayNodeSorter.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelListMapSorter.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/util/ArrayNodeObjectSorter.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => util}/Index.java (93%) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/util/ListMapSorter.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/util/MapComparator.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => util}/ModelUtil.java (84%) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/util/ObjectNodeComparator.java rename src/main/java/com/inteligr8/maven/aps/modeling/{ => xml}/CDATAFlexSection.java (93%) rename src/main/java/com/inteligr8/maven/aps/modeling/{ => xml}/DomNamespaceContext.java (97%) diff --git a/pom.xml b/pom.xml index 0088d9a..ce55cfa 100644 --- a/pom.xml +++ b/pom.xml @@ -184,6 +184,7 @@ jar public + true @@ -212,6 +213,13 @@ run + + ${aps-model.baseUrl} + ${aps-model.authType} + ${aps-model.basicAuth.username} + ${aps-model.basicAuth.password} + ${aps-model.appName} + ${run-it}/pom.xml @@ -223,6 +231,14 @@ + + + + inteligr8-releases + Inteligr8 Releases + https://repos.inteligr8.com/nexus/repository/inteligr8-public + + diff --git a/src/it/download-app/pom.xml b/src/it/download-app/pom.xml index 6f4c9dd..e3d8f43 100644 --- a/src/it/download-app/pom.xml +++ b/src/it/download-app/pom.xml @@ -12,10 +12,6 @@ Download App Plugin Tests - - FORMS Core - - @@ -29,9 +25,6 @@ download-app - - ${aps.app} - download-app-other @@ -40,8 +33,7 @@ download-app - ${aps.app} - ${basedir}/src/main/app + ${basedir}/src/main/aps @@ -57,8 +49,8 @@ - ${project.build.directory}/app/${aps.app}.zip - ${basedir}/src/main/app/${aps.app}.zip + ${project.build.directory}/aps/${aps-model.appName}.zip + ${basedir}/src/main/aps/${aps-model.appName}.zip diff --git a/src/it/translate-app/pom.xml b/src/it/translate-app/pom.xml index 0d70c2a..0c0bc72 100644 --- a/src/it/translate-app/pom.xml +++ b/src/it/translate-app/pom.xml @@ -12,10 +12,6 @@ Translate App Plugin Tests - - FORMS Core - - @@ -31,7 +27,7 @@ unpack-app - ${aps.app} + true @@ -41,8 +37,7 @@ translate-app - ${aps.app} - ${basedir}/src/main/app + ${basedir}/src/main/aps/app @@ -58,7 +53,7 @@ - ${basedir}/src/main/app/${aps.app}/${aps.app}.json + ${basedir}/src/main/aps/app/${aps-model.appName}/${aps-model.appName}.json diff --git a/src/it/unpack-app/pom.xml b/src/it/unpack-app/pom.xml index 085b737..df47b5b 100644 --- a/src/it/unpack-app/pom.xml +++ b/src/it/unpack-app/pom.xml @@ -12,10 +12,6 @@ Unpack App Plugin Tests - - FORMS Core - - @@ -30,9 +26,6 @@ download-app unpack-app - - ${aps.app} - unpack-app @@ -41,8 +34,7 @@ unpack-app - ${aps.app} - ${basedir}/src/main/app + ${basedir}/src/main/aps/app @@ -58,8 +50,8 @@ - ${project.build.directory}/app/${aps.app}/${aps.app}.json - ${basedir}/src/main/app/${aps.app}/${aps.app}.json + ${project.build.directory}/aps/app/${aps-model.appName}/${aps-model.appName}.json + ${basedir}/src/main/aps/app/${aps-model.appName}/${aps-model.appName}.json diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppAccessibleGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppAccessibleGoal.java deleted file mode 100644 index d59ff31..0000000 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppAccessibleGoal.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.inteligr8.maven.aps.modeling; - -import java.util.List; - -import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; -import com.inteligr8.alfresco.activiti.api.ModelsApi.ModelType; -import com.inteligr8.alfresco.activiti.model.Datum; -import com.inteligr8.alfresco.activiti.model.ResultListDataRepresentation; -import com.inteligr8.alfresco.activiti.model.Tenant; - -public abstract class ApsAppAccessibleGoal extends ApsAddressibleGoal { - - protected Long findTenantId() { - List tenants = this.getApsApi().getAdminApi().getTenants(); - if (tenants == null || tenants.isEmpty()) - return null; - return tenants.iterator().next().getId(); - } - - protected Long findAppByName(String appName) { - ApsPublicRestApiJerseyImpl api = this.getApsApi(); - - this.getLog().debug("Searching for APS App with name: " + appName); - ResultListDataRepresentation results = api.getModelsApi().get(null, null, ModelType.App.getId(), null); - this.getLog().debug("Found " + results.getTotal() + " APS Apps"); - for (Datum datum : results.getData()) { - String name = (String)datum.getAdditionalProperties().get("name"); - if (name.equals(appName)) { - Number id = (Number)datum.getAdditionalProperties().get("id"); - this.getLog().info("Found APS App ID: " + id); - return id == null ? null : id.longValue(); - } - } - - return null; - } - -} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/UnpackAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/UnpackAppGoal.java deleted file mode 100644 index ed755e0..0000000 --- a/src/main/java/com/inteligr8/maven/aps/modeling/UnpackAppGoal.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.inteligr8.maven.aps.modeling; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import javax.xml.transform.TransformerException; - -import org.apache.commons.io.IOUtils; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.codehaus.plexus.component.annotations.Component; -import org.xml.sax.SAXException; - -@Mojo( name = "unpack-app", threadSafe = true ) -@Component( role = org.apache.maven.plugin.Mojo.class ) -public class UnpackAppGoal extends AbstractMojo { - - @Parameter( property = "apsAppName", required = true ) - protected String apsAppName; - - @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/app" ) - protected File sourceDirectory; - - @Parameter( property = "targetDirectory", required = true, defaultValue = "${project.build.directory}/app" ) - protected File targetDirectory; - - @Parameter( property = "charset", required = true, defaultValue = "utf-8" ) - protected String charsetName = "utf-8"; - - @Parameter( property = "skip", required = true, defaultValue = "false" ) - protected boolean skip = false; - - private final int bufferSize = 128 * 1024; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - File sourceFile = this.validateSourceDirectory(); - - this.validateTargetDirectory(); - File appDirectory = this.validateAppDirectory(); - this.getLog().debug("Clearing directory: " + appDirectory); - this.clearDirectory(appDirectory); - - try { - this.unpack(sourceFile, appDirectory); - } catch (IOException ie) { - throw new MojoExecutionException("The downloaded APS App could not be unpacked", ie); - } - - this.normalizeFiles(appDirectory); - } - - protected File validateSourceDirectory() { - if (!this.sourceDirectory.exists()) { - throw new IllegalStateException("The 'sourceDirectory' does not exist: " + this.sourceDirectory); - } else if (!this.sourceDirectory.isDirectory()) { - throw new IllegalStateException("The 'sourceDirectory' is not a directory: " + this.sourceDirectory); - } - - File sourceFile = new File(this.sourceDirectory, this.apsAppName + ".zip"); - - if (!sourceFile.exists()) { - throw new IllegalStateException("The App file does not exists in the 'sourceDirectory': " + sourceFile); - } else if (!sourceFile.isFile()) { - throw new IllegalStateException("The App file is not a file: " + sourceFile); - } - - return sourceFile; - } - - protected void validateTargetDirectory() { - if (!this.targetDirectory.exists()) { - this.getLog().debug("Creating APS Apps directory: " + this.targetDirectory); - this.targetDirectory.mkdirs(); - } else if (!this.targetDirectory.isDirectory()) { - throw new IllegalStateException("The 'apsAppsDirectory' refers to a file and not a directory: " + this.targetDirectory); - } - } - - protected File validateAppDirectory() { - File appDirectory = new File(this.targetDirectory, this.apsAppName); - if (!appDirectory.exists()) { - this.getLog().debug("Creating APS App directory: " + appDirectory); - appDirectory.mkdirs(); - } else if (!appDirectory.isDirectory()) { - throw new IllegalStateException("The 'apsAppsDirectory' has a file where nothing or a directory is expected: " + appDirectory); - } - - return appDirectory; - } - - private void unpack(File appZip, File targetDirectory) throws IOException { - FileInputStream fistream = new FileInputStream(appZip); - BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); - try { - ZipInputStream zistream = new ZipInputStream(bistream); - try { - for (ZipEntry zentry = zistream.getNextEntry(); zentry != null; zentry = zistream.getNextEntry()) { - try { - String path = zentry.getName(); - if (!zentry.isDirectory()) { - this.getLog().debug("Unzipping path: " + zentry.getName()); - this.unzipToPath(zistream, Paths.get(path), targetDirectory); - } - } finally { - zistream.closeEntry(); - } - } - } finally { - zistream.close(); - } - } finally { - bistream.close(); - } - } - - private void unzipToPath(InputStream istream, Path relativePath, File targetDirectory) throws IOException { - Path path = targetDirectory.toPath().resolve(relativePath); - Path folderPath = path.getParent(); - Files.createDirectories(folderPath); - - FileOutputStream fostream = new FileOutputStream(path.toFile()); - BufferedOutputStream bostream = new BufferedOutputStream(fostream); - try { - IOUtils.copy(istream, bostream); - } finally { - bostream.close(); - } - } - - private void clearDirectory(File directory) { - for (File file : directory.listFiles()) { - if (file.isDirectory()) - this.clearDirectory(file); - file.delete(); - } - } - - private void normalizeFiles(File directory) throws MojoFailureException { - for (File file : directory.listFiles()) { - if (file.isDirectory()) { - this.normalizeFiles(file); - } else { - String ext = this.getFileExtension(file); - - try { - switch (ext) { - case "json": - ModelUtil.getInstance().json(file); - break; - case "xml": - case "bpmn": - ModelUtil.getInstance().xml(file); - break; - default: - file.delete(); - } - } catch (TransformerException | SAXException | IOException e) { - throw new MojoFailureException("The following file faild to be reformatted: " + file, e); - } - } - } - } - - private String getFileExtension(File file) { - int lastdot = file.getName().lastIndexOf('.'); - if (lastdot < 0) - return null; - - return file.getName().substring(lastdot+1).toLowerCase(); - } - -} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAddressibleGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java similarity index 65% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsAddressibleGoal.java rename to src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java index 5da59ff..5141963 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAddressibleGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java @@ -1,47 +1,47 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.goal; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.Parameter; import com.inteligr8.alfresco.activiti.ApsClientConfiguration; import com.inteligr8.alfresco.activiti.ApsClientJerseyImpl; import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; -public abstract class ApsAddressibleGoal extends AbstractMojo { +public abstract class ApsAddressibleGoal extends DisablableGoal { - @Parameter( property = "activitiAppBaseUrl", required = true, defaultValue = "http://localhost:8080/activiti-app" ) + @Parameter( property = "aps-model.baseUrl", required = true, defaultValue = "http://localhost:8080/activiti-app" ) protected String activitiAppBaseUrl; - @Parameter( property = "activitiAppAuth", required = true, defaultValue = "BASIC" ) + @Parameter( property = "aps-model.authType", required = true, defaultValue = "BASIC" ) protected String activitiAppAuthType; - @Parameter( property = "activitiAppBasicUsername", required = false, defaultValue = "admin" ) + @Parameter( property = "aps-model.basicAuth.username", required = false, defaultValue = "admin@app.activiti.com" ) protected String activitiAppAuthBasicUsername; - @Parameter( property = "activitiAppBasicPassword", required = false, defaultValue = "admin" ) + @Parameter( property = "aps-model.basicAuth.password", required = false, defaultValue = "admin" ) protected String activitiAppAuthBasicPassword; - @Parameter( property = "oAuthCode", required = false ) + @Parameter( property = "aps-model.oauth.code", required = false ) protected String oauthCode; - @Parameter( property = "oAuthClientId", required = false ) + @Parameter( property = "aps-model.oauth.clientId", required = false ) protected String oauthClientId; - @Parameter( property = "oAuthClientSecret", required = false ) + @Parameter( property = "aps-model.oauth.clientSecret", required = false ) protected String oauthClientSecret; - @Parameter( property = "oAuthUsername", required = false ) + @Parameter( property = "aps-model.oauth.username", required = false ) protected String oauthUsername; - @Parameter( property = "oAuthPassword", required = false ) + @Parameter( property = "aps-model.oauth.password", required = false ) protected String oauthPassword; - @Parameter( property = "oAuthTokenUrl", required = false ) + @Parameter( property = "aps-model.oauth.tokenUrl", required = false ) protected String oauthTokenUrl; private ApsPublicRestApiJerseyImpl api; public ApsClientConfiguration getApsClientConfiguration() { + this.getLog().debug("Configuring APS to URL: " + this.activitiAppBaseUrl); ApsClientConfiguration config = new ApsClientConfiguration(); config.setBaseUrl(this.activitiAppBaseUrl); switch (this.activitiAppAuthType.toUpperCase()) { @@ -64,7 +64,7 @@ public abstract class ApsAddressibleGoal extends AbstractMojo { config.setOAuthPassword(this.oauthPassword); break; default: - throw new IllegalArgumentException("The 'activitiAppAuth' configuration must be either 'Basic' or 'OAuth'"); + throw new IllegalArgumentException("The 'activitiAppAuthType' configuration must be either 'Basic' or 'OAuth'"); } return config; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppAccessibleGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppAccessibleGoal.java new file mode 100644 index 0000000..489852a --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppAccessibleGoal.java @@ -0,0 +1,57 @@ +package com.inteligr8.maven.aps.modeling.goal; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Parameter; + +import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; +import com.inteligr8.alfresco.activiti.api.ModelsApi.ModelType; +import com.inteligr8.alfresco.activiti.model.Datum; +import com.inteligr8.alfresco.activiti.model.ResultListDataRepresentation; +import com.inteligr8.alfresco.activiti.model.Tenant; + +public abstract class ApsAppAccessibleGoal extends ApsAddressibleGoal { + + @Parameter( property = "aps-model.appName", required = true ) + protected String apsAppName; + + protected Long findTenantId() { + List tenants = this.getApsApi().getAdminApi().getTenants(); + if (tenants == null || tenants.isEmpty()) + return null; + return tenants.iterator().next().getId(); + } + + protected Map findAppNameIds() { + ApsPublicRestApiJerseyImpl api = this.getApsApi(); + + Map apps = new HashMap<>(16); + + this.getLog().debug("Searching for all APS Apps"); + ResultListDataRepresentation results = api.getModelsApi().get("everyone", null, ModelType.App.getId(), null); + this.getLog().debug("Found " + results.getTotal() + " APS Apps"); + for (Datum datum : results.getData()) { + String name = (String)datum.getAdditionalProperties().get("name"); + Number id = (Number)datum.getAdditionalProperties().get("id"); + apps.put(name, id.longValue()); + } + + return apps; + } + + protected Long findAppId() throws MojoExecutionException { + return this.findAppIdByName(this.apsAppName); + } + + protected Long findAppIdByName(String appName) throws MojoExecutionException { + Map apps = this.findAppNameIds(); + Long appId = apps.get(this.apsAppName); + if (appId == null) + throw new MojoExecutionException("The APS App '' could not be found; valid apps: " + apps.keySet()); + return appId; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppGoal.java new file mode 100644 index 0000000..e60b081 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAppGoal.java @@ -0,0 +1,10 @@ +package com.inteligr8.maven.aps.modeling.goal; + +import org.apache.maven.plugins.annotations.Parameter; + +public abstract class ApsAppGoal extends DisablableGoal { + + @Parameter( property = "aps-model.appName", required = true ) + protected String apsAppName; + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsInfoGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsInfoGoal.java similarity index 85% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsInfoGoal.java rename to src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsInfoGoal.java index 07b8d01..d3764d5 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsInfoGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsInfoGoal.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.goal; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -12,7 +12,7 @@ import com.inteligr8.alfresco.activiti.model.AppVersion; public class ApsInfoGoal extends ApsAddressibleGoal { @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void executeEnabled() throws MojoExecutionException, MojoFailureException { AppVersion version = this.getApsApi() .getAppVersionApi() .get(); diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/DeployAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java similarity index 89% rename from src/main/java/com/inteligr8/maven/aps/modeling/DeployAppGoal.java rename to src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java index 07eee07..1a7d87e 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/DeployAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.goal; import java.io.BufferedInputStream; import java.io.File; @@ -19,26 +19,21 @@ import com.inteligr8.alfresco.activiti.model.AppDefinitionUpdateResultRepresenta @Component( role = org.apache.maven.plugin.Mojo.class ) public class DeployAppGoal extends ApsAppAccessibleGoal { - @Parameter( property = "apsAppName", required = true ) - protected String apsAppName; - - @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/app" ) + @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/aps/app" ) protected File sourceDirectory; @Parameter( property = "publish", required = true, defaultValue = "false" ) protected boolean publish = false; - @Parameter( property = "skip", required = true, defaultValue = "false" ) - protected boolean skip = false; - protected final int bufferSize = 128 * 1024; @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void executeEnabled() throws MojoExecutionException, MojoFailureException { File sourceFile = this.validateSourceDirectory(); + Long appId = this.findAppId(); + try { - Long appId = this.findAppByName(this.apsAppName); this.uploadApp(appId, sourceFile); } catch (IOException ie) { throw new MojoExecutionException("The downloaded APS App could not be unpacked", ie); diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/DisablableGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DisablableGoal.java new file mode 100644 index 0000000..ba1c368 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DisablableGoal.java @@ -0,0 +1,29 @@ +package com.inteligr8.maven.aps.modeling.goal; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Parameter; + +public abstract class DisablableGoal extends AbstractMojo { + + @Parameter( property = "aps-model.skip", required = true, defaultValue = "false" ) + protected boolean skip = false; + + protected boolean isEnabled() { + return !this.skip; + } + + protected boolean isDisabled() { + return this.skip; + } + + @Override + public final void execute() throws MojoExecutionException, MojoFailureException { + if (this.isEnabled()) + this.executeEnabled(); + } + + public abstract void executeEnabled() throws MojoExecutionException, MojoFailureException; + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/DownloadAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DownloadAppGoal.java similarity index 61% rename from src/main/java/com/inteligr8/maven/aps/modeling/DownloadAppGoal.java rename to src/main/java/com/inteligr8/maven/aps/modeling/goal/DownloadAppGoal.java index ce95e70..3b7001b 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/DownloadAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DownloadAppGoal.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.goal; import java.io.File; import java.io.IOException; @@ -16,23 +16,17 @@ import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; @Component( role = org.apache.maven.plugin.Mojo.class ) public class DownloadAppGoal extends ApsAppAccessibleGoal { - @Parameter( property = "apsAppName", required = true ) - protected String apsAppName; - - @Parameter( property = "targetDirectory", required = true, defaultValue = "${project.build.directory}/app" ) - protected File targetDirectory; - - @Parameter( property = "skip", required = true, defaultValue = "false" ) - protected boolean skip = false; + @Parameter( property = "aps-model.download.directory", required = true, defaultValue = "${project.build.directory}/aps" ) + protected File zipDirectory; @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void executeEnabled() throws MojoExecutionException, MojoFailureException { this.validateTargetDirectory(); - long appId = this.findAppByName(this.apsAppName); + Long appId = this.findAppId(); File appZip = this.downloadApp(appId); - File toAppZip = new File(this.targetDirectory, this.apsAppName + ".zip"); + File toAppZip = new File(this.zipDirectory, this.apsAppName + ".zip"); if (toAppZip.exists()) toAppZip.delete(); @@ -44,10 +38,10 @@ public class DownloadAppGoal extends ApsAppAccessibleGoal { } protected void validateTargetDirectory() { - if (!this.targetDirectory.exists()) { - this.getLog().debug("Creating APS Apps directory: " + this.targetDirectory); - this.targetDirectory.mkdirs(); - } else if (!this.targetDirectory.isDirectory()) { + if (!this.zipDirectory.exists()) { + this.getLog().debug("Creating APS Apps directory: " + this.zipDirectory); + this.zipDirectory.mkdirs(); + } else if (!this.zipDirectory.isDirectory()) { throw new IllegalStateException("The 'targetDirectory' refers to a file and not a directory"); } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/PackAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java similarity index 89% rename from src/main/java/com/inteligr8/maven/aps/modeling/PackAppGoal.java rename to src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java index fb24f77..2db4965 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/PackAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.goal; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -12,7 +12,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; @@ -21,24 +20,18 @@ import org.codehaus.plexus.component.annotations.Component; @Mojo( name = "pack-app", threadSafe = true ) @Component( role = org.apache.maven.plugin.Mojo.class ) -public class PackAppGoal extends AbstractMojo { +public class PackAppGoal extends ApsAppGoal { - @Parameter( property = "apsAppName", required = true ) - protected String apsAppName; - - @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/app" ) + @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/aps/app" ) protected File sourceDirectory; - @Parameter( property = "targetDirectory", required = true, defaultValue = "${project.build.directory}/app" ) + @Parameter( property = "targetDirectory", required = true, defaultValue = "${project.build.directory}/aps/app" ) protected File targetDirectory; - @Parameter( property = "skip", required = true, defaultValue = "false" ) - protected boolean skip = false; - protected final int bufferSize = 128 * 1024; @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void executeEnabled() throws MojoExecutionException, MojoFailureException { this.validateSourceDirectory(); File appDirectory = this.validateAppDirectory(); diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/TranslateAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java similarity index 59% rename from src/main/java/com/inteligr8/maven/aps/modeling/TranslateAppGoal.java rename to src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java index faa79ba..5f8a178 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/TranslateAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.goal; import java.io.File; import java.io.IOException; @@ -12,6 +12,8 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.annotations.Component; +import com.inteligr8.maven.aps.modeling.translator.ApsAppTranslator; + @Mojo( name = "translate-app", threadSafe = true ) @Component( role = org.apache.maven.plugin.Mojo.class ) public class TranslateAppGoal extends ApsAppAccessibleGoal { @@ -22,26 +24,20 @@ public class TranslateAppGoal extends ApsAppAccessibleGoal { @Parameter( defaultValue = "${session}", readonly = true ) protected MavenSession session; - @Parameter( property = "apsAppName", required = true ) - protected String apsAppName; - - @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/app" ) - protected File sourceDirectory; + @Parameter( property = "aps-model.app.directory", required = true, defaultValue = "${project.build.directory}/aps/app" ) + protected File unzipDirectory; - @Parameter( property = "targetDirectory", required = false, defaultValue = "${project.build.directory}/app" ) - protected File targetDirectory; + @Parameter( property = "aps-model.translatedApp.directory", required = false, defaultValue = "${project.build.directory}/aps/app" ) + protected File finalDirectory; - @Parameter( property = "overwrite", required = true, defaultValue = "true" ) + @Parameter( property = "aps-model.translate.overwrite", required = true, defaultValue = "true" ) protected boolean overwrite = true; - @Parameter( property = "charset", required = true, defaultValue = "utf-8" ) + @Parameter( property = "aps-model.translate.charset", required = true, defaultValue = "utf-8" ) protected String charsetName = "utf-8"; - @Parameter( property = "skip", required = true, defaultValue = "false" ) - protected boolean skip = false; - @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void executeEnabled() throws MojoExecutionException, MojoFailureException { File apsAppDirectory = this.validateApsAppDirectory(); File newApsAppDirectory = this.validateTargetDirectory(); @@ -63,34 +59,34 @@ public class TranslateAppGoal extends ApsAppAccessibleGoal { } protected File validateApsAppDirectory() throws MojoExecutionException { - if (!this.sourceDirectory.exists()) - throw new MojoExecutionException("The 'sourceDirectory' does not exist: " + this.sourceDirectory); - if (!this.sourceDirectory.isDirectory()) - throw new MojoExecutionException("The 'sourceDirectory' is not a directory: " + this.sourceDirectory); + if (!this.unzipDirectory.exists()) + throw new MojoExecutionException("The 'unzipDirectory' does not exist: " + this.unzipDirectory); + if (!this.unzipDirectory.isDirectory()) + throw new MojoExecutionException("The 'unzipDirectory' is not a directory: " + this.unzipDirectory); - File appDirectory = new File(this.sourceDirectory, this.apsAppName); + File appDirectory = new File(this.unzipDirectory, this.apsAppName); if (!appDirectory.exists()) - throw new MojoExecutionException("The 'apsAppName' does not exist in the 'apsAppsDirectory': " + appDirectory); + throw new MojoExecutionException("The 'apsAppName' does not exist in the 'unzipDirectory': " + appDirectory); if (!appDirectory.isDirectory()) - throw new MojoExecutionException("The 'apsAppName' is not a directory in the 'apsAppsDirectory': " + appDirectory); + throw new MojoExecutionException("The 'apsAppName' is not a directory in the 'unzipDirectory': " + appDirectory); return appDirectory; } protected File validateTargetDirectory() throws MojoExecutionException { - if (!this.targetDirectory.exists()) { - this.targetDirectory.mkdirs(); - } else if (!this.targetDirectory.isDirectory()) { - throw new MojoExecutionException("The 'targetDirectory' is not a directory: " + this.targetDirectory); + if (!this.finalDirectory.exists()) { + this.finalDirectory.mkdirs(); + } else if (!this.finalDirectory.isDirectory()) { + throw new MojoExecutionException("The 'finalDirectory' is not a directory: " + this.finalDirectory); } - File appDirectory = new File(this.targetDirectory, this.apsAppName); + File appDirectory = new File(this.finalDirectory, this.apsAppName); if (!appDirectory.exists()) { appDirectory.mkdirs(); } else if (!appDirectory.isDirectory()) { - throw new MojoExecutionException("The 'apsAppName' is not a directory in the 'targetDirectory': " + appDirectory); + throw new MojoExecutionException("The 'apsAppName' is not a directory in the 'finalDirectory': " + appDirectory); } return appDirectory; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java new file mode 100644 index 0000000..d597d4e --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java @@ -0,0 +1,302 @@ +package com.inteligr8.maven.aps.modeling.goal; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.xml.transform.TransformerException; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.component.annotations.Component; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.inteligr8.maven.aps.modeling.util.ApsModelArrayNodeSorter; +import com.inteligr8.maven.aps.modeling.util.ApsModelListMapSorter; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; + +@Mojo( name = "unpack-app", threadSafe = true ) +@Component( role = org.apache.maven.plugin.Mojo.class ) +public class UnpackAppGoal extends ApsAppGoal { + + @Parameter( property = "aps-model.download.directory", required = true, defaultValue = "${project.build.directory}/aps" ) + protected File zipDirectory; + + @Parameter( property = "aps-model.app.directory", required = true, defaultValue = "${project.build.directory}/aps/app" ) + protected File unzipDirectory; + + @Parameter( property = "aps-model.reformat", required = true, defaultValue = "true" ) + protected boolean reformat = true; + + @Parameter( property = "aps-model.normalize", required = true, defaultValue = "false" ) + protected boolean normalize; + + @Parameter( property = "aps-model.charset", required = true, defaultValue = "utf-8" ) + protected String charsetName = "utf-8"; + + private final int bufferSize = 128 * 1024; + + @Override + public void executeEnabled() throws MojoExecutionException, MojoFailureException { + this.validate(); + + File sourceFile = this.validateSourceDirectory(); + + this.validateTargetDirectory(); + File appDirectory = this.validateAppDirectory(); + this.getLog().debug("Clearing directory: " + appDirectory); + this.clearDirectory(appDirectory); + + try { + this.unpack(sourceFile, appDirectory); + } catch (IOException ie) { + throw new MojoExecutionException("The downloaded APS App could not be unpacked", ie); + } + + if (this.reformat) + this.reformatFiles(appDirectory); + } + + protected void validate() { + if (this.normalize && !this.reformat) { + this.getLog().warn("The reformat option is disabled, but normalization is enabled; enabling reformat anyway"); + this.reformat = true; + } + } + + protected File validateSourceDirectory() { + if (!this.zipDirectory.exists()) { + throw new IllegalStateException("The 'zipDirectory' does not exist: " + this.zipDirectory); + } else if (!this.zipDirectory.isDirectory()) { + throw new IllegalStateException("The 'zipDirectory' is not a directory: " + this.zipDirectory); + } + + File sourceFile = new File(this.zipDirectory, this.apsAppName + ".zip"); + + if (!sourceFile.exists()) { + throw new IllegalStateException("The App file does not exists in the 'zipDirectory': " + sourceFile); + } else if (!sourceFile.isFile()) { + throw new IllegalStateException("The App file is not a file: " + sourceFile); + } + + return sourceFile; + } + + protected void validateTargetDirectory() { + if (!this.unzipDirectory.exists()) { + this.getLog().debug("Creating unzip directory: " + this.unzipDirectory); + this.unzipDirectory.mkdirs(); + } else if (!this.unzipDirectory.isDirectory()) { + throw new IllegalStateException("The 'unzipDirectory' refers to a file and not a directory: " + this.unzipDirectory); + } + } + + protected File validateAppDirectory() { + File appDirectory = new File(this.unzipDirectory, this.apsAppName); + if (!appDirectory.exists()) { + this.getLog().debug("Creating APS App directory: " + appDirectory); + appDirectory.mkdirs(); + } else if (!appDirectory.isDirectory()) { + throw new IllegalStateException("The 'apsAppsDirectory' has a file where nothing or a directory is expected: " + appDirectory); + } + + return appDirectory; + } + + private void unpack(File appZip, File targetDirectory) throws IOException { + FileInputStream fistream = new FileInputStream(appZip); + BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); + try { + ZipInputStream zistream = new ZipInputStream(bistream); + try { + for (ZipEntry zentry = zistream.getNextEntry(); zentry != null; zentry = zistream.getNextEntry()) { + try { + String path = zentry.getName(); + if (!zentry.isDirectory()) { + this.getLog().debug("Unzipping path: " + zentry.getName()); + this.unzipToPath(zistream, Paths.get(path), targetDirectory); + } + } finally { + zistream.closeEntry(); + } + } + } finally { + zistream.close(); + } + } finally { + bistream.close(); + } + } + + private void unzipToPath(InputStream istream, Path relativePath, File targetDirectory) throws IOException { + Path path = targetDirectory.toPath().resolve(relativePath); + Path folderPath = path.getParent(); + Files.createDirectories(folderPath); + + FileOutputStream fostream = new FileOutputStream(path.toFile()); + BufferedOutputStream bostream = new BufferedOutputStream(fostream); + try { + IOUtils.copy(istream, bostream); + } finally { + bostream.close(); + } + } + + private void clearDirectory(File directory) { + for (File file : directory.listFiles()) { + if (file.isDirectory()) + this.clearDirectory(file); + file.delete(); + } + } + + private void reformatFiles(File directory) throws MojoFailureException { + for (File file : directory.listFiles()) { + if (file.isDirectory()) { + this.reformatFiles(file); + } else { + String ext = this.getFileExtension(file); + + try { + switch (ext) { + case "json": + this.getLog().debug("Reformatting file: " + file); + Map json = ModelUtil.getInstance().readJsonAsMap(file); + if (this.normalize) + this.normalizeJson(json); + ModelUtil.getInstance().writeJson(json, file); + break; + case "xml": + case "bpmn": + this.getLog().debug("Reformatting file: " + file); + Document xml = ModelUtil.getInstance().readXml(file); + if (this.normalize) + this.normalizeXml(xml); + ModelUtil.getInstance().writeXml(xml, file); + break; + default: + file.delete(); + } + } catch (TransformerException | SAXException | IOException e) { + throw new MojoFailureException("The following file faild to be reformatted: " + file, e); + } + } + } + } + + private boolean normalizeJson(Map jsonMap) { + boolean changed = false; + + try { + List> jsonModels = (List>)this.mapGetsListMaps(jsonMap, "definition", "models"); + changed = ApsModelListMapSorter.getInstance().sort(jsonModels, "name") || changed; + } catch (ClassCastException cce) { + // suppress + } catch (NullPointerException npe) { + // suppress + } + + try { + List> jsonChildShapes = (List>)this.mapGetsListMaps(jsonMap, "childShapes"); + changed = ApsModelListMapSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; + } catch (ClassCastException cce) { + // suppress + } catch (NullPointerException npe) { + // suppress + } + + try { + List> jsonChildShapes = (List>)this.mapGetsListMaps(jsonMap, "editorJson", "childShapes"); + changed = ApsModelListMapSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; + } catch (ClassCastException cce) { + // suppress + } catch (NullPointerException npe) { + // suppress + } + + return changed; + } + + @SuppressWarnings("unchecked") + private T mapGets(Map map, Class returnType, String... keys) { + for (int i = 0; i < keys.length - 1; i++) + map = (Map)map.get(keys[i]); + return (T)map.get(keys[keys.length-1]); + } + + @SuppressWarnings("unchecked") + private List mapGetsList(Map map, Class listType, String... keys) { + for (int i = 0; i < keys.length - 1; i++) + map = (Map)map.get(keys[i]); + return (List)map.get(keys[keys.length-1]); + } + + @SuppressWarnings("unchecked") + private List> mapGetsListMaps(Map map, String... keys) { + for (int i = 0; i < keys.length - 1; i++) + map = (Map)map.get(keys[i]); + return (List>)map.get(keys[keys.length-1]); + } + + private boolean normalizeJson(JsonNode json) { + boolean changed = false; + + try { + ArrayNode jsonModels = (ArrayNode)json.get("definition").get("models"); + changed = ApsModelArrayNodeSorter.getInstance().sort(jsonModels, "name") || changed; + } catch (ClassCastException cce) { + // suppress + } catch (NullPointerException npe) { + // suppress + } + + try { + ArrayNode jsonChildShapes = (ArrayNode)json.get("childShapes"); + changed = ApsModelArrayNodeSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; + } catch (ClassCastException cce) { + // suppress + } catch (NullPointerException npe) { + // suppress + } + + try { + ArrayNode jsonChildShapes = (ArrayNode)json.get("editorJson").get("childShapes"); + changed = ApsModelArrayNodeSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; + } catch (ClassCastException cce) { + // suppress + } catch (NullPointerException npe) { + // suppress + } + + return changed; + } + + private void normalizeXml(Document xml) { + } + + private String getFileExtension(File file) { + int lastdot = file.getName().lastIndexOf('.'); + if (lastdot < 0) + return null; + + return file.getName().substring(lastdot+1).toLowerCase(); + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java similarity index 86% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsAppJsonTranslator.java rename to src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java index 6b947a5..a514bac 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java @@ -1,7 +1,8 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Map; import org.slf4j.Logger; @@ -10,6 +11,8 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.inteligr8.alfresco.activiti.model.Group; +import com.inteligr8.maven.aps.modeling.util.Index; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; public class ApsAppJsonTranslator implements ApsFileTranslator { @@ -32,10 +35,9 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { @Override public void translateFile(File file, String modelName, Long modelId) throws IOException { this.logger.debug("Translating JSON file: {}", file); - - JsonNode descriptor = ModelUtil.getInstance().readJson(file); - + boolean changed = false; + JsonNode descriptor = ModelUtil.getInstance().readJson(file); for (JsonNode _jsonModel : descriptor.get("definition").get("models")) { ObjectNode jsonModel = (ObjectNode)_jsonModel; @@ -64,6 +66,11 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { this.logger.trace("The process '{}' ID does not change; leaving unchanged", fileProcessName); } } + + int fields = jsonModel.size(); + jsonModel.remove(Arrays.asList("version", "createdBy", "createdByFullName", "lastUpdatedBy", "lastUpdatedByFullName", "lastUpdated")); + if (jsonModel.size() < fields) + changed = true; } for (JsonNode _jsonIdentityInfo : descriptor.get("definition").get("publishIdentityInfo")) { @@ -77,14 +84,14 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { long apsOrgId = this.apsOrgIndex.get(fileOrgName).getId(); if (fileOrgId != apsOrgId) { - this.logger.debug("The organziation '{}' exists in APS with ID {}; changing descriptor", fileOrgName, apsOrgId); + this.logger.debug("The organization '{}' exists in APS with ID {}; changing descriptor", fileOrgName, apsOrgId); jsonGroup.put("id", apsOrgId); changed = true; } else { this.logger.trace("The organization '{}' ID does not change; leaving unchanged", fileOrgName); } } else { - this.logger.warn("The organziation '{}' does not exist in APS", fileOrgName); + this.logger.warn("The organization '{}' does not exist in APS", fileOrgName); // FIXME create the organization } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java similarity index 82% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsAppTranslator.java rename to src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java index 424d94e..17bbbd9 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsAppTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; @@ -13,18 +13,20 @@ import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.inteligr8.alfresco.activiti.ApsPublicRestApi; import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; import com.inteligr8.alfresco.activiti.api.ModelsApi.ModelType; import com.inteligr8.alfresco.activiti.model.Datum; import com.inteligr8.alfresco.activiti.model.Group; import com.inteligr8.alfresco.activiti.model.ResultListDataRepresentation; import com.inteligr8.alfresco.activiti.model.Tenant; +import com.inteligr8.maven.aps.modeling.util.Index; public class ApsAppTranslator { private final Logger logger = LoggerFactory.getLogger(ApsAppTranslator.class); private final Pattern filenamePattern = Pattern.compile("([^/]+)-([0-9]+)\\.(json|bpmn20\\.xml)"); - private final ApsPublicRestApiJerseyImpl api; + private final ApsPublicRestApi api; private final String appName; private final File appDirectory; private final File appDescriptor; @@ -71,22 +73,38 @@ public class ApsAppTranslator { this.logger.debug("APS App tenant ID: {}", tenantId); this.apsOrgIndex = this.buildApsGroupMap(tenantId); - this.logger.debug("APS App organizations: {}", this.apsOrgIndex); + this.logLarge("APS App organizations: {}", this.apsOrgIndex); this.apsProcessIndex = this.buildApsModelIndex(ModelType.Process); - this.logger.debug("APS process models: {}", this.apsProcessIndex); + this.logLarge("APS process models: {}", this.apsProcessIndex); this.apsFormIndex = this.buildApsModelIndex(ModelType.Form); - this.logger.debug("APS form models: {}", this.apsFormIndex); + this.logLarge("APS form models: {}", this.apsFormIndex); this.fileFormIndex = this.buildFileIndex("form-models", this.apsFormIndex, true); - this.logger.debug("File form models: {}", this.fileFormIndex); + this.logLarge("File form models: {}", this.fileFormIndex); this.fileProcessIndex = this.buildFileIndex("bpmn-models", this.apsProcessIndex, true); - this.logger.debug("File process models: {}", this.fileProcessIndex); + this.logLarge("File process models: {}", this.fileProcessIndex); this.fileSubprocessIndex = this.buildFileIndex("bpmn-subprocess-models", this.apsProcessIndex, true); - this.logger.debug("File subprocess models: {}", this.fileSubprocessIndex); + this.logLarge("File subprocess models: {}", this.fileSubprocessIndex); + } + + private void logLarge(String message, Map map) { + if (this.logger.isTraceEnabled()) { + this.logger.trace(message, map); + } else { + this.logger.debug(message, map.keySet()); + } + } + + private void logLarge(String message, Index index) { + if (this.logger.isTraceEnabled()) { + this.logger.trace(message, index); + } else { + this.logger.debug(message, index.valueSet()); + } } protected void translate() throws IOException { @@ -134,7 +152,8 @@ public class ApsAppTranslator { } protected Index buildApsModelIndex(ModelType modelType) { - ResultListDataRepresentation results = this.api.getModelsApi().get(null, null, modelType.getId(), null); + ResultListDataRepresentation results = this.api.getModelsApi().get("everyone", null, modelType.getId(), null); + this.logger.debug("APS {} models found: {} out of {}", modelType, results.getSize(), results.getTotal()); Index map = new Index<>(results.getSize().intValue(), false); @@ -143,11 +162,13 @@ public class ApsAppTranslator { Number defId = (Number)datum.getAdditionalProperties().get("id"); String defName = (String)datum.getAdditionalProperties().get("name"); map.put(defId.longValue(), defName); + + // FIXME add paging support } } catch (IllegalArgumentException iae) { throw new IllegalStateException("This goal does not support the current state of APS", iae); } - + return map; } @@ -157,19 +178,25 @@ public class ApsAppTranslator { } protected Index buildFileIndex(File modelDirectory, Index apsModelIndex, boolean renameFiles) { - if (!modelDirectory.exists()) + if (!modelDirectory.exists()) { + this.logger.debug("APS model directory doesn't exist; creating empty index: {}", modelDirectory); return new Index<>(0, false); + } + if (!modelDirectory.isDirectory()) throw new IllegalStateException("The APS model directory is actually a file: " + modelDirectory); Index fileModelIndex = new Index<>(apsModelIndex.size(), false); for (File modelFile : modelDirectory.listFiles()) { + this.logger.trace("Building index for model file: {}", modelFile); + Matcher matcher = this.filenamePattern.matcher(modelFile.getName()); String modelName = matcher.replaceFirst("$1"); String modelIdStr = matcher.replaceFirst("$2"); String modelExt = matcher.replaceFirst("$3"); long fileModelId = Long.parseLong(modelIdStr); + this.logger.trace("Building index for model {} ID: {}", modelName, fileModelId); fileModelIndex.put(fileModelId, modelName); @@ -184,7 +211,7 @@ public class ApsAppTranslator { File newModelFile = new File(modelFile.getParentFile(), modelName + "-" + apsModelId + "." + modelExt); modelFile.renameTo(newModelFile); } else { - this.logger.debug("The APS model '{}' and corresponding model file have matching IDs", modelName); + this.logger.debug("The APS model '{}' and corresponding model file '{}' have matching IDs", modelName, modelFile.getName()); } } } @@ -198,7 +225,14 @@ public class ApsAppTranslator { protected void translateModels(String modelsDirectoryName, Map translators) throws IOException { File modelsDirectory = new File(this.appDirectory, modelsDirectoryName); + if (!modelsDirectory.exists()) { + this.logger.debug("APS model directory doesn't exist; no models to translate: {}", modelsDirectory); + return; + } + for (File modelFile : modelsDirectory.listFiles()) { + this.logger.trace("Translating model: {}", modelFile); + Matcher matcher = this.filenamePattern.matcher(modelFile.getName()); if (!matcher.find()) { this.logger.warn("The '{}' folder has a file with an unexpected filename format; skipping: {}", modelsDirectoryName, modelFile); @@ -214,6 +248,8 @@ public class ApsAppTranslator { String modelName = matcher.group(1); Long modelId = new Long(matcher.group(2)); + this.logger.trace("Translating model {} ID: {}", modelName, modelId); + this.translate(translator, modelFile, modelName, modelId); } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsFileTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java similarity index 76% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsFileTranslator.java rename to src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java index 80d4862..b400a68 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsFileTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsFormJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java similarity index 91% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsFormJsonTranslator.java rename to src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java index 1d9228a..91b853a 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsFormJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; @@ -8,6 +8,8 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.inteligr8.maven.aps.modeling.util.Index; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; public class ApsFormJsonTranslator implements ApsFileTranslator { diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsProcessBpmnTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java similarity index 95% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsProcessBpmnTranslator.java rename to src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java index 61966ca..5a6a688 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsProcessBpmnTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; @@ -16,13 +16,16 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Attr; -import org.w3c.dom.CDATASection; +import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.inteligr8.alfresco.activiti.model.Group; +import com.inteligr8.maven.aps.modeling.util.Index; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; +import com.inteligr8.maven.aps.modeling.xml.CDATAFlexSection; public class ApsProcessBpmnTranslator implements ApsFileTranslator { @@ -72,7 +75,7 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { changed = this.translateTaskForm(formKeyElement) || changed; } - NodeList subprocessElements = ModelUtil.getInstance().xpath(definitionsElement, "//subProcess"); + NodeList subprocessElements = ModelUtil.getInstance().xpath(definitionsElement, "//tns:subProcess"); for (int n = 0; n < subprocessElements.getLength(); n++) { Element subprocessElement = (Element)subprocessElements.item(n); changed = this.translateSubprocess(subprocessElement) || changed; @@ -84,9 +87,9 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { changed = this.translateConditionDefinition(conditionDefinitionElement) || changed; } - NodeList conditionExpressions = ModelUtil.getInstance().xpath(definitionsElement, "//conditionExpression/text()"); + NodeList conditionExpressions = ModelUtil.getInstance().xpath(definitionsElement, "//tns:conditionExpression/text()"); for (int n = 0; n < conditionExpressions.getLength(); n++) { - CDATASection conditionExpression = (CDATASection)conditionExpressions.item(n); + CharacterData conditionExpression = (CharacterData)conditionExpressions.item(n); changed = this.translateConditionExpression(conditionExpression) || changed; } @@ -146,7 +149,7 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { if (apsOrgId != orgId) { this.logger.debug("The organization '{}' exists in APS with ID {}; changing model", groupName, apsOrgId); groupNameElement = (Element)groupNameElement.getOwnerDocument().renameNode(groupNameElement, - groupNameElement.getNamespaceURI(), "group-info-name-" + apsOrgId); + groupNameElement.getNamespaceURI(), "modeler:group-info-name-" + apsOrgId); changed = true; } else { this.logger.trace("The organization '{}' ID does not change; leaving unchanged", groupName); @@ -282,7 +285,7 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { return changed; } - private boolean translateConditionExpression(CDATASection conditionExpressionCdata) throws XPathExpressionException { + private boolean translateConditionExpression(CharacterData conditionExpressionCdata) throws XPathExpressionException { this.logger.trace("Translating condition expression for outcome form elements: {}", conditionExpressionCdata.getParentNode().getParentNode().getAttributes().getNamedItem("id")); boolean changed = false; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ApsProcessJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java similarity index 92% rename from src/main/java/com/inteligr8/maven/aps/modeling/ApsProcessJsonTranslator.java rename to src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java index f61b020..25f1bcf 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ApsProcessJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; @@ -11,6 +11,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.inteligr8.alfresco.activiti.model.Group; +import com.inteligr8.maven.aps.modeling.util.Index; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; public class ApsProcessJsonTranslator implements ApsFileTranslator { @@ -47,13 +49,23 @@ public class ApsProcessJsonTranslator implements ApsFileTranslator { } private boolean translateResourceId(ObjectNode jsonDescriptor, Long apsProcessId) { + ObjectNode resourceIdParentJson = jsonDescriptor; + JsonNode jsonResourceId = jsonDescriptor.get("resourceId"); + if (jsonResourceId == null || jsonResourceId.isNull()) { + JsonNode jsonEditorJson = jsonDescriptor.get("editorJson"); + if (jsonEditorJson != null && jsonEditorJson.isObject()) { + resourceIdParentJson = (ObjectNode)jsonEditorJson; + jsonResourceId = jsonEditorJson.get("resourceId"); + } + } + if (jsonResourceId == null || jsonResourceId.isNull()) { this.logger.trace("The process 'resourceId' is not defined; leaving unchanged", apsProcessId); return false; } else if (jsonResourceId.asLong() != apsProcessId) { this.logger.debug("The process is found in APS with ID {}; changing 'resourceId' in descriptor", apsProcessId); - jsonDescriptor.put("resourceId", apsProcessId); + resourceIdParentJson.put("resourceId", apsProcessId); return true; } else { this.logger.trace("The process ID {} does not change; leaving unchanged", apsProcessId); diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelArrayNodeSorter.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelArrayNodeSorter.java new file mode 100644 index 0000000..11c49c3 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelArrayNodeSorter.java @@ -0,0 +1,22 @@ +package com.inteligr8.maven.aps.modeling.util; + +import com.fasterxml.jackson.databind.node.ArrayNode; + +public class ApsModelArrayNodeSorter extends ArrayNodeObjectSorter { + + private static ApsModelArrayNodeSorter INSTANCE = new ApsModelArrayNodeSorter(); + + public static ApsModelArrayNodeSorter getInstance() { + return INSTANCE; + } + + + + protected ApsModelArrayNodeSorter() { + } + + public boolean sort(ArrayNode arrayNode, String jsonObjectFieldName) { + return this.sort(arrayNode, new ObjectNodeComparator(jsonObjectFieldName)); + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelListMapSorter.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelListMapSorter.java new file mode 100644 index 0000000..212a048 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ApsModelListMapSorter.java @@ -0,0 +1,23 @@ +package com.inteligr8.maven.aps.modeling.util; + +import java.util.List; +import java.util.Map; + +public class ApsModelListMapSorter extends ListMapSorter { + + private static ApsModelListMapSorter INSTANCE = new ApsModelListMapSorter(); + + public static ApsModelListMapSorter getInstance() { + return INSTANCE; + } + + + + protected ApsModelListMapSorter() { + } + + public boolean sort(List> jsonArrayAsMap, String jsonObjectFieldName) { + return this.sort(jsonArrayAsMap, new MapComparator(jsonObjectFieldName)); + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/ArrayNodeObjectSorter.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ArrayNodeObjectSorter.java new file mode 100644 index 0000000..b68297e --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ArrayNodeObjectSorter.java @@ -0,0 +1,82 @@ +package com.inteligr8.maven.aps.modeling.util; + +import java.util.Comparator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; + +public class ArrayNodeObjectSorter { + + private static ArrayNodeObjectSorter INSTANCE = new ArrayNodeObjectSorter(); + + public static ArrayNodeObjectSorter getInstance() { + return INSTANCE; + } + + + + protected ArrayNodeObjectSorter() { + } + + public boolean sort(ArrayNode arrayNode, Comparator comparator) { + if (arrayNode.isEmpty()) + return false; + + boolean sorted = false; + boolean changed = false; + int index = 0; + JsonNode node = arrayNode.get(index); + + // simple bubble sort + while (!sorted) { + sorted = true; + int nextIndex = index + 1; + + // forward bubble + while (nextIndex < arrayNode.size()) { + JsonNode nextNode = arrayNode.get(nextIndex); + int c = comparator.compare(node, nextNode); + + if (c == 0) { + node = nextNode; + } else if (c < 0) { + node = nextNode; + } else { + arrayNode.insert(index, arrayNode.remove(nextIndex)); + sorted = false; + changed = true; + } + + index = nextIndex; + nextIndex = index + 1; + } + + if (sorted) + break; + sorted = true; + nextIndex = index - 1; + + // reverse bubble + while (nextIndex >= 0) { + JsonNode nextNode = arrayNode.get(nextIndex); + int c = comparator.compare(nextNode, node); + + if (c == 0) { + node = nextNode; + } else if (c < 0) { + node = nextNode; + } else { + arrayNode.insert(nextIndex, arrayNode.remove(index)); + sorted = false; + changed = true; + } + + index = nextIndex; + nextIndex = index - 1; + } + } + + return changed; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/Index.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/Index.java similarity index 93% rename from src/main/java/com/inteligr8/maven/aps/modeling/Index.java rename to src/main/java/com/inteligr8/maven/aps/modeling/util/Index.java index 6eb7f79..631e37f 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/Index.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/Index.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.util; import java.util.HashMap; import java.util.HashSet; @@ -33,6 +33,14 @@ public class Index { return this.forwardMap.get(key); } + public Set keySet() { + return this.forwardMap.keySet(); + } + + public Set valueSet() { + return this.reverseMap.keySet(); + } + public Set getKeys(V value) { return this.reverseMap.get(value); } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/ListMapSorter.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ListMapSorter.java new file mode 100644 index 0000000..ae8b909 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ListMapSorter.java @@ -0,0 +1,70 @@ +package com.inteligr8.maven.aps.modeling.util; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public class ListMapSorter { + + public boolean sort(List> listOfMaps, Comparator> comparator) { + if (listOfMaps.isEmpty()) + return false; + + boolean sorted = false; + boolean changed = false; + int index = 0; + Map map = listOfMaps.get(index); + + // simple bubble sort + while (!sorted) { + sorted = true; + int nextIndex = index + 1; + + // forward bubble + while (nextIndex < listOfMaps.size()) { + Map nextMap = listOfMaps.get(nextIndex); + int c = comparator.compare(map, nextMap); + + if (c == 0) { + map = nextMap; + } else if (c < 0) { + map = nextMap; + } else { + listOfMaps.add(index, listOfMaps.remove(nextIndex)); + sorted = false; + changed = true; + } + + index = nextIndex; + nextIndex = index + 1; + } + + if (sorted) + break; + sorted = true; + nextIndex = index - 1; + + // reverse bubble + while (nextIndex >= 0) { + Map nextMap = listOfMaps.get(nextIndex); + int c = comparator.compare(nextMap, map); + + if (c == 0) { + map = nextMap; + } else if (c < 0) { + map = nextMap; + } else { + listOfMaps.add(nextIndex, listOfMaps.remove(index)); + sorted = false; + changed = true; + } + + index = nextIndex; + nextIndex = index - 1; + } + } + + return changed; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/MapComparator.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/MapComparator.java new file mode 100644 index 0000000..b5ebec1 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/MapComparator.java @@ -0,0 +1,49 @@ +package com.inteligr8.maven.aps.modeling.util; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public class MapComparator implements Comparator> { + + //private final Logger logger = LoggerFactory.getLogger(ArrayOfObjectNodeComparator.class); + private final List keys; + + public MapComparator(@SuppressWarnings("unchecked") K... keys) { + this(Arrays.asList(keys)); + } + + public MapComparator(List keys) { + this.keys = keys; + } + + @Override + public int compare(Map m1, Map m2) { + for (K key : keys) { + V value1 = m1.get(key); + V value2 = m2.get(key); + + if (value1 == null && value2 == null) + continue; + + if (value1 == null) + return 1; + if (value2 == null) + return -1; + if (!value1.getClass().equals(value2.getClass())) + return 0; + + if (value1 instanceof Boolean) { + return Boolean.compare((Boolean)value1, (Boolean)value2); + } if (value1 instanceof Number) { + return Double.compare(((Number)value1).doubleValue(), ((Number)value2).doubleValue()); + } if (value1 instanceof String) { + return ((String)value1).compareTo((String)value2); + } + } + + return 0; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/ModelUtil.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java similarity index 84% rename from src/main/java/com/inteligr8/maven/aps/modeling/ModelUtil.java rename to src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java index 2e1f7b6..38bed1d 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/ModelUtil.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -8,6 +8,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; +import java.util.Map; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; @@ -36,6 +38,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.inteligr8.maven.aps.modeling.xml.CDATAFlexSection; +import com.inteligr8.maven.aps.modeling.xml.DomNamespaceContext; public class ModelUtil { @@ -85,23 +89,8 @@ public class ModelUtil { public void json(File inplaceFile) throws IOException { - JsonNode json; - - FileInputStream fistream = new FileInputStream(inplaceFile); - BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); - try { - json = this.readJson(bistream); - } finally { - bistream.close(); - } - - FileOutputStream fostream = new FileOutputStream(inplaceFile); - BufferedOutputStream bostream = new BufferedOutputStream(fostream, this.bufferSize); - try { - this.writeJson(json, bostream); - } finally { - bostream.close(); - } + JsonNode json = this.readJson(inplaceFile); + this.writeJson(json, inplaceFile); } public void json(File sourceFile, File targetFile) throws IOException { @@ -130,7 +119,7 @@ public class ModelUtil { return this.readJson(file, JsonNode.class); } - public T readJson(File file, Class type) throws IOException { + public T readJson(File file, Class type) throws IOException { FileInputStream fistream = new FileInputStream(file); BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); try { @@ -140,14 +129,44 @@ public class ModelUtil { } } + public List readJsonAsList(File file) throws IOException { + FileInputStream fistream = new FileInputStream(file); + BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); + try { + return this.readJsonAsList(bistream); + } finally { + bistream.close(); + } + } + + public Map readJsonAsMap(File file) throws IOException { + FileInputStream fistream = new FileInputStream(file); + BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); + try { + return this.readJsonAsMap(bistream); + } finally { + bistream.close(); + } + } + public JsonNode readJson(InputStream istream) throws IOException { return this.om.readTree(istream); } - public T readJson(InputStream istream, Class type) throws IOException { + public T readJson(InputStream istream, Class type) throws IOException { return this.om.readValue(istream, type); } + @SuppressWarnings("unchecked") + public List readJsonAsList(InputStream istream) throws IOException { + return this.om.readValue(istream, List.class); + } + + @SuppressWarnings("unchecked") + public Map readJsonAsMap(InputStream istream) throws IOException { + return this.om.readValue(istream, Map.class); + } + public void writeJson(JsonNode json, File file) throws IOException { FileOutputStream fostream = new FileOutputStream(file); BufferedOutputStream bostream = new BufferedOutputStream(fostream, this.bufferSize); @@ -158,10 +177,24 @@ public class ModelUtil { } } + public void writeJson(Map map, File file) throws IOException { + FileOutputStream fostream = new FileOutputStream(file); + BufferedOutputStream bostream = new BufferedOutputStream(fostream, this.bufferSize); + try { + this.writeJson(map, bostream); + } finally { + bostream.close(); + } + } + public void writeJson(JsonNode json, OutputStream ostream) throws IOException { this.om.writeValue(ostream, json); } + public void writeJson(Map map, OutputStream ostream) throws IOException { + this.om.writeValue(ostream, map); + } + public void xml(File inplaceFile) throws IOException, SAXException, TransformerException { diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/ObjectNodeComparator.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ObjectNodeComparator.java new file mode 100644 index 0000000..729639c --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ObjectNodeComparator.java @@ -0,0 +1,58 @@ +package com.inteligr8.maven.aps.modeling.util; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import com.fasterxml.jackson.databind.JsonNode; + +public class ObjectNodeComparator implements Comparator { + + //private final Logger logger = LoggerFactory.getLogger(ArrayOfObjectNodeComparator.class); + private final List objectFieldNames; + + public ObjectNodeComparator(String... objectFieldNames) { + this(Arrays.asList(objectFieldNames)); + } + + public ObjectNodeComparator(List objectFieldNames) { + this.objectFieldNames = objectFieldNames; + } + + @Override + public int compare(JsonNode o1, JsonNode o2) { + if (!o1.isObject()) + return -1; + if (!o2.isObject()) + return 1; + + for (String objectFieldName : objectFieldNames) { + JsonNode value1 = o1.get(objectFieldName); + JsonNode value2 = o2.get(objectFieldName); + + if (value1 == null && value2 == null) + continue; + + if (value1 == null) + return 1; + if (value2 == null) + return -1; + if (!value1.getNodeType().equals(value2.getNodeType())) + return 0; + + switch (value1.getNodeType()) { + case BOOLEAN: + return Boolean.compare(value1.asBoolean(), value2.asBoolean()); + case NUMBER: + return Double.compare(value1.asDouble(), value2.asDouble()); + case STRING: + return value1.asText().compareTo(value2.asText()); + default: + return 0; + } + } + + return 0; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/CDATAFlexSection.java b/src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java similarity index 93% rename from src/main/java/com/inteligr8/maven/aps/modeling/CDATAFlexSection.java rename to src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java index 195b7ba..c0f5397 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/CDATAFlexSection.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.xml; import org.w3c.dom.CDATASection; import org.w3c.dom.Node; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/DomNamespaceContext.java b/src/main/java/com/inteligr8/maven/aps/modeling/xml/DomNamespaceContext.java similarity index 97% rename from src/main/java/com/inteligr8/maven/aps/modeling/DomNamespaceContext.java rename to src/main/java/com/inteligr8/maven/aps/modeling/xml/DomNamespaceContext.java index 09e0aef..4c9872d 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/DomNamespaceContext.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/xml/DomNamespaceContext.java @@ -1,4 +1,4 @@ -package com.inteligr8.maven.aps.modeling; +package com.inteligr8.maven.aps.modeling.xml; import java.util.Collections; import java.util.Iterator; From 0541cf502b9a113062c9f848bd605d1809c954fa Mon Sep 17 00:00:00 2001 From: Brian Long Date: Fri, 25 Mar 2022 15:55:38 +0100 Subject: [PATCH 2/3] added dynamic normalizer and reusable crawler --- src/it/pack-app/pom.xml | 4 +- src/it/translate-app/pom.xml | 1 + .../aps/modeling/crawler/ApsAppCrawlable.java | 13 ++ .../aps/modeling/crawler/ApsAppCrawler.java | 105 ++++++++++++++ .../modeling/crawler/ApsFileTransformer.java | 10 ++ .../aps/modeling/goal/TranslateAppGoal.java | 8 +- .../aps/modeling/goal/UnpackAppGoal.java | 115 ++------------- .../normalizer/ApsAppJsonNormalizer.java | 52 +++++++ .../modeling/normalizer/ApsAppNormalizer.java | 34 +++++ .../normalizer/ApsFileNormalizer.java | 7 + .../normalizer/ApsFormJsonNormalizer.java | 19 +++ .../normalizer/ApsProcessBpmnNormalizer.java | 52 +++++++ .../normalizer/ApsProcessJsonNormalizer.java | 50 +++++++ .../translator/ApsAppJsonTranslator.java | 7 +- .../modeling/translator/ApsAppTranslator.java | 137 ++++++------------ .../translator/ApsFileTranslator.java | 9 +- .../translator/ApsProcessBpmnTranslator.java | 5 +- .../translator/ApsProcessJsonTranslator.java | 5 + 18 files changed, 431 insertions(+), 202 deletions(-) create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawlable.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawler.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsFileTransformer.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppNormalizer.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFileNormalizer.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFormJsonNormalizer.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessBpmnNormalizer.java create mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java diff --git a/src/it/pack-app/pom.xml b/src/it/pack-app/pom.xml index 5d2a52b..9913626 100644 --- a/src/it/pack-app/pom.xml +++ b/src/it/pack-app/pom.xml @@ -31,7 +31,7 @@ unpack-app - ${aps.app} + true @@ -41,8 +41,6 @@ pack-app - ${aps.app} - ${basedir}/src/main/app diff --git a/src/it/translate-app/pom.xml b/src/it/translate-app/pom.xml index 0c0bc72..5f83a40 100644 --- a/src/it/translate-app/pom.xml +++ b/src/it/translate-app/pom.xml @@ -28,6 +28,7 @@ true + true diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawlable.java b/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawlable.java new file mode 100644 index 0000000..390da7f --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawlable.java @@ -0,0 +1,13 @@ +package com.inteligr8.maven.aps.modeling.crawler; + +public interface ApsAppCrawlable { + + ApsFileTransformer getAppJsonTransformer(); + + ApsFileTransformer getProcessJsonTransformer(); + + ApsFileTransformer getProcessBpmnTransformer(); + + ApsFileTransformer getFormJsonTransformer(); + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawler.java b/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawler.java new file mode 100644 index 0000000..9639572 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsAppCrawler.java @@ -0,0 +1,105 @@ +package com.inteligr8.maven.aps.modeling.crawler; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ApsAppCrawler { + + private final Logger logger = LoggerFactory.getLogger(ApsAppCrawler.class); + private final Pattern filenamePattern = Pattern.compile("([^/]+)-([0-9]+)\\.(json|bpmn20\\.xml)"); + private final String appName; + private final File appDirectory; + private final File appDescriptor; + private final boolean failOnIntegrityViolation; + + public ApsAppCrawler(String apsAppName, File apsAppDirectory, boolean failOnIntegrityViolation) { + this.appName = apsAppName; + this.appDirectory = apsAppDirectory; + this.failOnIntegrityViolation = failOnIntegrityViolation; + + this.appDescriptor = this.validateDescriptor(appDirectory); + if (this.logger.isDebugEnabled()) + this.logger.debug("APS App descriptor found: " + this.appDescriptor); + } + + protected File validateDescriptor(File appDirectory) { + File appDescriptor = new File(appDirectory, this.appName + ".json"); + + if (!appDescriptor.exists()) + throw new IllegalStateException("The APS App descriptor could not be found: " + appDescriptor); + if (!appDescriptor.isFile()) + throw new IllegalStateException("The APS App descriptor is not a file: " + appDescriptor); + + return appDescriptor; + } + + public void execute(ApsAppCrawlable crawlable) throws IOException { + this.logger.info("Crawling APS App ..."); + + Map processTransformers = new HashMap<>(); + processTransformers.put("json", crawlable.getProcessJsonTransformer()); + processTransformers.put("bpmn20.xml", crawlable.getProcessBpmnTransformer()); + processTransformers.put("bpmn", crawlable.getProcessBpmnTransformer()); + + this.transform(crawlable.getAppJsonTransformer(), this.appDescriptor, this.appName, null); + this.crawlModels("form-models", crawlable.getFormJsonTransformer()); + this.crawlModels("bpmn-models", processTransformers); + this.crawlModels("bpmn-subprocess-models", crawlable.getProcessJsonTransformer()); + } + + protected void crawlModels(String modelsDirectoryName, ApsFileTransformer transformer) throws IOException { + this.crawlModels(modelsDirectoryName, Collections.singletonMap("json", transformer)); + } + + protected void crawlModels(String modelsDirectoryName, Map transformers) throws IOException { + File modelsDirectory = new File(this.appDirectory, modelsDirectoryName); + if (!modelsDirectory.exists()) { + this.logger.debug("APS model directory doesn't exist; no models to transform: {}", modelsDirectory); + return; + } + + for (File modelFile : modelsDirectory.listFiles()) { + this.logger.trace("Transforming model: {}", modelFile); + + Matcher matcher = this.filenamePattern.matcher(modelFile.getName()); + if (!matcher.find()) { + this.logger.warn("The '{}' folder has a file with an unexpected filename format; skipping: {}", modelsDirectoryName, modelFile); + continue; + } + + String ext = matcher.group(3); + ApsFileTransformer transformer = transformers.get(ext.toLowerCase()); + if (transformer == null) { + this.logger.warn("The '{}' folder has a file with an unexpected extension; skipping: {}", modelsDirectoryName, modelFile); + continue; + } + + String modelName = matcher.group(1); + Long modelId = new Long(matcher.group(2)); + this.logger.trace("Transforming model {} ID: {}", modelName, modelId); + + this.transform(transformer, modelFile, modelName, modelId); + } + } + + private void transform(ApsFileTransformer transformer, File modelFile, String modelName, Long modelId) throws IOException { + try { + transformer.transformFile(modelFile, modelName, modelId); + } catch (IllegalArgumentException | IllegalStateException ie) { + if (this.failOnIntegrityViolation) { + throw ie; + } else { + this.logger.warn(ie.getMessage()); + } + } + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsFileTransformer.java b/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsFileTransformer.java new file mode 100644 index 0000000..8e9ac56 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/crawler/ApsFileTransformer.java @@ -0,0 +1,10 @@ +package com.inteligr8.maven.aps.modeling.crawler; + +import java.io.File; +import java.io.IOException; + +public interface ApsFileTransformer { + + void transformFile(File file, String modelName, Long modelId) throws IOException; + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java index 5f8a178..e33ebc6 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java @@ -12,6 +12,7 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.annotations.Component; +import com.inteligr8.maven.aps.modeling.crawler.ApsAppCrawler; import com.inteligr8.maven.aps.modeling.translator.ApsAppTranslator; @Mojo( name = "translate-app", threadSafe = true ) @@ -47,8 +48,11 @@ public class TranslateAppGoal extends ApsAppAccessibleGoal { apsAppDirectory = newApsAppDirectory; } - ApsAppTranslator translator = new ApsAppTranslator(this.apsAppName, apsAppDirectory, this.getApsApi(), true); - translator.execute(); + ApsAppTranslator translator = new ApsAppTranslator(apsAppDirectory, this.getApsApi()); + translator.buildIndexes(); + + ApsAppCrawler crawler = new ApsAppCrawler(this.apsAppName, apsAppDirectory, true); + crawler.execute(translator); } catch (IOException ie) { throw new MojoExecutionException("An I/O issue occurred", ie); } catch (IllegalArgumentException iae) { diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java index d597d4e..f47571c 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java @@ -10,7 +10,6 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -26,10 +25,8 @@ import org.codehaus.plexus.component.annotations.Component; import org.w3c.dom.Document; import org.xml.sax.SAXException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.inteligr8.maven.aps.modeling.util.ApsModelArrayNodeSorter; -import com.inteligr8.maven.aps.modeling.util.ApsModelListMapSorter; +import com.inteligr8.maven.aps.modeling.crawler.ApsAppCrawler; +import com.inteligr8.maven.aps.modeling.normalizer.ApsAppNormalizer; import com.inteligr8.maven.aps.modeling.util.ModelUtil; @Mojo( name = "unpack-app", threadSafe = true ) @@ -45,9 +42,12 @@ public class UnpackAppGoal extends ApsAppGoal { @Parameter( property = "aps-model.reformat", required = true, defaultValue = "true" ) protected boolean reformat = true; - @Parameter( property = "aps-model.normalize", required = true, defaultValue = "false" ) + @Parameter( property = "aps-model.normalize" ) protected boolean normalize; + @Parameter( property = "aps-model.normalize.diffFriendly" ) + protected boolean diffFriendly; + @Parameter( property = "aps-model.charset", required = true, defaultValue = "utf-8" ) protected String charsetName = "utf-8"; @@ -72,6 +72,15 @@ public class UnpackAppGoal extends ApsAppGoal { if (this.reformat) this.reformatFiles(appDirectory); + if (this.normalize) { + try { + ApsAppNormalizer normalizer = new ApsAppNormalizer(this.diffFriendly); + ApsAppCrawler crawler = new ApsAppCrawler(this.apsAppName, appDirectory, true); + crawler.execute(normalizer); + } catch (IOException ie) { + throw new MojoExecutionException("An I/O issue occurred", ie); + } + } } protected void validate() { @@ -179,16 +188,12 @@ public class UnpackAppGoal extends ApsAppGoal { case "json": this.getLog().debug("Reformatting file: " + file); Map json = ModelUtil.getInstance().readJsonAsMap(file); - if (this.normalize) - this.normalizeJson(json); ModelUtil.getInstance().writeJson(json, file); break; case "xml": case "bpmn": this.getLog().debug("Reformatting file: " + file); Document xml = ModelUtil.getInstance().readXml(file); - if (this.normalize) - this.normalizeXml(xml); ModelUtil.getInstance().writeXml(xml, file); break; default: @@ -201,96 +206,6 @@ public class UnpackAppGoal extends ApsAppGoal { } } - private boolean normalizeJson(Map jsonMap) { - boolean changed = false; - - try { - List> jsonModels = (List>)this.mapGetsListMaps(jsonMap, "definition", "models"); - changed = ApsModelListMapSorter.getInstance().sort(jsonModels, "name") || changed; - } catch (ClassCastException cce) { - // suppress - } catch (NullPointerException npe) { - // suppress - } - - try { - List> jsonChildShapes = (List>)this.mapGetsListMaps(jsonMap, "childShapes"); - changed = ApsModelListMapSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; - } catch (ClassCastException cce) { - // suppress - } catch (NullPointerException npe) { - // suppress - } - - try { - List> jsonChildShapes = (List>)this.mapGetsListMaps(jsonMap, "editorJson", "childShapes"); - changed = ApsModelListMapSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; - } catch (ClassCastException cce) { - // suppress - } catch (NullPointerException npe) { - // suppress - } - - return changed; - } - - @SuppressWarnings("unchecked") - private T mapGets(Map map, Class returnType, String... keys) { - for (int i = 0; i < keys.length - 1; i++) - map = (Map)map.get(keys[i]); - return (T)map.get(keys[keys.length-1]); - } - - @SuppressWarnings("unchecked") - private List mapGetsList(Map map, Class listType, String... keys) { - for (int i = 0; i < keys.length - 1; i++) - map = (Map)map.get(keys[i]); - return (List)map.get(keys[keys.length-1]); - } - - @SuppressWarnings("unchecked") - private List> mapGetsListMaps(Map map, String... keys) { - for (int i = 0; i < keys.length - 1; i++) - map = (Map)map.get(keys[i]); - return (List>)map.get(keys[keys.length-1]); - } - - private boolean normalizeJson(JsonNode json) { - boolean changed = false; - - try { - ArrayNode jsonModels = (ArrayNode)json.get("definition").get("models"); - changed = ApsModelArrayNodeSorter.getInstance().sort(jsonModels, "name") || changed; - } catch (ClassCastException cce) { - // suppress - } catch (NullPointerException npe) { - // suppress - } - - try { - ArrayNode jsonChildShapes = (ArrayNode)json.get("childShapes"); - changed = ApsModelArrayNodeSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; - } catch (ClassCastException cce) { - // suppress - } catch (NullPointerException npe) { - // suppress - } - - try { - ArrayNode jsonChildShapes = (ArrayNode)json.get("editorJson").get("childShapes"); - changed = ApsModelArrayNodeSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; - } catch (ClassCastException cce) { - // suppress - } catch (NullPointerException npe) { - // suppress - } - - return changed; - } - - private void normalizeXml(Document xml) { - } - private String getFileExtension(File file) { int lastdot = file.getName().lastIndexOf('.'); if (lastdot < 0) diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java new file mode 100644 index 0000000..27efb01 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java @@ -0,0 +1,52 @@ +package com.inteligr8.maven.aps.modeling.normalizer; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.inteligr8.maven.aps.modeling.util.ApsModelArrayNodeSorter; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; + +public class ApsAppJsonNormalizer implements ApsFileNormalizer { + + private final Logger logger = LoggerFactory.getLogger(ApsAppJsonNormalizer.class); + + private final boolean enableSorting; + + public ApsAppJsonNormalizer(boolean enableSorting) { + this.enableSorting = enableSorting; + } + + @Override + public void transformFile(File file, String modelName, Long modelId) throws IOException { + this.logger.debug("Normalizing App JSON file: {}", file); + + boolean changed = false; + JsonNode descriptor = ModelUtil.getInstance().readJson(file); + ArrayNode jsonModels = (ArrayNode)descriptor.get("definition").get("models"); + + // remove system-level and non-functional fields from models + for (JsonNode _jsonModel : jsonModels) { + ObjectNode jsonModel = (ObjectNode)_jsonModel; + + int fields = jsonModel.size(); + jsonModel.remove(Arrays.asList("createdBy", "createdByFullName", "lastUpdatedBy", "lastUpdatedByFullName", "lastUpdated")); + if (jsonModel.size() < fields) + changed = true; + } + + // sort the models for better 'diff' support + if (this.enableSorting) + ApsModelArrayNodeSorter.getInstance().sort(jsonModels, "name"); + + if (changed) + ModelUtil.getInstance().writeJson(descriptor, file); + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppNormalizer.java new file mode 100644 index 0000000..6e607db --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppNormalizer.java @@ -0,0 +1,34 @@ +package com.inteligr8.maven.aps.modeling.normalizer; + +import com.inteligr8.maven.aps.modeling.crawler.ApsAppCrawlable; +import com.inteligr8.maven.aps.modeling.crawler.ApsFileTransformer; + +public class ApsAppNormalizer implements ApsAppCrawlable { + + private final boolean enableSorting; + + public ApsAppNormalizer(boolean enableSorting) { + this.enableSorting = enableSorting; + } + + @Override + public ApsFileTransformer getAppJsonTransformer() { + return new ApsAppJsonNormalizer(this.enableSorting); + } + + @Override + public ApsFileTransformer getFormJsonTransformer() { + return new ApsFormJsonNormalizer(); + } + + @Override + public ApsFileTransformer getProcessJsonTransformer() { + return new ApsProcessJsonNormalizer(this.enableSorting); + } + + @Override + public ApsFileTransformer getProcessBpmnTransformer() { + return new ApsProcessBpmnNormalizer(); + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFileNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFileNormalizer.java new file mode 100644 index 0000000..9ff8d1f --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFileNormalizer.java @@ -0,0 +1,7 @@ +package com.inteligr8.maven.aps.modeling.normalizer; + +import com.inteligr8.maven.aps.modeling.crawler.ApsFileTransformer; + +public interface ApsFileNormalizer extends ApsFileTransformer { + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFormJsonNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFormJsonNormalizer.java new file mode 100644 index 0000000..5a96d51 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsFormJsonNormalizer.java @@ -0,0 +1,19 @@ +package com.inteligr8.maven.aps.modeling.normalizer; + +import java.io.File; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ApsFormJsonNormalizer implements ApsFileNormalizer { + + private final Logger logger = LoggerFactory.getLogger(ApsFormJsonNormalizer.class); + + @Override + public void transformFile(File file, String modelName, Long modelId) throws IOException { + this.logger.debug("Normalizing Form JSON file: {}", file); + this.logger.trace("Nothing to normalize: {}", file); + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessBpmnNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessBpmnNormalizer.java new file mode 100644 index 0000000..9120c45 --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessBpmnNormalizer.java @@ -0,0 +1,52 @@ +package com.inteligr8.maven.aps.modeling.normalizer; + +import java.io.File; +import java.io.IOException; + +import javax.xml.transform.TransformerException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import com.inteligr8.maven.aps.modeling.util.ModelUtil; + +public class ApsProcessBpmnNormalizer implements ApsFileNormalizer { + + private static final String NAMESPACE_ACTIVITI_MODELER = "http://activiti.com/modeler"; + + private final Logger logger = LoggerFactory.getLogger(ApsProcessBpmnNormalizer.class); + + @Override + public void transformFile(File file, String modelName, Long modelId) throws IOException { + this.logger.debug("Normalizing Process BPMN file: {}", file); + + boolean changed = false; + + try { + Document bpmn = ModelUtil.getInstance().readXml(file); + Element definitionsElement = bpmn.getDocumentElement(); + + changed = this.removeAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "exportDateTime") || changed; + changed = this.removeAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelLastUpdated") || changed; + + if (changed) + ModelUtil.getInstance().writeXml(bpmn, file); + } catch (TransformerException | SAXException e) { + throw new IllegalStateException("An XML issue occurred", e); + } + } + + private boolean removeAttributeIfSet(Element element, String attrNamespace, String attrName) { + Attr attr = element.getAttributeNodeNS(attrNamespace, attrName); + if (attr == null) + return false; + + element.removeAttributeNode(attr); + return true; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java new file mode 100644 index 0000000..46d903f --- /dev/null +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java @@ -0,0 +1,50 @@ +package com.inteligr8.maven.aps.modeling.normalizer; + +import java.io.File; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.inteligr8.maven.aps.modeling.util.ApsModelArrayNodeSorter; +import com.inteligr8.maven.aps.modeling.util.ModelUtil; + +public class ApsProcessJsonNormalizer implements ApsFileNormalizer { + + private final Logger logger = LoggerFactory.getLogger(ApsProcessJsonNormalizer.class); + + private final boolean enableSorting; + + public ApsProcessJsonNormalizer(boolean enableSorting) { + this.enableSorting = enableSorting; + } + + @Override + public void transformFile(File file, String modelName, Long modelId) throws IOException { + this.logger.debug("Normalizing Process JSON file: {}", file); + + if (!this.enableSorting) + return; + + boolean changed = false; + + ObjectNode jsonDescriptor = ModelUtil.getInstance().readJson(file, ObjectNode.class); + ArrayNode jsonChildShapes = this.getChildShapes(jsonDescriptor); + + changed = ApsModelArrayNodeSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; + + if (changed) + ModelUtil.getInstance().writeJson(jsonDescriptor, file); + } + + private ArrayNode getChildShapes(ObjectNode jsonDescriptor) { + JsonNode jsonChildShapes = jsonDescriptor.get("childShapes"); + if (jsonChildShapes == null) + jsonChildShapes = jsonDescriptor.get("editorJson").get("childShapes"); + return (ArrayNode)jsonChildShapes; + } + +} diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java index a514bac..0e581eb 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java @@ -68,7 +68,7 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { } int fields = jsonModel.size(); - jsonModel.remove(Arrays.asList("version", "createdBy", "createdByFullName", "lastUpdatedBy", "lastUpdatedByFullName", "lastUpdated")); + jsonModel.remove(Arrays.asList("version")); if (jsonModel.size() < fields) changed = true; } @@ -90,6 +90,11 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { } else { this.logger.trace("The organization '{}' ID does not change; leaving unchanged", fileOrgName); } + + if (jsonGroup.has("parentGroupId")) { + jsonGroup.remove("parentGroupName"); + changed = true; + } } else { this.logger.warn("The organization '{}' does not exist in APS", fileOrgName); // FIXME create the organization diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java index 17bbbd9..3c0c363 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppTranslator.java @@ -2,7 +2,6 @@ package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,18 +19,18 @@ import com.inteligr8.alfresco.activiti.model.Datum; import com.inteligr8.alfresco.activiti.model.Group; import com.inteligr8.alfresco.activiti.model.ResultListDataRepresentation; import com.inteligr8.alfresco.activiti.model.Tenant; +import com.inteligr8.maven.aps.modeling.crawler.ApsAppCrawlable; +import com.inteligr8.maven.aps.modeling.crawler.ApsFileTransformer; import com.inteligr8.maven.aps.modeling.util.Index; -public class ApsAppTranslator { +public class ApsAppTranslator implements ApsAppCrawlable { private final Logger logger = LoggerFactory.getLogger(ApsAppTranslator.class); private final Pattern filenamePattern = Pattern.compile("([^/]+)-([0-9]+)\\.(json|bpmn20\\.xml)"); private final ApsPublicRestApi api; - private final String appName; private final File appDirectory; - private final File appDescriptor; - private final boolean failOnIntegrityViolation; + private boolean indexesBuilt = false; private Map apsOrgIndex; private Index apsFormIndex; private Index apsProcessIndex; @@ -39,34 +38,15 @@ public class ApsAppTranslator { private Index fileProcessIndex; private Index fileSubprocessIndex; - public ApsAppTranslator(String apsAppName, File apsAppDirectory, ApsPublicRestApiJerseyImpl api, boolean failOnIntegrityViolation) { + public ApsAppTranslator(File apsAppDirectory, ApsPublicRestApiJerseyImpl api) { this.api = api; - this.appName = apsAppName; this.appDirectory = apsAppDirectory; - this.failOnIntegrityViolation = failOnIntegrityViolation; - - this.appDescriptor = this.validateDescriptor(appDirectory); - if (this.logger.isDebugEnabled()) - this.logger.debug("APS App descriptor found: " + this.appDescriptor); } - protected File validateDescriptor(File appDirectory) { - File appDescriptor = new File(appDirectory, this.appName + ".json"); + public synchronized void buildIndexes() throws IOException { + if (this.indexesBuilt) + return; - if (!appDescriptor.exists()) - throw new IllegalStateException("The APS App descriptor could not be found: " + appDescriptor); - if (!appDescriptor.isFile()) - throw new IllegalStateException("The APS App descriptor is not a file: " + appDescriptor); - - return appDescriptor; - } - - public void execute() throws IOException { - this.buildIndexes(); - this.translate(); - } - - protected void buildIndexes() throws IOException { this.logger.info("Building indexes ..."); long tenantId = this.findTenantId(); @@ -89,6 +69,8 @@ public class ApsAppTranslator { this.fileSubprocessIndex = this.buildFileIndex("bpmn-subprocess-models", this.apsProcessIndex, true); this.logLarge("File subprocess models: {}", this.fileSubprocessIndex); + + this.indexesBuilt = true; } private void logLarge(String message, Map map) { @@ -107,34 +89,47 @@ public class ApsAppTranslator { } } - protected void translate() throws IOException { - this.logger.info("Translating models ..."); + @Override + public ApsFileTransformer getAppJsonTransformer() { + if (!this.indexesBuilt) + throw new IllegalStateException("The indexes are never built"); - ApsFileTranslator descriptorTranslator = new ApsAppJsonTranslator( + return new ApsAppJsonTranslator( this.apsOrgIndex, this.apsProcessIndex, this.fileProcessIndex); - ApsFileTranslator formTranslator = new ApsFormJsonTranslator( + } + + @Override + public ApsFileTransformer getFormJsonTransformer() { + if (!this.indexesBuilt) + throw new IllegalStateException("The indexes are never built"); + + return new ApsFormJsonTranslator( this.apsFormIndex); - ApsFileTranslator processTranslator = new ApsProcessJsonTranslator( + } + + @Override + public ApsFileTransformer getProcessJsonTransformer() { + if (!this.indexesBuilt) + throw new IllegalStateException("The indexes are never built"); + + return new ApsProcessJsonTranslator( this.apsProcessIndex, this.apsOrgIndex, this.apsFormIndex); - ApsFileTranslator bpmnTranslator = new ApsProcessBpmnTranslator( + } + + @Override + public ApsFileTransformer getProcessBpmnTransformer() { + if (!this.indexesBuilt) + throw new IllegalStateException("The indexes are never built"); + + return new ApsProcessBpmnTranslator( this.apsProcessIndex, this.apsOrgIndex, this.apsFormIndex, this.fileFormIndex); - - Map processTranslators = new HashMap<>(); - processTranslators.put("json", processTranslator); - processTranslators.put("bpmn20.xml", bpmnTranslator); - processTranslators.put("bpmn", bpmnTranslator); - - this.translate(descriptorTranslator, this.appDescriptor, this.appName, null); - this.translateModels("form-models", formTranslator); - this.translateModels("bpmn-models", processTranslators); - this.translateModels("bpmn-subprocess-models", processTranslator); } protected Long findTenantId() { @@ -201,10 +196,15 @@ public class ApsAppTranslator { fileModelIndex.put(fileModelId, modelName); Set apsModelIds = apsModelIndex.getKeys(modelName); + if (apsModelIds == null || apsModelIds.isEmpty()) { + this.logger.debug("The model file '{}' has no corresponding model in APS; treating as new", modelFile.getName()); + continue; + } + if (apsModelIds.size() > 1) throw new IllegalStateException("An APS " + modelDirectory.getName() + " name was used multiple times: " + modelName); - if (renameFiles && !apsModelIds.isEmpty()) { + if (renameFiles) { Long apsModelId = apsModelIds.iterator().next(); if (apsModelId != fileModelId) { this.logger.debug("Renaming form model from '" + modelFile.getName() + "' to match APS form ID: " + apsModelId); @@ -218,52 +218,5 @@ public class ApsAppTranslator { return fileModelIndex; } - - protected void translateModels(String modelsDirectoryName, ApsFileTranslator translator) throws IOException { - this.translateModels(modelsDirectoryName, Collections.singletonMap("json", translator)); - } - - protected void translateModels(String modelsDirectoryName, Map translators) throws IOException { - File modelsDirectory = new File(this.appDirectory, modelsDirectoryName); - if (!modelsDirectory.exists()) { - this.logger.debug("APS model directory doesn't exist; no models to translate: {}", modelsDirectory); - return; - } - - for (File modelFile : modelsDirectory.listFiles()) { - this.logger.trace("Translating model: {}", modelFile); - - Matcher matcher = this.filenamePattern.matcher(modelFile.getName()); - if (!matcher.find()) { - this.logger.warn("The '{}' folder has a file with an unexpected filename format; skipping: {}", modelsDirectoryName, modelFile); - continue; - } - - String ext = matcher.group(3); - ApsFileTranslator translator = translators.get(ext.toLowerCase()); - if (translator == null) { - this.logger.warn("The '{}' folder has a file with an unexpected extension; skipping: {}", modelsDirectoryName, modelFile); - continue; - } - - String modelName = matcher.group(1); - Long modelId = new Long(matcher.group(2)); - this.logger.trace("Translating model {} ID: {}", modelName, modelId); - - this.translate(translator, modelFile, modelName, modelId); - } - } - - private void translate(ApsFileTranslator translator, File modelFile, String modelName, Long modelId) throws IOException { - try { - translator.translateFile(modelFile, modelName, modelId); - } catch (IllegalArgumentException | IllegalStateException ie) { - if (this.failOnIntegrityViolation) { - throw ie; - } else { - this.logger.warn(ie.getMessage()); - } - } - } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java index b400a68..f542e56 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFileTranslator.java @@ -3,7 +3,14 @@ package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; -public interface ApsFileTranslator { +import com.inteligr8.maven.aps.modeling.crawler.ApsFileTransformer; + +public interface ApsFileTranslator extends ApsFileTransformer { + + @Override + default void transformFile(File file, String modelName, Long modelId) throws IOException { + this.translateFile(file, modelName, modelId); + } void translateFile(File file, String modelName, Long modelId) throws IOException; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java index 5a6a688..783bc44 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java @@ -103,10 +103,9 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { private boolean translateRootElement(Element definitionsElement, Long apsProcessId) { boolean changed = false; - changed = this.setAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelId", apsProcessId) || changed; + if (apsProcessId != null) + changed = this.setAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelId", apsProcessId) || changed; changed = this.removeAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelVersion") || changed; - changed = this.removeAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "exportDateTime") || changed; - changed = this.removeAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelLastUpdated") || changed; return changed; } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java index 25f1bcf..ec6a60e 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java @@ -49,6 +49,11 @@ public class ApsProcessJsonTranslator implements ApsFileTranslator { } private boolean translateResourceId(ObjectNode jsonDescriptor, Long apsProcessId) { + if (apsProcessId == null) { + this.logger.trace("The process is not in APS; treating as new"); + return false; + } + ObjectNode resourceIdParentJson = jsonDescriptor; JsonNode jsonResourceId = jsonDescriptor.get("resourceId"); From 3d353cf6d5a068a5a5ad10c8281b8edb018c3c8b Mon Sep 17 00:00:00 2001 From: Brian Long Date: Mon, 28 Mar 2022 20:05:46 +0100 Subject: [PATCH 3/3] finished, tested and fixed initial impl --- pom.xml | 5 +- src/it/deploy-app/pom.xml | 42 ++++++++++-- src/it/download-app/pom.xml | 4 +- src/it/pack-app/pom.xml | 11 ++-- src/it/translate-app/pom.xml | 4 +- src/it/unpack-app/pom.xml | 4 +- .../aps/modeling/goal/ApsAddressibleGoal.java | 64 +++++++++++------- .../aps/modeling/goal/DeployAppGoal.java | 44 +++++++------ .../maven/aps/modeling/goal/PackAppGoal.java | 40 +++++------ .../aps/modeling/goal/TranslateAppGoal.java | 8 --- .../aps/modeling/goal/UnpackAppGoal.java | 2 +- .../normalizer/ApsAppJsonNormalizer.java | 7 +- .../normalizer/ApsProcessJsonNormalizer.java | 2 +- .../translator/ApsAppJsonTranslator.java | 21 ++++-- .../translator/ApsFormJsonTranslator.java | 2 +- .../translator/ApsProcessBpmnTranslator.java | 32 ++++++--- .../translator/ApsProcessJsonTranslator.java | 8 +-- .../maven/aps/modeling/util/ModelUtil.java | 66 +++++++++++-------- .../aps/modeling/xml/CDATAFlexSection.java | 31 --------- src/main/resources/bpmn.xslt | 7 +- 20 files changed, 223 insertions(+), 181 deletions(-) delete mode 100644 src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java diff --git a/pom.xml b/pom.xml index ce55cfa..f0236b6 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ com.inteligr8.alfresco aps-public-rest-api - 1.2.0 + 1.2.1 aps1 @@ -216,8 +216,7 @@ ${aps-model.baseUrl} ${aps-model.authType} - ${aps-model.basicAuth.username} - ${aps-model.basicAuth.password} + ${aps-model.basicAuth.mavenServerId} ${aps-model.appName} diff --git a/src/it/deploy-app/pom.xml b/src/it/deploy-app/pom.xml index d995edd..8f24f58 100644 --- a/src/it/deploy-app/pom.xml +++ b/src/it/deploy-app/pom.xml @@ -12,10 +12,6 @@ Deploy App Plugin Tests - - FORMS Core - - @@ -24,14 +20,46 @@ @pom.version@ - download-app - validate + download-unpack-app + generate-sources download-app + unpack-app + + + true + true + + + + translate-app + compile + + translate-app + + + ${project.build.directory}/aps-dev + + + + pack-app + package + + pack-app + + + ${project.build.directory}/aps-dev + ${project.build.directory}/aps-test + + + + deploy-app + package + deploy-app - ${aps.app} + ${project.build.directory}/aps-test diff --git a/src/it/download-app/pom.xml b/src/it/download-app/pom.xml index e3d8f43..b191c07 100644 --- a/src/it/download-app/pom.xml +++ b/src/it/download-app/pom.xml @@ -21,14 +21,14 @@ download-app - validate + generate-sources download-app download-app-other - validate + generate-sources download-app diff --git a/src/it/pack-app/pom.xml b/src/it/pack-app/pom.xml index 9913626..5d26679 100644 --- a/src/it/pack-app/pom.xml +++ b/src/it/pack-app/pom.xml @@ -12,10 +12,6 @@ Pack App Plugin Tests - - FORMS Core - - @@ -25,7 +21,7 @@ download-unpack-app - validate + generate-sources download-app unpack-app @@ -36,11 +32,12 @@ pack-app - validate + package pack-app + ${basedir} @@ -56,7 +53,7 @@ - ${basedir}/src/main/app/${aps.app}.zip + ${basedir}/${aps-model.appName}.zip diff --git a/src/it/translate-app/pom.xml b/src/it/translate-app/pom.xml index 5f83a40..5c38c84 100644 --- a/src/it/translate-app/pom.xml +++ b/src/it/translate-app/pom.xml @@ -21,7 +21,7 @@ download-unpack-app - validate + generate-sources download-app unpack-app @@ -33,7 +33,7 @@ translate-app - validate + compile translate-app diff --git a/src/it/unpack-app/pom.xml b/src/it/unpack-app/pom.xml index df47b5b..7dcc93a 100644 --- a/src/it/unpack-app/pom.xml +++ b/src/it/unpack-app/pom.xml @@ -21,7 +21,7 @@ download-unpack-app - validate + generate-sources download-app unpack-app @@ -29,7 +29,7 @@ unpack-app - validate + process-sources unpack-app diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java index 5141963..6393015 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/ApsAddressibleGoal.java @@ -1,6 +1,8 @@ package com.inteligr8.maven.aps.modeling.goal; +import org.apache.maven.execution.MavenSession; import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.settings.Server; import com.inteligr8.alfresco.activiti.ApsClientConfiguration; import com.inteligr8.alfresco.activiti.ApsClientJerseyImpl; @@ -8,32 +10,26 @@ import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; public abstract class ApsAddressibleGoal extends DisablableGoal { + @Parameter( defaultValue = "${session}", readonly = true ) + protected MavenSession session; + @Parameter( property = "aps-model.baseUrl", required = true, defaultValue = "http://localhost:8080/activiti-app" ) protected String activitiAppBaseUrl; @Parameter( property = "aps-model.authType", required = true, defaultValue = "BASIC" ) protected String activitiAppAuthType; - @Parameter( property = "aps-model.basicAuth.username", required = false, defaultValue = "admin@app.activiti.com" ) - protected String activitiAppAuthBasicUsername; - - @Parameter( property = "aps-model.basicAuth.password", required = false, defaultValue = "admin" ) - protected String activitiAppAuthBasicPassword; + @Parameter( property = "aps-model.basicAuth.mavenServerId", required = true, defaultValue = "aps" ) + protected String activitiAppAuthBasicServerId; @Parameter( property = "aps-model.oauth.code", required = false ) protected String oauthCode; - @Parameter( property = "aps-model.oauth.clientId", required = false ) - protected String oauthClientId; + @Parameter( property = "aps-model.oauth.client.mavenServerId", required = false ) + protected String oauthClientServerId; - @Parameter( property = "aps-model.oauth.clientSecret", required = false ) - protected String oauthClientSecret; - - @Parameter( property = "aps-model.oauth.username", required = false ) - protected String oauthUsername; - - @Parameter( property = "aps-model.oauth.password", required = false ) - protected String oauthPassword; + @Parameter( property = "aps-model.oauth.mavenServerId", required = false ) + protected String oauthServerId; @Parameter( property = "aps-model.oauth.tokenUrl", required = false ) protected String oauthTokenUrl; @@ -47,21 +43,41 @@ public abstract class ApsAddressibleGoal extends DisablableGoal { switch (this.activitiAppAuthType.toUpperCase()) { case "BASIC": this.getLog().info("Configuring APS with BASIC authentication"); - this.getLog().debug("Username: " + this.activitiAppAuthBasicUsername); - config.setBasicAuthUsername(this.activitiAppAuthBasicUsername); - config.setBasicAuthPassword(this.activitiAppAuthBasicPassword); + + Server creds = this.session.getSettings().getServer(this.activitiAppAuthBasicServerId); + if (this.activitiAppAuthBasicServerId != null && creds == null) + this.getLog().warn("The Maven configuration has no server '" + this.activitiAppAuthBasicServerId + "'; continuing with default credentials"); + + if (creds != null) { + this.getLog().debug("Username: " + creds.getUsername()); + config.setBasicAuthUsername(creds.getUsername()); + config.setBasicAuthPassword(creds.getPassword()); + } break; case "OAUTH": this.getLog().info("Configuring APS with OAuth authentication"); + + Server clientCreds = this.session.getSettings().getServer(this.oauthClientServerId); + Server oauthCreds = this.session.getSettings().getServer(this.oauthServerId); + if ((this.oauthClientServerId != null || this.oauthServerId != null) && clientCreds == null && oauthCreds == null) + this.getLog().warn("The Maven configuration has no server '" + this.oauthClientServerId + "' or '" + this.oauthServerId + "'; continuing without credentials"); + this.getLog().debug("OAuth Code: " + this.oauthCode); - this.getLog().debug("OAuth Client ID: " + this.oauthClientId); - this.getLog().debug("OAuth Username: " + this.oauthUsername); config.setOAuthAuthCode(this.oauthCode); + + if (clientCreds != null) { + this.getLog().debug("OAuth Client ID: " + clientCreds.getUsername()); + config.setOAuthClientId(clientCreds.getUsername()); + config.setOAuthClientSecret(clientCreds.getPassword()); + } + + if (oauthCreds != null) { + this.getLog().debug("OAuth Username: " + oauthCreds.getUsername()); + config.setOAuthUsername(oauthCreds.getUsername()); + config.setOAuthPassword(oauthCreds.getPassword()); + } + config.setOAuthTokenUrl(this.oauthTokenUrl); - config.setOAuthClientId(this.oauthClientId); - config.setOAuthClientSecret(this.oauthClientSecret); - config.setOAuthUsername(this.oauthUsername); - config.setOAuthPassword(this.oauthPassword); break; default: throw new IllegalArgumentException("The 'activitiAppAuthType' configuration must be either 'Basic' or 'OAuth'"); diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java index 1a7d87e..2f8540e 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/DeployAppGoal.java @@ -5,12 +5,15 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import javax.ws.rs.core.MediaType; + import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.codehaus.plexus.component.annotations.Component; -import org.glassfish.jersey.media.multipart.FormDataContentDisposition; +import org.glassfish.jersey.media.multipart.FormDataMultiPart; +import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart; import com.inteligr8.alfresco.activiti.ApsPublicRestApiJerseyImpl; import com.inteligr8.alfresco.activiti.model.AppDefinitionUpdateResultRepresentation; @@ -19,13 +22,14 @@ import com.inteligr8.alfresco.activiti.model.AppDefinitionUpdateResultRepresenta @Component( role = org.apache.maven.plugin.Mojo.class ) public class DeployAppGoal extends ApsAppAccessibleGoal { - @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/aps/app" ) - protected File sourceDirectory; + @Parameter( property = "aps-model.upload.directory", required = true, defaultValue = "${project.build.directory}/aps" ) + protected File zipDirectory; @Parameter( property = "publish", required = true, defaultValue = "false" ) protected boolean publish = false; protected final int bufferSize = 128 * 1024; + private final MediaType zipMediaType = MediaType.valueOf("application/zip"); @Override public void executeEnabled() throws MojoExecutionException, MojoFailureException { @@ -36,21 +40,21 @@ public class DeployAppGoal extends ApsAppAccessibleGoal { try { this.uploadApp(appId, sourceFile); } catch (IOException ie) { - throw new MojoExecutionException("The downloaded APS App could not be unpacked", ie); + throw new MojoExecutionException("The APS App could not be uploaded", ie); } } protected File validateSourceDirectory() { - if (!this.sourceDirectory.exists()) { - throw new IllegalStateException("The 'sourceDirectory' does not exist: " + this.sourceDirectory); - } else if (!this.sourceDirectory.isDirectory()) { - throw new IllegalStateException("The 'sourceDirectory' is not a directory: " + this.sourceDirectory); + if (!this.zipDirectory.exists()) { + throw new IllegalStateException("The 'zipDirectory' does not exist: " + this.zipDirectory); + } else if (!this.zipDirectory.isDirectory()) { + throw new IllegalStateException("The 'zipDirectory' is not a directory: " + this.zipDirectory); } - File sourceFile = new File(this.sourceDirectory, this.apsAppName + ".zip"); + File sourceFile = new File(this.zipDirectory, this.apsAppName + ".zip"); if (!sourceFile.exists()) { - throw new IllegalStateException("The App file does not exists in the 'sourceDirectory': " + sourceFile); + throw new IllegalStateException("The App file does not exists in the 'zipDirectory': " + sourceFile); } else if (!sourceFile.isFile()) { throw new IllegalStateException("The App file is not a file: " + sourceFile); } @@ -61,33 +65,31 @@ public class DeployAppGoal extends ApsAppAccessibleGoal { private void uploadApp(Long appId, File appZip) throws IOException, MojoExecutionException { ApsPublicRestApiJerseyImpl api = this.getApsApi(); - FormDataContentDisposition cdisposition = FormDataContentDisposition.name("file") - .size(appZip.length()) - .fileName(appZip.getName()) - .build(); - FileInputStream fistream = new FileInputStream(appZip); BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); try { + FormDataMultiPart multipart = new FormDataMultiPart(); + multipart.bodyPart(new StreamDataBodyPart("file", bistream, appZip.getName(), this.zipMediaType)); + if (appId == null) { if (this.publish) { this.getLog().info("Uploading & publishing new APS App: " + this.apsAppName); - AppDefinitionUpdateResultRepresentation appDefUpdate = api.getAppDefinitionsJerseyApi().publishApp(bistream, cdisposition); + AppDefinitionUpdateResultRepresentation appDefUpdate = api.getAppDefinitionsJerseyApi().publishApp(multipart); if (Boolean.TRUE.equals(appDefUpdate.getError())) throw new MojoExecutionException(appDefUpdate.getErrorDescription()); } else { this.getLog().info("Uploading new APS App: " + this.apsAppName); - api.getAppDefinitionsJerseyApi().import_(bistream, cdisposition); + api.getAppDefinitionsJerseyApi().importApp(multipart, true); } } else { if (this.publish) { - this.getLog().info("Uploading, versioning, & publishing APS App: " + this.apsAppName); - AppDefinitionUpdateResultRepresentation appDefUpdate = api.getAppDefinitionsJerseyApi().publishApp(appId, bistream, cdisposition); + this.getLog().info("Uploading, versioning, & publishing APS App: " + this.apsAppName + " (" + appId + ")"); + AppDefinitionUpdateResultRepresentation appDefUpdate = api.getAppDefinitionsJerseyApi().publishApp(appId, multipart); if (Boolean.TRUE.equals(appDefUpdate.getError())) throw new MojoExecutionException(appDefUpdate.getErrorDescription()); } else { - this.getLog().info("Uploading & versioning APS App: " + this.apsAppName); - api.getAppDefinitionsJerseyApi().import_(appId, bistream, cdisposition); + this.getLog().info("Uploading & versioning APS App: " + this.apsAppName + " (" + appId + ")"); + api.getAppDefinitionsJerseyApi().importApp(appId, multipart, true); } } } finally { diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java index 2db4965..69bf6c1 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/PackAppGoal.java @@ -22,11 +22,11 @@ import org.codehaus.plexus.component.annotations.Component; @Component( role = org.apache.maven.plugin.Mojo.class ) public class PackAppGoal extends ApsAppGoal { - @Parameter( property = "sourceDirectory", required = true, defaultValue = "${project.build.directory}/aps/app" ) - protected File sourceDirectory; - - @Parameter( property = "targetDirectory", required = true, defaultValue = "${project.build.directory}/aps/app" ) - protected File targetDirectory; + @Parameter( property = "aps-model.app.directory", required = true, defaultValue = "${project.build.directory}/aps/app" ) + protected File unzipDirectory; + + @Parameter( property = "aps-model.upload.directory", required = true, defaultValue = "${project.build.directory}/aps" ) + protected File zipDirectory; protected final int bufferSize = 128 * 1024; @@ -45,17 +45,17 @@ public class PackAppGoal extends ApsAppGoal { } protected void validateSourceDirectory() { - if (!this.sourceDirectory.exists()) { - throw new IllegalStateException("The 'sourceDirectory' does not exist: " + this.sourceDirectory); - } else if (!this.sourceDirectory.isDirectory()) { - throw new IllegalStateException("The 'sourceDirectory' is not a directory: " + this.sourceDirectory); + if (!this.unzipDirectory.exists()) { + throw new IllegalStateException("The 'unzipDirectory' does not exist: " + this.unzipDirectory); + } else if (!this.unzipDirectory.isDirectory()) { + throw new IllegalStateException("The 'unzipDirectory' is not a directory: " + this.unzipDirectory); } } protected File validateAppDirectory() { - File appDirectory = new File(this.sourceDirectory, this.apsAppName); + File appDirectory = new File(this.unzipDirectory, this.apsAppName); if (!appDirectory.exists()) { - throw new IllegalStateException("The 'apsAppName' does not exist in the source directory: " + this.sourceDirectory); + throw new IllegalStateException("The 'apsAppName' does not exist in the source directory: " + this.unzipDirectory); } else if (!appDirectory.isDirectory()) { throw new IllegalStateException("The 'apsAppName' refers to a file and not a directory: " + appDirectory); } @@ -64,19 +64,19 @@ public class PackAppGoal extends ApsAppGoal { } protected File validateTargetDirectory() { - if (!this.targetDirectory.exists()) { - this.getLog().debug("The 'targetDirectory' does not exist; creating: " + this.targetDirectory); - this.targetDirectory.mkdirs(); - } else if (!this.targetDirectory.isDirectory()) { - throw new IllegalStateException("The 'targetDirectory' is not a directory: " + this.targetDirectory); + if (!this.zipDirectory.exists()) { + this.getLog().debug("The 'zipDirectory' does not exist; creating: " + this.zipDirectory); + this.zipDirectory.mkdirs(); + } else if (!this.zipDirectory.isDirectory()) { + throw new IllegalStateException("The 'zipDirectory' is not a directory: " + this.zipDirectory); } - File targetFile = new File(this.targetDirectory, this.apsAppName + ".zip"); + File targetFile = new File(this.zipDirectory, this.apsAppName + ".zip"); if (targetFile.isDirectory()) { throw new IllegalStateException("The App file is not a file: " + targetFile); } else if (targetFile.exists()) { - this.getLog().debug("The App file in the 'targetDirectory' exists; deleting: " + targetFile); + this.getLog().debug("The App file in the 'zipDirectory' exists; deleting: " + targetFile); targetFile.delete(); } @@ -92,6 +92,7 @@ public class PackAppGoal extends ApsAppGoal { ZipOutputStream zostream = new ZipOutputStream(bostream); try { this.zipDirectory(appDirectory, null, zostream); + zostream.flush(); } finally { zostream.close(); } @@ -118,7 +119,8 @@ public class PackAppGoal extends ApsAppGoal { this.getLog().debug("Packing APS file: " + path); - ZipEntry zentry = new ZipEntry(path.toString()); + // Activit App processing requires forward slashes (WOW) + ZipEntry zentry = new ZipEntry(path.toString().replace('\\', '/')); zostream.putNextEntry(zentry); try { FileInputStream fistream = new FileInputStream(file); diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java index e33ebc6..126a899 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/TranslateAppGoal.java @@ -4,12 +4,10 @@ import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; -import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.annotations.Component; import com.inteligr8.maven.aps.modeling.crawler.ApsAppCrawler; @@ -19,12 +17,6 @@ import com.inteligr8.maven.aps.modeling.translator.ApsAppTranslator; @Component( role = org.apache.maven.plugin.Mojo.class ) public class TranslateAppGoal extends ApsAppAccessibleGoal { - @Parameter( defaultValue = "${project}", readonly = true ) - protected MavenProject project; - - @Parameter( defaultValue = "${session}", readonly = true ) - protected MavenSession session; - @Parameter( property = "aps-model.app.directory", required = true, defaultValue = "${project.build.directory}/aps/app" ) protected File unzipDirectory; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java b/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java index f47571c..062e68e 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/goal/UnpackAppGoal.java @@ -188,7 +188,7 @@ public class UnpackAppGoal extends ApsAppGoal { case "json": this.getLog().debug("Reformatting file: " + file); Map json = ModelUtil.getInstance().readJsonAsMap(file); - ModelUtil.getInstance().writeJson(json, file); + ModelUtil.getInstance().writeJson(json, file, false); break; case "xml": case "bpmn": diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java index 27efb01..a7277e7 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsAppJsonNormalizer.java @@ -36,17 +36,18 @@ public class ApsAppJsonNormalizer implements ApsFileNormalizer { ObjectNode jsonModel = (ObjectNode)_jsonModel; int fields = jsonModel.size(); - jsonModel.remove(Arrays.asList("createdBy", "createdByFullName", "lastUpdatedBy", "lastUpdatedByFullName", "lastUpdated")); + jsonModel.remove(Arrays.asList("lastUpdated")); +// jsonModel.remove(Arrays.asList("createdBy", "createdByFullName", "lastUpdatedBy", "lastUpdatedByFullName", "lastUpdated")); if (jsonModel.size() < fields) changed = true; } // sort the models for better 'diff' support if (this.enableSorting) - ApsModelArrayNodeSorter.getInstance().sort(jsonModels, "name"); + changed = ApsModelArrayNodeSorter.getInstance().sort(jsonModels, "name") || changed; if (changed) - ModelUtil.getInstance().writeJson(descriptor, file); + ModelUtil.getInstance().writeJson(descriptor, file, this.enableSorting); } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java index 46d903f..cc0e1ac 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/normalizer/ApsProcessJsonNormalizer.java @@ -37,7 +37,7 @@ public class ApsProcessJsonNormalizer implements ApsFileNormalizer { changed = ApsModelArrayNodeSorter.getInstance().sort(jsonChildShapes, "resourceId") || changed; if (changed) - ModelUtil.getInstance().writeJson(jsonDescriptor, file); + ModelUtil.getInstance().writeJson(jsonDescriptor, file, this.enableSorting); } private ArrayNode getChildShapes(ObjectNode jsonDescriptor) { diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java index 0e581eb..95eccca 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsAppJsonTranslator.java @@ -2,7 +2,6 @@ package com.inteligr8.maven.aps.modeling.translator; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.Map; import org.slf4j.Logger; @@ -67,10 +66,14 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { } } - int fields = jsonModel.size(); - jsonModel.remove(Arrays.asList("version")); - if (jsonModel.size() < fields) + if (jsonModel.has("version")) { + jsonModel.put("version", 0); changed = true; + } +// int fields = jsonModel.size(); +// jsonModel.remove(Arrays.asList("version")); +// if (jsonModel.size() < fields) +// changed = true; } for (JsonNode _jsonIdentityInfo : descriptor.get("definition").get("publishIdentityInfo")) { @@ -90,11 +93,15 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { } else { this.logger.trace("The organization '{}' ID does not change; leaving unchanged", fileOrgName); } - + if (jsonGroup.has("parentGroupId")) { - jsonGroup.remove("parentGroupName"); + jsonGroup.put("parentGroupId", 0); changed = true; } +// if (jsonGroup.has("parentGroupId")) { +// jsonGroup.remove("parentGroupId"); +// changed = true; +// } } else { this.logger.warn("The organization '{}' does not exist in APS", fileOrgName); // FIXME create the organization @@ -102,7 +109,7 @@ public class ApsAppJsonTranslator implements ApsFileTranslator { } if (changed) - ModelUtil.getInstance().writeJson(descriptor, file); + ModelUtil.getInstance().writeJson(descriptor, file, false); } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java index 91b853a..eb46762 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsFormJsonTranslator.java @@ -46,7 +46,7 @@ public class ApsFormJsonTranslator implements ApsFileTranslator { } if (changed) - ModelUtil.getInstance().writeJson(jsonDescriptor, file); + ModelUtil.getInstance().writeJson(jsonDescriptor, file, false); } } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java index 783bc44..456f74a 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessBpmnTranslator.java @@ -25,7 +25,6 @@ import org.xml.sax.SAXException; import com.inteligr8.alfresco.activiti.model.Group; import com.inteligr8.maven.aps.modeling.util.Index; import com.inteligr8.maven.aps.modeling.util.ModelUtil; -import com.inteligr8.maven.aps.modeling.xml.CDATAFlexSection; public class ApsProcessBpmnTranslator implements ApsFileTranslator { @@ -106,6 +105,7 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { if (apsProcessId != null) changed = this.setAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelId", apsProcessId) || changed; changed = this.removeAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelVersion") || changed; + //changed = this.setAttributeIfSet(definitionsElement, NAMESPACE_ACTIVITI_MODELER, "modelVersion", "0") || changed; return changed; } @@ -200,17 +200,17 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { this.logger.trace("The form '{}' key does not change; leaving unchanged", formRefName); } - CDATAFlexSection formRefIdData = (CDATAFlexSection)ModelUtil.getInstance().xpath( + CharacterData formRefIdData = (CharacterData)ModelUtil.getInstance().xpath( taskElement, "tns:extensionElements/modeler:form-reference-id", ModelUtil.CDATA_FLEX); if (formRefIdData == null) throw new IllegalStateException("A form was detected in the task, but no form reference ID was found: " + taskElement.getAttribute("id")); - long formRefId = Long.parseLong(formRefIdData.getValue()); + long formRefId = Long.parseLong(formRefIdData.getData()); if (apsFormId != formRefId) { this.logger.debug("The form '{}' reference exists in APS with ID {}; changing model", formRefName, apsFormId); - formRefIdData.setValue(apsFormId.toString()); + formRefIdData.setData(apsFormId.toString()); changed = true; } else { this.logger.trace("The form '{}' reference ID does not change; leaving unchanged", formRefName); @@ -244,14 +244,14 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { this.logger.trace("Found ID {} for the subprocess '{}' in the APS Process BPMN model", apsProcessId, subprocessName); - CDATAFlexSection subprocessIdData = (CDATAFlexSection)ModelUtil.getInstance().xpath( + CharacterData subprocessIdData = (CharacterData)ModelUtil.getInstance().xpath( subprocessElement, "tns:extensionElements/modeler:subprocess-id", ModelUtil.CDATA_FLEX); - long subprocessId = Long.parseLong(subprocessIdData.getValue()); + long subprocessId = Long.parseLong(subprocessIdData.getData()); if (apsProcessId != subprocessId) { this.logger.debug("The process '{}' exists in APS with ID {}; changing model", subprocessName, apsProcessId); - subprocessIdData.setValue(apsProcessId.toString()); + subprocessIdData.setData(apsProcessId.toString()); changed = true; } else { this.logger.trace("The process '{}' ID does not change; leaving unchanged", subprocessName); @@ -289,23 +289,37 @@ public class ApsProcessBpmnTranslator implements ApsFileTranslator { boolean changed = false; String conditionExpression = conditionExpressionCdata.getData(); - Pattern formOutcomeInExpressionPattern = Pattern.compile("[^A-Za-z0-9]form([0-9]+)outcome[^A-Za-z0-9]"); + StringBuilder newce = new StringBuilder(); + int pos = 0; + + Pattern formOutcomeInExpressionPattern = Pattern.compile("form([0-9]+)outcome"); Matcher matcher = formOutcomeInExpressionPattern.matcher(conditionExpression); while (matcher.find()) { + newce.append(conditionExpression.substring(pos, matcher.start(1))); + long formId = Long.parseLong(matcher.group(1)); String formName = this.fileFormIndex.getValue(formId); if (formName == null) { this.logger.warn("The form ID '{}' does not exist in the form model files; ignoring", formId); + newce.append(formId); } else { Long apsFormId = this.apsFormIndex.getFirstKey(formName); if (apsFormId == null) { this.logger.warn("The form '{}' does not exist in APS; leaving unchanged", formName); + newce.append(formId); } else { - conditionExpressionCdata.setData(apsFormId.toString()); + newce.append(apsFormId); changed = true; } } + + pos = matcher.end(1); } + + newce.append(conditionExpression.substring(pos)); + + if (changed) + conditionExpressionCdata.setData(newce.toString()); return changed; } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java index ec6a60e..9c0175e 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/translator/ApsProcessJsonTranslator.java @@ -45,7 +45,7 @@ public class ApsProcessJsonTranslator implements ApsFileTranslator { changed = this.translateSubprocesses(jsonDescriptor) || changed; if (changed) - ModelUtil.getInstance().writeJson(jsonDescriptor, file); + ModelUtil.getInstance().writeJson(jsonDescriptor, file, false); } private boolean translateResourceId(ObjectNode jsonDescriptor, Long apsProcessId) { @@ -130,8 +130,8 @@ public class ApsProcessJsonTranslator implements ApsFileTranslator { for (JsonNode jsonChildShape : this.getChildShapes(jsonDescriptor)) { try { - JsonNode jsonConditionalSequenceFlow = (JsonNode)jsonChildShape.get("properties").get("conditionalsequenceflow"); - changed = this.translateConditionalSequenceFlow(jsonConditionalSequenceFlow) || changed; + JsonNode jsonConditionalSequenceFlow = (JsonNode)jsonChildShape.get("properties").get("conditionsequenceflow"); + changed = this.translateConditionSequenceFlow(jsonConditionalSequenceFlow) || changed; JsonNode jsonFormRef = (JsonNode)jsonChildShape.get("properties").get("formreference"); changed = this.translateReference(jsonFormRef, this.apsFormIndex) || changed; @@ -143,7 +143,7 @@ public class ApsProcessJsonTranslator implements ApsFileTranslator { return changed; } - private boolean translateConditionalSequenceFlow(JsonNode jsonConditionalSequenceFlow) { + private boolean translateConditionSequenceFlow(JsonNode jsonConditionalSequenceFlow) { if (jsonConditionalSequenceFlow == null || !jsonConditionalSequenceFlow.isObject()) return false; diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java b/src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java index 38bed1d..65f7739 100644 --- a/src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java +++ b/src/main/java/com/inteligr8/maven/aps/modeling/util/ModelUtil.java @@ -27,7 +27,7 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.w3c.dom.CDATASection; +import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -38,12 +38,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.inteligr8.maven.aps.modeling.xml.CDATAFlexSection; import com.inteligr8.maven.aps.modeling.xml.DomNamespaceContext; public class ModelUtil { - public final static QName CDATA = QName.valueOf("inteligr8:xml:cdata"); public final static QName CDATA_FLEX = QName.valueOf("inteligr8:xml:cdata_or_element"); private final static ModelUtil INSTANCE = new ModelUtil(); @@ -54,6 +52,7 @@ public class ModelUtil { private final ObjectMapper om = new ObjectMapper(); + private final ObjectMapper omsorted = new ObjectMapper(); private final DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); private final DocumentBuilder docBuilder; private final XPathFactory xpathfactory = XPathFactory.newInstance(); @@ -81,26 +80,28 @@ public class ModelUtil { // makes Git handling a whole lot better this.om.enable(SerializationFeature.INDENT_OUTPUT); + this.omsorted.enable(SerializationFeature.INDENT_OUTPUT); + // makes Git handling better between environment nuances - this.om.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); - this.om.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); + this.omsorted.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); + this.omsorted.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); } - public void json(File inplaceFile) throws IOException { + public void json(File inplaceFile, boolean enableSorting) throws IOException { JsonNode json = this.readJson(inplaceFile); - this.writeJson(json, inplaceFile); + this.writeJson(json, inplaceFile, enableSorting); } - public void json(File sourceFile, File targetFile) throws IOException { + public void json(File sourceFile, File targetFile, boolean enableSorting) throws IOException { FileInputStream fistream = new FileInputStream(sourceFile); BufferedInputStream bistream = new BufferedInputStream(fistream, this.bufferSize); try { FileOutputStream fostream = new FileOutputStream(targetFile); BufferedOutputStream bostream = new BufferedOutputStream(fostream, this.bufferSize); try { - this.json(bistream, bostream); + this.json(bistream, bostream, enableSorting); } finally { bostream.close(); } @@ -109,10 +110,10 @@ public class ModelUtil { } } - public void json(InputStream istream, OutputStream ostream) throws IOException { + public void json(InputStream istream, OutputStream ostream, boolean enableSorting) throws IOException { // FIXME stream it for lower memory/IO usage JsonNode json = this.readJson(istream); - this.writeJson(json, ostream); + this.writeJson(json, ostream, enableSorting); } public JsonNode readJson(File file) throws IOException { @@ -167,32 +168,40 @@ public class ModelUtil { return this.om.readValue(istream, Map.class); } - public void writeJson(JsonNode json, File file) throws IOException { + public void writeJson(JsonNode json, File file, boolean enableSorting) throws IOException { FileOutputStream fostream = new FileOutputStream(file); BufferedOutputStream bostream = new BufferedOutputStream(fostream, this.bufferSize); try { - this.writeJson(json, bostream); + this.writeJson(json, bostream, enableSorting); } finally { bostream.close(); } } - public void writeJson(Map map, File file) throws IOException { + public void writeJson(Map map, File file, boolean enableSorting) throws IOException { FileOutputStream fostream = new FileOutputStream(file); BufferedOutputStream bostream = new BufferedOutputStream(fostream, this.bufferSize); try { - this.writeJson(map, bostream); + this.writeJson(map, bostream, enableSorting); } finally { bostream.close(); } } - public void writeJson(JsonNode json, OutputStream ostream) throws IOException { - this.om.writeValue(ostream, json); + public void writeJson(JsonNode json, OutputStream ostream, boolean enableSorting) throws IOException { + if (enableSorting) { + this.omsorted.writeValue(ostream, json); + } else { + this.om.writeValue(ostream, json); + } } - public void writeJson(Map map, OutputStream ostream) throws IOException { - this.om.writeValue(ostream, map); + public void writeJson(Map map, OutputStream ostream, boolean enableSorting) throws IOException { + if (enableSorting) { + this.omsorted.writeValue(ostream, map); + } else { + this.om.writeValue(ostream, map); + } } @@ -277,22 +286,23 @@ public class ModelUtil { XPath xpath = this.xpathfactory.newXPath(); xpath.setNamespaceContext(new DomNamespaceContext(node)); - boolean isFlex = returnType.equals(ModelUtil.CDATA_FLEX); - - if (returnType.equals(ModelUtil.CDATA) || isFlex) { + if (returnType.equals(ModelUtil.CDATA_FLEX)) { NodeList nodes = (NodeList)xpath.evaluate(xpathExpr, node, XPathConstants.NODESET); Node anode = this.getFirstCDataSectionOrLastElement(nodes); - if (anode instanceof CDATASection) { + if (anode instanceof CharacterData) { // nothing special } else if (anode instanceof Element) { Node anode2 = this.getFirstCDataSectionOrLastElement(anode.getChildNodes()); - if (anode2 != null) - return anode = anode2; + if (anode2 == null) + return null; + if (!(anode2 instanceof CharacterData)) + throw new IllegalStateException(); + anode = anode2; } else { return null; } - return isFlex ? new CDATAFlexSection(anode) : anode; + return anode; } else { return xpath.evaluate(xpathExpr, node, returnType); } @@ -303,8 +313,8 @@ public class ModelUtil { for (int n = 0; n < nodes.getLength(); n++) { Node anode = nodes.item(n); - if (anode.getNodeType() == Node.CDATA_SECTION_NODE) { - return (CDATASection)anode; + if (anode instanceof CharacterData) { + return (CharacterData)anode; } else if (anode.getNodeType() == Node.ELEMENT_NODE) { element = (Element)anode; } diff --git a/src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java b/src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java deleted file mode 100644 index c0f5397..0000000 --- a/src/main/java/com/inteligr8/maven/aps/modeling/xml/CDATAFlexSection.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.inteligr8.maven.aps.modeling.xml; - -import org.w3c.dom.CDATASection; -import org.w3c.dom.Node; - -public class CDATAFlexSection { - - private final Node node; - - public CDATAFlexSection(Node node) { - this.node = node; - } - - public String getValue() { - if (this.node instanceof CDATASection) { - return ((CDATASection)this.node).getData(); - } else { - String value = this.node.getTextContent(); - return value == null ? null : this.node.getTextContent().trim(); - } - } - - public void setValue(String value) { - if (this.node instanceof CDATASection) { - ((CDATASection)this.node).setData(value); - } else { - this.node.setTextContent(value); - } - } - -} diff --git a/src/main/resources/bpmn.xslt b/src/main/resources/bpmn.xslt index 9659319..05b3f84 100644 --- a/src/main/resources/bpmn.xslt +++ b/src/main/resources/bpmn.xslt @@ -1,9 +1,14 @@ - + + + <![CDATA[ + + ]]> +