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:
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.
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).
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.
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:
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)); } }
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:
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 }
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
.
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:
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>
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.
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.
3.145.84.112