Create Image Renditions in AEM

Create Image Renditions with an Event Handler

Goal

Create an event handler that creates image renditions after the image has been uploaded in your DAM.

Use Case

Generally, you can update your workflow launchers, or even extend them. In my case, I didn’t to modify the existent workflow because we had different projects on our instance. I remember I tried to extend it, but sometimes we got different errors, regarding some Oak conflicts and concurrency. For this reason, I came up with the idea to use an Event Handler that had to be triggered after the image had been uploaded.

Procedure

Let’s create our Event Handler as follows:

package com.adobe.training.core.listeners;

import com.adobe.training.core.utils.Lab2020CommonMethods;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.DamEvent;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.renditions.RenditionMaker;
import com.day.cq.dam.api.renditions.RenditionTemplate;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * Created by r.teruzzi on 18/09/2019.
 */
@Designate(ocd = UpdateAssetListener.UpdateAssetListenerConfiguration.class)
@Component(
        service = EventHandler.class,
        immediate = true,
        enabled = true,
        property = {
                Constants.SERVICE_DESCRIPTION + "=Lab2020 Activation Event Listener ",
                EventConstants.EVENT_TOPIC + "=" + DamEvent.EVENT_TOPIC
        }

)
public class UpdateAssetListener implements EventHandler {

    @ObjectClassDefinition(name = "Lab2020 - Event Handler for Update Asset Workflow")
    public @interface UpdateAssetListenerConfiguration {

        @AttributeDefinition(
                name = "List of Renditions",
                description = "Enter the renditions needed with the following format: 'width:height:false' e.g. '800:533:false"
        )
        String[] getListOfRenditions();
    }

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private RenditionMaker renditionMaker;

    @Activate
    @Modified
    protected void activate(UpdateAssetListenerConfiguration config) {
        renditions = config.getListOfRenditions();
    }

    private String[] renditions;
    private final Logger LOG = LoggerFactory.getLogger(getClass());

    @Override
    public void handleEvent(Event event) {

        Object type = event.getProperty("type");
        if (type == DamEvent.Type.DAM_UPDATE_ASSET_WORKFLOW_COMPLETED) {
            String path = DamEvent.fromEvent(event).getAssetPath();
            LOG.info("EVENT TYPE: " + type);
            LOG.info("PATH: " + path);
            if (StringUtils.startsWith(path, "/content/dam/Lab2020")) { //The IF case is just an additional check
                //The resource should be null because the resource resolver should not has the permission for other paths except for costa-app
                try (ResourceResolver resourceResolver = Lab2020CommonMethods.getResourceResolver(resourceResolverFactory)) {
                    Resource resource = resourceResolver.getResource(path);
                    if (resource != null) {
                        Asset asset = resource.adaptTo(Asset.class);
                        createRenditions(asset);
                        resourceResolver.commit();
                    }
                } catch (PersistenceException e) {
                    LOG.error("Failed to commit changes", e);
                }
            }
        }
    }

    private void createRenditions(Asset asset) {
        if (asset != null) {
            for (String rendition : renditions) {
                String[] renditionValues = rendition.split(":");
                if (renditionValues.length == 3) {
                    String renditionWidthValue = renditionValues[0];
                    int renditionWidth = Integer.parseInt(renditionWidthValue);
                    String renditionHeightValue = renditionValues[1];
                    int renditionHeight = Integer.parseInt(renditionHeightValue);
                    boolean center = Boolean.parseBoolean(renditionValues[2]);
                    RenditionTemplate thumbnailTemplate = renditionMaker.createThumbnailTemplate(asset,
                            renditionWidth, renditionHeight, center);
                    List<Rendition> renditions = renditionMaker.generateRenditions(asset, thumbnailTemplate);
                    for (Rendition r : renditions) {
                        asset.addRendition("cq5dam.thumbnail.".concat(renditionWidthValue).concat(".")
                                .concat(renditionHeightValue).concat(".png"), r.getStream(), "image/png");
                    }
                }
            }
        }
    }

}

As you can see:

  • The event topic is: DamEvent.EVENT_TOPIC
  • But then the specific event type checked is related to the completed workflow: DamEvent.Type.DAM_UPDATE_ASSET_WORKFLOW_COMPLETED
  • The list of renditions has been configured. You could do the same for the list fo path or whatever you want like extensions and so on!
  • It’s been used a resource resolver associated with a system user with specific ACLs. If you don’t know how to create it, check this article.
  • The rendition maker service will do the job

That’s all guys! Here you can find different articles with some OSGi examples.

Cheers! 🥠

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: