This post will explain the different approaches to manage the Vanity/Redirect URL through centralized management UI.
Option1: Manage the Vanity URL's through VanityPath page property
- Build a UI application to read vanityPath properties from the repository content nodes and enable management options - Add, Delete, Modification
- Refer the following URL for details on this approach - https://helpx.adobe.com/experience-manager/using/vanitypath.html (only working in 6.1)
- Whitelist vanity paths in dispatcher - https://helpx.adobe.com/experience-manager/dispatcher/using/dispatcher-configuration.html#EnablingAccesstoVanityURLsvanityurls
The same approach can be followed to manage the redirect path enabled in AEM
Vanity URL's are handled in AEM so all the request reaches AEM publisher
Option2 - Manage the Vanity/redirects through AEM UI on XML and redirect through Java Filter
Store the vanity and redirects mapping in an XML file within AEM repository
<rules>
<rule>
<siteName></siteName>
<code></code>
<sourcePath></sourcePath>
<target></target>
</rule>
<rules>
- Build a UI application to manage the redirects in the file - modify/remove and add new rules
- Replicate the mapping XML to the publisher on every modification through replication API
- Build a Java filter that will redirect to the target URL if the vanity path is defined in the XML file(the filter should be restricted only required path)
- Whitelist the vanitypaths in Apache – Expose the vanitypaths through custom URL(Servlet expose the list of vanity URLs by parsing the XML) and configure the URL in Apache for whitelisting(Refer https://helpx.adobe.com/experience-manager/dispatcher/using/dispatcher-configuration.html#EnablingAccesstoVanityURLsvanityurls for apache configuration, the vanity list URL should be custom)
Vanity/Redirects are handled in AEM so all the request reaches AEM publisher
Sample Rule file:
<rules>
<rule>
<sitename>test</sitename>
<type>302</type>
<target>http://localhost:8080/content/geometrixx-outdoors/en.html</target>
<source>/testVanity</source>
</rule>
<rule>
<sitename>test</sitename>
<type>302</type>
<target>http://localhost:8080/content/geometrixx-outdoors/en/men.html</target>
<source>/testVanity1</source>
</rule>
</rules>
Add the vanity_urls configuration to the farm:
/vanity_urls {
/url "/services/getRedirects.html"
/file "/tmp/vanity_urls"
/delay 30
}
Add a filter rule in the dispatcher to allow the vanity URL to be called on Publish instance:
/0100 { /type "allow" /url "/services/getRedirects.html" }
Add a caching rule to prevent caching of this URL:
/0001 { /type "deny" /glob "/services/getRedirects.html" }
Sample GetRedirects Servlet:
import java.io.IOException;
import java.io.PrintWriter;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.Session;
import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
@SuppressWarnings("serial")
@SlingServlet(paths= "/services/getRedirects")
public class GetRedirects extends SlingSafeMethodsServlet {
@Reference
private ResourceResolverFactory resolverFactory;
@Override
protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp)
throws ServletException, IOException {
String redirectMapPath="/content/geometrixx-outdoors/en/redirectmap.xml/jcr:content";
Session session = null;
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
// this is for demo purpose
//the redirect should be parsed and stored in HashMap and updated on every modification
//Servlet should list the redirects from HashMap
try {
ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
session=resourceResolver.adaptTo(Session.class);
// create a new DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
if(session.nodeExists(redirectMapPath)) {
Node node = session.getNode(redirectMapPath);
if(node.hasProperty("jcr:data")) {
Property jcrData = node.getProperty("jcr:data");
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(jcrData.getBinary().getStream());
Document doc = builder.parse(is);
NodeList rules=doc.getElementsByTagName("rule");
for (int i = 0; i < rules.getLength(); i++) {
Element rule = (Element) rules.item(i);
NodeList sourceElements = rule.getElementsByTagName("source");
Element source = (Element) sourceElements.item(0);
NodeList sourceChildren = source.getChildNodes();
String sourceValue = sourceChildren.item(0).getNodeValue();
pw.println(sourceValue);
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(session!=null)
{
session.logout();
}
}
}
}
Sample RedirectFilter.java:
import java.io.IOException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.Session;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.sling.SlingFilter;
import org.apache.felix.scr.annotations.sling.SlingFilterScope;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* Simple servlet filter component that logs incoming requests.
*/
@SlingFilter(order = -700, scope = SlingFilterScope.REQUEST)
public class RedirectFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Reference
ResourceResolverFactory resourceResolverFactory;
@Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain filterChain) throws IOException, ServletException {
String redirectMapPath="/content/geometrixx-outdoors/en/redirectmap.xml/jcr:content";
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse ) response;
Session session=null;
// this is for demo purpose
//the redirect should be parsed and stored in HashMap and updated on every modification
//Filter should verify the the redirects in HashMap and redirect accordingly
try {
ResourceResolver resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
session=resourceResolver.adaptTo(Session.class);
// create a new DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
String requestPath=slingRequest.getPathInfo();
logger.error("requestPath:"+requestPath);
if(session.nodeExists(redirectMapPath)) {
Node node = session.getNode(redirectMapPath);
if(node.hasProperty("jcr:data")) {
Property jcrData = node.getProperty("jcr:data");
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(jcrData.getBinary().getStream());
Document doc = builder.parse(is);
NodeList rules=doc.getElementsByTagName("rule");
for (int i = 0; i < rules.getLength(); i++) {
Element rule = (Element) rules.item(i);
NodeList sourceElements = rule.getElementsByTagName("source");
Element source = (Element) sourceElements.item(0);
NodeList sourceChildren = source.getChildNodes();
String sourceValue = sourceChildren.item(0).getNodeValue();
logger.error("sourceValue:"+sourceValue);
if(sourceValue.equals(requestPath))
{
NodeList targetElements = rule.getElementsByTagName("target");
Element target = (Element) targetElements.item(0);
NodeList targetChildren = target.getChildNodes();
String targetValue = targetChildren.item(0).getNodeValue();
logger.error("targetValue:"+targetValue);
NodeList typeElements = rule.getElementsByTagName("type");
Element type = (Element) typeElements.item(0);
NodeList typeChildren = type.getChildNodes();
String typeValue = typeChildren.item(0).getNodeValue();
logger.error("typeValue:"+typeValue);
if(typeValue.equals("301"))
{
slingResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
}else
{
slingResponse.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
}
slingResponse.setHeader("Location", targetValue);
return;
}
}
logger.error("Vanity Path not defined, continuing the filter chan in");
filterChain.doFilter(request, response);
return;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}finally {
if(session!=null)
{
session.logout();
}
}
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}
Option3 - Apache RedirectMap with Vanity/redirects Management UI in AEM
Define a redirect mapping XML file in AEM
<rules>
<rule>
<siteName></siteName>
<code></code>
<sourcePath></sourcePath>
<target></target>
</rule>
<rules>
- Build a UI application to manage the redirects in the XML file
- Validate the rules before adding to XML for accepted characters.
- Replicate the mapping XML to a publisher on every modification through replication API
- Configure the RedirectMap in apache - Separate map for 301 and 302 redirect
- Build a Servlet in AEM that provides the txt file with redirect details based on the redirect type(301 or 302) from XML rule file
#Source Target
test test1
test2 test3
test4 test5
- Create a Cron job(script) in dispatcher that will get the redirect file(txt) file from AEM through servlet – separate request for 301 and 302 types of redirects every one hour and format it to Apache RedirectMap dbm format. The redirect map is reloaded with new redirects
Apache server handles the redirect so the load on AEM publisher will be reduced.
Sample Rule file:
<rules>
<rule>
<sitename>test</sitename>
<type>302</type>
<target>http://localhost:8080/content/geometrixx-outdoors/en.html</target>
<source>/testVanity</source>
</rule>
<rule>
<sitename>test</sitename>
<type>302</type>
<target>http://localhost:8080/content/geometrixx-outdoors/en/men.html</target>
<source>/testVanity1</source>
</rule>
</rules>
Cron Job to fetch the redirects: Add the following script into /etc/cron.hourly:
#!/bin/bash
wget http://localhost:4503/services/getRedirectMapping.html -O /tmp/redirectmap.txt >> /var/log/update-redirect-map.log 2>&1
httxt2dbm -i /tmp/redirectmap.txt -o /etc/httpd/conf/redirectmap.map >> /var/log/update-redirect-map.log 2>&1
Redirect Map configuration in Virtualhost:
RewriteMap redirects "dbm=db:C:\Albin\SW\redirectmap.txt"
RewriteCond ${redirects:$1} !=""
RewriteRule ^(.*)$ ${redirects:$1} [redirect=permanent,last]
Sample GetRedirectMapping Servlet:
import java.io.IOException;
import java.io.PrintWriter;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.Session;
import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
@SuppressWarnings("serial")
@SlingServlet(paths= "/services/getRedirectMapping")
public class GetRedirectMapping extends SlingSafeMethodsServlet {
@Reference
private ResourceResolverFactory resolverFactory;
@Override
protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp)
throws ServletException, IOException {
String redirectMapPath="/content/geometrixx-outdoors/en/redirectmap.xml/jcr:content";
Session session = null;
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
// this is for demo purpose
//the redirect should be parsed and stored in HashMap and updated on every modification
//Servlet should list the redirects from HashMap
try {
ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
session=resourceResolver.adaptTo(Session.class);
// create a new DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
if(session.nodeExists(redirectMapPath)) {
Node node = session.getNode(redirectMapPath);
if(node.hasProperty("jcr:data")) {
Property jcrData = node.getProperty("jcr:data");
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(jcrData.getBinary().getStream());
Document doc = builder.parse(is);
NodeList rules=doc.getElementsByTagName("rule");
for (int i = 0; i < rules.getLength(); i++) {
Element rule = (Element) rules.item(i);
NodeList sourceElements = rule.getElementsByTagName("source");
Element source = (Element) sourceElements.item(0);
NodeList sourceChildren = source.getChildNodes();
String sourceValue = sourceChildren.item(0).getNodeValue();
NodeList targetElements = rule.getElementsByTagName("target");
Element target = (Element) targetElements.item(0);
NodeList targetChildren = target.getChildNodes();
String targetValue = targetChildren.item(0).getNodeValue();
pw.println(sourceValue+" "+targetValue );
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(session!=null)
{
session.logout();
}
}
}
}
Option4: ACS Redirect Map Manager
Refer the following URL for more details https://adobe-consulting-services.github.io/acs-aem-commons/features/redirect-map-manager/index.html
Apache server handles the redirect so the load on AEM publisher will be reduced.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.