AEM Tip: Passing String Arguments from HTL to the Back End

Exadel Digital Experience Team Tech Tips May 12, 2019 8 min read

HTL (HTML Template Language) is what Adobe recommends for rendering HTML markup in AEM (Adobe Experience Manager), instead of using JSP (JavaServer Pages). Although HTL has many advantages, it leaves out several things used in JSP, so we need to consider workarounds for HTL in these situations.

For instance, consider a situation in which you need to pass a string argument from HTL to the back end. Let’s say, a user name is returned by a service, and we need to render a data-attribute containing a token associated with that user name.

<sly data-sly-use.userService="com.acme.UserService" />
<a href="#" data-user-token="<em>__user_token_to_be_rendered_here__</em>">
   Enter ${userService.currentUser} account
</a>
See more See less

We can leverage the fact that HTL supports expressions like this:

See more See less

Here myObject can be an instance of the java.util.Map. So, we can create a helper class with a field that pretends to be a Map:

import java.util.Map;
import com.adobe.cq.sightly.WCMUsePojo;
public class MyHelper extends WCMUsePojo {    
    private Map<String, String> mapProxy;

    @Override
    public void activate() throws Exception {        
        mapProxy = MyProxyFactory.create();
    }
    public Map<?,?> getTokenForUser() {
        return mapProxy;
    } 
}
See more See less

The helper class uses a factory to create a map proxy:

Java

import java.lang.reflect.Proxy;
import java.util.Map;</p>
<p>public class MyProxyFactory {
   public static Map<String, String> create() {
       return (Map<String, String>) Proxy.newProxyInstance(
               MapProxyFactory.class.getClassLoader(),
               new Class[] { Map.class },
               new MyInvocationHandler());
   } 
}
1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Proxy;
import java.util.Map;
 
public class MyProxyFactory {
   public static Map<String, String> create() {
       return (Map<String, String>) Proxy.newProxyInstance(
               MapProxyFactory.class.getClassLoader(),
               new Class[] { Map.class },
               new MyInvocationHandler());
   }
}
See more See less

The factory, in turn, uses the invocation handler below for the map proxy:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.apache.commons.lang3.ArrayUtils;
 
public class MyInvocationHandler implements InvocationHandler {
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (ArrayUtils.isEmpty(args)) {
            throw new IllegalArgumentException(
 
            "There must be a parameter for method: " + method.getName());
        }
        if (!"get".equals(method.getName())) {
            throw new UnsupportedOperationException(
 
                "Unsupported method: " + method.getName());
        }
 
        Object param = args[0];
        return handle(param);
    }
    
    private Object handle(Object param) {
        return generateToken((String)param);
    }
    
    private String generateToken(String userName) {
        String token = ... //generate a token based on a user name
        return token;
    }
}
See more See less

Now that we’ve set up all that, we can use the helper class in HTL to render a username-based token:

<sly data-sly-use.userService="com.acme.UserService" />
<sly data-sly-use.helper="com.acme.MyHelper" />
 
<a href="#" data-user-token="${helper.tokenForUser[userService.currentUser]}">
    Enter to ${userService.currentUser} account
</a>
See more See less

Author: Denis Glushkov

Was this article useful for you?

Get in the know with our publications, including the latest expert blogs