CQ & Sling servlets: old legacy URL

SlingIn this short article (first of a serie I hope) I’ll show you how to create a Sling Servlet in CQ.

Scenario

You are migrating your existing application in CQ a piece at time and it’s time for the Servlets (controllers) part, so for the first stage you’d like to keep the old URLs. Maybe because some other external services are calling you at these coordinates or because you don’t want to enter each jsp and change the forms action.

Ingredients

  • CQ instance running in Author (localhost:4502)
  • CRXDE (let’s ease the game for starting)

Process

Create a new OSGi bundle under geometrixx (or your app). In this create a new class that extends the SlingSafeMethodsServlet.
Then you have to register as a service in the OSGi container. This is done using the Felix annotations as following:

/**
 * @scr.component immediate="true" metatype="false"
 * @scr.service interface="javax.servlet.Servlet"
 * @scr.property name="sling.servlet.methods" values.0="GET"
 * @scr.property name="sling.servlet.paths" values.0="/path/to/my/servlet"
 *                                          values.1="/apps/path/to/my/servlet"
 */

I’ll leave you to the references section of this post for getting deeper into the meaning of each.

At the end your code will look something like the following:

package com.samples.sling;

import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @scr.component immediate="true" metatype="false"
 * @scr.service interface="javax.servlet.Servlet"
 * @scr.property name="sling.servlet.methods" values.0="GET"
 * @scr.property name="sling.servlet.paths" values.0="/path/to/my/servlet"
 *                values.1="/apps/path/to/my/servlet"
 */
public class AbsoluteUrlServlet extends SlingSafeMethodsServlet {
   private static final long serialVersionUID = -1920460619265757059L;
   private static final Logger logger = LoggerFactory.getLogger(AbsoluteUrlServlet.class);

   @Override
   protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
      logger.debug("AbsoluteUrlServlet::doGet()");
      response.setContentType("text/plain");
      response.getOutputStream().print("Hello AbsoluteUrlServlet World!");
   }
}

Looking at the sling.servlet.paths annotation we se on which url we are actually mapping the servlet: /path/to/my/servlet and /apps/path/to/my/servlet.

Building the bundle and CRXDE+felix will do the magic of the deployment.

Now if you try to run localhost:4502/apps/path/to/my/servlet you’ll see our beautiful Helloworld, but if you’ll try to access localhost:4502/path/to/my/servlet you’ll get a 404 or a content listing.
This is due to a settings in felix+Sling. Open the felix console at the configuration tab (/system/console/configMgr) and search for Apache Sling Servlet/Script Resolver and Error Handler. Here you’ll have to add “/path/” to the Execution Paths section.

Done done!

References

Advertisements

5 thoughts on “CQ & Sling servlets: old legacy URL

  1. While your code might work is not following any best-practice. It’d be good if you change your code to:

    1. Use Java Annotations instead of javadoc comments for the annotations
    2. immediate=true is not necessary in this case. You should try to promote lazy activation and use the default value for immediate=false
    3. While using servlet.paths is perfectly viable it’s highly recommended to use servlet.resourceTypes and associate the servlet to any resourceType you wish and execute the servlet in the context of a content node (with its respective sling:resourceType assigned to it).
    4. Last but not least, in the case of declaring a Servlet it’s recomended to use @SlingServlet annotation, which effectively speaking is a shortcut for @Component and @Service all together, making the code easier to write and to read. It also will give you out-of-the-box parameter configuration through the Felix console.

    @SlingServlet(
    methods = { “GET” },
    paths = { “/path/to/my/servlet” },
    extensions = { “html” })
    public class HelloWorld extends SlingSafeMethodsServlet{
    private static final long serialVersionUID = 217529099388338070L;

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
    throws ServletException, IOException {

    // do whatever
    }
    }

  2. Hi Xavi, thanks for your feedback. I agree with you with everything.

    Regarding resourceType vs path I was showing and example of old legacy url 🙂

    Immediate=true. I was just showing the javadoc stuff.

    I didn’t know of the @SlingServlet annotation. Interesting. I got the stuff from the felix site and the Sling one as well show to use the javadoc instead of annotation. I’d really like to use annotation instead.

    Do you have any links regarding the best practice you mentioned?

  3. Using Java5-style annotations the literal equivalent of the javadoc block would be:

    @Component(immediate = true, metatype=false)
    @Service(value=javax.servlet.Servlet.class)
    @Properties(value = {
    @Property(name=”sling.servlet.methods”, value={ “GET” }),
    @Property(name=”sling.servlet.paths”, value={ “/path/to/my/servlet”, “/apps/path/to/my/servlet” })
    })

    The syntax for dealing with the properties is a little convoluted, but when using the @SlingServlet annotation instead (as per Xavi’s post) it looks a lot nicer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s