AEM integration with webp

A question from my colleagues in regards to AEM’s support for webp started a research path. Since, I took more than 1 year pause from AEM, I started looking around for the possibility of serving also webp images, for all the images already in DAM.

As a first step I started ask the community on stackoverflow and Adobe Forum. The answer received was to use a webp java library that provides support for reading and writing webp images .

Note: You will notice I have usedcom.github.nintha library. At the end of the article I will explain the reasoning for it and how I actually ditched it to work.

The steps for resolving this were:

  1. To add the 3rd party jar to AEM
  2. Create a simple servlet that receives as queryparam the image path as and returns the webp image.

Adding the 3rd party jar

In AEM one can install via in Apache Felix only bundled jar, for the simple jars, one can embed them using the maven-bundle-plugin in tree steps:

  1. Add the library jar to your application pom.xml (the main one)
<dependency>
<groupId>com.github.nintha</groupId>
<artifactId>webp-imageio-core</artifactId>
<version>0.0.1</version>
</dependency>

2. Modify the pom.xml from the core app to add the dependency and embed the jar. The core/pom.xml will have something like:

<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>javax.inject;version=0.0.0,*</Import-Package>
<Export-Package>andra.core.*</Export-Package>
<Sling-Model-Packages>
andra.core
</Sling-Model-Packages>
<Embed-Dependency>webp-imageio-core</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
....
<dependency>
<groupId>com.github.nintha</groupId>
<artifactId>webp-imageio-core</artifactId>
</dependency>

Things to mention: the Embed-Dependency tag needs to be populated with the artifactId. The <Embed-Transitive> tag specifies to embed also the dependencies of the embedded jar. (eg. the webp-imageio-core has as dependency org.scijava – native-lib-loader, which will be embedded as well)

In order to test that your 3rd party was successfully embedded, one can take a look at the bundle information:

Verify that:
– Bundle Classpath includes the 3rd party jar
– The manifest header contains the embedded dependencies, artifacts and transitive set as expected

Create the Servlet to transform to webp

For the purposes of this POC, it will be only a basic Servlet, it can be enhanced, of course to work with image renditions, or to work receive the path as a suffix – which would be the right way as we want the images to be cached.

package andra.core.servlets;


import com.day.cq.dam.api.Asset;
import com.luciad.imageio.webp.WebPWriteParam;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
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.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.framework.Constants;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import javax.servlet.ServletException;
import java.awt.image.BufferedImage;
import java.io.IOException;

@SlingServlet(paths = "/bin/webpTransformer",
methods = HttpConstants.METHOD_GET
)
@Properties({
@Property(name = Constants.SERVICE_DESCRIPTION, value = "WebP Transformer Servlet")
})
public class WepServlet extends SlingSafeMethodsServlet {


public static final String IMAGE_WEBP = "image/webp";


@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
response.setContentType(IMAGE_WEBP);

BufferedImage image = getImage(request);

// Obtain a WebP ImageWriter instance
ImageWriter writer = ImageIO.getImageWritersByMIMEType(IMAGE_WEBP).next();

// Configure encoding parameters
WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("Lossless");
ImageOutputStream outputStream = new MemoryCacheImageOutputStream(
response.getOutputStream());

// Configure the output on the ImageWriter
writer.setOutput(outputStream);

// Encode
writer.write(null, new IIOImage(image, null, null), writeParam);
outputStream.flush();
outputStream.close();


}

private BufferedImage getImage(SlingHttpServletRequest request) throws IOException {
ResourceResolver resourceResolver = request.getResourceResolver();
String path = request.getParameter("path");
Resource res = resourceResolver.getResource(path);
Asset asset = res.adaptTo(Asset.class);
return ImageIO.read(asset.getOriginal().getStream());
}

}

Why and how nintha ?

A question I would ask is why use nintha over luciad, as in the end nintha is just a luciad wrapper. Of course, I always try to use the standard lib, where possible, however when embedding luciad compiled on my machine I would get the following error:

Could not initialize class com.luciad.imageio.webp.WebPEncoderOptions
Cannot serve request to /bin/webpTransformer in andra.core.servlets.WepServlet

Exception:
java.lang.NoClassDefFoundError: Could not initialize class com.luciad.imageio.webp.WebPEncoderOptions
at com.luciad.imageio.webp.WebPWriteParam.(WebPWriteParam.java:30)
at com.luciad.imageio.webp.WebPWriter.getDefaultWriteParam(WebPWriter.java:38)

After a couple of days, I decided to give nintha a go. However, it did not go as smooth as I wanted, as know I would receive:

org.scijava.nativelib.NativeLibraryUtil Problem with library
java.lang.UnsatisfiedLinkError:

Meaning, that the native library included in nintha was not compatible with the OS the app was running. So, I had to add compile the native app on my local machine, add it manually to the nintha library and recomile. This is not a solution I like as it would be a downside when migration to another server – the library needs to encapsulate the native app for the specific environment.

Javaist learning Python – Part 2 – conditions

I will write about what something I found funny – the conditions

  1. Python has a elif statement. This makes the code easier to read. Python you have a +1 from me for this
  2. Ok… now we go to the part where we find that one does not need to check for a boolean condition. Not only it does not need to check for this, it is advised NOT to check against False, True or None (a singleton that is somehow similar to the null in Java). Everything except the next statements are considered True(This reminded me of highschool and C++):
    • None
    • False
    • zero – for numeric types
    • empty dictionaries, sequences
    • 0 returned when calling __length__
    • False returned when __nonzero__ is called
  3. Now, an even stranger thing: one can have an else after a for… say whaat? yep… One can face something like
    for loop:
        if condition:
            ….
            break
    else:
        statement
    So, what is this all about? The statement is executed when after the program finished the loop… Every time, with one exception: when a break was executed inside the loop. I still have mixed feelings about this, but probably after using more of it, it will come natural.

This was all from my Python adventure till this moment.

Javaist learning Python – Part 1

I always enjoyed stuff that had to do with programming languages and learning how to set your mind to new things/paradigms. One of my favourite courses in the University was Logic and Functional Programming – learning Prolog and LISP (Let’s Insert Some Parenthesis :D).

I played a little bit with Python for my Mater’s Thesis (Static vs Dynamic Typed Programming Languages) some years ago and I always wanted to go a little bit more in depth with it. So, now I considered it to be a great time to restart playing with Python.

The syntax I consider it to be pretty straightforward and there is quite a good documentation on the internet. But, somehow I felt I was missing the Java API docs format. For me it is more convenient to search for something and see what else I can do with that object. Also, the font size seems to be kind of unfriendly – small and a little bit cramped(I know one can resolve this with zoom on the page). I will probably get used to the documentation, but in the meantime I will crave for Java documentation.

Even though, I knew it is not a pure object oriented programming language, is it still funny to white something “outside an object”, but this did not bother me at all, on the contrary, I found it really easy to get started writing code.

Another thing I really like the command line interpretor, that can be run from the command line. Having this is really helpful for the people that have their first contacts with Python. One can actually test something before adding it to a program.

That’s all for now, but I will keep posting further impressions :).

Collections quick review


Parse error: syntax error, unexpected token "{" in /home3/r33432andr/public_html/wp-content/plugins/easy-table/inc/Encoding.php on line 156