Highlighted

Html minification using AEM 6.1

susheel

07-05-2018

There might be requirements to minify html but we dont have anything in AEM OOTB.

I have written a custom filter to minify the html and this is how the filter config looks like:

htmlcompressionfilter.png

The below is the rough code for html compression.

HtmlCompressionFilter.java

import java.io.IOException;

import java.util.Map;

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 org.apache.felix.scr.annotations.Activate;

import org.apache.felix.scr.annotations.Deactivate;

import org.apache.felix.scr.annotations.Property;

import org.apache.felix.scr.annotations.PropertyUnbounded;

import org.apache.felix.scr.annotations.sling.SlingFilter;

import org.apache.felix.scr.annotations.sling.SlingFilterScope;

import org.apache.jackrabbit.oak.commons.PropertiesUtil;

import org.apache.sling.api.SlingHttpServletRequest;

import org.apache.sling.api.SlingHttpServletResponse;

import org.apache.sling.api.resource.Resource;

import org.osgi.service.component.ComponentContext;

import com.capella.website.common.CommonLogger;

import com.googlecode.htmlcompressor.compressor.HtmlCompressor;

/**

* This Class HtmlCompressionFilter will have two options in config to enable

* and disable html compression. We can also provide the paths where we need to

* add compression.

*/

@SlingFilter(label = "HTML Compression Filter", description = "HTML Compression Filter", metatype = true, order = 0, scope = SlingFilterScope.REQUEST)

public class HtmlCompressionFilter implements Filter {

private HtmlCompressor compressor;

@Property(label = "Enable html compression", boolValue = false, description = "Please Check the property to enable html compression")

public static final String HTML_COMPRESSION_ENABLED = "html.compression.enabled";

@Property(value = {

"/content/project/en" }, unbounded = PropertyUnbounded.ARRAY, label = "Compress html paths", cardinality = 50, description = "Please enter the paths where you need to compress the html")

private static final String HTML_COMPRESSION_PATHS = "html.compression.paths";

private boolean isHtmlCompressionEnabled;

private String[] htmlCompressionPaths;

@Override

public void init(FilterConfig filterConfig) throws ServletException {

compressor = new HtmlCompressor();

compressor.setCompressJavaScript(true);

    compressor.setCompressCss(true);

}

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException {

if (!(request instanceof SlingHttpServletRequest) || !(response instanceof SlingHttpServletResponse)) {

chain.doFilter(request, response);

return;

}

final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;

final Resource resource = slingRequest.getResource();

String requestPath = resource.getPath() + ".html";

if (isHtmlCompressionEnabled && startsWithInArr(htmlCompressionPaths, requestPath)) {

CharResponseWrapper responseWrapper = new CharResponseWrapper((HttpServletResponse) response);

chain.doFilter(request, responseWrapper);

String servletResponse = responseWrapper.toString();

response.getWriter().write(compressor.compress(servletResponse));

} else {

chain.doFilter(request, response);

}

}

public boolean startsWithInArr(String[] htmlCompressionPaths, String requestPath) {

boolean flag = false;

for (int i = 0; i < htmlCompressionPaths.length; i++) {

if (requestPath.startsWith(htmlCompressionPaths[i])) {

flag = true;

break;

}

}

return flag;

}

@Override

public void destroy() {

}

@Activate

protected void activate(final Map<String, String> properties) {

this.isHtmlCompressionEnabled = PropertiesUtil.toBoolean(properties.get(HTML_COMPRESSION_ENABLED), false);

this.htmlCompressionPaths = PropertiesUtil.toStringArray(properties.get(HTML_COMPRESSION_PATHS));

}

@Deactivate

protected void deactivate(ComponentContext componentContext) {

}

}

CharResponseWrapper.java

import java.io.CharArrayWriter;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpServletResponseWrapper;

public class CharResponseWrapper extends HttpServletResponseWrapper {

  private final CharArrayWriter output;

  @Override

  public String toString() {

    return output.toString();

  }

  public CharResponseWrapper(HttpServletResponse response) {

    super(response);

    output = new CharArrayWriter();

  }

  @Override

  public PrintWriter getWriter() {

    return new PrintWriter(output);

  }

}

Dependencies required:

<dependency>

<groupId>com.googlecode.htmlcompressor</groupId>

<artifactId>htmlcompressor</artifactId>

<version>1.4</version>

</dependency>

<dependency>

<groupId>com.yahoo.platform.yui</groupId>

<artifactId>yuicompressor</artifactId>

<version>2.4.6</version>

</dependency>

In the above dependencies we need to embed htmlcompressor dependency as we don't have in it AEM. We can either manually install the bundle or embed it from pom file.

Replies

Highlighted

PuzanovsP

MVP

09-05-2018

Dear wimswims,

You are confusing minification and gzip. Gzip encodes data to make it smaller, whereas minification changes the output before encoding to make it smaller.

susheel, embed the artefact in your OSGi bundle instead of declaring it as a run time dependency to resolve this issue. You do not have to declare it as OSGi bundle if you want to use it as a library only in this bundle.

Regards,

Peter

Highlighted

PuzanovsP

MVP

09-05-2018

Dear susheel

You have two options, make that bundle OSGi compatible[1]

or

You can use Apache Felix - Apache Felix Maven Bundle Plugin (BND) embed dependency configuration to add your jar directly to your bundle, not ask it to be picked up as a externally provided bundle.

[1] how to embed dependent jar ?

Regards,

Peter

Highlighted

wimsymons

09-05-2018

In the past I was indeed thinking the same, but the overhead of doing whitespace compression is much bigger than the gain of just enabling gzip compression on the non-minified content. Gzip is pretty good in compressing spaces as well.

More at https://www.peterbe.com/plog/html-whitespace-compression and Any reason not to strip whitespace in HTML - Stack Overflow​.

Even so, some HTML elements should not be stripped from whitespace (see Collapse Whitespace ). In my opinion, it isn't worth the extra complexity.

Highlighted

susheel

14-05-2018

You are right but html compression plugin is smart enough to take care of all those issues. Here gzip is already enabled and it does great job of compression. But by removing white space 20% more was saved.