diff --git a/CHANGELOG.md b/CHANGELOG.md index be5eb6ed27..c36cadb265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com) - ## Unreleased ([details][unreleased changes details]) -- #3480 - Sites Copy Publish URLs + +- #3484 - Redirect Manager: A servlet to export redirects to a TXT file to use with pipeline-free redirects +- #3480 - AEM Sites Copy Publish URLs ### Fixed - #3479 - Fixed Configurations Model for Redirect Manager after change in "redirect" resource as "sling:Folder" diff --git a/all/pom.xml b/all/pom.xml index bd627dd784..1308ebaad6 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/bundle-cloud/pom.xml b/bundle-cloud/pom.xml index 1b60a6d39b..01c9534ac2 100644 --- a/bundle-cloud/pom.xml +++ b/bundle-cloud/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/bundle-onprem/pom.xml b/bundle-onprem/pom.xml index 5604477d36..a6db518ee4 100644 --- a/bundle-onprem/pom.xml +++ b/bundle-onprem/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/bundle/pom.xml b/bundle/pom.xml index 23ea706103..56be916ea2 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/RewriteMapServlet.java b/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/RewriteMapServlet.java new file mode 100644 index 0000000000..b1a1ca345e --- /dev/null +++ b/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/RewriteMapServlet.java @@ -0,0 +1,83 @@ +/*- + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2013 - 2024 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.adobe.acs.commons.redirects.servlets; + +import com.adobe.acs.commons.redirects.filter.RedirectFilter; +import com.adobe.acs.commons.redirects.models.RedirectRule; +import org.apache.http.entity.ContentType; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.servlets.SlingSafeMethodsServlet; +import org.osgi.service.component.annotations.Component; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; + +import static com.adobe.acs.commons.redirects.servlets.CreateRedirectConfigurationServlet.REDIRECTS_RESOURCE_PATH; + + +/** + * Servlet for generating an Apache RewriteMap text file to use with + * he Pipeline-free URL Redirects feature in AEM as a Cloud Service + * + * Usage: http://localhost:4502/conf/my-site/settings/redirects.txt + * To filter by status code: http://localhost:4502/conf/my-site/settings/redirects.301.txt + * + * See https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/content-delivery/pipeline-free-url-redirects + * + */ +@Component(service = Servlet.class, property = { + "sling.servlet.methods=GET", + "sling.servlet.extensions=txt", + "sling.servlet.resourceTypes=" + REDIRECTS_RESOURCE_PATH +}) +public class RewriteMapServlet extends SlingSafeMethodsServlet { + + private static final long serialVersionUID = -3564475196678277711L; + + @Override + @SuppressWarnings("java:S3457") + protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) + throws ServletException, IOException { + response.setContentType(ContentType.TEXT_PLAIN.getMimeType()); + + String[] selectors = request.getRequestPathInfo().getSelectors(); + int statusCode = 0; + if(selectors != null && selectors.length > 0) { + statusCode = Integer.parseInt(selectors[0]); + } + Collection rules = RedirectFilter.getRules(request.getResource()); + PrintWriter out = response.getWriter(); + out.printf("# %s Redirects\n", statusCode == 0 ? "All" : "" + statusCode); + for (RedirectRule rule : rules) { + if(statusCode != 0 && rule.getStatusCode() != statusCode) { + continue; + } + String note = rule.getNote(); + if(note != null && !note.isEmpty()) { + out.printf("# %s\n", note); + } + out.printf("%s %s\n", rule.getSource(), rule.getTarget()); + } + } +} diff --git a/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/package-info.java b/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/package-info.java index 29b37a5407..7185a8e113 100755 --- a/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/package-info.java +++ b/bundle/src/main/java/com/adobe/acs/commons/redirects/servlets/package-info.java @@ -15,5 +15,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@org.osgi.annotation.versioning.Version("1.1.0") +@org.osgi.annotation.versioning.Version("1.2.0") package com.adobe.acs.commons.redirects.servlets; diff --git a/bundle/src/test/java/com/adobe/acs/commons/redirects/servlets/RewriteMapServletTest.java b/bundle/src/test/java/com/adobe/acs/commons/redirects/servlets/RewriteMapServletTest.java new file mode 100755 index 0000000000..fd7bf8a1c1 --- /dev/null +++ b/bundle/src/test/java/com/adobe/acs/commons/redirects/servlets/RewriteMapServletTest.java @@ -0,0 +1,137 @@ +/*- + * #%L + * ACS AEM Commons Bundle + * %% + * Copyright (C) 2013 - 2024 Adobe + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.adobe.acs.commons.redirects.servlets; + +import com.adobe.acs.commons.redirects.RedirectResourceBuilder; +import org.apache.http.entity.ContentType; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.testing.mock.sling.ResourceResolverType; +import org.apache.sling.testing.mock.sling.junit.SlingContext; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import javax.servlet.ServletException; +import java.io.IOException; +import java.util.Calendar; + +import static org.junit.Assert.assertEquals; + +/** + * @author Yegor Kozlov + */ +public class RewriteMapServletTest { + @Rule + public SlingContext context = new SlingContext(ResourceResolverType.RESOURCERESOLVER_MOCK); + + private RewriteMapServlet servlet; + private final String redirectStoragePath = "/conf/acs-commons/redirects"; + + @Before + public void setUp() throws PersistenceException { + new RedirectResourceBuilder(context, redirectStoragePath) + .setSource("/content/one") + .setTarget("/content/two") + .setStatusCode(302) + .setUntilDate(new Calendar.Builder().setDate(2022, 9, 9).build()) + .setEffectiveFrom(new Calendar.Builder().setDate(2025, 2, 2).build()) + .setNotes("note-1") + .setEvaluateURI(true) + .setContextPrefixIgnored(true) + .setTagIds(new String[]{"redirects:tag1"}) + .setCreatedBy("john.doe") + .setModifiedBy("jane.doe") + .setCreated(new Calendar.Builder().setDate(1974, 1, 16).build()) + .setModified(new Calendar.Builder().setDate(1976, 10, 22).build()) + .build(); + new RedirectResourceBuilder(context, redirectStoragePath) + .setSource("/content/three") + .setTarget("/content/four") + .setStatusCode(301) + .setTagIds(new String[]{"redirects:tag2"}) + .setModifiedBy("john.doe") + .build(); + + Resource redirects = context.resourceResolver().getResource(redirectStoragePath); + context.request().setResource(redirects); + servlet = new RewriteMapServlet(); + } + + + @Test + public void testGet() throws ServletException, IOException { + MockSlingHttpServletRequest request = context.request(); + MockSlingHttpServletResponse response = context.response(); + + servlet.doGet(request, response); + + assertEquals(ContentType.TEXT_PLAIN.getMimeType(), response.getContentType()); + String[] lines = response.getOutputAsString().split("\n"); + assertEquals(4, lines.length); // header + 2 rules + assertEquals("# All Redirects", lines[0]); + assertEquals("# note-1", lines[1]); + + String[] rule1 = lines[2].split(" "); + assertEquals("/content/one", rule1[0]); + assertEquals("/content/two", rule1[1]); + + String[] rule2 = lines[3].split(" "); + assertEquals("/content/three", rule2[0]); + assertEquals("/content/four", rule2[1]); + } + + @Test + public void test301Selector() throws ServletException, IOException { + MockSlingHttpServletRequest request = context.request(); + MockSlingHttpServletResponse response = context.response(); + + context.requestPathInfo().setSelectorString("301"); + servlet.doGet(request, response); + + assertEquals(ContentType.TEXT_PLAIN.getMimeType(), response.getContentType()); + String[] lines = response.getOutputAsString().split("\n"); + assertEquals(2, lines.length); // header + 1 rule + assertEquals("# 301 Redirects", lines[0]); + String[] rule1 = lines[1].split(" "); + assertEquals("/content/three", rule1[0]); + assertEquals("/content/four", rule1[1]); + } + + @Test + public void test302Selector() throws ServletException, IOException { + MockSlingHttpServletRequest request = context.request(); + MockSlingHttpServletResponse response = context.response(); + + context.requestPathInfo().setSelectorString("302"); + servlet.doGet(request, response); + + assertEquals(ContentType.TEXT_PLAIN.getMimeType(), response.getContentType()); + String[] lines = response.getOutputAsString().split("\n"); + assertEquals(3, lines.length); // header + notes + 1st rule + assertEquals("# 302 Redirects", lines[0]); + + String[] rule1 = lines[2].split(" "); + assertEquals("/content/one", rule1[0]); + assertEquals("/content/two", rule1[1]); + } +} \ No newline at end of file diff --git a/oakpal-checks/pom.xml b/oakpal-checks/pom.xml index af51347817..049977ac3f 100644 --- a/oakpal-checks/pom.xml +++ b/oakpal-checks/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index 9fe8eca330..81f7b1e502 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT pom ACS AEM Commons - Reactor Project diff --git a/ui.apps/pom.xml b/ui.apps/pom.xml index 455b2730da..9e69ad8891 100644 --- a/ui.apps/pom.xml +++ b/ui.apps/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/ui.config/pom.xml b/ui.config/pom.xml index 3b3aa08ea3..1e70526e75 100644 --- a/ui.config/pom.xml +++ b/ui.config/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT diff --git a/ui.content/pom.xml b/ui.content/pom.xml index 8188e9cb80..cf2fb0e777 100644 --- a/ui.content/pom.xml +++ b/ui.content/pom.xml @@ -25,7 +25,7 @@ com.adobe.acs acs-aem-commons - 6.9.7-SNAPSHOT + 6.10.0-SNAPSHOT