Search code examples
osgiaemapache-felixsling

How to enable Async support for Sling Servlet - Felix


I have this code

  @Component(service = Servlet.class, scope = ServiceScope.PROTOTYPE, property={
    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN+"=/content/*",
    HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED+"=true",
    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED+"=true"}
    )
  @SlingServletResourceTypes(
    resourceTypes="cq/Page",
    methods=HttpConstants.METHOD_GET,
    selectors = "asynctest",
    extensions="json")
   public class ReactiveServlet extends HttpServlet {

@Override
protected void doGet(
        HttpServletRequest  request, HttpServletResponse response)
        throws ServletException, IOException {
         AsyncContext async = request.startAsync();
        // Some logic
        }
 }          

When calling this servlet /content/mypage.asynctest.json then getting this error

 java.lang.IllegalStateException: null
at org.apache.felix.http.base.internal.dispatch.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:338) [org.apache.felix.http.jetty:4.1.10]
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:369) [org.apache.felix.http.servlet-api:1.1.2]
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:369) [org.apache.felix.http.servlet-api:1.1.2]

Solution

  • The short answer is that async is NOT supported by Sling-Servlets. Your exception is thrown in this class:

    https://github.com/apache/felix-dev/blob/master/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestWrapper.java

    @Override
    public AsyncContext startAsync() throws IllegalStateException
    {
        if ( !this.asyncSupported )
        {
            throw new IllegalStateException();
        }
        return super.startAsync();
    }
    

    But you are mixing OSGi Http-Whiteboard Pattern with Sling-Servlets. I'm not sure, what you wanted to do.

    Sling/AEM is a technology-stack, where layer is built on layer. Unfortunately multiple of these layers allow registering a servlet.

    • Sling Servlets = Apache Sling (recommended, default)
    • OSGi HTTP Whiteboard Pattern = Apache Felix (only for special cases)
    • JEE Servlet = Jetty Servlet Container (NOT recommended)

    Sling-Servlet

    The Sling-Servlet you registered with @SlingServletResourceTypes doesn't support async. The output of the following servlet is: Async is not supported by an Sling-Servlet! (http://localhost:4502/content/we-retail.asynctest.json)

    import static org.apache.sling.api.servlets.ServletResolverConstants.*;
    
    import javax.servlet.AsyncContext;
    import javax.servlet.Servlet;
    import javax.servlet.ServletException;
    import java.io.IOException;
    
    import org.apache.sling.api.SlingHttpServletRequest;
    import org.apache.sling.api.SlingHttpServletResponse;
    import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
    import org.osgi.service.component.annotations.Component;
    
    @Component(service = Servlet.class, property = {
            SLING_SERVLET_RESOURCE_TYPES + "=cq:Page",
            SLING_SERVLET_SELECTORS + "=asynctest",
            SLING_SERVLET_EXTENSIONS + "=json",
            SLING_SERVLET_METHODS + "=GET"
    })
    public class AsyncSlingServlet extends SlingSafeMethodsServlet {
    
        @Override
        protected void doGet(
                SlingHttpServletRequest request,
                SlingHttpServletResponse response
        ) throws ServletException, IOException {
    
            if (request.isAsyncSupported() /* false */) {
                final AsyncContext async = request.startAsync();
                async.start(() -> {
                    async.getResponse().setContentType("text/plain");
                    async.getResponse().setCharacterEncoding("utf-8");
                    try {
                        async.getResponse().getWriter().println(
                                "Hello from the Sling-Servlet!");
                    } catch (IOException e) {
                        // ignore
                    }
                    async.complete();
                });
            } else {
                response.setContentType("text/plain");
                response.setCharacterEncoding("utf-8");
                response.getWriter().println(
                        "Async is not supported by an Sling-Servlet!");
            }
        }
    }
    

    OSGi HTTP Whiteboard Pattern

    The almost same servlet registered via the OSGi HTTP Whiteboard pattern supports async. It returns Hello from the OSGi Http-Whiteboard Servlet! (http://localhost:4502/my-project/hello). But such servlets are aside to Sling, so they are "rivals" or "competitors". You have to be careful, not to negatively impact Sling. So the /content path should be avoided.

    import javax.servlet.AsyncContext;
    import javax.servlet.Servlet;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    import org.osgi.service.component.annotations.Component;
    import org.osgi.service.component.annotations.ServiceScope;
    import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
    
    @Component(
            service = Servlet.class,
            scope = ServiceScope.PROTOTYPE,
            property = {
                    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN
                            + "=/my-project/*",
                    HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT
                            + "=("
                            + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME
                            + "=org.apache.sling)",
                    HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED
                            + "=true",
                    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED
                            + "=true" }
    )
    public class AsyncOSGiServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            if (request.isAsyncSupported() /* true */) {
                final AsyncContext async = request.startAsync();
                async.start(() -> {
                    async.getResponse().setContentType("text/plain");
                    async.getResponse().setCharacterEncoding("utf-8");
                    try {
                        async.getResponse().getWriter().println(
                                "Hello from the OSGi Http-Whiteboard Servlet!");
                    } catch (IOException e) {
                        // ignore
                    }
                    async.complete();
                });
            } else {
                response.setContentType("text/plain");
                response.setCharacterEncoding("utf-8");
                response.getWriter().println(
                        "Async is not supported by an OSGi Http-Whiteboard Servlet!");
            }
        }
    }