Question

I am working on a component which requires some properties (that the user sets during run time) for it to work as intended.

Initially, I was simply using a properties.get('foo') to fetch the needed property from my component, but I'm trying to remove all traces of script-let code from my component jsp file.

How do I fetch this property 'foo' (which is set during runtime on my component) within my Java code? I remember reading somewhere that using a ValueMap was the best way, so I tried using this:-

public static Map<String, Object> getResourceProperties(String path,
            SlingHttpServletRequest request) {
        ResourceResolver resourceResolver = request.getResourceResolver();
        Map<String, Object> props= new HashMap<String, Object>();
        Resource resource = resourceResolver.getResource(path);
        if (null != resource) {
            props.putAll(resource.adaptTo(ValueMap.class));
        }
        return props;
    } 

and this in my jsp:- <c:set var="refProperties" value="${xyz:getResourceProperties(properties.path,slingRequest)}" />

But this doesn't return the value I want.

Was it helpful?

Solution

This simplest method to accomplish this is to include the /libs/foundation/global.jsp and just use the properties object already in scope ${properties.foo}.

Include the global.jsp at the top of the component jsp like so:

<%@include file="/libs/foundation/global.jsp"%>

As the comments in the file indicate, it basically registers the Sling (sling), CQ (cq), and JSTL (c,fmt,fn) taglib namespaces for use in the JSP.

Then, with the help of the cq:defineObjects taglib, it brings many helpful objects into scope.

<cq:defineObjects />

Here is the list:

@param slingRequest SlingHttpServletRequest
@param slingResponse SlingHttpServletResponse
@param resource the current resource
@param currentNode the current node
@param log default logger
@param sling sling script helper

@param componentContext component context of this request
@param editContext edit context of this request
@param properties properties of the addressed resource (aka "localstruct")
@param pageManager page manager
@param currentPage containing page addressed by the request (aka "actpage")
@param resourcePage containing page of the addressed resource (aka "myPage")
@param pageProperties properties of the containing page
@param component current CQ5 component
@param designer designer
@param currentDesign design of the addressed resource  (aka "actdesign")
@param resourceDesign design of the addressed resource (aka "myDesign")
@param currentStyle style of the addressed resource (aka "actstyle")

This means that by simply using the cq:defineObjects taglib you already have access to the properties ValueMap through JSP Expression Language (EL). No additional conversion is required to access the properties within the JSP.

<c:out value="${properties.foo}" />

To access the properties within your own Java taglib or bean you would just pass the appropriate object to your code using the standard JSTL tags. You could pass the whole request, the current resource, or just the properties. Passing the whole request gives your Java code access to the current resource and all the objects created by the cq:defineObjects taglib including the properties ValueMap.

In JSP:

<jsp:useBean id="mybean" scope="request" class="com.my.impl.TheBean">
   <jsp:setProperty name="mybean" property="slingRequest" value="${slingRequest}"/>
   <jsp:setProperty name="mybean" property="resource" value="${resource}"/>
   <jsp:setProperty name="mybean" property="properties" value="${properties}"/>
</jsp:useBean>

In bean:

public void setSlingRequest(final SlingHttpServletRequest slingRequest) {
    this.slingRequest = slingRequest;
    // Use the one created by cq:defineObjects
    this.properties = (ValueMap)this.slingRequest.getAttribute("properties");
    // OR adapt the resource
    this.properties = this.slingRequest.getResource().adaptTo(ValueMap.class);
}

public void setResource(final Resource resource) {
    this.resource = resource;
    this.properties = this.resource.adaptTo(ValueMap.class);
}

public void setProperties(final ValueMap properties) {
    this.properties = properties;
}

OTHER TIPS

In this particular case you are trying to create Map<String, Object> containing all resource properties. This map would be the same as properties object (which is also a Map), so I guess the whole method is redundant (and - as you write - it doesn't work). properties object doesn't contain path method and probably that's the reason it doesn't work.

What's more, you may have used request.getResource() (instead of getting resolver and resource by path). Also, instead of adapting the resource to ValueMap you may have simple pass properties from the JSP.

More general, if you want to extract logic from JSP to Java class, I think it's a good idea to create some kind of model class, passing slingRequest to it's constructor and then invoking its methods in the JSP. Example:

GET.jsp

<c:set var="model" value="<%= new MyModel(slingRequest) %>" />
Result of the first method: ${model.firstValue}<br/>
Result of the second method: ${model.secondValue}

MyModel.java

public class MyModel {
    private final SlingHttpServletRequest request;

    private final Resource resource;

    private final ValueMap properties;

    public MyModel(SlingHttpServletRequest request) {
        this.request = request;
        this.resource = request.getResource();
        this.properties = resource.adaptTo(ValueMap.class);
    }

    public String getFirstMethod() {
        // do some clever things
        return "";
    }

    public String getSecondMethod() {
        // do more clever things
        return "";
    }
}

Please notice that if you invoke ${model.firstMethod} you need to add get prefix to the method name (getFirstMethod()).

I would use the useBean tag to create an instance of a class that can give you whatever info you need:

<jsp:useBean id="mycomponent" scope="request" class="com.company.components.SomeComponent">
   <jsp:setProperty name="mycomponent" property="request" value="<%= slingRequest %>"/>
</jsp:useBean>

Then just create a setter in the class.

 public void setRequest(final SlingHttpServletRequest request) {
    this.request = request;
    //or more likely an init() method that inits all your props
    //you could even use reflection to look for props that match all the field names
    //to init them automatically
    ValueMap props=request.getResource().adaptTo(ValueMap.class)
    this.interestingProp= props.get("interestingProp");
}

public String getInterestingProp(){
   return this.interestingProp;
}

Then in your jsp:

<c:out value="${mycomponent.interestingProp}"/>

Get the JCR session in your Java file, then iterate through the nodes like content/your_site_name/node/some_parsys. Then you can get the values which are set by the author at run time.

if(node.hasProperty("title")){
    String title=node.getProperty().getValue().getString();
}

Well I did manage to answer my own question. All I did was to use resource.path in my jsp.

resource here refers to the component in question, and so using the path I was able to create my ValueMap correctly.

So, the code in my jsp file is as follows:-

<c:set var="refProperties" value="${xyz:getResourceProperties(resource.path,slingRequest)}"\>

Using this, I can now reference any property of the component that I want:-

${refProperties.foo}

In order to use resource.path, we must also include global.jsp, else it will not be recognised.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top