diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 470c26e..d420926 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -9,11 +9,11 @@ jobs: steps: - name: Git Clone uses: actions/checkout@v3 - - name: Set up JDK 1.8 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 11 + java-version: 21 server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml server-username: MAVEN_USERNAME # env variable for username in deploy server-password: MAVEN_PASSWORD # env variable for token in deploy diff --git a/aem-classification-maven-plugin/pom.xml b/aem-classification-maven-plugin/pom.xml index e8a846d..5779786 100644 --- a/aem-classification-maven-plugin/pom.xml +++ b/aem-classification-maven-plugin/pom.xml @@ -23,6 +23,10 @@ + + 11 + + @@ -121,16 +125,6 @@ aem-classification-validator 1.1.1 - - commons-lang - commons-lang - 2.5 - - - commons-io - commons-io - 2.7 - org.apache.felix diff --git a/aem-classification-maven-plugin/src/main/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojo.java b/aem-classification-maven-plugin/src/main/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojo.java index 1ea955f..bbd747d 100644 --- a/aem-classification-maven-plugin/src/main/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojo.java +++ b/aem-classification-maven-plugin/src/main/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojo.java @@ -14,14 +14,18 @@ */ import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; +import java.io.OutputStream; +import java.net.URI; import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Base64; import java.util.Date; import java.util.EnumSet; @@ -31,10 +35,9 @@ import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; import org.apache.felix.utils.json.JSONParser; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; @@ -64,7 +67,7 @@ public class DownloadContentClassificationMojo extends AbstractMojo { * the base URL where AEM is deployed */ @Parameter(property="baseUrl", defaultValue="http://localhost:4502") - URL baseUrl; + URI baseUrl; /** * the user name to access the {@link baseUrl} @@ -84,12 +87,13 @@ public class DownloadContentClassificationMojo extends AbstractMojo { */ @Parameter(property="relativeFileNameInJar", required = false) File relativeFileNameInJar; - + @Override public void execute() throws MojoExecutionException, MojoFailureException { Log log = getLog(); + HttpClient httpClient = HttpClient.newHttpClient(); try { - String aemVersion = getAemVersion(); + String aemVersion = getAemVersion(httpClient); log.warn("Make sure that the relevant search index definitions are deployed on AEM at " + baseUrl + ". Otherwise this goal will fail!"); log.info("Start retrieving the classification and deprecation data from " + baseUrl); @@ -102,31 +106,34 @@ public void execute() throws MojoExecutionException, MojoFailureException { if (classification.isLabelMixin() == false) { continue; } - retrieveClassificationForMixin(classification, map); + retrieveClassificationForMixin(httpClient, classification, map); } // 2. update map with deprecation entries - retrieveDeprecatedResourceTypes(map); + retrieveDeprecatedResourceTypes(httpClient, map); // 3. persist the map - File outputFile = File.createTempFile("contentclassification", ".map"); - try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) { + Path outputFile = Files.createTempFile("contentclassification", ".map"); + try (OutputStream fileOutputStream = Files.newOutputStream(outputFile)) { map.write(fileOutputStream); } log.info("Written classification map to " + outputFile + " containing " + map.size() + " entries."); // 4. optionally wrap in a JAR if (relativeFileNameInJar != null) { - File jarFile = createJarWrapper(outputFile, relativeFileNameInJar); + File jarFile = createJarWrapper(outputFile, relativeFileNameInJar.toPath()); log.info("Written wrapper jar to " + jarFile); } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new MojoFailureException("Could not retrieve classification metadata: " + e.getMessage(), e); } catch(IOException|IllegalStateException e) { - throw new MojoFailureException("Could not generate classification JAR:" + e.getMessage(), e); + throw new MojoFailureException("Could not retrieve classification metadata: " + e.getMessage(), e); } } - String getAemVersion() throws IOException { - try (InputStream input = getHttpConnectionInputStream("/libs/granite/operations/content/systemoverview/export.json")) { + String getAemVersion(HttpClient httpClient) throws IOException, InterruptedException { + try (InputStream input = downloadFromAem(httpClient, "/libs/granite/operations/content/systemoverview/export.json")) { JSONParser parser = new JSONParser(input); Map response = parser.getParsed(); getLog().debug("Received JSON response " + response); @@ -142,7 +149,7 @@ String getAemVersion() throws IOException { } @SuppressWarnings({ "unchecked", "rawtypes" }) - void retrieveClassificationForMixin(ContentClassification classification, MutableContentClassificationMap map) throws IOException { + void retrieveClassificationForMixin(HttpClient httpClient, ContentClassification classification, MutableContentClassificationMap map) throws IOException, InterruptedException { // Uses the crxde search to find the current classification // (http://localhost:8080/crx/de/query.jsp?_dc=1536334082630&_charset_=utf-8&type=JCR_SQL2&stmt=SELECT%20*%20FROM%20%5Bgranite%3AInternalArea%5D%0A&showResults=true) // the index is crucial for that though (property index limited to properties jcr:primaryType and jcr:mixinTypes) @@ -150,7 +157,7 @@ void retrieveClassificationForMixin(ContentClassification classification, Mutabl String query = "SELECT * FROM [" + classification.getLabel() + "]"; StringBuilder urlParameters = new StringBuilder(); urlParameters.append("_dc=").append(new Date().getTime()).append("&_charset_=utf-8&type=JCR-SQL2&stmt=").append(URLEncoder.encode(query, "ASCII")).append("&showResults=true"); - try (InputStream input = getHttpConnectionInputStream( "/crx/de/query.jsp?" + urlParameters)) { + try (InputStream input = downloadFromAem(httpClient, "/crx/de/query.jsp?" + urlParameters)) { JSONParser parser = new JSONParser(input); Map response = parser.getParsed(); getLog().debug("Received JSON response " + response); @@ -171,7 +178,7 @@ void retrieveClassificationForMixin(ContentClassification classification, Mutabl } for (Map result : (List>)results) { String resourceType = (String)result.get("path"); - if (StringUtils.isNotBlank(resourceType)) { + if (resourceType != null) { map.put(resourceType, classification, null); } } @@ -181,11 +188,11 @@ void retrieveClassificationForMixin(ContentClassification classification, Mutabl } @SuppressWarnings("unchecked") - void retrieveDeprecatedResourceTypes(MutableContentClassificationMap map) throws IOException { + void retrieveDeprecatedResourceTypes(HttpClient httpClient, MutableContentClassificationMap map) throws IOException, InterruptedException { // uses query builder api to retrieve all deprecation metadata String query = "1_property=cq:deprecated&1_property.operation=exists&p.limit=-1&p.hits=selective&p.properties=" + URLEncoder.encode("jcr:mixinTypes jcr:path cq:deprecated cq:deprecatedReason", "ASCII"); EnumSet allContentUsages = EnumSet.allOf(ContentUsage.class); - try (InputStream input = getHttpConnectionInputStream( "/bin/querybuilder.json?" + query)) { + try (InputStream input = downloadFromAem(httpClient, "/bin/querybuilder.json?" + query)) { JSONParser parser = new JSONParser(input); Map response = parser.getParsed(); getLog().debug("Received JSON response " + response); @@ -211,31 +218,37 @@ void retrieveDeprecatedResourceTypes(MutableContentClassificationMap map) throws } @SuppressWarnings("java:S2647") // basic auth is ok in this context - private InputStream getHttpConnectionInputStream(String path) throws IOException { - URL url = new URL(baseUrl, path); - getLog().debug("Connecting to " + url + "..."); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + private InputStream downloadFromAem(HttpClient httpClient, String path) throws IOException, InterruptedException { String credentials = username+":"+password; - // use basic auth - String encoded = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); //Java 8 - connection.setRequestProperty("Authorization", "Basic "+encoded); - return connection.getInputStream(); + String encoded = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); + URI uri = baseUrl.resolve(path); + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + // preemptive-auth only natively supported once authentication in cache (after first successful request), https://stackoverflow.com/a/58612586 + .header("Authorization", "Basic "+encoded) + .build(); + getLog().debug("Connecting to " + uri + "..."); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); + return response.body(); } - File createJarWrapper(File sourceFile, File relativeFileNameInJar) throws IOException { + File createJarWrapper(Path sourceFile, Path relativeFileNameInJar) throws IOException { Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); File outputFile = File.createTempFile("contentclassification", ".jar"); try (JarOutputStream target = new JarOutputStream(new FileOutputStream(outputFile), manifest)) { - // convert to forward slashes - JarEntry entry = new JarEntry(FilenameUtils.separatorsToUnix(relativeFileNameInJar.getPath())); - entry.setTime(sourceFile.lastModified()); + JarEntry entry = new JarEntry(getPathWithUnixSeparators(relativeFileNameInJar)); + entry.setTime(Files.getLastModifiedTime(sourceFile).toMillis()); target.putNextEntry(entry); - try (InputStream input = new FileInputStream(sourceFile)) { - IOUtils.copy(input, target); + try (InputStream input = Files.newInputStream(sourceFile)) { + input.transferTo(target); } target.closeEntry(); } return outputFile; } + + static String getPathWithUnixSeparators(Path path) { + return StreamSupport.stream(path.spliterator(), false).map(Path::toString).collect(Collectors.joining("/")); + } } diff --git a/aem-classification-maven-plugin/src/site/site.xml b/aem-classification-maven-plugin/src/site/site.xml index c114fdd..afd9a87 100644 --- a/aem-classification-maven-plugin/src/site/site.xml +++ b/aem-classification-maven-plugin/src/site/site.xml @@ -51,6 +51,7 @@ org.apache.maven.skins maven-fluido-skin + 1.12.0 diff --git a/aem-classification-maven-plugin/src/test/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojoTest.java b/aem-classification-maven-plugin/src/test/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojoTest.java new file mode 100644 index 0000000..e23cd9b --- /dev/null +++ b/aem-classification-maven-plugin/src/test/java/biz/netcentric/filevault/validator/aem/classification/mojo/DownloadContentClassificationMojoTest.java @@ -0,0 +1,31 @@ +package biz.netcentric.filevault.validator.aem.classification.mojo; + +/*- +* #%L +* AEM Classification Maven Plugin +* %% +* Copyright (C) 2024 Cognizant Netcentric +* %% +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* #L% +*/ + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +class DownloadContentClassificationMojoTest { + + @Test + void testGetPathWithUnixSeparators() { + Path path = Paths.get("my", "test", "path"); + assertEquals("my/test/path", DownloadContentClassificationMojo.getPathWithUnixSeparators(path)); + } + +} diff --git a/pom.xml b/pom.xml index c0d2db6..073015e 100644 --- a/pom.xml +++ b/pom.xml @@ -53,9 +53,7 @@ 3.3.9 - 8 - 1.${java.target.version} - ${maven.compiler.target} + 8 UTF-8 5.9.0 @@ -142,13 +140,6 @@ maven-site-plugin 3.12.0 - - - org.apache.maven.skins - maven-fluido-skin - 1.11.1 - - maven-scm-publish-plugin @@ -230,7 +221,7 @@ - 1.${java.target.version} + 21 ${maven.version} @@ -240,27 +231,6 @@ - - - org.codehaus.mojo - animal-sniffer-maven-plugin - - - org.codehaus.mojo.signature - java1${java.target.version} - 1.0 - - - - - check-java-compatibility - process-classes - - check - - - - org.apache.maven.plugins @@ -380,7 +350,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.12 prepare-jacoco-agent