Learning a few Servlet tricks

The Java Servlet API has been around for quite some time now—more than 10 years—and is the base for technologies such as JavaServer Pages, the one we just discussed. WebLogic Server 12c comes with Java Servlet 3.0, defined by JSR 315, which brings some new features, such as:

  • Annotations support, which helps in easing the task of configuring components
  • Dynamic component registration
  • Asynchronous request processing

Deprecated features

Up to Version 11g, WebLogic Server provides proprietary annotations to ease the development of servlets—instead of declaring them in the web.xml configuration file, you could use the @WLServlet, @WLFilter, and @WLInitParam decorations to set the attributes of servlets and filters. These annotations are deprecated in Version 12c as we can now use the standard ones defined by Servlet 3.0 specifications: @WebServlet, @WebFilter, and @InitParam, respectively.

Note

If you're migrating @WLServlet to @WebServlet decorations, remember that the runAs attribute of the former is now implemented through a specific annotation, javax.security.RunAs. All other attributes have direct correspondence.

The weblogic.servlet.http.AbstractAsyncServlet class that enables us to write asynchronous servlets is also deprecated and you're supposed to use the asyncSupported attribute of @WebServlet, which defines a servlet, or the corresponding entry at the web.xml deployment descriptor async-supported (which is part of the servlet tag).

Identifying the default name of a servlet

As happens with most annotations, there's always the possibility of overriding the parameters via the deployment descriptor, specifically the web.xml file, when dealing with a web application. This is also true for the @WebServlet decoration, but there's a detail about it that can pass unnoticed and give us a headache down the road.

What happens is that the name parameter of the annotation @WebServlet is optional. You can declare your servlet as shown in the following code (without a name entry):

package com.packt.servlets;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

@WebServlet(
   urlPatterns = { "/StoreFront" },
   initParams = { @WebInitParam(name = "maxValue", value = "1000") }
public StoreFront {
  …
}

The container will fill the name attribute for you and this is done by using the fully qualified class name. So, the sample servlet above will be named com.packt.servlets.StoreFront by WebLogic Server.

As we usually name the servlet with just the class name, StoreFront, when we create the deployment descriptors entry to map the servlet, most likely we will do something similar to this:

<servlet>
   <servlet-name>StoreFront</servlet-name>
   <servlet-class>com.packt.servlets.StoreFront</servlet-class>
   <init-param>
      <param-name>autoApproveThreshold</param-name>
      <param-value>3000</param-value>
   </init-param>
</servlet>

If this is the case, we will end up with two servlets available—StoreFront and com.packt.servlets.StoreFront—each with its own set of initial parameters. This can lead to unpredictable behavior.

Note

As a best practice, always declare a name attribute when adding decorations such as @WebServlet or @WebService, as each one uses a different algorithm to decide how to name a component.

Asynchronous request processing

As said earlier, there's a new Servlet 3.0 feature that enables us to asynchronously process a request. The following diagram shows a basic scheme of how it works:

Asynchronous request processing

The client makes a request to the asynchronous servlet and it doesn't know a thing about the servlet's characteristic—from its point of view, it's just a regular synchronous request/response invocation. When the servlet receives the request, it must create an asynchronous context, which is the component that deals with the associated structure, and inform the context about which piece of software will be responsible for actually processing the request. This component does its work and produces the response that the client is waiting for.

The servlet acts as a dispatcher, setting up the necessary environment and forwarding the request to the actual processor. Here's a very simple implementation of such a servlet:

@WebServlet(name = "AsyncServlet", 
            urlPatterns = { "/async" },
            asyncSupported = true)

public class AsyncServlet extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req,
                         HttpServletResponse resp)
      throws ServletException, IOException 
      {
    AsyncContext ctx = req.startAsync();
    ctx.start(new SomeBasicTask(ctx));
  }
}

Note

Notice that the WebServlet annotation has an asyncSupported attribute set to true to tell the container about our intention—if we don't do so, at runtime, an IllegalStateException exception is thrown.

In the service method, we acquired the asynchronous context via the startAsync request method and then passed a Runnable instance to the start method, passing the context as a constructor parameter. Here's the general structure of this executor class:

public class SomeBasicTask implements Runnable {
  private AsyncContext ctx;
  
  public SomeTask(AsyncContext ctx) {
    this.ctx = ctx;
  }

  @Override
  public void run() {
    // Do some long running work
    
    // Set up the result
    ctx.getResponse().setContentType("text/html");

    try {
      ctx.getResponse().getWriter().write("done");
    } catch (IOException ioe) { }
    
    // Wrap up processing
    ctx.complete();
  }
}

As you may have noticed, the result output is generated here, accessing the response object via the asynchronous context. You could put something into the response buffer from the servlet; but if the goal is to release its thread as soon as possible, this doesn't make sense, so try to keep the output generation in the executor class.

The single most relevant line in this class is the ctx.complete() call. This method tells the container that the processing is done and it can release the connection to the client. If you don't do so, the connection will be kept open indefinitely, which brings us two problems:

  • The client will wait for a signal that tells that the execution will require more time for completion, basically until a timeout, bringing sluggishness into your application.
  • The TCP socket will be kept open at WebLogic Server. Eventually, they will be recycled, but this is not an optimal scenario.

Note

Make sure that your execution flow always sends a call to asyncContext.complete() to avoid server contention and client locks.

We can also attach a listener to the asynchronous context we're dealing with and get notifications about the context's state. The list of events published by the context that can be captured by a javax.servlet.AsyncListener instance are as follows:

  • onStartAsync: This event is raised when the container starts the asynchronous context.
  • onTimeout: When the configured timeout value is reached, this event is raised. From this event you can release the client's connection or notify it about the current status of the execution.
  • onError: When the thread throws an exception, the listener is notified through this event.
  • onComplete: This event is raised when the execution is completed without any errors.

This listener is especially useful when we configure the context's timeout to avoid keeping the client locked for more than the usual time the request takes to get processed. When this is the case, the onTimeout method is called, and we can inform the client about it.

Here's the servlet updated to set a timeout limit and to deal with the corresponding event:

public class AsyncServlet extends HttpServlet
                          implements javax.servlet.AsyncListener {
  @Override
  protected void service(HttpServletRequest req,
                         HttpServletResponse resp)
      throws ServletException, IOException 
  {
    AsyncContext ctx = req.startAsync();
    ctx.setTimeout(2000);
    ctx.addListener(this);
    ctx.start(new SomeBasicTask(ctx));
  }
  @Override
  public void onTimeout(AsyncEvent ae) throws IOException {
    // Format some response …

    // … and release the client's connection
    ae.getAsyncContext().complete();
  }

  // Other listener methods suppressed
}

Note

Notice that even when we send a response to the client and complete the context, the spawned thread will continue to run until its processing is complete.

A point worth mentioning is that WebLogic Server's default timeout value is 120 seconds, which is a rather large time to keep a user/client waiting for a response. This value can be overridden individually by the setTimeout method of AsyncContext or at the application level at the weblogic.xml deployment descriptor:

<wls:async-descriptor>
   <wls:timeout-secs>5</wls:timeout-secs>
</wls:async-descriptor>

Another parameter we can configure via weblogic.xml is the interval at which the asynchronous mechanism will check if a timeout situation has been reached for every context created. The element that defines this value is named timeout-check-interval-secs.

Note

The default check interval value is 30 seconds, so if you set up timeouts shorter than this, chances are the engine will not generate the onTimeout events.

If you set up a 10-seconds timeout and leave the default check interval, your code may end up waiting for about 30 seconds to get the onTimeout event or be completed before that. Here's a graphical representation of this scenario:

Asynchronous request processing

Note

Keep in mind that the timeout is attached to the start of the asynchronous context, not its start() or dispatch() methods. You can have an async context with no associated operation that will receive a timeout event in the same way.

If you need to control the timeout of short processes, consider adjusting the timeout check interval value to a smaller value at weblogic.xml:

<wls:async-descriptor>
   <wls:timeout-secs>5</wls:timeout-secs>
   <wls:timeout-check-interval-secs>1</wls:timeout-check-interval-secs>
</wls:async-descriptor>

Creating dynamic components

Another nice new feature of Java EE 6, or more specifically Servlet 3.0, is the possibility to dynamically create and bind servlets, filters, and listeners. This is a somewhat advanced procedure, but it is good to know about as it can be very handy if you ever need to create a more flexible structure to load your servlets. Let's say you need to create a structure that reads servlet binding information from a data source; when a mapping has to be changed, you don't have to change the deployed application—by just restarting the deployment, the new mapping will be processed and available.

Here's a sample servlet created at the Store project, referencing the project's entity manager and a singleton bean of the same project to test context injection:

public class DynamicServlet extends HttpServlet {
  @Inject
  ControlGeneratorBean cgb;
  
  @PersistenceContext(unitName = "StoreBO")
  EntityManager em;

  @Override
  protected void service(HttpServletRequest req,
                          HttpServletResponse resp)
     throws ServletException, IOException {

    List<Movie> movies = em.createNamedQuery(Movie.findAll).
                                  getResultList();
    
    for (Movie movie : movies) {
      System.out.println(movie.getName());
    }
    
    System.out.println("Next control # is " + 
                                  cgb.getNextId(2, 0, 0));
  }
}

Notice that the servlet doesn't have the WebServlet annotation; this is because if you declare it with the same name as the dynamic procedure, the annotation engine will process the class upon deployment and the dynamic registration will not be considered.

We can't register a servlet from another servlet because, at this point, the servlet context—the engine that instantiates and controls this kind of component—is already closed to changes. So, we do it using a servlet context listener that has its contextInitialized method called when the application is deployed, as shown in the following code:

@WebListener
public class DynamicSetupListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
   ServletRegistration.Dynamic servlet = 
event.getServletContext().addServlet("DynamicServlet",
        "com.packt.store.DynamicServlet");
  
   servlet.addMapping("/dynamic");
}
}

As the listener is decorated with @WebListener, we don't need to change any deployment descriptors; the package is scanned upon deployment and the listener is found and processed accordingly.

After publishing the project, go to the appropriate address—for the sample code, this would be http://localhost:7001/store/dynamic—and the list of movies will be printed at the console window.

Tip

Even though the official documentation states that no dependency injection is done for dynamic components, the tests done using the preceding sample code showed that the annotations are processed. The statement can be found in the Limitations section on the WebLogic Annotation for Web Components page at http://docs.oracle.com/middleware/1212/wls/WBAPP/annotateservlet.htm.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.84.112