Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

How to configure a servlet via resource type?

Avatar

Level 9

There are several tutorials on creating sling servlets. All of them use paths, e.g.

 

 

"sling.servlet.paths=" + "/bin/readjson" 

 

 

(and this can be hit on www.mysite.com/bin/readjason )

All of them say its better to use resource types, e.g. 

 

 

"sling.servlet.resourceTypes="+ "noidea",

 

 

but how do I hit the servlet?  www.mysite.com/noidea doesnt work.

What needs to be setup, and how, to use a resource type (and setup in source code  - not in the local running AEM instance, where it cannot be distributed to other instances)?  Presumably there is some configuration somewhere which ties the resource type to a URL. 

 

If you have an API with say 20 endpoints, so you have say 20 URLs, its easy to give each one its own path in the servlet directly, e.g. /bin/api/login or /bin/api/register. This concept doesn't really map onto resource type, as you would have to manually create a resource type for each endpoint, and there may be 100 endpoints in some APIs.   We assume selector could be used, or may be postfix, but the documentation implies sector should only be used to define how the result should be formatted, not what the URL endpoint is?

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @TB3dock 

 

When we register a servlet using path, we must be specify what all paths are allowed as if we define something randomly, our servlet might not be functioning properly. Only a limited set of paths are allowed and the rest are blocked. We can add more path using Apache Sling Servlet / Script Resolver and Error Handler. Allowing more paths to execute servlets makes our application vulnerable. That’s why we should not open more doors for servlets to run until and unless it is required and cannot be achieved using resource type.


We might also need to tell specific paths to your consumers, who are consuming servlet response using ajax and any change in that path could have a serious affect. This might not be the case when you use resourceType.

 

Sling Engine will take care of permissions if you register servlet using Resource Type. Users who cannot access a particular resource will not be able to invoke the servlet. Path-bound servlets cannot be access controlled using the default JCR repository ACLs.

 

@Component(service = Servlet.class, property = {"process.label=Some Servlet",
Constants.SERVICE_DESCRIPTION + "=This Servlet is responsible for Something."})
@SlingServletResourceTypes(resourceTypes = ServiceConstants.YOUR_SERVLET_RESOURCE_TYPE,
methods = HttpConstants.METHOD_POST,
extensions = "json",
selectors = "something")
public class SomeServlet extends SlingAllMethodsServlet {

// Code

}

 

Links to refer:

https://sling.apache.org/documentation/the-sling-engine/servlets.html#example-registration-by-resour...

 

Very detailed article:

http://www.sgaemsolutions.com/2017/12/apache-sling-servlets-and-scripts.html

 

Thanks!

View solution in original post

12 Replies

Avatar

Correct answer by
Community Advisor

Hi @TB3dock 

 

When we register a servlet using path, we must be specify what all paths are allowed as if we define something randomly, our servlet might not be functioning properly. Only a limited set of paths are allowed and the rest are blocked. We can add more path using Apache Sling Servlet / Script Resolver and Error Handler. Allowing more paths to execute servlets makes our application vulnerable. That’s why we should not open more doors for servlets to run until and unless it is required and cannot be achieved using resource type.


We might also need to tell specific paths to your consumers, who are consuming servlet response using ajax and any change in that path could have a serious affect. This might not be the case when you use resourceType.

 

Sling Engine will take care of permissions if you register servlet using Resource Type. Users who cannot access a particular resource will not be able to invoke the servlet. Path-bound servlets cannot be access controlled using the default JCR repository ACLs.

 

@Component(service = Servlet.class, property = {"process.label=Some Servlet",
Constants.SERVICE_DESCRIPTION + "=This Servlet is responsible for Something."})
@SlingServletResourceTypes(resourceTypes = ServiceConstants.YOUR_SERVLET_RESOURCE_TYPE,
methods = HttpConstants.METHOD_POST,
extensions = "json",
selectors = "something")
public class SomeServlet extends SlingAllMethodsServlet {

// Code

}

 

Links to refer:

https://sling.apache.org/documentation/the-sling-engine/servlets.html#example-registration-by-resour...

 

Very detailed article:

http://www.sgaemsolutions.com/2017/12/apache-sling-servlets-and-scripts.html

 

Thanks!

Avatar

Level 9

Thanks for the reply; Everything you have said I already know - writing the servlet to use a resource type is easy, but there are no tutorials or guides which tell you how to do the magic step of configuring the resource type and mapping it to a path. This is the missing from every servlet tutorial unfortunately, and means we have to use the path option. In your example you have "

ServiceConstants.YOUR_SERVLET_RESOURCE_TYPE

Where does this come from? where is  is YOUR_SERVLET_RESOURCE_TYPE defined and linked to a path?

Avatar

Community Advisor

Hi @TB3dock 

 

The below screenshot gives the complete details how the request is mapped to a servlet which is registered using resource type and the resource resolution happnes.

asutosh_jena_0-1618767745126.png

If you go through the link below you will be able to get the complete idea about the resource based servlet.

http://www.sgaemsolutions.com/2017/12/apache-sling-servlets-and-scripts.html

 

Now coming to 

ServiceConstants.YOUR_SERVLET_RESOURCE_TYPE

 it's just a constant that I have defined in my code. You can either define in your code and use it else you can directly use like below:

@Component(service = Servlet.class, property = {"process.label=Some Servlet",
Constants.SERVICE_DESCRIPTION + "=This Servlet is responsible for Something."})
@SlingServletResourceTypes(resourceTypes = "/project/component/someresource",
methods = HttpConstants.METHOD_POST,
extensions = "json",
selectors = "something")
public class SomeServlet extends SlingAllMethodsServlet {

// Code

}

Avatar

Level 9

Thanks for the comment. We know the source code we need to put in the servlet. We dont know what files and file content to put into our git to make the resource type magically appear, and how to link a resource type to the URL to it the servlet.. You have a screen shot of something which appears to have been setup, but how? how to you create or configure a resource type? This is the missing piece from all the turorials and information pages. What do we need to add to our git to make a resource type magically appear when we deploy the code to one of our envs using mvn clean install -PautoInsallSinglePackage?

Looking at the screen shot you kindly provided, it shows a resource type of "community-componets/component/componet-page" however, the URL which appears to be being used to access the content is "/content/community-components/en/tagcloud/jcr:content/sample.  There seems to be no correlation between the URL and the resource type, and we dont know how to configure the resource type.

Avatar

Community Advisor

Hi @TB3dock 

 

You can create separate nodes (nt:unstructured) which will be called as resource and for each of the resource you can define the sling:resourceType property which will hold the value which will be used in the servlet.

 

Let's say we are creating these nodes under /content/wknd as /services/api1 and api2 as below:

 

asutosh_jena_0-1618770204901.png

 

Please consider only the highlighted part below which will be used to generate the node via code and deployed via git. you need to adjust the filter.xml.

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:allowedTemplates="[/conf/wknd/settings/wcm/templates/landing-page-template,/conf/wknd/settings/wcm/templates/article-page-template,/conf/wknd/settings/wcm/templates/content-page-template,/conf/wknd/settings/wcm/templates/adventure-page-template]"
cq:conf="/conf/wknd"
cq:lastModified="{Date}2020-01-06T15:53:34.296-08:00"
cq:lastModifiedBy="admin"
jcr:primaryType="cq:PageContent"
jcr:title="WKND Site"
sling:configRef="/conf/wknd"
sling:redirect="true"
sling:redirectStatus="{Long}302"
sling:resourceType="foundation/components/redirect"
redirectTarget="/content/wknd/en">
<image jcr:primaryType="nt:unstructured">
<file/>
</image>
</jcr:content>
<services
jcr:primaryType="nt:unstructured">

<api1
jcr:primaryType="nt:unstructured"
sling:resourceType="api/service1">

</api1>
<api2
jcr:primaryType="nt:unstructured"
sling:resourceType="api/service1">

</api2>
</services>
</jcr:root>

 

Now each of the node has a sling:resourceType property which will be used in the servlet and when the resource path will be invoked, it will internally invoke the servlet whose resource type will match.

 

@Component(service = Servlet.class, property = {"process.label=Some Servlet",
Constants.SERVICE_DESCRIPTION + "=This Servlet is responsible for Something."})
@SlingServletResourceTypes(resourceTypes = "api/service1",
methods = HttpConstants.METHOD_POST,
extensions = "json",
selectors = "something")
public class SomeServlet extends SlingAllMethodsServlet {

// Code

}

Here the servlet is registered using api/service1 as the resource type and can be invoked by using the resource path i.e. /content/wknd/services/api1 with something as the selector and json as the extension. 

Request URL: /content/wknd/services/api1.something.json which will invoke the servlet.

 

If you do not want to register using any selector and extension then the same can be removed from servlet and the request will become only /content/wknd/services/api1

 

Hope this helps!

Thanks!

Avatar

Level 9
This is awesome, we are almost there. Where is the filter.xml we need to edit to add the resource type nodes? In your screen shot, you have a dir called services, which is nice, how can this be created in code?

Avatar

Community Advisor
The xml code I shared above will create all the required nodes. You will find the filter.xml inside the ui.content filter where you can apply the logic. The same can be created on crx/de and can be synced back to code base as well using the vlt tool.

Avatar

Level 9

Hi, where is ui.content filter? We dont seem to have anything like filter.xml in the source code. If we need to add it, which directory does it go in? 

Below is the source code for wknd. We cant find anything like filter.xml or ui.content/filter.

TB3dock_0-1618781670301.png

 

Avatar

Community Advisor

Hi @TB3dock 

 

filter.xml is always available within the META-INF/vault folder. Please see the screenshot below:

Here it's part of aem-guides-wknd.ui.content.sample module for WKND tutorial.

 

asutosh_jena_0-1618803246122.png

 

In this way it will be deployed into all AEM instances.

If you do not want to keep it in code base, then you need to move it as a content package which is again a manual process and opens a chance of human error as we might forget to move certain node and it will break the application on upper environments.

 

Filter.xml file already has the entry for /content/wknd so here we do not have to make any change, but if it;s not there then we need to add it.

asutosh_jena_1-1618803314316.png

 

Thanks!

Avatar

Level 9

Awesome thanks. We have been looking for a tutorial on how to get required content into the git repo for some time, and this might be it. We found the filter.xml in WEB-INF as you say, but are not sure how to combine it with the content you posted. is it this?

 

 

<?xml version="1.0" encoding="UTF-8"?>
<workspaceFilter version="1.0">
    <filter root="/conf/wknd" mode="merge"/>
    <filter root="/content/wknd" mode="merge"/>
    <filter root="/content/dam/wknd" mode="merge"/>
    <filter root="/content/experience-fragments/wknd" mode="merge"/>
    <filter root="/content/cq:tags/wknd" mode="merge"/>
    <filter root="/content/cq:graphql" mode="merge" />

    <jcr:content
        cq:allowedTemplates="[/conf/wknd/settings/wcm/templates/landing-page-template,/conf/wknd/settings/wcm/templates/article-page-template,/conf/wknd/settings/wcm/templates/content-page-template,/conf/wknd/settings/wcm/templates/adventure-page-template]"
        cq:conf="/conf/wknd"
        cq:lastModified="{Date}2020-01-06T15:53:34.296-08:00"
        cq:lastModifiedBy="admin"
        jcr:primaryType="cq:PageContent"
        jcr:title="WKND Site"
        sling:configRef="/conf/wknd"
        sling:redirect="true"
        sling:redirectStatus="{Long}302"
        sling:resourceType="foundation/components/redirect"
        redirectTarget="/content/wknd/en">
        <image jcr:primaryType="nt:unstructured">
            <file/>
        </image>
    </jcr:content>
    <services
        jcr:primaryType="nt:unstructured">
        <api1
            jcr:primaryType="nt:unstructured"
            sling:resourceType="api/service1">
        </api1>
        <api2
            jcr:primaryType="nt:unstructured"
            sling:resourceType="api/service1">
        </api2>
    </services>

</workspaceFilter>

 

 

Or should it be just this?

 

<?xml version="1.0" encoding="UTF-8"?>
<workspaceFilter version="1.0">
    <filter root="/conf/wknd" mode="merge"/>
    <filter root="/content/wknd" mode="merge"/>
    <filter root="/content/dam/wknd" mode="merge"/>
    <filter root="/content/experience-fragments/wknd" mode="merge"/>
    <filter root="/content/cq:tags/wknd" mode="merge"/>
    <filter root="/content/cq:graphql" mode="merge" />

    <services
        jcr:primaryType="nt:unstructured">
        <api1
            jcr:primaryType="nt:unstructured"
            sling:resourceType="api/service1">
        </api1>
        <api2
            jcr:primaryType="nt:unstructured"
            sling:resourceType="api/service1">
        </api2>
    </services>


</workspaceFilter>

 

 

Avatar

Community Advisor

Hi @TB3dock 

 

You need to add the XML snippet in the .context.xml file of wknd node in ui.content.sample package as it's being used by the wknd tutorial. Please see the screenshot below:

asutosh_jena_0-1618825727650.png

 

No update is required here for filter.xml as the parent node /content/wknd is already present in the filter.xml

 

asutosh_jena_1-1618825806180.png

 

Thanks!

Avatar

Community Advisor

@TB3dock 

Registering Servlet using ResourceType:

I have a component Demo (/apps/ata/components/demo). Now am registering the Servlet with the resource type of my demo component.

import javax.servlet.Servlet;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceDescription;

@Component(service = { Servlet.class })
@SlingServletResourceTypes(
		resourceTypes="ata/components/demo",
		methods=HttpConstants.METHOD_GET,
		extensions="txt")
@ServiceDescription("Demo Component ResourceType Servlet")
public class DemoResourceTypeServlet extends SlingSafeMethodsServlet{
	@Override
	public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
		//Code
	}
}

How to hit the servlet registered using ResourceType:

  • Drop the demo component in a page (ex: /content/en/us/test)
  • Copy the path till the demo component (ex: /content/en/us/test/_jcr_content/par/demo)
  • Here we registered with extension txt, so add txt extension to above path and hit (ex: /content/en/us/test/_jcr_content/par/demo.txt)
  • Request URL: /content/en/us/test/_jcr_content/par/demo.txt

Servlet can be registered using html and json extension as well. Refer: https://sling.apache.org/documentation/the-sling-engine/servlets.html#example-registration-by-resour...