Serving the static resources through different domains(Cookie less Domains) - Adobe Experience Manager(AEM)
This post will explain the approach to improve the page load time of the websites through serving static resources via multiple cookie less domain.
The page load time of the website directly related to the number of resources requested to render the page in browser, most of the cases the browser makes multiple calls to receive the related resources.
The browser is restricted with number of default simultaneous connections per server, if the number of resources requested in the same domain is more then that will delay the page load time - the resources are loaded sequentially(this restriction is more for HTTP 1.1 but HTTP 2 protocol support parallel downloading). This issue can be addressed by distributing the static resources across multiple domains that will intern increase the parallel download of resources.
The static resource to be on a cookie-less domain that makes the content load faster. The Cookies are uploaded with every request on a domain although they are only required on dynamic pages. This will add additional overhead while requesting the static resources through a cookie aware domain and increases the page load time - loading the cookie to request and download the cookie from the response.
To avoid this problem as discussed above define multiple domains and those are cookie less.
To define a cookie less domain, create a multiple domains e.g static1.example.com, static2.example.com etc and CNAME points to the parent domain - Make sure the cookie is set in the server only specific to the parent domain.
In Adobe Experience Manager(AEM) this can be achieved in the below two approaches
Component level:
Changing the static resource URL's in individual components with newly defined domains based on the resource type - may be separate domain for images, scripts, css etc. This will required more effort and also every component level changes to assign the specific URL's for the static resources. There is a possibly the developer will not implement this in all the components and the resources will be served from main domain.
Sling Rewriter to change the static resource URL's:
Define a Static Resource Transformer that will rewrite the resource URL's with static domains defined - multiple domains can be used
The ACS Static Reference Rewriter can be used to change the static resource URL's -
https://adobe-consulting-services.github.io/acs-aem-commons/features/utils-and-apis/static-reference-rewriter/index.html
I have defined a Static Resource rewriter based on the above one to add some additional functionalities to match our requirements, thought of sharing as this may help someone with same requirement.
- Exclude the rewrite based on attribute name and values
- Exclude the rewrite for external resources
- Exclude based on the complete path and URL prefix
- Rewrite the URL's for all srcset URL's
- The Rewriter is invoked only for specific content path.
StaticResourceTransformFactory.java
import java.io.IOException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.rewriter.ProcessingComponentConfiguration;
import org.apache.sling.rewriter.ProcessingContext;
import org.apache.sling.rewriter.Transformer;
import org.apache.sling.rewriter.TransformerFactory;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
@Component(
label = "Static Resources Transformer Factory",
description = "Static Resources Transformer Factory",
metatype = true,policy = ConfigurationPolicy.REQUIRE)
@Service
@Properties({
@Property(
name = "pipeline.type", label = "Static Resources Transformer Pipeline Type",description ="Static Resources Transformer Pipeline Type"),
@Property(
name = "webconsole.configurationFactory.nameHint",
value = "Static Resources Transformer: {pipeline.type}")
})
public final class StaticResourceTransformFactory implements TransformerFactory {
public final class StaticResourceTransformer extends org.apache.cocoon.xml.sax.AbstractSAXPipe
implements org.apache.sling.rewriter.Transformer {
@Override
public void startElement(String uri, String localName, String qname, Attributes attr) throws SAXException {
final String[] includeElementAttributesArray =includeElementAttributes.get(localName);
AttributesImpl attrs = new AttributesImpl(attr);
boolean excludeAttributeValue=false;
for (int i = 0; i < attrs.getLength(); i++) {
final String[] excludeAttributeValuesArray = excludeAttributeValues.get(attrs.getLocalName(i));
excludeAttributeValue=ArrayUtils.contains(excludeAttributeValuesArray, attrs.getValue(i));
LOG.info("excludeAttributeValue:"+excludeAttributeValue);
if(excludeAttributeValue)
{
break;
}
}
for (int i = 0; i < attrs.getLength(); i++) {
String name = attrs.getLocalName(i);
String value = attrs.getValue(i);
if (ArrayUtils.contains(includeElementAttributesArray, name)
&& !ArrayUtils.contains(excludePath, value) && !isExcludedPrefix(excludePrefix,value)
&&!(value.startsWith("https") || value.startsWith("http") || value.startsWith("//")) && !excludeAttributeValue) {
{
if(name.equals("srcset"))
{
String[] srcset=value.split(",");
String srcsetValue="";
for(int j=0;j<srcset.length;j++)
{
if(!(value.startsWith("https") || value.startsWith("http") || value.startsWith("//")))
{
srcsetValue=!srcsetValue.equals("")?srcsetValue+","+ prependHostName(srcset[j].trim()):srcsetValue+prependHostName(srcset[j].trim());
}else
{
srcsetValue=srcsetValue+","+srcset[j];
}
}
attrs.setValue(i, srcsetValue);
}else
{
attrs.setValue(i, prependHostName(value));
}
}
}
}
super.startElement(uri, localName, qname, attrs);
}