Article updated on Jan 30, 2017.

Today, in the part four of the introduction to the HTML Template Language (HTL), formerly known as Sightly, I’ll share a deep-dive on how to use the Java Use-API to implement your custom (complex) logic into your components.

Interested in the others parts? Here they are: part 1, part 2, part 3, part 5.

The basic notation of the Use-API looks like this is an HTL component:

<div data-sly-use.myComponent="com.myproject.MyComponent">
    ${myComponent.calculatedValue}
</div>

For this example, to show the full range of possibilities, let’s also pass a parameter, so the content of the data-sly-use attribute must be changed to an expression with an option:

<div data-sly-use.myComponent="${'com.myproject.MyComponent' @ param1='one', param2='two'}">
    ${myComponent.calculatedValue}
</div>

In this example you have the following:

  • the class ‘com.myproject.MyComponent’ is instantiated
  • two parameters are passed to the object via expression options
  • the identifier ‘myComponent’ is used to expose the resulting object
  • the method getCalculatedValue() is called

Now let’s look at the implementation for this, you have 7(!) options:

  1. Class that implements the Use interface
  2. Class that extends WCMUsePojo class
  3. Class that is adaptable from Resource (resource.adaptTo(YourClass))
  4. Class that is adaptable from Request (request.adaptTo(YourClass))
  5. Sling Models
  6. OSGi service
  7. POJO class

Actually, there is even a 8th option, but I wrote a separate article for that one:
Using server-side JavaScript with the HTML Template Language

Below, I will show an example of the 7 Java options, so that you can see what is needed for each.
You can switch the implementation between all 7 options without changing anything in the component’s HTL file.

Option 1: Class implementing the Use interface

In this case you need to implement init(), and do the all the logic in there. Via the bindings-object you can access all the objects also available on the component.

import org.apache.sling.scripting.sightly.pojo.Use;

public class MyComponent implements Use {

    private String value;

    @Override
    public void init(Bindings bindings) {
        // All standard objects are available as bindings
        Resource resource = (Resource) bindings.get("resource");

        // Parameters are also passed as bindings
        String param1 = (String) bindings.get("param1");
        String param2 = (String) bindings.get("param2");

        value = resource.getPath() + param1 + param2;
    }

    public String getCalculatedValue() {
        return value;
    }
}

It would also have been possible to make the “value” member of the class public to access it directly from the HTL file, instead of creating a getCalculatedValue method.

Option 2: Class extending WCMUsePojo class

This option is similar like the Use-interface, but has some more helper functionality that you can use. Methods like getResource(), getCurrentPage() etc are already there for you to use.

import com.adobe.cq.sightly.WCMUsePojo;

public class MyComponent extends WCMUsePojo {

    private String value;

    @Override
    public void activate() {
       // Convenience methods allow to access the default bindings
       Resource resource = getResource();

       // Parameters are can be accessed via a get() method
       String param1 = get("param1", String.class);
       String param2 = get("param2", String.class);

       value = resource.getPath() + param1 + param2;
    }

    public String getCalculatedValue() {
        return value;
    }
}

Option 3: Class adaptable from Resource

The code example given here show how to make your class capable of being adapted from a Resource. Based on that Resource-object you need to pass in the right info to your POJO. In this case you can have a plain POJO without any dependency to AEM or HTL.

NOTE: In this option, you cannot access the parameters.

@Component(metatype = true, immediate = true)
@Service
public class MyComponentAdapter implements AdapterFactory {

    @Property(name = "adapters")
    protected static final String[] ADAPTER_CLASSES = { MyComponent.class.getName() };

    @Property(name = "adaptables")
    protected static final String[] ADAPTABLE_CLASSES = { Resource.class.getName() };

    @Override
    public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
        if (adaptable instanceof Resource) {
            return (AdapterType) new MyComponent();
        }
        return null;
    }
}

Option 4: Class adaptable from Request

Like option 3, but now based on the request. Also it is possible here to access the parameters; they are passed in as attributes.

@Component(metatype = true, immediate = true)
@Service
public class MyComponentAdapter implements AdapterFactory {

    @Property(name = "adapters")
    protected static final String[] ADAPTER_CLASSES = { MyComponent.class.getName() };
    
    @Property(name = "adaptables")
    protected static final String[] ADAPTABLE_CLASSES = { SlingHttpServletRequest.class.getName() };
    
    @Override
    public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
        if (adaptable instanceof SlingHttpServletRequest) {
            SlingHttpServletRequest request = (SlingHttpServletRequest) adaptable;
            String param1 = (String) request.getAttribute("param1");
            String param2 = (String) request.getAttribute("param2");
            return (AdapterType) new MyComponent();
        }
        return null;
    }
}

Option 5: Sling Models

You can implement Options 3 and 4 with Apache Sling Models; this saves you from creating your own adapters. Here’s an example of what that would look like:

@Model(adaptables=SlingHttpServletRequest.class) 
public class MyComponent { 

  @Inject private Resource resource; 

  @Inject private String param1; 

  @Inject private String param2; 

  private String value; 

  @PostConstruct 
  public void activate() { 
    value = resource.getPath() + param1 + param2; 
  } 

  public String getCalculatedValue() { 
    return value; 
  } 
}

Option 6: OSGi-service

You can also use an OSGi-service in the data-sly-use attributes, however you can’t pass in parameters

Option 7: POJO class

Last but not least you can also use a POJO class in your HTL use-api, you can’t pass in parameters. But it might come handy in case you want to use constants that are located in a class.
Main requirement is that the class has a constructor that has no parameters.

Conclusion

The great thing of the Use-API is that you have several options to implement the logic on the server-side. And you can mix options in your components, depending on the requirements you have. I hope you enjoyed this part, I am already thinking of part 5.

@heervisscher

Read-on

Here are the other articles of my introduction series:

Other posts on the topic:

And here other resources to learn more about it:

0 comments