From f08581bc5313e63c578b0368fc61ce69ab985602 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Mon, 29 Jul 2024 22:44:41 +0300 Subject: [PATCH 01/18] 03-3512: SDK should handle reading dependencies from content.properties and ensuring they are part of the distro --- maven-plugin/pom.xml | 5 + .../openmrs/maven/plugins/BuildDistro.java | 100 ++++++++++++++++-- .../maven/plugins/model/DistroProperties.java | 26 +++++ 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml index ffdda75f..fac8ca66 100644 --- a/maven-plugin/pom.xml +++ b/maven-plugin/pom.xml @@ -29,6 +29,11 @@ org.apache.maven.shared maven-verifier + + com.vdurmont + semver4j + 3.1.0 + org.apache.maven maven-model diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index e609621b..8fa6d89d 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -1,5 +1,7 @@ package org.openmrs.maven.plugins; +import com.vdurmont.semver4j.Semver; +import com.vdurmont.semver4j.SemverException; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; @@ -8,11 +10,8 @@ import org.apache.commons.lang.StringUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; -import org.eclipse.jgit.api.errors.GitAPIException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import org.eclipse.jgit.api.CloneCommand; -import org.eclipse.jgit.api.Git; import org.openmrs.maven.plugins.model.Artifact; import org.openmrs.maven.plugins.model.DistroProperties; import org.openmrs.maven.plugins.model.Server; @@ -27,6 +26,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; +import java.io.BufferedReader; +import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -35,6 +36,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Properties; /** * Create docker configuration for distributions. @@ -48,6 +50,8 @@ public class BuildDistro extends AbstractTask { private static final String OPENMRS_DISTRO_PROPERTIES = "openmrs-distro.properties"; + private static final String CONTENT_PROPERTIES = "content.properties"; + private static final String DOCKER_COMPOSE_PATH = "build-distro/docker-compose.yml"; private static final String DOCKER_COMPOSE_OVERRIDE_PATH = "build-distro/docker-compose.override.yml"; @@ -78,7 +82,7 @@ public class BuildDistro extends AbstractTask { private static final Logger log = LoggerFactory.getLogger(BuildDistro.class); - /** + /** * Path to the openmrs-distro.properties file. */ @Parameter(property = "distro") @@ -195,6 +199,7 @@ public void executeTask() throws MojoExecutionException, MojoFailureException { throw new MojoExecutionException("The distro you specified, '" + distro + "' could not be retrieved"); } + parseContentProperties(userDir, distroProperties); String distroName = buildDistro(buildDirectory, distroArtifact, distroProperties); wizard.showMessage( @@ -510,7 +515,7 @@ private void copyDbDump(File targetDirectory, InputStream stream) throws MojoExe } try (FileWriter writer = new FileWriter(dbDump); - BufferedInputStream bis = new BufferedInputStream(stream)) { + BufferedInputStream bis = new BufferedInputStream(stream)) { writer.write(DUMP_PREFIX); int c; @@ -604,4 +609,87 @@ public boolean accept(File dir, String name) { } } } -} + + /** + * Reads the content.properties file. + *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties + * matches the range defined in content.properties; otherwise, we output an error to the user to fix this.

+ * For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version and add that to the OMODs and ESMs used by the distro. + * + * @param userDir + * @param distroProperties + * @throws MojoExecutionException + */ + private void parseContentProperties(File userDir, DistroProperties distroProperties) throws MojoExecutionException { + File contentFile = new File(userDir, CONTENT_PROPERTIES); + if (!contentFile.exists()) { + log.info("No content.properties file found in the user directory."); + return; + } + + Properties contentProperties = new Properties(); + try (BufferedReader reader = new BufferedReader(new FileReader(contentFile))) { + contentProperties.load(reader); + } catch (IOException e) { + throw new MojoExecutionException("Failed to read content.properties file", e); + } + + for (String dependency : contentProperties.stringPropertyNames()) { + String versionRange = contentProperties.getProperty(dependency); + String distroVersion = distroProperties.get(dependency); + + if (distroVersion == null) { + String latestVersion = findLatestMatchingVersion(dependency, versionRange); + if (latestVersion == null) { + throw new MojoExecutionException("No matching version found for dependency " + dependency); + } + + distroProperties.add(dependency, latestVersion); + } else { + checkVersionInRange(dependency, versionRange, distroVersion); + } + } + } + + /** + * Checks if the version from distro.properties satisfies the range specified in content.properties. + * Throws an exception if there is a mismatch. + * + * @param contentDependencyKey the dependency key + * @param contentDependencyVersionRange the version range from content.properties + * @param distroPropertyVersion the version from distro.properties + * @throws MojoExecutionException if there is a version mismatch + */ + private void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion) throws MojoExecutionException { + Semver semverVersion = new Semver(distroPropertyVersion, Semver.SemverType.NPM); + String[] ranges = contentDependencyVersionRange.split(","); + boolean inRange = false; + + for (String range : ranges) { + range = range.trim(); + try { + if (range.startsWith("^") || range.startsWith("~")) { + inRange = semverVersion.satisfies(range); + } else { + Semver rangeVersion = new Semver(range, Semver.SemverType.NPM); + inRange = semverVersion.diff(rangeVersion) == Semver.VersionDiff.NONE; + } + if (inRange) break; + } catch (SemverException e) { + throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + ": " + contentDependencyVersionRange, e); + } + } + + if (!inRange) { + throw new MojoExecutionException( + "Incompatible version for " + contentDependencyKey + ". Specified range: " + contentDependencyVersionRange + + ", found in distribution: " + distroPropertyVersion); + } + } + + private String findLatestMatchingVersion(String dependency, String versionRange) { + // TODO + // Implement logic to find the latest version matching the versionRange + return null; + } +} \ No newline at end of file diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java index 09f1e1e5..2cd9a376 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java @@ -291,4 +291,30 @@ private boolean hasPlaceholder(Object object){ int index = asString.indexOf("{"); return index != -1 && asString.substring(index).contains("}"); } + + public boolean contains(String propertyName) { + return properties.containsKey(propertyName); + } + + /** + * Adds a dependency with the specified version to the properties. + * + * @param dependency The name of the dependency. + * @param version The version of the dependency. + */ + public void add(String dependency, String version) { + if (StringUtils.isBlank(dependency) || StringUtils.isBlank(version)) { + log.error("Dependency name or version cannot be blank"); + return; + } + + if (dependency.startsWith("omod") || dependency.startsWith("war")) { + properties.setProperty(dependency, version); + log.info("Added dependency: {} with version: {}", dependency, version); + } + } + + public String get(String contentDependencyKey) { + return properties.getProperty(contentDependencyKey); + } } From 01070acd5afe75e3636bb26959d402b7262b05fb Mon Sep 17 00:00:00 2001 From: mherman22 Date: Thu, 1 Aug 2024 14:41:16 +0300 Subject: [PATCH 02/18] ensure to find the latest matching version --- .../java/org/openmrs/maven/plugins/BuildDistro.java | 12 +++++------- .../openmrs/maven/plugins/utility/DistroHelper.java | 8 ++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index 8fa6d89d..3d14429b 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -651,6 +651,10 @@ private void parseContentProperties(File userDir, DistroProperties distroPropert } } + private String findLatestMatchingVersion(String dependency, String versionRange) throws MojoExecutionException { + return distroHelper.findLatestMatchingVersion(dependency, versionRange); + } + /** * Checks if the version from distro.properties satisfies the range specified in content.properties. * Throws an exception if there is a mismatch. @@ -686,10 +690,4 @@ private void checkVersionInRange(String contentDependencyKey, String contentDepe + ", found in distribution: " + distroPropertyVersion); } } - - private String findLatestMatchingVersion(String dependency, String versionRange) { - // TODO - // Implement logic to find the latest version matching the versionRange - return null; - } -} \ No newline at end of file +} diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 1e53baac..70964198 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -12,6 +12,7 @@ import org.openmrs.maven.plugins.model.UpgradeDifferential; import org.openmrs.maven.plugins.model.Version; import org.twdata.maven.mojoexecutor.MojoExecutor; +import org.twdata.maven.mojoexecutor.MojoExecutor.Element; import java.io.File; import java.io.IOException; @@ -553,4 +554,11 @@ public DistroProperties resolveParentArtifact(Artifact parentArtifact, Server se return resolveParentArtifact(parentArtifact, server.getServerDirectory(), distroProperties, appShellVersion); } + public String findLatestMatchingVersion(String dependency, String versionRange) throws MojoExecutionException { + if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("spa.frontend")) { + Properties properties = new Properties(); + properties.setProperty(dependency, versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest"))); + } + throw new MojoExecutionException("Unsupported dependency type: " + dependency); + } } From 6046c07c6ac9cd393382a97af492a4dfbe8c8dc7 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Thu, 1 Aug 2024 22:02:32 +0300 Subject: [PATCH 03/18] fix according to code reviews --- maven-plugin/pom.xml | 5 - .../openmrs/maven/plugins/BuildDistro.java | 108 +++++++++--------- .../maven/plugins/utility/DistroHelper.java | 13 ++- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml index fac8ca66..ffdda75f 100644 --- a/maven-plugin/pom.xml +++ b/maven-plugin/pom.xml @@ -29,11 +29,6 @@ org.apache.maven.shared maven-verifier
- - com.vdurmont - semver4j - 3.1.0 - org.apache.maven maven-model diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index 3d14429b..a16f78b8 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -1,7 +1,6 @@ package org.openmrs.maven.plugins; -import com.vdurmont.semver4j.Semver; -import com.vdurmont.semver4j.SemverException; +import com.github.zafarkhaja.semver.ParseException; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; @@ -612,13 +611,15 @@ public boolean accept(File dir, String name) { /** * Reads the content.properties file. - *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties + *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties * matches the range defined in content.properties; otherwise, we output an error to the user to fix this.

- * For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version and add that to the OMODs and ESMs used by the distro. - * - * @param userDir - * @param distroProperties - * @throws MojoExecutionException + * For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version + * and add that to the OMODs and ESMs used by the distro. + * + * @param userDir The directory where the distro.properties or content.properties file is located. This is typically the user's working directory. + * @param distroProperties An object representing the properties defined in distro.properties. This includes versions of various dependencies + * currently used in the distribution. + * @throws MojoExecutionException if there is an error reading the content.properties file or if a version conflict is detected. */ private void parseContentProperties(File userDir, DistroProperties distroProperties) throws MojoExecutionException { File contentFile = new File(userDir, CONTENT_PROPERTIES); @@ -628,66 +629,69 @@ private void parseContentProperties(File userDir, DistroProperties distroPropert } Properties contentProperties = new Properties(); + String contentPackageName = null; + String contentPackageVersion = null; + try (BufferedReader reader = new BufferedReader(new FileReader(contentFile))) { contentProperties.load(reader); + contentPackageName = contentProperties.getProperty("name"); + contentPackageVersion = contentProperties.getProperty("version"); + + if (contentPackageName == null || contentPackageVersion == null) { + throw new MojoExecutionException("Content package name or version not specified in content.properties"); + } } catch (IOException e) { throw new MojoExecutionException("Failed to read content.properties file", e); } for (String dependency : contentProperties.stringPropertyNames()) { - String versionRange = contentProperties.getProperty(dependency); - String distroVersion = distroProperties.get(dependency); - - if (distroVersion == null) { - String latestVersion = findLatestMatchingVersion(dependency, versionRange); - if (latestVersion == null) { - throw new MojoExecutionException("No matching version found for dependency " + dependency); + if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") + || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { + String versionRange = contentProperties.getProperty(dependency); + String distroVersion = distroProperties.get(dependency); + + if (distroVersion == null) { + String latestVersion = findLatestMatchingVersion(dependency); + if (latestVersion == null) { + throw new MojoExecutionException("No matching version found for dependency " + dependency); + } + distroProperties.add(dependency, latestVersion); + } else { + checkVersionInRange(dependency, versionRange, distroVersion, contentPackageName); } - - distroProperties.add(dependency, latestVersion); - } else { - checkVersionInRange(dependency, versionRange, distroVersion); } } } - private String findLatestMatchingVersion(String dependency, String versionRange) throws MojoExecutionException { - return distroHelper.findLatestMatchingVersion(dependency, versionRange); + + private String findLatestMatchingVersion(String dependency) throws MojoExecutionException { + return distroHelper.findLatestMatchingVersion(dependency); } /** - * Checks if the version from distro.properties satisfies the range specified in content.properties. - * Throws an exception if there is a mismatch. - * - * @param contentDependencyKey the dependency key - * @param contentDependencyVersionRange the version range from content.properties - * @param distroPropertyVersion the version from distro.properties - * @throws MojoExecutionException if there is a version mismatch - */ - private void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion) throws MojoExecutionException { - Semver semverVersion = new Semver(distroPropertyVersion, Semver.SemverType.NPM); - String[] ranges = contentDependencyVersionRange.split(","); - boolean inRange = false; - - for (String range : ranges) { - range = range.trim(); - try { - if (range.startsWith("^") || range.startsWith("~")) { - inRange = semverVersion.satisfies(range); - } else { - Semver rangeVersion = new Semver(range, Semver.SemverType.NPM); - inRange = semverVersion.diff(rangeVersion) == Semver.VersionDiff.NONE; - } - if (inRange) break; - } catch (SemverException e) { - throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + ": " + contentDependencyVersionRange, e); - } - } + * Checks if the version from distro.properties satisfies the range specified in content.properties. + * Throws an exception if there is a mismatch. + * + * @param contentDependencyKey The key of the content dependency. + * @param contentDependencyVersionRange The version range specified in content.properties. + * @param distroPropertyVersion The version specified in distro.properties. + * @param contentPackageName The name of the content package. + * @throws MojoExecutionException If the version does not fall within the specified range or if the range format is invalid. + */ + private void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { + com.github.zafarkhaja.semver.Version semverVersion = com.github.zafarkhaja.semver.Version.parse(distroPropertyVersion); - if (!inRange) { - throw new MojoExecutionException( - "Incompatible version for " + contentDependencyKey + ". Specified range: " + contentDependencyVersionRange - + ", found in distribution: " + distroPropertyVersion); + try { + boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); + if (!inRange) { + throw new MojoExecutionException( + "Incompatible version for " + contentDependencyKey + " in content package " + contentPackageName + ". Specified range: " + contentDependencyVersionRange + + ", found in distribution: " + distroPropertyVersion); + } + } catch (ParseException e) { + throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); } } + + } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 70964198..5996f097 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -554,11 +554,16 @@ public DistroProperties resolveParentArtifact(Artifact parentArtifact, Server se return resolveParentArtifact(parentArtifact, server.getServerDirectory(), distroProperties, appShellVersion); } - public String findLatestMatchingVersion(String dependency, String versionRange) throws MojoExecutionException { - if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("spa.frontend")) { - Properties properties = new Properties(); - properties.setProperty(dependency, versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest"))); + public String findLatestMatchingVersion(String dependency) throws MojoExecutionException { + if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("spa.frontendModule") || + dependency.startsWith("content.") || dependency.startsWith("war.")) { + try { + return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); + } catch (Exception e) { + throw new MojoExecutionException("Error retrieving latest version for dependency: " + dependency, e); + } } throw new MojoExecutionException("Unsupported dependency type: " + dependency); } + } From 90305bac2b73b0f0a8f09967692918949539e151 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Fri, 2 Aug 2024 18:36:38 +0300 Subject: [PATCH 04/18] ensure frontendModule versions are looked up from an NPM registry --- .../openmrs/maven/plugins/BuildDistro.java | 2 +- sdk-commons/pom.xml | 5 ++ .../maven/plugins/utility/DistroHelper.java | 27 ++++++----- .../plugins/utility/NpmVersionHelper.java | 47 +++++++++++++++++++ 4 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index a16f78b8..0070f481 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -664,7 +664,7 @@ private void parseContentProperties(File userDir, DistroProperties distroPropert } - private String findLatestMatchingVersion(String dependency) throws MojoExecutionException { + private String findLatestMatchingVersion(String dependency) { return distroHelper.findLatestMatchingVersion(dependency); } diff --git a/sdk-commons/pom.xml b/sdk-commons/pom.xml index 52a066ad..f845d9df 100644 --- a/sdk-commons/pom.xml +++ b/sdk-commons/pom.xml @@ -81,6 +81,11 @@ mockito-core test
+ + org.json + json + 20230618 + diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 5996f097..349bb17a 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -8,6 +8,7 @@ import org.apache.maven.project.MavenProject; import org.openmrs.maven.plugins.model.Artifact; import org.openmrs.maven.plugins.model.DistroProperties; +import org.openmrs.maven.plugins.model.PackageJson; import org.openmrs.maven.plugins.model.Server; import org.openmrs.maven.plugins.model.UpgradeDifferential; import org.openmrs.maven.plugins.model.Version; @@ -49,14 +50,19 @@ public class DistroHelper { final VersionsHelper versionHelper; + final NpmVersionHelper npmVersionHelper; + + PackageJson packageJson; + public DistroHelper(MavenProject mavenProject, MavenSession mavenSession, BuildPluginManager pluginManager, - Wizard wizard, VersionsHelper versionHelper) { + Wizard wizard, VersionsHelper versionHelper, NpmVersionHelper npmVersionHelper) { this.mavenProject = mavenProject; this.mavenSession = mavenSession; this.pluginManager = pluginManager; this.wizard = wizard; this.versionHelper = versionHelper; - } + this.npmVersionHelper = npmVersionHelper; + } /** * @return distro properties from openmrs-distro.properties file in current directory or null if not exist @@ -554,16 +560,13 @@ public DistroProperties resolveParentArtifact(Artifact parentArtifact, Server se return resolveParentArtifact(parentArtifact, server.getServerDirectory(), distroProperties, appShellVersion); } - public String findLatestMatchingVersion(String dependency) throws MojoExecutionException { - if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("spa.frontendModule") || - dependency.startsWith("content.") || dependency.startsWith("war.")) { - try { - return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); - } catch (Exception e) { - throw new MojoExecutionException("Error retrieving latest version for dependency: " + dependency, e); - } + public String findLatestMatchingVersion(String dependency) { + if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("content.") || dependency.startsWith("war.")) { + return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); + } else if (dependency.startsWith("spa.frontendModule")) { + packageJson.setName(dependency.substring("spa.frontendModules.".length())); + return npmVersionHelper.getLatestReleasedVersionFromNpmRegistry(packageJson); } - throw new MojoExecutionException("Unsupported dependency type: " + dependency); + throw new IllegalArgumentException("Unsupported dependency type: " + dependency); } - } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java new file mode 100644 index 00000000..01537482 --- /dev/null +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java @@ -0,0 +1,47 @@ +package org.openmrs.maven.plugins.utility; + +import org.openmrs.maven.plugins.model.PackageJson; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import org.json.JSONObject; + +public class NpmVersionHelper { + + public String getLatestReleasedVersionFromNpmRegistry(PackageJson packageJson) { + try { + String packageName = packageJson.getName(); + if (packageName == null || packageName.isEmpty()) { + throw new IllegalArgumentException("Package name cannot be null or empty"); + } + + URL url = new URL("https://registry.npmjs.org/" + packageName.replace("/", "%2F")); + JSONObject json = getJson(url); + return json.getJSONObject("dist-tags").getString("latest"); + + } catch (Exception e) { + throw new RuntimeException("Error retrieving latest version from NPM ", e); + } + } + + private static JSONObject getJson(URL url) throws IOException { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + if (conn.getResponseCode() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); + } + + BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); + StringBuilder sb = new StringBuilder(); + String output; + while ((output = br.readLine()) != null) { + sb.append(output); + } + + conn.disconnect(); + return new JSONObject(sb.toString()); + } +} From 2689d34de0d7c41b241270ff55f931c1ee4a129c Mon Sep 17 00:00:00 2001 From: mherman22 Date: Fri, 2 Aug 2024 18:42:13 +0300 Subject: [PATCH 05/18] fix --- .../java/org/openmrs/maven/plugins/utility/DistroHelper.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 349bb17a..699cfef9 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -50,18 +50,17 @@ public class DistroHelper { final VersionsHelper versionHelper; - final NpmVersionHelper npmVersionHelper; + NpmVersionHelper npmVersionHelper; PackageJson packageJson; public DistroHelper(MavenProject mavenProject, MavenSession mavenSession, BuildPluginManager pluginManager, - Wizard wizard, VersionsHelper versionHelper, NpmVersionHelper npmVersionHelper) { + Wizard wizard, VersionsHelper versionHelper) { this.mavenProject = mavenProject; this.mavenSession = mavenSession; this.pluginManager = pluginManager; this.wizard = wizard; this.versionHelper = versionHelper; - this.npmVersionHelper = npmVersionHelper; } /** From 0996390936ef8bd050b2c4a0923028bac71226ab Mon Sep 17 00:00:00 2001 From: mherman22 Date: Fri, 2 Aug 2024 20:57:24 +0300 Subject: [PATCH 06/18] extract logic into propertiesUtils file --- .../openmrs/maven/plugins/BuildDistro.java | 91 +------------------ .../java/org/openmrs/maven/plugins/Setup.java | 2 + .../plugins/utility/PropertiesUtils.java | 91 +++++++++++++++++++ 3 files changed, 95 insertions(+), 89 deletions(-) diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index 0070f481..2ef46e1b 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -17,6 +17,7 @@ import org.openmrs.maven.plugins.model.Version; import org.openmrs.maven.plugins.utility.DistroHelper; import org.openmrs.maven.plugins.model.Project; +import org.openmrs.maven.plugins.utility.PropertiesUtils; import org.openmrs.maven.plugins.utility.SDKConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,8 +50,6 @@ public class BuildDistro extends AbstractTask { private static final String OPENMRS_DISTRO_PROPERTIES = "openmrs-distro.properties"; - private static final String CONTENT_PROPERTIES = "content.properties"; - private static final String DOCKER_COMPOSE_PATH = "build-distro/docker-compose.yml"; private static final String DOCKER_COMPOSE_OVERRIDE_PATH = "build-distro/docker-compose.override.yml"; @@ -198,7 +197,7 @@ public void executeTask() throws MojoExecutionException, MojoFailureException { throw new MojoExecutionException("The distro you specified, '" + distro + "' could not be retrieved"); } - parseContentProperties(userDir, distroProperties); + PropertiesUtils.parseContentProperties(userDir, distroProperties); String distroName = buildDistro(buildDirectory, distroArtifact, distroProperties); wizard.showMessage( @@ -608,90 +607,4 @@ public boolean accept(File dir, String name) { } } } - - /** - * Reads the content.properties file. - *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties - * matches the range defined in content.properties; otherwise, we output an error to the user to fix this.

- * For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version - * and add that to the OMODs and ESMs used by the distro. - * - * @param userDir The directory where the distro.properties or content.properties file is located. This is typically the user's working directory. - * @param distroProperties An object representing the properties defined in distro.properties. This includes versions of various dependencies - * currently used in the distribution. - * @throws MojoExecutionException if there is an error reading the content.properties file or if a version conflict is detected. - */ - private void parseContentProperties(File userDir, DistroProperties distroProperties) throws MojoExecutionException { - File contentFile = new File(userDir, CONTENT_PROPERTIES); - if (!contentFile.exists()) { - log.info("No content.properties file found in the user directory."); - return; - } - - Properties contentProperties = new Properties(); - String contentPackageName = null; - String contentPackageVersion = null; - - try (BufferedReader reader = new BufferedReader(new FileReader(contentFile))) { - contentProperties.load(reader); - contentPackageName = contentProperties.getProperty("name"); - contentPackageVersion = contentProperties.getProperty("version"); - - if (contentPackageName == null || contentPackageVersion == null) { - throw new MojoExecutionException("Content package name or version not specified in content.properties"); - } - } catch (IOException e) { - throw new MojoExecutionException("Failed to read content.properties file", e); - } - - for (String dependency : contentProperties.stringPropertyNames()) { - if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") - || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { - String versionRange = contentProperties.getProperty(dependency); - String distroVersion = distroProperties.get(dependency); - - if (distroVersion == null) { - String latestVersion = findLatestMatchingVersion(dependency); - if (latestVersion == null) { - throw new MojoExecutionException("No matching version found for dependency " + dependency); - } - distroProperties.add(dependency, latestVersion); - } else { - checkVersionInRange(dependency, versionRange, distroVersion, contentPackageName); - } - } - } - } - - - private String findLatestMatchingVersion(String dependency) { - return distroHelper.findLatestMatchingVersion(dependency); - } - - /** - * Checks if the version from distro.properties satisfies the range specified in content.properties. - * Throws an exception if there is a mismatch. - * - * @param contentDependencyKey The key of the content dependency. - * @param contentDependencyVersionRange The version range specified in content.properties. - * @param distroPropertyVersion The version specified in distro.properties. - * @param contentPackageName The name of the content package. - * @throws MojoExecutionException If the version does not fall within the specified range or if the range format is invalid. - */ - private void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { - com.github.zafarkhaja.semver.Version semverVersion = com.github.zafarkhaja.semver.Version.parse(distroPropertyVersion); - - try { - boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); - if (!inRange) { - throw new MojoExecutionException( - "Incompatible version for " + contentDependencyKey + " in content package " + contentPackageName + ". Specified range: " + contentDependencyVersionRange - + ", found in distribution: " + distroPropertyVersion); - } - } catch (ParseException e) { - throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); - } - } - - } diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java index 00b23e95..34a5ac27 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java @@ -36,6 +36,7 @@ import org.openmrs.maven.plugins.model.Version; import org.openmrs.maven.plugins.utility.DBConnector; import org.openmrs.maven.plugins.utility.DistroHelper; +import org.openmrs.maven.plugins.utility.PropertiesUtils; import org.openmrs.maven.plugins.utility.SDKConstants; import org.openmrs.maven.plugins.utility.ServerHelper; @@ -276,6 +277,7 @@ public void setup(Server server, DistroProperties distroProperties) throws MojoE // `setServerVersionsFromDistroProperties`, and `server.setValuesFromDistroPropertiesModules`. distroHelper.savePropertiesToServer(distroProperties, server); setServerVersionsFromDistroProperties(server, distroProperties); + PropertiesUtils.parseContentProperties(server.getServerDirectory(), distroProperties); moduleInstaller.installModulesForDistro(server, distroProperties, distroHelper); setConfigFolder(server, distroProperties); if (spaInstaller != null) { diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 9bf8955e..e07280e7 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -3,9 +3,11 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -19,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.zafarkhaja.semver.ParseException; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -28,6 +31,7 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.maven.plugin.MojoExecutionException; import org.openmrs.maven.plugins.model.Artifact; +import org.openmrs.maven.plugins.model.DistroProperties; import org.openmrs.maven.plugins.model.Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +40,10 @@ public class PropertiesUtils { + private static final String CONTENT_PROPERTIES = "content.properties"; + + static DistroHelper distroHelper; + private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); /** @@ -301,4 +309,87 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx document.getDocumentElement().normalize(); return document; } + + /** + * Reads the content.properties file. + *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties + * matches the range defined in content.properties; otherwise, we output an error to the user to fix this.

+ * For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version + * and add that to the OMODs and ESMs used by the distro. + * + * @param userDir The directory where the distro.properties or content.properties file is located. This is typically the user's working directory. + * @param distroProperties An object representing the properties defined in distro.properties. This includes versions of various dependencies + * currently used in the distribution. + * @throws MojoExecutionException if there is an error reading the content.properties file or if a version conflict is detected. + */ + public static void parseContentProperties(File userDir, DistroProperties distroProperties) throws MojoExecutionException { + File contentFile = new File(userDir, CONTENT_PROPERTIES); + if (!contentFile.exists()) { + log.info("No content.properties file found in the user directory."); + return; + } + + Properties contentProperties = new Properties(); + String contentPackageName = null; + String contentPackageVersion = null; + + try (BufferedReader reader = new BufferedReader(new FileReader(contentFile))) { + contentProperties.load(reader); + contentPackageName = contentProperties.getProperty("name"); + contentPackageVersion = contentProperties.getProperty("version"); + + if (contentPackageName == null || contentPackageVersion == null) { + throw new MojoExecutionException("Content package name or version not specified in content.properties"); + } + } catch (IOException e) { + throw new MojoExecutionException("Failed to read content.properties file", e); + } + + for (String dependency : contentProperties.stringPropertyNames()) { + if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") + || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { + String versionRange = contentProperties.getProperty(dependency); + String distroVersion = distroProperties.get(dependency); + + if (distroVersion == null) { + String latestVersion = findLatestMatchingVersion(dependency); + if (latestVersion == null) { + throw new MojoExecutionException("No matching version found for dependency " + dependency); + } + distroProperties.add(dependency, latestVersion); + } else { + checkVersionInRange(dependency, versionRange, distroVersion, contentPackageName); + } + } + } + } + + private static String findLatestMatchingVersion(String dependency) { + return distroHelper.findLatestMatchingVersion(dependency); + } + + /** + * Checks if the version from distro.properties satisfies the range specified in content.properties. + * Throws an exception if there is a mismatch. + * + * @param contentDependencyKey The key of the content dependency. + * @param contentDependencyVersionRange The version range specified in content.properties. + * @param distroPropertyVersion The version specified in distro.properties. + * @param contentPackageName The name of the content package. + * @throws MojoExecutionException If the version does not fall within the specified range or if the range format is invalid. + */ + private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { + com.github.zafarkhaja.semver.Version semverVersion = com.github.zafarkhaja.semver.Version.parse(distroPropertyVersion); + + try { + boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); + if (!inRange) { + throw new MojoExecutionException( + "Incompatible version for " + contentDependencyKey + " in content package " + contentPackageName + ". Specified range: " + contentDependencyVersionRange + + ", found in distribution: " + distroPropertyVersion); + } + } catch (ParseException e) { + throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); + } + } } From 21acc6aa482c00e86fca2b75705e38d21bfdc920 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Wed, 7 Aug 2024 21:28:09 +0300 Subject: [PATCH 07/18] change back to semver4j --- maven-plugin/pom.xml | 5 ++ .../openmrs/maven/plugins/BuildDistro.java | 4 -- sdk-commons/pom.xml | 6 ++ .../maven/plugins/utility/DistroHelper.java | 4 +- .../plugins/utility/NpmVersionHelper.java | 69 ++++++++++++------- .../plugins/utility/PropertiesUtils.java | 10 +-- 6 files changed, 63 insertions(+), 35 deletions(-) diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml index ffdda75f..fac8ca66 100644 --- a/maven-plugin/pom.xml +++ b/maven-plugin/pom.xml @@ -29,6 +29,11 @@ org.apache.maven.shared maven-verifier + + com.vdurmont + semver4j + 3.1.0 + org.apache.maven maven-model diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index 2ef46e1b..a10bf79f 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -1,6 +1,5 @@ package org.openmrs.maven.plugins; -import com.github.zafarkhaja.semver.ParseException; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; @@ -26,8 +25,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; -import java.io.BufferedReader; -import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -36,7 +33,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Properties; /** * Create docker configuration for distributions. diff --git a/sdk-commons/pom.xml b/sdk-commons/pom.xml index f845d9df..577f5589 100644 --- a/sdk-commons/pom.xml +++ b/sdk-commons/pom.xml @@ -86,6 +86,12 @@ json 20230618 + + com.vdurmont + semver4j + 3.1.0 + compile + diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 699cfef9..d9e901ce 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -559,12 +559,12 @@ public DistroProperties resolveParentArtifact(Artifact parentArtifact, Server se return resolveParentArtifact(parentArtifact, server.getServerDirectory(), distroProperties, appShellVersion); } - public String findLatestMatchingVersion(String dependency) { + public String findLatestMatchingVersion(String dependency, String versionRange) { if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("content.") || dependency.startsWith("war.")) { return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); } else if (dependency.startsWith("spa.frontendModule")) { packageJson.setName(dependency.substring("spa.frontendModules.".length())); - return npmVersionHelper.getLatestReleasedVersionFromNpmRegistry(packageJson); + return npmVersionHelper.getLatestReleasedVersionFromNpmRegistry(packageJson, versionRange); } throw new IllegalArgumentException("Unsupported dependency type: " + dependency); } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java index 01537482..25713d1a 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java @@ -1,47 +1,68 @@ package org.openmrs.maven.plugins.utility; +import org.json.JSONArray; import org.openmrs.maven.plugins.model.PackageJson; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class NpmVersionHelper { - public String getLatestReleasedVersionFromNpmRegistry(PackageJson packageJson) { + private static final Logger log = LoggerFactory.getLogger(NpmVersionHelper.class); + + /** + * Retrieves the resolved version of an NPM package based on the supplied semver range. + *

+ * This method runs the `npm pack --dry-run --json @` command to + * get the exact version of the package that satisfies the specified semver range. + * + * @param packageJson The PackageJson object containing the name of the package. + * @param versionRange The semver range to resolve the version against. + * @return The resolved version of the package that satisfies the semver range. + * @throws RuntimeException if the command fails or the resolved version cannot be determined. + */ + public String getLatestReleasedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { try { String packageName = packageJson.getName(); - if (packageName == null || packageName.isEmpty()) { - throw new IllegalArgumentException("Package name cannot be null or empty"); + JSONArray jsonArray = getJsonArray(versionRange, packageName); + if (jsonArray.isEmpty()) { + throw new RuntimeException("No versions found for the specified range: " + versionRange); } - URL url = new URL("https://registry.npmjs.org/" + packageName.replace("/", "%2F")); - JSONObject json = getJson(url); - return json.getJSONObject("dist-tags").getString("latest"); - - } catch (Exception e) { - throw new RuntimeException("Error retrieving latest version from NPM ", e); + JSONObject jsonObject = jsonArray.getJSONObject(0); + return jsonObject.getString("version"); + } catch (IOException | InterruptedException e) { + log.error(e.getMessage()); + throw new RuntimeException("Error retrieving resolved version from NPM", e); } } - private static JSONObject getJson(URL url) throws IOException { - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("GET"); - conn.setRequestProperty("Accept", "application/json"); - if (conn.getResponseCode() != 200) { - throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); + private static JSONArray getJsonArray(String versionRange, String packageName) throws IOException, InterruptedException { + if (packageName == null || packageName.isEmpty()) { + throw new IllegalArgumentException("Package name cannot be null or empty"); } - BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); - StringBuilder sb = new StringBuilder(); - String output; - while ((output = br.readLine()) != null) { - sb.append(output); + ProcessBuilder processBuilder = new ProcessBuilder() + .command("npm", "pack", "--dry-run", "--json", packageName + "@" + versionRange) + .redirectErrorStream(true) + .inheritIO(); + Process process = processBuilder.start(); + + // Read the command output + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder outputBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + outputBuilder.append(line); } - conn.disconnect(); - return new JSONObject(sb.toString()); + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new RuntimeException("npm pack --dry-run --json command failed with exit code " + exitCode); + } + return new JSONArray(outputBuilder.toString()); } } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index e07280e7..074c1be1 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -22,10 +22,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.zafarkhaja.semver.ParseException; +import com.vdurmont.semver4j.Semver; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; @@ -352,7 +352,7 @@ public static void parseContentProperties(File userDir, DistroProperties distroP String distroVersion = distroProperties.get(dependency); if (distroVersion == null) { - String latestVersion = findLatestMatchingVersion(dependency); + String latestVersion = findLatestMatchingVersion(dependency, versionRange); if (latestVersion == null) { throw new MojoExecutionException("No matching version found for dependency " + dependency); } @@ -364,8 +364,8 @@ public static void parseContentProperties(File userDir, DistroProperties distroP } } - private static String findLatestMatchingVersion(String dependency) { - return distroHelper.findLatestMatchingVersion(dependency); + private static String findLatestMatchingVersion(String dependency, String versionRange) { + return distroHelper.findLatestMatchingVersion(dependency, versionRange); } /** @@ -379,7 +379,7 @@ private static String findLatestMatchingVersion(String dependency) { * @throws MojoExecutionException If the version does not fall within the specified range or if the range format is invalid. */ private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { - com.github.zafarkhaja.semver.Version semverVersion = com.github.zafarkhaja.semver.Version.parse(distroPropertyVersion); + Semver semverVersion = new Semver(distroPropertyVersion, Semver.SemverType.NPM); try { boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); From cc747572772f535f8a2993097391233272fb0c95 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Wed, 14 Aug 2024 00:48:36 +0300 Subject: [PATCH 08/18] fix --- maven-plugin/pom.xml | 5 -- pom.xml | 6 ++ sdk-commons/pom.xml | 11 ++-- .../maven/plugins/utility/DistroHelper.java | 2 +- .../plugins/utility/NpmVersionHelper.java | 2 +- .../plugins/utility/PropertiesUtils.java | 59 +++++++++++-------- 6 files changed, 46 insertions(+), 39 deletions(-) diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml index fac8ca66..ffdda75f 100644 --- a/maven-plugin/pom.xml +++ b/maven-plugin/pom.xml @@ -29,11 +29,6 @@ org.apache.maven.shared maven-verifier - - com.vdurmont - semver4j - 3.1.0 - org.apache.maven maven-model diff --git a/pom.xml b/pom.xml index da561c15..bd1ef447 100644 --- a/pom.xml +++ b/pom.xml @@ -403,6 +403,12 @@ 0.10.2 + + org.semver4j + semver4j + 5.3.0 + + org.jline diff --git a/sdk-commons/pom.xml b/sdk-commons/pom.xml index 577f5589..4c6235e4 100644 --- a/sdk-commons/pom.xml +++ b/sdk-commons/pom.xml @@ -34,6 +34,11 @@ java-semver + + org.semver4j + semver4j + + org.apache.httpcomponents @@ -86,12 +91,6 @@ json 20230618 - - com.vdurmont - semver4j - 3.1.0 - compile - diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index d9e901ce..df4a64f7 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -564,7 +564,7 @@ public String findLatestMatchingVersion(String dependency, String versionRange) return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); } else if (dependency.startsWith("spa.frontendModule")) { packageJson.setName(dependency.substring("spa.frontendModules.".length())); - return npmVersionHelper.getLatestReleasedVersionFromNpmRegistry(packageJson, versionRange); + return npmVersionHelper.getResolvedVersionFromNpmRegistry(packageJson, versionRange); } throw new IllegalArgumentException("Unsupported dependency type: " + dependency); } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java index 25713d1a..a6d08010 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java @@ -24,7 +24,7 @@ public class NpmVersionHelper { * @return The resolved version of the package that satisfies the semver range. * @throws RuntimeException if the command fails or the resolved version cannot be determined. */ - public String getLatestReleasedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { + public String getResolvedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { try { String packageName = packageJson.getName(); JSONArray jsonArray = getJsonArray(versionRange, packageName); diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 074c1be1..e8cb49e2 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -21,8 +21,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.zafarkhaja.semver.ParseException; -import com.vdurmont.semver4j.Semver; +import org.semver4j.Semver; +import org.semver4j.SemverException; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -311,38 +311,45 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx } /** - * Reads the content.properties file. + *

The distro.properties should have entries like: content.hiv=1.0.0-SNAPSHOT. + * This should correspond to a Zip file stored in a Maven repository and the content.properties file is in that Zip file

+ * + *

So this should be able to read the underlying content.properties file and then.

*

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties * matches the range defined in content.properties; otherwise, we output an error to the user to fix this.

- * For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version - * and add that to the OMODs and ESMs used by the distro. + *

For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version + * and add that to the OMODs and ESMs used by the distro.

* - * @param userDir The directory where the distro.properties or content.properties file is located. This is typically the user's working directory. + * @param file The directory where the distro.properties or content.properties file is located. This is typically the user's working directory. * @param distroProperties An object representing the properties defined in distro.properties. This includes versions of various dependencies * currently used in the distribution. * @throws MojoExecutionException if there is an error reading the content.properties file or if a version conflict is detected. */ - public static void parseContentProperties(File userDir, DistroProperties distroProperties) throws MojoExecutionException { - File contentFile = new File(userDir, CONTENT_PROPERTIES); - if (!contentFile.exists()) { - log.info("No content.properties file found in the user directory."); - return; - } - + public static void parseContentProperties(File file, DistroProperties distroProperties) throws MojoExecutionException { Properties contentProperties = new Properties(); - String contentPackageName = null; - String contentPackageVersion = null; + boolean found = false; - try (BufferedReader reader = new BufferedReader(new FileReader(contentFile))) { - contentProperties.load(reader); - contentPackageName = contentProperties.getProperty("name"); - contentPackageVersion = contentProperties.getProperty("version"); - - if (contentPackageName == null || contentPackageVersion == null) { - throw new MojoExecutionException("Content package name or version not specified in content.properties"); + try (ZipFile zipFile = new ZipFile(file)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (CONTENT_PROPERTIES.equals(zipEntry.getName())) { + try (InputStream inputStream = zipFile.getInputStream(zipEntry)) { + contentProperties.load(inputStream); + found = true; + log.info("content.properties file found and parsed successfully."); + + if (contentProperties.getProperty("name") == null || contentProperties.getProperty("version") == null) { + throw new MojoExecutionException("Content package name or version not specified in content.properties"); + } + } + } + } + if (!found) { + throw new MojoExecutionException("content.properties file not found in ZIP file"); } } catch (IOException e) { - throw new MojoExecutionException("Failed to read content.properties file", e); + throw new MojoExecutionException("Error reading content.properties from ZIP file: " + e.getMessage(), e); } for (String dependency : contentProperties.stringPropertyNames()) { @@ -358,7 +365,7 @@ public static void parseContentProperties(File userDir, DistroProperties distroP } distroProperties.add(dependency, latestVersion); } else { - checkVersionInRange(dependency, versionRange, distroVersion, contentPackageName); + checkVersionInRange(dependency, versionRange, distroVersion, contentProperties.getProperty("name")); } } } @@ -379,7 +386,7 @@ private static String findLatestMatchingVersion(String dependency, String versio * @throws MojoExecutionException If the version does not fall within the specified range or if the range format is invalid. */ private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { - Semver semverVersion = new Semver(distroPropertyVersion, Semver.SemverType.NPM); + Semver semverVersion = new Semver(distroPropertyVersion); try { boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); @@ -388,7 +395,7 @@ private static void checkVersionInRange(String contentDependencyKey, String cont "Incompatible version for " + contentDependencyKey + " in content package " + contentPackageName + ". Specified range: " + contentDependencyVersionRange + ", found in distribution: " + distroPropertyVersion); } - } catch (ParseException e) { + } catch (SemverException e) { throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); } } From be9aa33a4a3ae37dab2a73952ec8996ddbdc3955 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Mon, 19 Aug 2024 20:41:02 +0300 Subject: [PATCH 09/18] fix --- .../plugins/utility/PropertiesUtils.java | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index e8cb49e2..1577a84f 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -312,46 +312,66 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx /** *

The distro.properties should have entries like: content.hiv=1.0.0-SNAPSHOT. - * This should correspond to a Zip file stored in a Maven repository and the content.properties file is in that Zip file

+ * This should correspond to a Zip file stored in a Maven repository, and the content.properties file is in that Zip file.

* - *

So this should be able to read the underlying content.properties file and then.

- *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties - * matches the range defined in content.properties; otherwise, we output an error to the user to fix this.

+ *

This method reads all underlying content.properties files and checks dependencies against distro.properties.

+ *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties + * matches the range defined in content.properties; otherwise, it outputs an error for the user to fix.

*

For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version * and add that to the OMODs and ESMs used by the distro.

* - * @param file The directory where the distro.properties or content.properties file is located. This is typically the user's working directory. + * @param directory The directory where the distro.properties file is located. This is typically the user's working directory. * @param distroProperties An object representing the properties defined in distro.properties. This includes versions of various dependencies * currently used in the distribution. * @throws MojoExecutionException if there is an error reading the content.properties file or if a version conflict is detected. */ - public static void parseContentProperties(File file, DistroProperties distroProperties) throws MojoExecutionException { - Properties contentProperties = new Properties(); - boolean found = false; + public static void parseContentProperties(File directory, DistroProperties distroProperties) throws MojoExecutionException { + File[] zipFiles = directory.listFiles((dir, name) -> name.endsWith(".zip")); - try (ZipFile zipFile = new ZipFile(file)) { - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); - if (CONTENT_PROPERTIES.equals(zipEntry.getName())) { - try (InputStream inputStream = zipFile.getInputStream(zipEntry)) { - contentProperties.load(inputStream); - found = true; - log.info("content.properties file found and parsed successfully."); - - if (contentProperties.getProperty("name") == null || contentProperties.getProperty("version") == null) { - throw new MojoExecutionException("Content package name or version not specified in content.properties"); + if (zipFiles == null || zipFiles.length == 0) { + log.info("No ZIP files found in directory: {}", directory.getAbsolutePath()); + return; + } + + boolean foundAny = false; + + for (File zipFile : zipFiles) { + Properties contentProperties = new Properties(); + boolean found = false; + + try (ZipFile zip = new ZipFile(zipFile)) { + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (CONTENT_PROPERTIES.equals(zipEntry.getName())) { + try (InputStream inputStream = zip.getInputStream(zipEntry)) { + contentProperties.load(inputStream); + found = true; + foundAny = true; + log.info("content.properties file found in {} and parsed successfully.", zipFile.getName()); + + if (contentProperties.getProperty("name") == null || contentProperties.getProperty("version") == null) { + throw new MojoExecutionException("Content package name or version not specified in content.properties in " + zipFile.getName()); + } } + break; } } + } catch (IOException e) { + throw new MojoExecutionException("Error reading content.properties from ZIP file: " + zipFile.getName() + ": " + e.getMessage(), e); } - if (!found) { - throw new MojoExecutionException("content.properties file not found in ZIP file"); + + if (found) { + processContentProperties(contentProperties, distroProperties, zipFile.getName()); } - } catch (IOException e) { - throw new MojoExecutionException("Error reading content.properties from ZIP file: " + e.getMessage(), e); } + if (!foundAny) { + log.info("No content.properties file found in any ZIP file in directory: {}", directory.getAbsolutePath()); + } + } + + private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { for (String dependency : contentProperties.stringPropertyNames()) { if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { @@ -361,7 +381,7 @@ public static void parseContentProperties(File file, DistroProperties distroProp if (distroVersion == null) { String latestVersion = findLatestMatchingVersion(dependency, versionRange); if (latestVersion == null) { - throw new MojoExecutionException("No matching version found for dependency " + dependency); + throw new MojoExecutionException("No matching version found for dependency " + dependency + " in " + zipFileName); } distroProperties.add(dependency, latestVersion); } else { From dc165f68dadebed8dc5596a28068c02d8a8fc190 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Wed, 21 Aug 2024 09:15:32 +0300 Subject: [PATCH 10/18] changes according to reviews --- .../maven/plugins/model/DistroProperties.java | 43 +++---- .../plugins/utility/NpmVersionHelper.java | 117 +++++++++--------- .../plugins/utility/PropertiesUtils.java | 110 +++++++++------- 3 files changed, 150 insertions(+), 120 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java index 2cd9a376..4899af3f 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java @@ -296,25 +296,26 @@ public boolean contains(String propertyName) { return properties.containsKey(propertyName); } - /** - * Adds a dependency with the specified version to the properties. - * - * @param dependency The name of the dependency. - * @param version The version of the dependency. - */ - public void add(String dependency, String version) { - if (StringUtils.isBlank(dependency) || StringUtils.isBlank(version)) { - log.error("Dependency name or version cannot be blank"); - return; - } - - if (dependency.startsWith("omod") || dependency.startsWith("war")) { - properties.setProperty(dependency, version); - log.info("Added dependency: {} with version: {}", dependency, version); - } - } - - public String get(String contentDependencyKey) { - return properties.getProperty(contentDependencyKey); - } + /** + * Adds a dependency with the specified version to the properties. + * + * @param dependency The name of the dependency. + * @param version The version of the dependency. + */ + public void add(String dependency, String version) { + if (StringUtils.isBlank(dependency) || StringUtils.isBlank(version)) { + log.error("Dependency name or version cannot be blank"); + return; + } + + if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") + || dependency.startsWith("spa.frontendModule")) { + properties.setProperty(dependency, version); + log.info("Added dependency: {} with version: {}", dependency, version); + } + } + + public String get(String contentDependencyKey) { + return properties.getProperty(contentDependencyKey); + } } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java index a6d08010..105eb629 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java @@ -2,67 +2,72 @@ import org.json.JSONArray; import org.openmrs.maven.plugins.model.PackageJson; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; + import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NpmVersionHelper { - - private static final Logger log = LoggerFactory.getLogger(NpmVersionHelper.class); - - /** - * Retrieves the resolved version of an NPM package based on the supplied semver range. - *

- * This method runs the `npm pack --dry-run --json @` command to - * get the exact version of the package that satisfies the specified semver range. - * - * @param packageJson The PackageJson object containing the name of the package. - * @param versionRange The semver range to resolve the version against. - * @return The resolved version of the package that satisfies the semver range. - * @throws RuntimeException if the command fails or the resolved version cannot be determined. - */ - public String getResolvedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { - try { - String packageName = packageJson.getName(); - JSONArray jsonArray = getJsonArray(versionRange, packageName); - if (jsonArray.isEmpty()) { - throw new RuntimeException("No versions found for the specified range: " + versionRange); - } - - JSONObject jsonObject = jsonArray.getJSONObject(0); - return jsonObject.getString("version"); - } catch (IOException | InterruptedException e) { - log.error(e.getMessage()); - throw new RuntimeException("Error retrieving resolved version from NPM", e); - } - } - - private static JSONArray getJsonArray(String versionRange, String packageName) throws IOException, InterruptedException { - if (packageName == null || packageName.isEmpty()) { - throw new IllegalArgumentException("Package name cannot be null or empty"); - } - - ProcessBuilder processBuilder = new ProcessBuilder() - .command("npm", "pack", "--dry-run", "--json", packageName + "@" + versionRange) - .redirectErrorStream(true) - .inheritIO(); - Process process = processBuilder.start(); - - // Read the command output - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - StringBuilder outputBuilder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - outputBuilder.append(line); - } - - int exitCode = process.waitFor(); - if (exitCode != 0) { - throw new RuntimeException("npm pack --dry-run --json command failed with exit code " + exitCode); - } - return new JSONArray(outputBuilder.toString()); - } + + private static final Logger log = LoggerFactory.getLogger(NpmVersionHelper.class); + + /** + * Retrieves the resolved version of an NPM package based on the supplied semver range. + *

+ * This method runs the `npm pack --dry-run --json @` command to get the exact + * version of the package that satisfies the specified semver range. + * + * @param packageJson The PackageJson object containing the name of the package. + * @param versionRange The semver range to resolve the version against. + * @return The resolved version of the package that satisfies the semver range. + * @throws RuntimeException if the command fails or the resolved version cannot be determined. + */ + public String getResolvedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { + try { + String packageName = packageJson.getName(); + JSONArray jsonArray = getJsonArray(versionRange, packageName); + if (jsonArray.isEmpty()) { + throw new RuntimeException("No versions found for the specified range: " + versionRange); + } + + JSONObject jsonObject = jsonArray.getJSONObject(0); + return jsonObject.getString("version"); + } + catch (IOException | InterruptedException e) { + log.error(e.getMessage()); + throw new RuntimeException("Error retrieving resolved version from NPM", e); + } + } + + private static JSONArray getJsonArray(String versionRange, String packageName) throws IOException, InterruptedException { + if (packageName == null || packageName.isEmpty()) { + throw new IllegalArgumentException("Package name cannot be null or empty"); + } + + ProcessBuilder processBuilder = new ProcessBuilder() + .command("npm", "pack", "--dry-run", "--json", packageName + "@" + versionRange).redirectErrorStream(true) + .inheritIO(); + Process process = processBuilder.start(); + + // Read the command output + StringBuilder outputBuilder = new StringBuilder(); + char[] buffer = new char[4096]; + try (Reader reader = new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)) { + int read; + while ((read = reader.read(buffer)) >= 0) { + outputBuilder.append(buffer, 0, read); + } + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new RuntimeException( + "npm pack --dry-run --json command failed with exit code " + exitCode + ". Output: " + outputBuilder); + } + return new JSONArray(outputBuilder.toString()); + } } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 1577a84f..91e0279a 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -311,34 +311,47 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx } /** - *

The distro.properties should have entries like: content.hiv=1.0.0-SNAPSHOT. - * This should correspond to a Zip file stored in a Maven repository, and the content.properties file is in that Zip file.

+ *

+ * The distro.properties should have entries like: content.hiv=1.0.0-SNAPSHOT. This + * should correspond to a Zip file stored in a Maven repository, and the content.properties file is + * in that Zip file. + *

+ *

+ * This method reads all underlying content.properties files and checks dependencies against + * distro.properties. + *

+ *

+ * For dependencies defined in both content.properties and distro.properties, this ensures the + * version specified in distro.properties matches the range defined in content.properties; + * otherwise, it outputs an error for the user to fix. + *

+ *

+ * For dependencies not defined in distro.properties but defined in content.properties, the SDK + * should locate the latest matching version and add that to the OMODs and ESMs used by the distro. + *

* - *

This method reads all underlying content.properties files and checks dependencies against distro.properties.

- *

For dependencies defined in both content.properties and distro.properties, this ensures the version specified in distro.properties - * matches the range defined in content.properties; otherwise, it outputs an error for the user to fix.

- *

For dependencies not defined in distro.properties but defined in content.properties, the SDK should locate the latest matching version - * and add that to the OMODs and ESMs used by the distro.

- * - * @param directory The directory where the distro.properties file is located. This is typically the user's working directory. - * @param distroProperties An object representing the properties defined in distro.properties. This includes versions of various dependencies - * currently used in the distribution. - * @throws MojoExecutionException if there is an error reading the content.properties file or if a version conflict is detected. + * @param directory The directory where the distro.properties file is located. This is typically the + * user's working directory. + * @param distroProperties An object representing the properties defined in distro.properties. This + * includes versions of various dependencies currently used in the distribution. + * @throws MojoExecutionException if there is an error reading the content.properties file or if a + * version conflict is detected. */ - public static void parseContentProperties(File directory, DistroProperties distroProperties) throws MojoExecutionException { + public static void parseContentProperties(File directory, DistroProperties distroProperties) + throws MojoExecutionException { File[] zipFiles = directory.listFiles((dir, name) -> name.endsWith(".zip")); - + if (zipFiles == null || zipFiles.length == 0) { - log.info("No ZIP files found in directory: {}", directory.getAbsolutePath()); + log.info("No ZIP files found in directory: {}", directory.getAbsolutePath()); return; } - + boolean foundAny = false; - + for (File zipFile : zipFiles) { Properties contentProperties = new Properties(); boolean found = false; - + try (ZipFile zip = new ZipFile(zipFile)) { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { @@ -348,40 +361,47 @@ public static void parseContentProperties(File directory, DistroProperties distr contentProperties.load(inputStream); found = true; foundAny = true; - log.info("content.properties file found in {} and parsed successfully.", zipFile.getName()); - - if (contentProperties.getProperty("name") == null || contentProperties.getProperty("version") == null) { - throw new MojoExecutionException("Content package name or version not specified in content.properties in " + zipFile.getName()); + log.info("content.properties file found in {} and parsed successfully.", zipFile.getName()); + + if (contentProperties.getProperty("name") == null + || contentProperties.getProperty("version") == null) { + throw new MojoExecutionException( + "Content package name or version not specified in content.properties in " + + zipFile.getName()); } } break; } } - } catch (IOException e) { - throw new MojoExecutionException("Error reading content.properties from ZIP file: " + zipFile.getName() + ": " + e.getMessage(), e); } - + catch (IOException e) { + throw new MojoExecutionException( + "Error reading content.properties from ZIP file: " + zipFile.getName() + ": " + e.getMessage(), e); + } + if (found) { processContentProperties(contentProperties, distroProperties, zipFile.getName()); } } - + if (!foundAny) { - log.info("No content.properties file found in any ZIP file in directory: {}", directory.getAbsolutePath()); - } + log.info("No content.properties file found in any ZIP file in directory: {}", directory.getAbsolutePath()); + } } - private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { + private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, + String zipFileName) throws MojoExecutionException { for (String dependency : contentProperties.stringPropertyNames()) { if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") - || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { + || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { String versionRange = contentProperties.getProperty(dependency); String distroVersion = distroProperties.get(dependency); - + if (distroVersion == null) { String latestVersion = findLatestMatchingVersion(dependency, versionRange); if (latestVersion == null) { - throw new MojoExecutionException("No matching version found for dependency " + dependency + " in " + zipFileName); + throw new MojoExecutionException( + "No matching version found for dependency " + dependency + " in " + zipFileName); } distroProperties.add(dependency, latestVersion); } else { @@ -399,24 +419,28 @@ private static String findLatestMatchingVersion(String dependency, String versio * Checks if the version from distro.properties satisfies the range specified in content.properties. * Throws an exception if there is a mismatch. * - * @param contentDependencyKey The key of the content dependency. + * @param contentDependencyKey The key of the content dependency. * @param contentDependencyVersionRange The version range specified in content.properties. - * @param distroPropertyVersion The version specified in distro.properties. - * @param contentPackageName The name of the content package. - * @throws MojoExecutionException If the version does not fall within the specified range or if the range format is invalid. + * @param distroPropertyVersion The version specified in distro.properties. + * @param contentPackageName The name of the content package. + * @throws MojoExecutionException If the version does not fall within the specified range or if the + * range format is invalid. */ - private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { + private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, + String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { Semver semverVersion = new Semver(distroPropertyVersion); - + try { boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); if (!inRange) { - throw new MojoExecutionException( - "Incompatible version for " + contentDependencyKey + " in content package " + contentPackageName + ". Specified range: " + contentDependencyVersionRange - + ", found in distribution: " + distroPropertyVersion); + throw new MojoExecutionException("Incompatible version for " + contentDependencyKey + " in content package " + + contentPackageName + ". Specified range: " + contentDependencyVersionRange + + ", found in distribution: " + distroPropertyVersion); } - } catch (SemverException e) { - throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); + } + catch (SemverException e) { + throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); } } } From d83fd213d138adf42a469321fcdad14c07111a7f Mon Sep 17 00:00:00 2001 From: mherman22 Date: Thu, 22 Aug 2024 17:43:07 +0300 Subject: [PATCH 11/18] change to using jackson library --- sdk-commons/pom.xml | 5 -- .../maven/plugins/model/DistroProperties.java | 51 ++++++++++--------- .../maven/plugins/utility/DistroHelper.java | 40 +++++++++++---- .../plugins/utility/NpmVersionHelper.java | 25 +++++---- 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/sdk-commons/pom.xml b/sdk-commons/pom.xml index 4c6235e4..7020304c 100644 --- a/sdk-commons/pom.xml +++ b/sdk-commons/pom.xml @@ -86,11 +86,6 @@ mockito-core test - - org.json - json - 20230618 - diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java index 4899af3f..04153a69 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java @@ -1,8 +1,5 @@ package org.openmrs.maven.plugins.model; -import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromFile; -import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromResource; - import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.plugin.MojoExecutionException; @@ -21,6 +18,9 @@ import java.util.Properties; import java.util.Set; +import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromFile; +import static org.openmrs.maven.plugins.utility.PropertiesUtils.loadPropertiesFromResource; + /** * */ @@ -296,26 +296,27 @@ public boolean contains(String propertyName) { return properties.containsKey(propertyName); } - /** - * Adds a dependency with the specified version to the properties. - * - * @param dependency The name of the dependency. - * @param version The version of the dependency. - */ - public void add(String dependency, String version) { - if (StringUtils.isBlank(dependency) || StringUtils.isBlank(version)) { - log.error("Dependency name or version cannot be blank"); - return; - } - - if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") - || dependency.startsWith("spa.frontendModule")) { - properties.setProperty(dependency, version); - log.info("Added dependency: {} with version: {}", dependency, version); - } - } - - public String get(String contentDependencyKey) { - return properties.getProperty(contentDependencyKey); - } + /** + * Adds a dependency with the specified version to the properties. + * + * @param dependency The name of the dependency. + * @param version The version of the dependency. + */ + public void add(String dependency, String version) { + if (StringUtils.isBlank(dependency) || StringUtils.isBlank(version)) { + log.error("Dependency name or version cannot be blank"); + return; + } + + if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") + || dependency.startsWith("spa.frontendModule")) { + properties.setProperty(dependency, version); + log.info("Added dependency: {} with version: {}", dependency, version); + } + } + + public String get(String contentDependencyKey) { + return properties.getProperty(contentDependencyKey); + } + } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index df4a64f7..f2761dd6 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -18,13 +18,26 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_ARTIFACT_ID; import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_GROUP_ID; -import static org.twdata.maven.mojoexecutor.MojoExecutor.*; +import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; +import static org.twdata.maven.mojoexecutor.MojoExecutor.element; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment; +import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; +import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.name; +import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; +import static org.twdata.maven.mojoexecutor.MojoExecutor.version; public class DistroHelper { @@ -50,18 +63,14 @@ public class DistroHelper { final VersionsHelper versionHelper; - NpmVersionHelper npmVersionHelper; - - PackageJson packageJson; - public DistroHelper(MavenProject mavenProject, MavenSession mavenSession, BuildPluginManager pluginManager, - Wizard wizard, VersionsHelper versionHelper) { + Wizard wizard, VersionsHelper versionHelper) { this.mavenProject = mavenProject; this.mavenSession = mavenSession; this.pluginManager = pluginManager; this.wizard = wizard; this.versionHelper = versionHelper; - } + } /** * @return distro properties from openmrs-distro.properties file in current directory or null if not exist @@ -563,9 +572,20 @@ public String findLatestMatchingVersion(String dependency, String versionRange) if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("content.") || dependency.startsWith("war.")) { return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); } else if (dependency.startsWith("spa.frontendModule")) { - packageJson.setName(dependency.substring("spa.frontendModules.".length())); - return npmVersionHelper.getResolvedVersionFromNpmRegistry(packageJson, versionRange); + PackageJson packageJson = createPackageJson(dependency); + return getResolvedVersionFromNpmRegistry(packageJson, versionRange); } throw new IllegalArgumentException("Unsupported dependency type: " + dependency); } + + private PackageJson createPackageJson(String dependency) { + PackageJson packageJson = new PackageJson(); + packageJson.setName(dependency.substring("spa.frontendModules.".length())); + return packageJson; + } + + private String getResolvedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { + NpmVersionHelper npmVersionHelper = new NpmVersionHelper(); + return npmVersionHelper.getResolvedVersionFromNpmRegistry(packageJson, versionRange); + } } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java index 105eb629..f6f993aa 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/NpmVersionHelper.java @@ -1,20 +1,22 @@ package org.openmrs.maven.plugins.utility; -import org.json.JSONArray; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.openmrs.maven.plugins.model.PackageJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class NpmVersionHelper { private static final Logger log = LoggerFactory.getLogger(NpmVersionHelper.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); + /** * Retrieves the resolved version of an NPM package based on the supplied semver range. *

@@ -29,21 +31,21 @@ public class NpmVersionHelper { public String getResolvedVersionFromNpmRegistry(PackageJson packageJson, String versionRange) { try { String packageName = packageJson.getName(); - JSONArray jsonArray = getJsonArray(versionRange, packageName); + JsonNode jsonArray = getPackageMetadata(versionRange, packageName); if (jsonArray.isEmpty()) { throw new RuntimeException("No versions found for the specified range: " + versionRange); } - JSONObject jsonObject = jsonArray.getJSONObject(0); - return jsonObject.getString("version"); + JsonNode jsonObject = jsonArray.get(0); + return jsonObject.get("version").asText(); } catch (IOException | InterruptedException e) { - log.error(e.getMessage()); + log.error(e.getMessage(), e); throw new RuntimeException("Error retrieving resolved version from NPM", e); } } - private static JSONArray getJsonArray(String versionRange, String packageName) throws IOException, InterruptedException { + private static JsonNode getPackageMetadata(String versionRange, String packageName) throws IOException, InterruptedException { if (packageName == null || packageName.isEmpty()) { throw new IllegalArgumentException("Package name cannot be null or empty"); } @@ -68,6 +70,7 @@ private static JSONArray getJsonArray(String versionRange, String packageName) t throw new RuntimeException( "npm pack --dry-run --json command failed with exit code " + exitCode + ". Output: " + outputBuilder); } - return new JSONArray(outputBuilder.toString()); + + return objectMapper.readTree(outputBuilder.toString()); } } From e3120fa958706d6cc4ec38d5fe25b4179ae2390d Mon Sep 17 00:00:00 2001 From: mherman22 Date: Fri, 23 Aug 2024 14:08:03 +0300 Subject: [PATCH 12/18] add temporary file and make changes accordingly --- .../openmrs/maven/plugins/BuildDistro.java | 4 +- .../java/org/openmrs/maven/plugins/Setup.java | 42 ++-- .../plugins/utility/PropertiesUtils.java | 237 +++++++++++------- 3 files changed, 167 insertions(+), 116 deletions(-) diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index a10bf79f..52a6e5e8 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -12,10 +12,10 @@ import org.apache.maven.plugins.annotations.Parameter; import org.openmrs.maven.plugins.model.Artifact; import org.openmrs.maven.plugins.model.DistroProperties; +import org.openmrs.maven.plugins.model.Project; import org.openmrs.maven.plugins.model.Server; import org.openmrs.maven.plugins.model.Version; import org.openmrs.maven.plugins.utility.DistroHelper; -import org.openmrs.maven.plugins.model.Project; import org.openmrs.maven.plugins.utility.PropertiesUtils; import org.openmrs.maven.plugins.utility.SDKConstants; import org.slf4j.Logger; @@ -193,7 +193,7 @@ public void executeTask() throws MojoExecutionException, MojoFailureException { throw new MojoExecutionException("The distro you specified, '" + distro + "' could not be retrieved"); } - PropertiesUtils.parseContentProperties(userDir, distroProperties); + PropertiesUtils.parseContentProperties(distroProperties); String distroName = buildDistro(buildDirectory, distroArtifact, distroProperties); wizard.showMessage( diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java index 34a5ac27..f5d24531 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java @@ -1,25 +1,5 @@ package org.openmrs.maven.plugins; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import org.apache.commons.io.FileUtils; @@ -40,6 +20,26 @@ import org.openmrs.maven.plugins.utility.SDKConstants; import org.openmrs.maven.plugins.utility.ServerHelper; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + /** * Set up a new instance of OpenMRS server. It can be used for setting up a platform or a distribution. It prompts for any missing, but required parameters. @@ -277,7 +277,7 @@ public void setup(Server server, DistroProperties distroProperties) throws MojoE // `setServerVersionsFromDistroProperties`, and `server.setValuesFromDistroPropertiesModules`. distroHelper.savePropertiesToServer(distroProperties, server); setServerVersionsFromDistroProperties(server, distroProperties); - PropertiesUtils.parseContentProperties(server.getServerDirectory(), distroProperties); + PropertiesUtils.parseContentProperties(distroProperties); moduleInstaller.installModulesForDistro(server, distroProperties, distroHelper); setConfigFolder(server, distroProperties); if (spaInstaller != null) { diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 91e0279a..17461ece 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -1,28 +1,8 @@ package org.openmrs.maven.plugins.utility; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Map; -import java.util.Properties; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.semver4j.Semver; -import org.semver4j.SemverException; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -33,15 +13,40 @@ import org.openmrs.maven.plugins.model.Artifact; import org.openmrs.maven.plugins.model.DistroProperties; import org.openmrs.maven.plugins.model.Server; +import org.semver4j.Semver; +import org.semver4j.SemverException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + public class PropertiesUtils { private static final String CONTENT_PROPERTIES = "content.properties"; + private static final String CONTENT_PREFIX = "content."; + static DistroHelper distroHelper; private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); @@ -311,97 +316,132 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx } /** + * Parses the content properties from the distro properties file and processes the corresponding ZIP files. *

- * The distro.properties should have entries like: content.hiv=1.0.0-SNAPSHOT. This - * should correspond to a Zip file stored in a Maven repository, and the content.properties file is - * in that Zip file. - *

- *

- * This method reads all underlying content.properties files and checks dependencies against - * distro.properties. - *

- *

- * For dependencies defined in both content.properties and distro.properties, this ensures the - * version specified in distro.properties matches the range defined in content.properties; - * otherwise, it outputs an error for the user to fix. - *

- *

- * For dependencies not defined in distro.properties but defined in content.properties, the SDK - * should locate the latest matching version and add that to the OMODs and ESMs used by the distro. - *

+ * This method filters out the properties starting with "content." from the distro properties file, + * fetches the corresponding ZIP files from the Maven repository, and processes the content.properties + * files inside the ZIP files. It checks the dependencies against the distro properties file and + * ensures that the versions match. * - * @param directory The directory where the distro.properties file is located. This is typically the - * user's working directory. - * @param distroProperties An object representing the properties defined in distro.properties. This - * includes versions of various dependencies currently used in the distribution. - * @throws MojoExecutionException if there is an error reading the content.properties file or if a - * version conflict is detected. + * @param distroProperties the distro properties file to parse + * @throws MojoExecutionException if an error occurs during processing */ - public static void parseContentProperties(File directory, DistroProperties distroProperties) - throws MojoExecutionException { - File[] zipFiles = directory.listFiles((dir, name) -> name.endsWith(".zip")); - + public static void parseContentProperties(DistroProperties distroProperties) throws MojoExecutionException { + File tempDirectory = null; + try { + tempDirectory = Files.createTempDirectory("content-packages").toFile(); + String[] contentPackages = getContentPackages(distroProperties); + + for (String contentPackage : contentPackages) { + String version = distroProperties.get(contentPackage); + File zipFile = fetchZipFromMavenRepo(contentPackage, version, tempDirectory); + if (zipFile != null) { + processZipFile(zipFile, distroProperties); + } + } + + processTempDirectory(tempDirectory, distroProperties); + } catch (IOException e) { + throw new MojoExecutionException("Failed to process content packages", e); + } finally { + // Clean up temporary files + if (tempDirectory != null && tempDirectory.exists()) { + try { + FileUtils.deleteDirectory(tempDirectory); + } catch (IOException e) { + log.warn("Failed to delete temporary directory: {}", tempDirectory.getAbsolutePath(), e); + } + } + } + } + + private static File fetchZipFromMavenRepo(String contentPackage, String version, File tempDirectory) throws MojoExecutionException { + // Base URL for the OpenMRS JFrog repository + String repositoryUrl = "https://openmrs.jfrog.io/artifactory/public/"; + String groupId = "org.openmrs.content"; + String artifactId = contentPackage.replace("content.", ""); + String url = repositoryUrl + groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".zip"; + + try (InputStream in = new URL(url).openStream()) { + File zipFile = new File(tempDirectory, artifactId + "-" + version + ".zip"); + Files.copy(in, zipFile.toPath()); + return zipFile; + } catch (IOException e) { + throw new MojoExecutionException("Failed to download ZIP file from JFrog repository: " + url, e); + } + } + + private static void processTempDirectory(File tempDirectory, DistroProperties distroProperties) throws MojoExecutionException { + File[] zipFiles = tempDirectory.listFiles((dir, name) -> name.endsWith(".zip")); + if (zipFiles == null || zipFiles.length == 0) { - log.info("No ZIP files found in directory: {}", directory.getAbsolutePath()); + log.info("No ZIP files found in temporary directory: {}", tempDirectory.getAbsolutePath()); return; } - - boolean foundAny = false; - + for (File zipFile : zipFiles) { - Properties contentProperties = new Properties(); - boolean found = false; - - try (ZipFile zip = new ZipFile(zipFile)) { - Enumeration entries = zip.entries(); - while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); - if (CONTENT_PROPERTIES.equals(zipEntry.getName())) { - try (InputStream inputStream = zip.getInputStream(zipEntry)) { - contentProperties.load(inputStream); - found = true; - foundAny = true; - log.info("content.properties file found in {} and parsed successfully.", zipFile.getName()); - - if (contentProperties.getProperty("name") == null - || contentProperties.getProperty("version") == null) { - throw new MojoExecutionException( - "Content package name or version not specified in content.properties in " - + zipFile.getName()); - } + processZipFile(zipFile, distroProperties); + } + } + + /** + * Processes a ZIP file by extracting its contents and checking its dependencies. + *

+ * This method extracts the contents of the ZIP file to a temporary directory, + * checks the dependencies of the ZIP file against the distro properties file, + * and ensures that the versions match. + * + * @param zipFile the ZIP file to process + * @param distroProperties the distro properties file to check dependencies against + * @throws MojoExecutionException if an error occurs during processing + */ + private static void processZipFile(File zipFile, DistroProperties distroProperties) throws MojoExecutionException { + Properties contentProperties = new Properties(); + boolean found = false; + + try (ZipFile zip = new ZipFile(zipFile)) { + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (zipEntry.getName().equals("content.properties")) { + try (InputStream inputStream = zip.getInputStream(zipEntry)) { + contentProperties.load(inputStream); + found = true; + log.info("content.properties file found in {} and parsed successfully.", zipFile.getName()); + + if (contentProperties.getProperty("name") == null + || contentProperties.getProperty("version") == null) { + throw new MojoExecutionException( + "Content package name or version not specified in content.properties in " + + zipFile.getName()); } - break; } + break; } } - catch (IOException e) { - throw new MojoExecutionException( - "Error reading content.properties from ZIP file: " + zipFile.getName() + ": " + e.getMessage(), e); - } - - if (found) { - processContentProperties(contentProperties, distroProperties, zipFile.getName()); - } + } catch (IOException e) { + throw new MojoExecutionException( + "Error reading content.properties from ZIP file: " + zipFile.getName() + ": " + e.getMessage(), e); } - - if (!foundAny) { - log.info("No content.properties file found in any ZIP file in directory: {}", directory.getAbsolutePath()); + + if (found) { + processContentProperties(contentProperties, distroProperties, zipFile.getName()); } } private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, - String zipFileName) throws MojoExecutionException { + String zipFileName) throws MojoExecutionException { for (String dependency : contentProperties.stringPropertyNames()) { if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") - || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { + || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { String versionRange = contentProperties.getProperty(dependency); String distroVersion = distroProperties.get(dependency); - + if (distroVersion == null) { String latestVersion = findLatestMatchingVersion(dependency, versionRange); if (latestVersion == null) { throw new MojoExecutionException( - "No matching version found for dependency " + dependency + " in " + zipFileName); + "No matching version found for dependency " + dependency + " in " + zipFileName); } distroProperties.add(dependency, latestVersion); } else { @@ -427,20 +467,31 @@ private static String findLatestMatchingVersion(String dependency, String versio * range format is invalid. */ private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, - String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { + String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { Semver semverVersion = new Semver(distroPropertyVersion); - + try { boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); if (!inRange) { throw new MojoExecutionException("Incompatible version for " + contentDependencyKey + " in content package " - + contentPackageName + ". Specified range: " + contentDependencyVersionRange - + ", found in distribution: " + distroPropertyVersion); + + contentPackageName + ". Specified range: " + contentDependencyVersionRange + + ", found in distribution: " + distroPropertyVersion); } } catch (SemverException e) { throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey - + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); + + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); + } + } + + private static String[] getContentPackages(DistroProperties distroProperties) { + List contentPackages = new ArrayList<>(); + for (Object keyObject : distroProperties.getAllKeys()) { + String key = keyObject.toString(); + if (key.startsWith(CONTENT_PREFIX)) { + contentPackages.add(key); + } } + return contentPackages.toArray(new String[0]); } } From e5c1b6d1c071bdebe9cc0fda9fe2a225b2adce0c Mon Sep 17 00:00:00 2001 From: mherman22 Date: Sat, 24 Aug 2024 20:24:03 +0300 Subject: [PATCH 13/18] improve implementation --- .../plugins/utility/PropertiesUtils.java | 141 +++++++----------- 1 file changed, 56 insertions(+), 85 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 17461ece..baded16d 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -29,13 +29,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Properties; import java.util.zip.ZipEntry; @@ -330,107 +327,82 @@ public static void parseContentProperties(DistroProperties distroProperties) thr File tempDirectory = null; try { tempDirectory = Files.createTempDirectory("content-packages").toFile(); - String[] contentPackages = getContentPackages(distroProperties); - - for (String contentPackage : contentPackages) { - String version = distroProperties.get(contentPackage); - File zipFile = fetchZipFromMavenRepo(contentPackage, version, tempDirectory); - if (zipFile != null) { - processZipFile(zipFile, distroProperties); + + for (Object key : distroProperties.getAllKeys()) { + String keyOb = key.toString(); + if (!keyOb.startsWith(CONTENT_PREFIX)) { + continue; + } + + String version = distroProperties.get(keyOb); + File zipFile = distroHelper.downloadDistro(tempDirectory, + new Artifact(keyOb.replace(CONTENT_PREFIX, ""), version, + checkIfOverwritten(keyOb, distroProperties, "org.openmrs.content"), "zip"), + keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"); + if (zipFile == null) { + log.warn("ZIP file not found for content package: {}", keyOb); + continue; } + + // Process the ZIP file after it has been successfully fetched + processZipFile(zipFile, distroProperties); } - - processTempDirectory(tempDirectory, distroProperties); - } catch (IOException e) { + + } + catch (IOException e) { throw new MojoExecutionException("Failed to process content packages", e); - } finally { + } + finally { // Clean up temporary files if (tempDirectory != null && tempDirectory.exists()) { try { FileUtils.deleteDirectory(tempDirectory); - } catch (IOException e) { + } + catch (IOException e) { log.warn("Failed to delete temporary directory: {}", tempDirectory.getAbsolutePath(), e); } } } } - private static File fetchZipFromMavenRepo(String contentPackage, String version, File tempDirectory) throws MojoExecutionException { - // Base URL for the OpenMRS JFrog repository - String repositoryUrl = "https://openmrs.jfrog.io/artifactory/public/"; - String groupId = "org.openmrs.content"; - String artifactId = contentPackage.replace("content.", ""); - String url = repositoryUrl + groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".zip"; - - try (InputStream in = new URL(url).openStream()) { - File zipFile = new File(tempDirectory, artifactId + "-" + version + ".zip"); - Files.copy(in, zipFile.toPath()); - return zipFile; - } catch (IOException e) { - throw new MojoExecutionException("Failed to download ZIP file from JFrog repository: " + url, e); - } - } - - private static void processTempDirectory(File tempDirectory, DistroProperties distroProperties) throws MojoExecutionException { - File[] zipFiles = tempDirectory.listFiles((dir, name) -> name.endsWith(".zip")); - - if (zipFiles == null || zipFiles.length == 0) { - log.info("No ZIP files found in temporary directory: {}", tempDirectory.getAbsolutePath()); - return; - } - - for (File zipFile : zipFiles) { - processZipFile(zipFile, distroProperties); - } - } - - /** - * Processes a ZIP file by extracting its contents and checking its dependencies. - *

- * This method extracts the contents of the ZIP file to a temporary directory, - * checks the dependencies of the ZIP file against the distro properties file, - * and ensures that the versions match. - * - * @param zipFile the ZIP file to process - * @param distroProperties the distro properties file to check dependencies against - * @throws MojoExecutionException if an error occurs during processing - */ - private static void processZipFile(File zipFile, DistroProperties distroProperties) throws MojoExecutionException { + private static void processZipFile(File contentPackageZipFile, DistroProperties distroProperties) throws MojoExecutionException { Properties contentProperties = new Properties(); - boolean found = false; - try (ZipFile zip = new ZipFile(zipFile)) { + try (ZipFile zip = new ZipFile(contentPackageZipFile)) { + boolean foundContentProperties = false; Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); - if (zipEntry.getName().equals("content.properties")) { + if (zipEntry.getName().equals(CONTENT_PROPERTIES)) { + foundContentProperties = true; + try (InputStream inputStream = zip.getInputStream(zipEntry)) { contentProperties.load(inputStream); - found = true; - log.info("content.properties file found in {} and parsed successfully.", zipFile.getName()); + log.info("content.properties file found in {} and parsed successfully.", contentPackageZipFile.getName()); - if (contentProperties.getProperty("name") == null - || contentProperties.getProperty("version") == null) { + if (contentProperties.getProperty("name") == null || contentProperties.getProperty("version") == null) { throw new MojoExecutionException( - "Content package name or version not specified in content.properties in " - + zipFile.getName()); + "Content package name or version not specified in content.properties in " + contentPackageZipFile.getName()); } + + processContentProperties(contentProperties, distroProperties, contentPackageZipFile.getName()); } break; } } + + if (!foundContentProperties) { + throw new MojoExecutionException("No content.properties file found in ZIP file: " + contentPackageZipFile.getName()); + } + } catch (IOException e) { throw new MojoExecutionException( - "Error reading content.properties from ZIP file: " + zipFile.getName() + ": " + e.getMessage(), e); - } - - if (found) { - processContentProperties(contentProperties, distroProperties, zipFile.getName()); + "Error reading content.properties from ZIP file: " + contentPackageZipFile.getName() + ": " + e.getMessage(), e); } } - private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, - String zipFileName) throws MojoExecutionException { + private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { for (String dependency : contentProperties.stringPropertyNames()) { if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { @@ -466,8 +438,7 @@ private static String findLatestMatchingVersion(String dependency, String versio * @throws MojoExecutionException If the version does not fall within the specified range or if the * range format is invalid. */ - private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, - String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { + private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { Semver semverVersion = new Semver(distroPropertyVersion); try { @@ -477,21 +448,21 @@ private static void checkVersionInRange(String contentDependencyKey, String cont + contentPackageName + ". Specified range: " + contentDependencyVersionRange + ", found in distribution: " + distroPropertyVersion); } - } - catch (SemverException e) { + } catch (SemverException e) { throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); } } - private static String[] getContentPackages(DistroProperties distroProperties) { - List contentPackages = new ArrayList<>(); - for (Object keyObject : distroProperties.getAllKeys()) { - String key = keyObject.toString(); - if (key.startsWith(CONTENT_PREFIX)) { - contentPackages.add(key); - } - } - return contentPackages.toArray(new String[0]); + /** + * Checks if the groupId for a given dependency is overwritten in the distro properties. + * + * @param dependencyKey The key of the dependency. + * @param distroProperties The distro properties file. + * @return The groupId to use for this dependency. + */ + private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties, String defaultGroupId) { + String groupId = distroProperties.get(dependencyKey + ".groupId"); + return groupId != null ? groupId : defaultGroupId; } } From db9f61a802bd04582ada64d70dc3350a198a5fe8 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Sat, 24 Aug 2024 20:43:11 +0300 Subject: [PATCH 14/18] improve docs --- .../plugins/utility/PropertiesUtils.java | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index baded16d..26613963 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -313,52 +313,59 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx } /** - * Parses the content properties from the distro properties file and processes the corresponding ZIP files. + * Parses the content properties specified in the provided distro properties file and processes the corresponding content package ZIP files. *

- * This method filters out the properties starting with "content." from the distro properties file, - * fetches the corresponding ZIP files from the Maven repository, and processes the content.properties - * files inside the ZIP files. It checks the dependencies against the distro properties file and - * ensures that the versions match. + * This method performs the following tasks: + *

    + *
  1. Creates a temporary directory to store downloaded ZIP files.
  2. + *
  3. Iterates through all keys in the distro properties file.
  4. + *
  5. Filters keys to only process those starting with {@code CONTENT_PREFIX}.
  6. + *
  7. Downloads the ZIP files for each content package from the Maven repository and stores them in the temporary directory.
  8. + *
  9. Processes each downloaded ZIP file to extract and validate the {@code content.properties} file contained within.
  10. + *
  11. Handles errors and logs warnings if ZIP files are not found or if there are issues during processing.
  12. + *
+ *

+ * After processing, the temporary directory used for storing ZIP files is cleaned up to avoid leaving unnecessary files on the system. * - * @param distroProperties the distro properties file to parse - * @throws MojoExecutionException if an error occurs during processing + * @param distroProperties The distro properties file containing information about content packages and their versions. + * This file is used to determine which content packages to download and process. + * @throws MojoExecutionException If an error occurs during the downloading or processing of content packages. + * This exception may be thrown due to issues with file operations, invalid content properties, + * or other runtime errors encountered during the execution of this method. */ public static void parseContentProperties(DistroProperties distroProperties) throws MojoExecutionException { File tempDirectory = null; try { tempDirectory = Files.createTempDirectory("content-packages").toFile(); - for (Object key : distroProperties.getAllKeys()) { String keyOb = key.toString(); if (!keyOb.startsWith(CONTENT_PREFIX)) { continue; } - + String version = distroProperties.get(keyOb); - File zipFile = distroHelper.downloadDistro(tempDirectory, - new Artifact(keyOb.replace(CONTENT_PREFIX, ""), version, - checkIfOverwritten(keyOb, distroProperties, "org.openmrs.content"), "zip"), - keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"); + String zipFileName = keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"; + String artifactId = keyOb.replace(CONTENT_PREFIX, ""); + String groupId = checkIfOverwritten(keyOb, distroProperties); + + Artifact artifact = new Artifact(artifactId, version, groupId, "zip"); + File zipFile = distroHelper.downloadDistro(tempDirectory, artifact, zipFileName); + if (zipFile == null) { log.warn("ZIP file not found for content package: {}", keyOb); continue; } - - // Process the ZIP file after it has been successfully fetched + processZipFile(zipFile, distroProperties); } - - } - catch (IOException e) { + + } catch (IOException e) { throw new MojoExecutionException("Failed to process content packages", e); - } - finally { - // Clean up temporary files + } finally { if (tempDirectory != null && tempDirectory.exists()) { try { FileUtils.deleteDirectory(tempDirectory); - } - catch (IOException e) { + } catch (IOException e) { log.warn("Failed to delete temporary directory: {}", tempDirectory.getAbsolutePath(), e); } } @@ -461,8 +468,8 @@ private static void checkVersionInRange(String contentDependencyKey, String cont * @param distroProperties The distro properties file. * @return The groupId to use for this dependency. */ - private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties, String defaultGroupId) { + private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties) { String groupId = distroProperties.get(dependencyKey + ".groupId"); - return groupId != null ? groupId : defaultGroupId; + return groupId != null ? groupId : "org.openmrs.content"; } } From b6478742684455de086845270759640c339b47fd Mon Sep 17 00:00:00 2001 From: mherman22 Date: Sun, 25 Aug 2024 15:45:38 +0300 Subject: [PATCH 15/18] improve implementation --- .../maven/plugins/utility/DistroHelper.java | 104 +++++++++++++ .../plugins/utility/PropertiesUtils.java | 139 ++++++------------ 2 files changed, 149 insertions(+), 94 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index f2761dd6..80877843 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -12,6 +12,8 @@ import org.openmrs.maven.plugins.model.Server; import org.openmrs.maven.plugins.model.UpgradeDifferential; import org.openmrs.maven.plugins.model.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.twdata.maven.mojoexecutor.MojoExecutor; import org.twdata.maven.mojoexecutor.MojoExecutor.Element; @@ -28,6 +30,7 @@ import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_ARTIFACT_ID; import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_GROUP_ID; +import static org.openmrs.maven.plugins.utility.PropertiesUtils.processContentProperties; import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; import static org.twdata.maven.mojoexecutor.MojoExecutor.element; @@ -41,6 +44,11 @@ public class DistroHelper { + private static final String CONTENT_PROPERTIES = "content.properties"; + + private static final String CONTENT_PREFIX = "content."; + + private static final Logger log = LoggerFactory.getLogger(DistroHelper.class); /** * The project currently being build. */ @@ -290,6 +298,102 @@ public DistroProperties downloadDistroProperties(File path, Artifact artifact) t return distroProperties; } + /** + * Downloads and processes content packages specified in the given distro properties. + * + *

This method filters out properties starting with a specific prefix (defined by {@code CONTENT_PREFIX}) + * from the {@code distroProperties} file, identifies the corresponding versions, and downloads the + * associated ZIP files from the Maven repository. It then processes each downloaded ZIP file to locate + * and parse a {@code content.properties} file, ensuring that the content package is valid and meets + * the expected requirements.

+ * + *

If a {@code groupId} is overridden for a particular content package, the method uses the overridden + * value when fetching the package from Maven. The ZIP files are temporarily stored and processed to extract + * the {@code content.properties} file, which is then validated and compared against the dependencies specified + * in the {@code distro.properties} file.

+ * + * @param contentPackageZipFile The directory where content package ZIP files will be temporarily stored. + * @param distroProperties The {@code DistroProperties} object containing key-value pairs that specify + * content packages and other properties needed to build a distribution. + * + * @throws MojoExecutionException If there is an error during the download or processing of the content packages, + * such as missing or invalid {@code content.properties} files, or any IO issues. + */ + public void downloadContentPackages(File contentPackageZipFile, DistroProperties distroProperties) + throws MojoExecutionException { + Properties contentProperties = new Properties(); + + for (Object key : distroProperties.getAllKeys()) { + String keyOb = key.toString(); + if (!keyOb.startsWith(CONTENT_PREFIX)) { + continue; + } + + String version = distroProperties.get(keyOb); + String zipFileName = keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"; + String artifactId = keyOb.replace(CONTENT_PREFIX, ""); + String groupId = checkIfOverwritten(keyOb, distroProperties); + + Artifact artifact = new Artifact(artifactId, version, groupId, "zip"); + File zipFile = downloadDistro(contentPackageZipFile, artifact, zipFileName); + + if (zipFile == null) { + log.warn("ZIP file not found for content package: {}", keyOb); + continue; + } + + try (ZipFile zip = new ZipFile(contentPackageZipFile)) { + boolean foundContentProperties = false; + Enumeration entries = zip.entries(); + + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (zipEntry.getName().equals(CONTENT_PROPERTIES)) { + foundContentProperties = true; + + try (InputStream inputStream = zip.getInputStream(zipEntry)) { + contentProperties.load(inputStream); + log.info("content.properties file found in {} and parsed successfully.", + contentPackageZipFile.getName()); + + if (contentProperties.getProperty("name") == null + || contentProperties.getProperty("version") == null) { + throw new MojoExecutionException( + "Content package name or version not specified in content.properties in " + + contentPackageZipFile.getName()); + } + + processContentProperties(contentProperties, distroProperties, contentPackageZipFile.getName()); + } + break; + } + } + + if (!foundContentProperties) { + throw new MojoExecutionException( + "No content.properties file found in ZIP file: " + contentPackageZipFile.getName()); + } + + } + catch (IOException e) { + throw new MojoExecutionException("Error reading content.properties from ZIP file: " + + contentPackageZipFile.getName() + ": " + e.getMessage(), e); + } + } + } + + /** + * Checks if the groupId for a given dependency is overwritten in the distro properties. + * + * @param dependencyKey The key of the dependency. + * @param distroProperties The distro properties file. + * @return The groupId to use for this dependency. + */ + private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties) { + String groupId = distroProperties.get(dependencyKey + ".groupId"); + return groupId != null ? groupId : "org.openmrs.content"; + } + public DistroProperties downloadDistroProperties(File serverPath, Server server, String fileType) throws MojoExecutionException { Artifact artifact = new Artifact(server.getDistroArtifactId(), server.getVersion(), server.getDistroGroupId(), fileType); diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 26613963..93539768 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -39,11 +39,6 @@ import java.util.zip.ZipFile; public class PropertiesUtils { - - private static final String CONTENT_PROPERTIES = "content.properties"; - - private static final String CONTENT_PREFIX = "content."; - static DistroHelper distroHelper; private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); @@ -313,51 +308,28 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx } /** - * Parses the content properties specified in the provided distro properties file and processes the corresponding content package ZIP files. - *

- * This method performs the following tasks: - *

    - *
  1. Creates a temporary directory to store downloaded ZIP files.
  2. - *
  3. Iterates through all keys in the distro properties file.
  4. - *
  5. Filters keys to only process those starting with {@code CONTENT_PREFIX}.
  6. - *
  7. Downloads the ZIP files for each content package from the Maven repository and stores them in the temporary directory.
  8. - *
  9. Processes each downloaded ZIP file to extract and validate the {@code content.properties} file contained within.
  10. - *
  11. Handles errors and logs warnings if ZIP files are not found or if there are issues during processing.
  12. - *
- *

- * After processing, the temporary directory used for storing ZIP files is cleaned up to avoid leaving unnecessary files on the system. + * Parses and processes content properties from content packages defined in the given {@code DistroProperties} object. + * + *

This method creates a temporary directory to download and process content package ZIP files specified + * in the {@code distroProperties} file. The method delegates the download and processing of content packages + * to the {@code downloadContentPackages} method, ensuring that the content packages are correctly handled + * and validated.

+ * + *

After processing, the temporary directory used for storing the downloaded ZIP files is deleted, + * even if an error occurs during processing. If the temporary directory cannot be deleted, a warning is logged.

+ * + * @param distroProperties The {@code DistroProperties} object containing key-value pairs specifying + * content packages and other properties needed to build a distribution. * - * @param distroProperties The distro properties file containing information about content packages and their versions. - * This file is used to determine which content packages to download and process. - * @throws MojoExecutionException If an error occurs during the downloading or processing of content packages. - * This exception may be thrown due to issues with file operations, invalid content properties, - * or other runtime errors encountered during the execution of this method. + * @throws MojoExecutionException If there is an error during the processing of content packages, + * such as issues with creating the temporary directory, downloading + * the content packages, or IO errors during file operations. */ public static void parseContentProperties(DistroProperties distroProperties) throws MojoExecutionException { File tempDirectory = null; try { tempDirectory = Files.createTempDirectory("content-packages").toFile(); - for (Object key : distroProperties.getAllKeys()) { - String keyOb = key.toString(); - if (!keyOb.startsWith(CONTENT_PREFIX)) { - continue; - } - - String version = distroProperties.get(keyOb); - String zipFileName = keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"; - String artifactId = keyOb.replace(CONTENT_PREFIX, ""); - String groupId = checkIfOverwritten(keyOb, distroProperties); - - Artifact artifact = new Artifact(artifactId, version, groupId, "zip"); - File zipFile = distroHelper.downloadDistro(tempDirectory, artifact, zipFileName); - - if (zipFile == null) { - log.warn("ZIP file not found for content package: {}", keyOb); - continue; - } - - processZipFile(zipFile, distroProperties); - } + distroHelper.downloadContentPackages(tempDirectory, distroProperties); } catch (IOException e) { throw new MojoExecutionException("Failed to process content packages", e); @@ -372,44 +344,35 @@ public static void parseContentProperties(DistroProperties distroProperties) thr } } - private static void processZipFile(File contentPackageZipFile, DistroProperties distroProperties) throws MojoExecutionException { - Properties contentProperties = new Properties(); - - try (ZipFile zip = new ZipFile(contentPackageZipFile)) { - boolean foundContentProperties = false; - Enumeration entries = zip.entries(); - - while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); - if (zipEntry.getName().equals(CONTENT_PROPERTIES)) { - foundContentProperties = true; - - try (InputStream inputStream = zip.getInputStream(zipEntry)) { - contentProperties.load(inputStream); - log.info("content.properties file found in {} and parsed successfully.", contentPackageZipFile.getName()); - - if (contentProperties.getProperty("name") == null || contentProperties.getProperty("version") == null) { - throw new MojoExecutionException( - "Content package name or version not specified in content.properties in " + contentPackageZipFile.getName()); - } - - processContentProperties(contentProperties, distroProperties, contentPackageZipFile.getName()); - } - break; - } - } - - if (!foundContentProperties) { - throw new MojoExecutionException("No content.properties file found in ZIP file: " + contentPackageZipFile.getName()); - } - - } catch (IOException e) { - throw new MojoExecutionException( - "Error reading content.properties from ZIP file: " + contentPackageZipFile.getName() + ": " + e.getMessage(), e); - } - } - - private static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { + /** + * Processes the {@code content.properties} file of a content package and validates the dependencies + * against the {@code DistroProperties} provided. This method ensures that the dependencies defined + * in the {@code content.properties} file are either present in the {@code distroProperties} file with + * a version that matches the specified version range, or it finds the latest matching version if not + * already specified in {@code distroProperties}. + * + *

The method iterates over each dependency listed in the {@code content.properties} file, focusing on + * dependencies that start with specific prefixes such as {@code omod.}, {@code owa.}, {@code war}, + * {@code spa.frontendModule}, or {@code content.}. For each dependency, the method performs the following:

+ *
    + *
  • If the dependency is not present in {@code distroProperties}, it attempts to find the latest version + * matching the specified version range and adds it to {@code distroProperties}.
  • + *
  • If the dependency is present, it checks whether the version specified in {@code distroProperties} + * falls within the version range specified in {@code content.properties}. If it does not, an error is thrown.
  • + *
+ * + * @param contentProperties The {@code Properties} object representing the {@code content.properties} file + * of a content package. + * @param distroProperties The {@code DistroProperties} object containing key-value pairs specifying + * the current distribution's dependencies and their versions. + * @param zipFileName The name of the ZIP file containing the {@code content.properties} file being processed. + * Used in error messages to provide context. + * + * @throws MojoExecutionException If no matching version is found for a dependency not defined in + * {@code distroProperties}, or if the version specified in {@code distroProperties} + * does not match the version range in {@code content.properties}. + */ + protected static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { for (String dependency : contentProperties.stringPropertyNames()) { if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { @@ -460,16 +423,4 @@ private static void checkVersionInRange(String contentDependencyKey, String cont + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); } } - - /** - * Checks if the groupId for a given dependency is overwritten in the distro properties. - * - * @param dependencyKey The key of the dependency. - * @param distroProperties The distro properties file. - * @return The groupId to use for this dependency. - */ - private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties) { - String groupId = distroProperties.get(dependencyKey + ".groupId"); - return groupId != null ? groupId : "org.openmrs.content"; - } } From 9a603008cc16cc0c461e2c2386e26c43ef1306c2 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Mon, 26 Aug 2024 15:10:48 +0300 Subject: [PATCH 16/18] move logic to distroHelper --- .../openmrs-distro.properties | 1 + .../openmrs/maven/plugins/BuildDistro.java | 3 +- .../java/org/openmrs/maven/plugins/Setup.java | 3 +- .../maven/plugins/utility/DistroHelper.java | 309 ++++++++++++------ .../plugins/utility/PropertiesUtils.java | 125 +------ 5 files changed, 216 insertions(+), 225 deletions(-) diff --git a/integration-tests/src/test/resources/integration-test/openmrs-distro.properties b/integration-tests/src/test/resources/integration-test/openmrs-distro.properties index 97a28026..4e210880 100644 --- a/integration-tests/src/test/resources/integration-test/openmrs-distro.properties +++ b/integration-tests/src/test/resources/integration-test/openmrs-distro.properties @@ -13,3 +13,4 @@ spa.frontendModules.@openmrs/esm-login-app=3.3.1 spa.frontendModules.@openmrs/esm-patient-chart-app=3.1.0 spa.apiUrl=notopenmrs spa.configUrls=foo +content.hiv=1.0.0 diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index 52a6e5e8..1503f17c 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -16,7 +16,6 @@ import org.openmrs.maven.plugins.model.Server; import org.openmrs.maven.plugins.model.Version; import org.openmrs.maven.plugins.utility.DistroHelper; -import org.openmrs.maven.plugins.utility.PropertiesUtils; import org.openmrs.maven.plugins.utility.SDKConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -193,7 +192,7 @@ public void executeTask() throws MojoExecutionException, MojoFailureException { throw new MojoExecutionException("The distro you specified, '" + distro + "' could not be retrieved"); } - PropertiesUtils.parseContentProperties(distroProperties); + distroHelper.parseContentProperties(distroProperties); String distroName = buildDistro(buildDirectory, distroArtifact, distroProperties); wizard.showMessage( diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java index f5d24531..3df0cc82 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/Setup.java @@ -16,7 +16,6 @@ import org.openmrs.maven.plugins.model.Version; import org.openmrs.maven.plugins.utility.DBConnector; import org.openmrs.maven.plugins.utility.DistroHelper; -import org.openmrs.maven.plugins.utility.PropertiesUtils; import org.openmrs.maven.plugins.utility.SDKConstants; import org.openmrs.maven.plugins.utility.ServerHelper; @@ -277,7 +276,7 @@ public void setup(Server server, DistroProperties distroProperties) throws MojoE // `setServerVersionsFromDistroProperties`, and `server.setValuesFromDistroPropertiesModules`. distroHelper.savePropertiesToServer(distroProperties, server); setServerVersionsFromDistroProperties(server, distroProperties); - PropertiesUtils.parseContentProperties(distroProperties); + distroHelper.parseContentProperties(distroProperties); moduleInstaller.installModulesForDistro(server, distroProperties, distroHelper); setConfigFolder(server, distroProperties); if (spaInstaller != null) { diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 80877843..4e6a4d55 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -12,6 +12,8 @@ import org.openmrs.maven.plugins.model.Server; import org.openmrs.maven.plugins.model.UpgradeDifferential; import org.openmrs.maven.plugins.model.Version; +import org.semver4j.Semver; +import org.semver4j.SemverException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.twdata.maven.mojoexecutor.MojoExecutor; @@ -20,6 +22,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -30,7 +33,6 @@ import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_ARTIFACT_ID; import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_GROUP_ID; -import static org.openmrs.maven.plugins.utility.PropertiesUtils.processContentProperties; import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; import static org.twdata.maven.mojoexecutor.MojoExecutor.element; @@ -298,102 +300,6 @@ public DistroProperties downloadDistroProperties(File path, Artifact artifact) t return distroProperties; } - /** - * Downloads and processes content packages specified in the given distro properties. - * - *

This method filters out properties starting with a specific prefix (defined by {@code CONTENT_PREFIX}) - * from the {@code distroProperties} file, identifies the corresponding versions, and downloads the - * associated ZIP files from the Maven repository. It then processes each downloaded ZIP file to locate - * and parse a {@code content.properties} file, ensuring that the content package is valid and meets - * the expected requirements.

- * - *

If a {@code groupId} is overridden for a particular content package, the method uses the overridden - * value when fetching the package from Maven. The ZIP files are temporarily stored and processed to extract - * the {@code content.properties} file, which is then validated and compared against the dependencies specified - * in the {@code distro.properties} file.

- * - * @param contentPackageZipFile The directory where content package ZIP files will be temporarily stored. - * @param distroProperties The {@code DistroProperties} object containing key-value pairs that specify - * content packages and other properties needed to build a distribution. - * - * @throws MojoExecutionException If there is an error during the download or processing of the content packages, - * such as missing or invalid {@code content.properties} files, or any IO issues. - */ - public void downloadContentPackages(File contentPackageZipFile, DistroProperties distroProperties) - throws MojoExecutionException { - Properties contentProperties = new Properties(); - - for (Object key : distroProperties.getAllKeys()) { - String keyOb = key.toString(); - if (!keyOb.startsWith(CONTENT_PREFIX)) { - continue; - } - - String version = distroProperties.get(keyOb); - String zipFileName = keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"; - String artifactId = keyOb.replace(CONTENT_PREFIX, ""); - String groupId = checkIfOverwritten(keyOb, distroProperties); - - Artifact artifact = new Artifact(artifactId, version, groupId, "zip"); - File zipFile = downloadDistro(contentPackageZipFile, artifact, zipFileName); - - if (zipFile == null) { - log.warn("ZIP file not found for content package: {}", keyOb); - continue; - } - - try (ZipFile zip = new ZipFile(contentPackageZipFile)) { - boolean foundContentProperties = false; - Enumeration entries = zip.entries(); - - while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); - if (zipEntry.getName().equals(CONTENT_PROPERTIES)) { - foundContentProperties = true; - - try (InputStream inputStream = zip.getInputStream(zipEntry)) { - contentProperties.load(inputStream); - log.info("content.properties file found in {} and parsed successfully.", - contentPackageZipFile.getName()); - - if (contentProperties.getProperty("name") == null - || contentProperties.getProperty("version") == null) { - throw new MojoExecutionException( - "Content package name or version not specified in content.properties in " - + contentPackageZipFile.getName()); - } - - processContentProperties(contentProperties, distroProperties, contentPackageZipFile.getName()); - } - break; - } - } - - if (!foundContentProperties) { - throw new MojoExecutionException( - "No content.properties file found in ZIP file: " + contentPackageZipFile.getName()); - } - - } - catch (IOException e) { - throw new MojoExecutionException("Error reading content.properties from ZIP file: " - + contentPackageZipFile.getName() + ": " + e.getMessage(), e); - } - } - } - - /** - * Checks if the groupId for a given dependency is overwritten in the distro properties. - * - * @param dependencyKey The key of the dependency. - * @param distroProperties The distro properties file. - * @return The groupId to use for this dependency. - */ - private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties) { - String groupId = distroProperties.get(dependencyKey + ".groupId"); - return groupId != null ? groupId : "org.openmrs.content"; - } - public DistroProperties downloadDistroProperties(File serverPath, Server server, String fileType) throws MojoExecutionException { Artifact artifact = new Artifact(server.getDistroArtifactId(), server.getVersion(), server.getDistroGroupId(), fileType); @@ -672,6 +578,215 @@ public DistroProperties resolveParentArtifact(Artifact parentArtifact, Server se return resolveParentArtifact(parentArtifact, server.getServerDirectory(), distroProperties, appShellVersion); } + /** + * Parses and processes content properties from content packages defined in the given {@code DistroProperties} object. + * + *

This method creates a temporary directory to download and process content package ZIP files specified + * in the {@code distroProperties} file. The method delegates the download and processing of content packages + * to the {@code downloadContentPackages} method, ensuring that the content packages are correctly handled + * and validated.

+ * + *

After processing, the temporary directory used for storing the downloaded ZIP files is deleted, + * even if an error occurs during processing. If the temporary directory cannot be deleted, a warning is logged.

+ * + * @param distroProperties The {@code DistroProperties} object containing key-value pairs specifying + * content packages and other properties needed to build a distribution. + * + * @throws MojoExecutionException If there is an error during the processing of content packages, + * such as issues with creating the temporary directory, downloading + * the content packages, or IO errors during file operations. + */ + public void parseContentProperties(DistroProperties distroProperties) throws MojoExecutionException { + File tempDirectory = null; + try { + tempDirectory = Files.createTempDirectory("content-packages").toFile(); + downloadContentPackages(tempDirectory, distroProperties); + + } catch (IOException e) { + throw new MojoExecutionException("Failed to process content packages", e); + } finally { + if (tempDirectory != null && tempDirectory.exists()) { + try { + FileUtils.deleteDirectory(tempDirectory); + } catch (IOException e) { + log.warn("Failed to delete temporary directory: {}", tempDirectory.getAbsolutePath(), e); + } + } + } + } + + /** + * Downloads and processes content packages specified in the given distro properties. + * + *

This method filters out properties starting with a specific prefix (defined by {@code CONTENT_PREFIX}) + * from the {@code distroProperties} file, identifies the corresponding versions, and downloads the + * associated ZIP files from the Maven repository. It then processes each downloaded ZIP file to locate + * and parse a {@code content.properties} file, ensuring that the content package is valid and meets + * the expected requirements.

+ * + *

If a {@code groupId} is overridden for a particular content package, the method uses the overridden + * value when fetching the package from Maven. The ZIP files are temporarily stored and processed to extract + * the {@code content.properties} file, which is then validated and compared against the dependencies specified + * in the {@code distro.properties} file.

+ * + * @param contentPackageZipFile The directory where content package ZIP files will be temporarily stored. + * @param distroProperties The {@code DistroProperties} object containing key-value pairs that specify + * content packages and other properties needed to build a distribution. + * + * @throws MojoExecutionException If there is an error during the download or processing of the content packages, + * such as missing or invalid {@code content.properties} files, or any IO issues. + */ + public void downloadContentPackages(File contentPackageZipFile, DistroProperties distroProperties) + throws MojoExecutionException { + Properties contentProperties = new Properties(); + + for (Object key : distroProperties.getAllKeys()) { + String keyOb = key.toString(); + if (!keyOb.startsWith(CONTENT_PREFIX)) { + continue; + } + + String version = distroProperties.get(keyOb); + String zipFileName = keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"; + String artifactId = keyOb.replace(CONTENT_PREFIX, ""); + String groupId = checkIfOverwritten(keyOb, distroProperties); + + Artifact artifact = new Artifact(artifactId, version, groupId, "zip"); + File zipFile = downloadDistro(contentPackageZipFile, artifact, zipFileName); + + if (zipFile == null) { + log.warn("ZIP file not found for content package: {}", keyOb); + continue; + } + + try (ZipFile zip = new ZipFile(zipFile)) { + boolean foundContentProperties = false; + Enumeration entries = zip.entries(); + + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + if (zipEntry.getName().equals(CONTENT_PROPERTIES)) { + foundContentProperties = true; + + try (InputStream inputStream = zip.getInputStream(zipEntry)) { + contentProperties.load(inputStream); + log.info("content.properties file found in {} and parsed successfully.", + contentPackageZipFile.getName()); + + if (contentProperties.getProperty("name") == null + || contentProperties.getProperty("version") == null) { + throw new MojoExecutionException( + "Content package name or version not specified in content.properties in " + + contentPackageZipFile.getName()); + } + + processContentProperties(contentProperties, distroProperties, contentPackageZipFile.getName()); + } + break; + } + } + + if (!foundContentProperties) { + throw new MojoExecutionException( + "No content.properties file found in ZIP file: " + contentPackageZipFile.getName()); + } + + } + catch (IOException e) { + throw new MojoExecutionException("Error reading content.properties from ZIP file: " + + contentPackageZipFile.getName() + ": " + e.getMessage(), e); + } + } + } + + /** + * Processes the {@code content.properties} file of a content package and validates the dependencies + * against the {@code DistroProperties} provided. This method ensures that the dependencies defined + * in the {@code content.properties} file are either present in the {@code distroProperties} file with + * a version that matches the specified version range, or it finds the latest matching version if not + * already specified in {@code distroProperties}. + * + *

The method iterates over each dependency listed in the {@code content.properties} file, focusing on + * dependencies that start with specific prefixes such as {@code omod.}, {@code owa.}, {@code war}, + * {@code spa.frontendModule}, or {@code content.}. For each dependency, the method performs the following:

+ *
    + *
  • If the dependency is not present in {@code distroProperties}, it attempts to find the latest version + * matching the specified version range and adds it to {@code distroProperties}.
  • + *
  • If the dependency is present, it checks whether the version specified in {@code distroProperties} + * falls within the version range specified in {@code content.properties}. If it does not, an error is thrown.
  • + *
+ * + * @param contentProperties The {@code Properties} object representing the {@code content.properties} file + * of a content package. + * @param distroProperties The {@code DistroProperties} object containing key-value pairs specifying + * the current distribution's dependencies and their versions. + * @param zipFileName The name of the ZIP file containing the {@code content.properties} file being processed. + * Used in error messages to provide context. + * + * @throws MojoExecutionException If no matching version is found for a dependency not defined in + * {@code distroProperties}, or if the version specified in {@code distroProperties} + * does not match the version range in {@code content.properties}. + */ + protected void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { + for (String dependency : contentProperties.stringPropertyNames()) { + if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") + || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { + String versionRange = contentProperties.getProperty(dependency); + String distroVersion = distroProperties.get(dependency); + + if (distroVersion == null) { + String latestVersion = findLatestMatchingVersion(dependency, versionRange); + if (latestVersion == null) { + throw new MojoExecutionException( + "No matching version found for dependency " + dependency + " in " + zipFileName); + } + distroProperties.add(dependency, latestVersion); + } else { + checkVersionInRange(dependency, versionRange, distroVersion, contentProperties.getProperty("name")); + } + } + } + } + + /** + * Checks if the groupId for a given dependency is overwritten in the distro properties. + * + * @param dependencyKey The key of the dependency. + * @param distroProperties The distro properties file. + * @return The groupId to use for this dependency. + */ + private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties) { + String groupId = distroProperties.get(dependencyKey + ".groupId"); + return groupId != null ? groupId : "org.openmrs.content"; + } + + /** + * Checks if the version from distro.properties satisfies the range specified in content.properties. + * Throws an exception if there is a mismatch. + * + * @param contentDependencyKey The key of the content dependency. + * @param contentDependencyVersionRange The version range specified in content.properties. + * @param distroPropertyVersion The version specified in distro.properties. + * @param contentPackageName The name of the content package. + * @throws MojoExecutionException If the version does not fall within the specified range or if the + * range format is invalid. + */ + private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { + Semver semverVersion = new Semver(distroPropertyVersion); + + try { + boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); + if (!inRange) { + throw new MojoExecutionException("Incompatible version for " + contentDependencyKey + " in content package " + + contentPackageName + ". Specified range: " + contentDependencyVersionRange + + ", found in distribution: " + distroPropertyVersion); + } + } catch (SemverException e) { + throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey + + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); + } + } + public String findLatestMatchingVersion(String dependency, String versionRange) { if (dependency.startsWith("omod") || dependency.startsWith("owa") || dependency.startsWith("content.") || dependency.startsWith("war.")) { return versionHelper.getLatestReleasedVersion(new Artifact(dependency, "latest")); diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 93539768..8756b0bc 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -11,10 +10,7 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.maven.plugin.MojoExecutionException; import org.openmrs.maven.plugins.model.Artifact; -import org.openmrs.maven.plugins.model.DistroProperties; import org.openmrs.maven.plugins.model.Server; -import org.semver4j.Semver; -import org.semver4j.SemverException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -30,7 +26,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.Enumeration; import java.util.Iterator; import java.util.Map; @@ -38,8 +33,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -public class PropertiesUtils { - static DistroHelper distroHelper; +public class PropertiesUtils { private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); @@ -306,121 +300,4 @@ private static Document parseXMLFromURL(String url) throws ParserConfigurationEx document.getDocumentElement().normalize(); return document; } - - /** - * Parses and processes content properties from content packages defined in the given {@code DistroProperties} object. - * - *

This method creates a temporary directory to download and process content package ZIP files specified - * in the {@code distroProperties} file. The method delegates the download and processing of content packages - * to the {@code downloadContentPackages} method, ensuring that the content packages are correctly handled - * and validated.

- * - *

After processing, the temporary directory used for storing the downloaded ZIP files is deleted, - * even if an error occurs during processing. If the temporary directory cannot be deleted, a warning is logged.

- * - * @param distroProperties The {@code DistroProperties} object containing key-value pairs specifying - * content packages and other properties needed to build a distribution. - * - * @throws MojoExecutionException If there is an error during the processing of content packages, - * such as issues with creating the temporary directory, downloading - * the content packages, or IO errors during file operations. - */ - public static void parseContentProperties(DistroProperties distroProperties) throws MojoExecutionException { - File tempDirectory = null; - try { - tempDirectory = Files.createTempDirectory("content-packages").toFile(); - distroHelper.downloadContentPackages(tempDirectory, distroProperties); - - } catch (IOException e) { - throw new MojoExecutionException("Failed to process content packages", e); - } finally { - if (tempDirectory != null && tempDirectory.exists()) { - try { - FileUtils.deleteDirectory(tempDirectory); - } catch (IOException e) { - log.warn("Failed to delete temporary directory: {}", tempDirectory.getAbsolutePath(), e); - } - } - } - } - - /** - * Processes the {@code content.properties} file of a content package and validates the dependencies - * against the {@code DistroProperties} provided. This method ensures that the dependencies defined - * in the {@code content.properties} file are either present in the {@code distroProperties} file with - * a version that matches the specified version range, or it finds the latest matching version if not - * already specified in {@code distroProperties}. - * - *

The method iterates over each dependency listed in the {@code content.properties} file, focusing on - * dependencies that start with specific prefixes such as {@code omod.}, {@code owa.}, {@code war}, - * {@code spa.frontendModule}, or {@code content.}. For each dependency, the method performs the following:

- *
    - *
  • If the dependency is not present in {@code distroProperties}, it attempts to find the latest version - * matching the specified version range and adds it to {@code distroProperties}.
  • - *
  • If the dependency is present, it checks whether the version specified in {@code distroProperties} - * falls within the version range specified in {@code content.properties}. If it does not, an error is thrown.
  • - *
- * - * @param contentProperties The {@code Properties} object representing the {@code content.properties} file - * of a content package. - * @param distroProperties The {@code DistroProperties} object containing key-value pairs specifying - * the current distribution's dependencies and their versions. - * @param zipFileName The name of the ZIP file containing the {@code content.properties} file being processed. - * Used in error messages to provide context. - * - * @throws MojoExecutionException If no matching version is found for a dependency not defined in - * {@code distroProperties}, or if the version specified in {@code distroProperties} - * does not match the version range in {@code content.properties}. - */ - protected static void processContentProperties(Properties contentProperties, DistroProperties distroProperties, String zipFileName) throws MojoExecutionException { - for (String dependency : contentProperties.stringPropertyNames()) { - if (dependency.startsWith("omod.") || dependency.startsWith("owa.") || dependency.startsWith("war") - || dependency.startsWith("spa.frontendModule") || dependency.startsWith("content.")) { - String versionRange = contentProperties.getProperty(dependency); - String distroVersion = distroProperties.get(dependency); - - if (distroVersion == null) { - String latestVersion = findLatestMatchingVersion(dependency, versionRange); - if (latestVersion == null) { - throw new MojoExecutionException( - "No matching version found for dependency " + dependency + " in " + zipFileName); - } - distroProperties.add(dependency, latestVersion); - } else { - checkVersionInRange(dependency, versionRange, distroVersion, contentProperties.getProperty("name")); - } - } - } - } - - private static String findLatestMatchingVersion(String dependency, String versionRange) { - return distroHelper.findLatestMatchingVersion(dependency, versionRange); - } - - /** - * Checks if the version from distro.properties satisfies the range specified in content.properties. - * Throws an exception if there is a mismatch. - * - * @param contentDependencyKey The key of the content dependency. - * @param contentDependencyVersionRange The version range specified in content.properties. - * @param distroPropertyVersion The version specified in distro.properties. - * @param contentPackageName The name of the content package. - * @throws MojoExecutionException If the version does not fall within the specified range or if the - * range format is invalid. - */ - private static void checkVersionInRange(String contentDependencyKey, String contentDependencyVersionRange, String distroPropertyVersion, String contentPackageName) throws MojoExecutionException { - Semver semverVersion = new Semver(distroPropertyVersion); - - try { - boolean inRange = semverVersion.satisfies(contentDependencyVersionRange.trim()); - if (!inRange) { - throw new MojoExecutionException("Incompatible version for " + contentDependencyKey + " in content package " - + contentPackageName + ". Specified range: " + contentDependencyVersionRange - + ", found in distribution: " + distroPropertyVersion); - } - } catch (SemverException e) { - throw new MojoExecutionException("Invalid version range format for " + contentDependencyKey - + " in content package " + contentPackageName + ": " + contentDependencyVersionRange, e); - } - } } From 4223434b7d1584ce7ade50fecd0949902d7c52e5 Mon Sep 17 00:00:00 2001 From: mherman22 Date: Thu, 29 Aug 2024 22:52:47 +0300 Subject: [PATCH 17/18] try to answer reviews and make this work --- .../openmrs/maven/plugins/BuildDistro.java | 2 +- .../openmrs/maven/plugins/model/Artifact.java | 1 + .../plugins/model/BaseSdkProperties.java | 12 ++++++---- .../maven/plugins/model/DistroProperties.java | 5 +++++ .../maven/plugins/utility/DefaultWizard.java | 3 +-- .../maven/plugins/utility/DistroHelper.java | 22 +++++-------------- .../plugins/utility/PropertiesUtils.java | 2 +- 7 files changed, 23 insertions(+), 24 deletions(-) diff --git a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java index 1503f17c..dc714425 100644 --- a/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java +++ b/maven-plugin/src/main/java/org/openmrs/maven/plugins/BuildDistro.java @@ -75,7 +75,7 @@ public class BuildDistro extends AbstractTask { private static final Logger log = LoggerFactory.getLogger(BuildDistro.class); - /** + /** * Path to the openmrs-distro.properties file. */ @Parameter(property = "distro") diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/Artifact.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/Artifact.java index 175e9d28..fc825555 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/Artifact.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/Artifact.java @@ -24,6 +24,7 @@ public class Artifact { public static final String GROUP_MODULE = "org.openmrs.module"; public static final String GROUP_OWA = "org.openmrs.owa"; public static final String GROUP_WEB = "org.openmrs.web"; + public static final String GROUP_CONTENT = "org.openmrs.content"; public static final String GROUP_OPENMRS = "org.openmrs"; public static final String GROUP_H2 = "com.h2database"; public static final String GROUP_DISTRO = "org.openmrs.distro"; diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java index a843e133..c751ef36 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java @@ -16,14 +16,15 @@ public abstract class BaseSdkProperties { public static final String PROPERTY_DISTRO_ARTIFACT_ID = "distro.artifactId"; public static final String PROPERTY_DISTRO_GROUP_ID = "distro.groupId"; - protected static final String ARTIFACT_ID = "artifactId"; - protected static final String TYPE = "type"; - protected static final String GROUP_ID = "groupId"; + public static final String ARTIFACT_ID = "artifactId"; + public static final String TYPE = "type"; + public static final String GROUP_ID = "groupId"; protected static final String TYPE_OMOD = "omod"; protected static final String TYPE_WAR = "war"; protected static final String TYPE_JAR = "jar"; protected static final String NAME = "name"; protected static final String VERSION = "version"; + protected static final String TYPE_CONTENT = "content"; protected static final String TYPE_DISTRO = "distro"; protected static final String TYPE_OWA = "owa"; protected static final String TYPE_SPA = "spa"; @@ -189,6 +190,8 @@ protected String checkIfOverwritten(String key, String param) { case TYPE_DISTRO: case TYPE_CONFIG: return properties.getProperty(PROPERTY_DISTRO_GROUP_ID, Artifact.GROUP_DISTRO); + case TYPE_CONTENT: + return Artifact.GROUP_CONTENT; default: return ""; } @@ -200,7 +203,8 @@ protected String checkIfOverwritten(String key, String param) { return TYPE_JAR; case TYPE_WAR: return TYPE_WAR; - case TYPE_CONFIG: + case TYPE_CONFIG: + case TYPE_CONTENT: return TYPE_ZIP; default: return ""; diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java index 04153a69..32d2e8d9 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/DistroProperties.java @@ -319,4 +319,9 @@ public String get(String contentDependencyKey) { return properties.getProperty(contentDependencyKey); } + @Override + public String checkIfOverwritten(String key, String param) { + return super.checkIfOverwritten(key, param); + } + } diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DefaultWizard.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DefaultWizard.java index dcc4a648..ca872e5d 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DefaultWizard.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DefaultWizard.java @@ -781,8 +781,7 @@ private Map getDistroVersionsOptionsMap(Set versions, Ve * @param optionTemplate The template for generating option keys in the map. * @return A LinkedHashMap containing the generated options map. */ - private Map getO3VersionsOptionsMap(VersionsHelper versionsHelper, - String optionTemplate) { + private Map getO3VersionsOptionsMap(VersionsHelper versionsHelper, String optionTemplate) { Map optionsMap = new LinkedHashMap<>(); { diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java index 4e6a4d55..756af603 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/DistroHelper.java @@ -31,8 +31,11 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import static org.openmrs.maven.plugins.model.BaseSdkProperties.ARTIFACT_ID; +import static org.openmrs.maven.plugins.model.BaseSdkProperties.GROUP_ID; import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_ARTIFACT_ID; import static org.openmrs.maven.plugins.model.BaseSdkProperties.PROPERTY_DISTRO_GROUP_ID; +import static org.openmrs.maven.plugins.model.BaseSdkProperties.TYPE; import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; import static org.twdata.maven.mojoexecutor.MojoExecutor.element; @@ -646,12 +649,11 @@ public void downloadContentPackages(File contentPackageZipFile, DistroProperties continue; } + Artifact artifact = new Artifact(distroProperties.checkIfOverwritten(keyOb.replace(CONTENT_PREFIX, ""), ARTIFACT_ID), distroProperties.getParam(keyOb), + distroProperties.checkIfOverwritten(keyOb, GROUP_ID), distroProperties.checkIfOverwritten(keyOb, TYPE)); + String version = distroProperties.get(keyOb); String zipFileName = keyOb.replace(CONTENT_PREFIX, "") + "-" + version + ".zip"; - String artifactId = keyOb.replace(CONTENT_PREFIX, ""); - String groupId = checkIfOverwritten(keyOb, distroProperties); - - Artifact artifact = new Artifact(artifactId, version, groupId, "zip"); File zipFile = downloadDistro(contentPackageZipFile, artifact, zipFileName); if (zipFile == null) { @@ -748,18 +750,6 @@ protected void processContentProperties(Properties contentProperties, DistroProp } } - /** - * Checks if the groupId for a given dependency is overwritten in the distro properties. - * - * @param dependencyKey The key of the dependency. - * @param distroProperties The distro properties file. - * @return The groupId to use for this dependency. - */ - private static String checkIfOverwritten(String dependencyKey, DistroProperties distroProperties) { - String groupId = distroProperties.get(dependencyKey + ".groupId"); - return groupId != null ? groupId : "org.openmrs.content"; - } - /** * Checks if the version from distro.properties satisfies the range specified in content.properties. * Throws an exception if there is a mismatch. diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java index 8756b0bc..60264dad 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/utility/PropertiesUtils.java @@ -33,7 +33,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -public class PropertiesUtils { +public class PropertiesUtils { private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); From 93eaf0bf2decb41e32f520f205c508e2c5a08370 Mon Sep 17 00:00:00 2001 From: Ian <52504170+ibacher@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:46:48 +0300 Subject: [PATCH 18/18] Try to fix whitespaces --- .../plugins/model/BaseSdkProperties.java | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java index c751ef36..e6ed4ee9 100644 --- a/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java +++ b/sdk-commons/src/main/java/org/openmrs/maven/plugins/model/BaseSdkProperties.java @@ -180,37 +180,36 @@ protected String checkIfOverwritten(String key, String param) { } else { switch (param) { case ARTIFACT_ID: - return extractArtifactId(key); + return extractArtifactId(key); case GROUP_ID: - switch (getArtifactType(key)) { - case TYPE_WAR: //for openmrs.war use org.openmrs.web groupId - return Artifact.GROUP_WEB; - case TYPE_OMOD: - return Artifact.GROUP_MODULE; - case TYPE_DISTRO: - case TYPE_CONFIG: - return properties.getProperty(PROPERTY_DISTRO_GROUP_ID, Artifact.GROUP_DISTRO); - case TYPE_CONTENT: - return Artifact.GROUP_CONTENT; - default: - return ""; - } - + switch (getArtifactType(key)) { + case TYPE_WAR: //for openmrs.war use org.openmrs.web groupId + return Artifact.GROUP_WEB; + case TYPE_OMOD: + return Artifact.GROUP_MODULE; + case TYPE_DISTRO: + case TYPE_CONFIG: + return properties.getProperty(PROPERTY_DISTRO_GROUP_ID, Artifact.GROUP_DISTRO); + case TYPE_CONTENT: + return Artifact.GROUP_CONTENT; + default: + return ""; + } case TYPE: - switch (getArtifactType(key)) { - case TYPE_OMOD: - case TYPE_DISTRO: - return TYPE_JAR; - case TYPE_WAR: - return TYPE_WAR; - case TYPE_CONFIG: - case TYPE_CONTENT: - return TYPE_ZIP; - default: - return ""; - } + switch (getArtifactType(key)) { + case TYPE_OMOD: + case TYPE_DISTRO: + return TYPE_JAR; + case TYPE_WAR: + return TYPE_WAR; + case TYPE_CONFIG: + case TYPE_CONTENT: + return TYPE_ZIP; + default: + return ""; + } default: - return ""; + return ""; } } }