Arti­cle updat­ed on Jan 30, 2017.

Today, in the part four of the intro­duc­tion to the HTML Tem­plate Lan­guage (HTL), for­mer­ly known as Sight­ly, I’ll share a deep-dive on how to use the Java Use-API to imple­ment your cus­tom (com­plex) log­ic into your com­po­nents.

Inter­est­ed in the oth­ers parts? Here they are: part 1, part 2, part 3, part 5.

The basic nota­tion of the Use-API looks like this is an HTL com­po­nent:

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

For this exam­ple, to show the full range of pos­si­bil­i­ties, let’s also pass a para­me­ter, so the con­tent of the data-sly-use attribute must be changed to an expres­sion with an option:

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

In this exam­ple you have the fol­low­ing:

  • the class ‘com.myproject.MyComponent’ is instan­ti­at­ed
  • two para­me­ters are passed to the object via expres­sion options
  • the iden­ti­fi­er ‘myCompo­nent’ is used to expose the result­ing object
  • the method get­Cal­cu­lat­ed­Val­ue() is called

Now let’s look at the imple­men­ta­tion for this, you have 7(!) options:

  1. Class that imple­ments the Use inter­face
  2. Class that extends WCMUse­Po­jo class
  3. Class that is adapt­able from Resource (resource.adaptTo(YourClass))
  4. Class that is adapt­able from Request (request.adaptTo(YourClass))
  5. Sling Mod­els
  6. OSGi ser­vice
  7. POJO class

Actu­al­ly, there is even a 8th option, but I wrote a sep­a­rate arti­cle for that one:
Using serv­er-side JavaScript with the HTML Tem­plate Lan­guage

Below, I will show an exam­ple of the 7 Java options, so that you can see what is need­ed for each.
You can switch the imple­men­ta­tion between all 7 options with­out chang­ing any­thing in the component’s HTL file.

Option 1: Class implementing the Use interface

In this case you need to imple­ment init(), and do the all the log­ic in there. Via the bind­ings-object you can access all the objects also avail­able on the com­po­nent.

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 pos­si­ble to make the “val­ue” mem­ber of the class pub­lic to access it direct­ly from the HTL file, instead of cre­at­ing a get­Cal­cu­lat­ed­Val­ue method.

Option 2: Class extending WCMUsePojo class

This option is sim­i­lar like the Use-inter­face, but has some more helper func­tion­al­i­ty that you can use. Meth­ods like getRe­source(), getCur­rent­Page() 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 exam­ple giv­en here show how to make your class capa­ble of being adapt­ed 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 with­out any depen­den­cy to AEM or HTL.

NOTE: In this option, you can­not access the para­me­ters.

@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 pos­si­ble here to access the para­me­ters; they are passed in as attrib­ut­es.

@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 imple­ment Options 3 and 4 with Apache Sling Mod­els; this saves you from cre­at­ing your own adapters. Here’s an exam­ple 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-ser­vice in the data-sly-use attrib­ut­es, how­ev­er you can’t pass in para­me­ters

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 para­me­ters. But it might come handy in case you want to use con­stants that are locat­ed in a class.
Main require­ment is that the class has a con­struc­tor that has no para­me­ters.

Conclusion

The great thing of the Use-API is that you have sev­er­al options to imple­ment the log­ic on the serv­er-side. And you can mix options in your com­po­nents, depend­ing on the require­ments you have. I hope you enjoyed this part, I am already think­ing of part 5.

@heervisscher

Read-on

Here are the oth­er arti­cles of my intro­duc­tion series:

Oth­er posts on the top­ic:

And here oth­er resources to learn more about it:

0 comments