What should be done to manually stop Servlet as calling destroy doesn't help unless all threads exited from service.
Say, If I have n number of Servlets and I want to stop only one of them.
That behavior is very important when dealing with Servlets. Instances can be created after the multi-thread model and are thus not thread-safe.
The container does not allow a thread to invoke the service method after destroy has been called.
This gives you the means to close all resources that your Servlet is using (db, file, memory, etc).
#WebServlet
public class OncePerApplicationServlet extends HttpServlet {
private Connection connection;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if(req.getParameter("closeServlet").equals("true"))
this.destroy();
else
this.service(req, resp); // normal flow
}
// this method will never be called by the container after the destroy method has been invoked
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.
try {
connection = DriverManager.getConnection("someDbUrl");
Statement stm = connection.createStatement();
stm.execute("select * from someTable");
} catch (SQLException e) {
e.printStackTrace();
}
}
#Override
public void destroy() {
// the point is that when this method is called you should be able to
// clean up and close all resources, you can rest assured that there are no "loose"
// threads that need the connection-instance
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Here is a quote from the API-docs:
This interface defines methods to initialize a servlet, to service
requests, and to remove a servlet from the server. These are known as
life-cycle methods and are called in the following sequence:
The servlet is constructed, then initialized with the init method. Any
calls from clients to the service method are handled. The servlet is
taken out of service, then destroyed with the destroy method, then
garbage collected and finalized.
|
Link to the documentation
Good luck!
Related
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//some code here
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//performTask(req, resp);
//some code here
}
private void insertRequestTemplate() {
HttpSession session = req.getSession();
responsePage = req.getParameter("ResponsePage");
ServletContext ctx = getServletConfig().getServletContext();
ctx.getRequestDispatcher(responsePage).forward(req,resp);
readMessage();
public void readMessage()
{
System.out.println("calling MessageTrigger_ABean");
MessageTrigger_ABean msg = new MessageTrigger_ABean();
msg.read();
}
msg.read() has the code to read messages from MQ. Inside insertRequestTemplate method, I am calling readMessage method after ctx.getRequestDispatcher(responsePage).forward(req,resp);is this the correct way of calling this?
But inside insertRequestTemplate method, the page is not getting forwarded to the next page untill readMessage() is executed because of which the page keeps on loading for a long time until message is read from MQ. Could you please help me on this.
Most examples I have seen of a servlet forwarding the request to another servlet have the dispatcher forward invocation at the end of the method. ie. there is no more code, other than closing braces at the end of the method.
I am guessing that the forwarding doesn't happen until the invoking method completes. So where you have your msg.read() will stop the insertRequestTemplate method from completing. This will more than likely be because the code inside msg.read is being performed synchronously. Leading to http timeouts on the http request.
How you solve this will depend on what you want to do with the messages you obtain from msg.read().
I'm trying to practice multiple asynchronous request chain in servlets and I'm bumping into a weird behavior. Not sure if it has anything to do with tomcat.
So here's the scenario. I have a simple J2EE maven web application.
I have two servlets and a filter. I have marked all of them with asyncSupported=true. When I click on a link in a JSP, the first servlet does indeed take the request and spawns a new worker thread using AsyncContext. The worker thread then writes something to the response, commits it (as I learned it's legal for asynchronous processing in servlets) and then dispatches the request to another servlet. It works fine till this point.
The second servlet is supposed to spawn a second worker thread and then the plan was to make the second worker thread call dispatch (As I was also trying to practice the parameter-less call to dispatch()) to go back to the Second servlet that called it. However, I get the below error when calling startAsync() on the second servlet
06-Apr-2018 19:04:48.128 WARNING [RMI TCP Connection(5)-127.0.0.1] org.apache.catalina.startup.ContextConfig.validateSecurityRoles Security role name [authSupervisor] used in an <auth-constraint> without being defined in a <security-role>
06-Apr-2018 19:04:48.261 INFO [RMI TCP Connection(5)-127.0.0.1] com.kingshuk.listeners.MyServletContextListener.contextInitialized The servlet class com.kingshuk.servlets.MyAppDynamicServlet is now being registered
06-Apr-2018 19:05:09.025 WARNING [http-nio-8080-exec-8] org.apache.catalina.connector.Request.startAsync Unable to start async because the following classes in the processing chain do not support async []
java.lang.IllegalStateException: A filter or servlet of the current chain does not support asynchronous operations.
at org.apache.catalina.connector.Request.startAsync(Request.java:1636)
at org.apache.catalina.connector.Request.startAsync(Request.java:1628)
at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:1043)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:378)
at com.kingshuk.servlets.BiggestAsyncSecondServlet.doGet(BiggestAsyncSecondServlet.java:23)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.kingshuk.filters.AsyncRequestLoggingFilter.doFilter(AsyncRequestLoggingFilter.java:25)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:633)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:601)
at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:566)
at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:352)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:196)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:235)
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:228)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Below are all the related files
The Filter
#WebFilter(filterName = "AsyncRequestLoggingFilter",
urlPatterns = {"/asyncServlet", "/biggestAsyncRequestTest", "/biggestAsyncRequestTest2"},
asyncSupported = true,
dispatcherTypes = {DispatcherType.ASYNC, DispatcherType.REQUEST})
public class AsyncRequestLoggingFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
System.out.println("<<AsyncRequestLoggingFilter>> Initializing the Filter");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws ServletException, IOException {
if (DispatcherType.ASYNC.equals(req.getDispatcherType())) {
System.out.println("<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter during the ASYNC dispatching");
} else {
System.out.println("<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter");
}
chain.doFilter(req, resp);
if (DispatcherType.ASYNC.equals(req.getDispatcherType())) {
System.out.println("<<AsyncRequestLoggingFilter>> This is AFTER returning from the doFilter call after the ASYNC dispatching");
} else {
System.out.println("<<AsyncRequestLoggingFilter>> This is AFTER returning from the doFilter call");
}
}
public void destroy() {
System.out.println("<<AsyncRequestLoggingFilter>> Destroying the Filter");
}
}
The First Servlet
#WebServlet(name = "BiggestAsyncFirstServlet",
urlPatterns = "/biggestAsyncRequestTest",
asyncSupported = true)
public class BiggestAsyncFirstServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(10000);
asyncContext.addListener(asyncContext.createListener(BiggestAsyncContextListener.class));
//asyncContext.start(new BiggestAsyncFirstWorkerThread());
/*
Step 5.Get the reference to the thread pool that was created in the context listener class
when the app was deployed
*/
ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");
/*
Step 6.Actually creating the worker thread
and kick starting the thread by calling the run method of the class implementing the runnable interface.
*/
executor.execute(new BiggestAsyncFirstWorkerThread(asyncContext));
System.out.println("Hi I'm the servlet " + getServletName() + " and my job is done");
}
}
The Second servlet
#WebServlet(name = "BiggestAsyncSecondServlet",
urlPatterns = "/biggestAsyncRequestTest2",
asyncSupported = true)
public class BiggestAsyncSecondServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
//asyncContext.setTimeout(10000);
//asyncContext.createListener(BiggestAsyncContextListener.class);
//asyncContext.start(new BiggestAsyncFirstWorkerThread());
/*
Step 5.Get the reference to the thread pool that was created in the context listener class
when the app was deployed
*/
ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");
/*
Step 6.Actually creating the worker thread
and kick starting the thread by calling the run method of the class implementing the runnable interface.
*/
executor.execute(new BiggestAsyncSecondWorkerThread(asyncContext));
System.out.println("Hi I'm the servlet " + getServletName() + " and my job is done");
}
}
The first worker thread
public class BiggestAsyncFirstWorkerThread implements Runnable {
private AsyncContext context;
public BiggestAsyncFirstWorkerThread(AsyncContext context) {
this.context = context;
}
#Override
public void run() {
//The idea is to write something to the response and then dispatch.
try {
AsyncRequestProcessor.waitingTime(6000);
PrintWriter writer = context.getResponse().getWriter();
writer.print("<html>\n" +
"<head>\n" +
" <title>User login</title>\n" +
"\n" +
" <link rel=\"stylesheet\" type=\"text/css\" href=\"/" +
context.getRequest().getServletContext().getServletContextName() + "/style/master_css.css\">\n" +
"\n" +
"\n" +
"</head>");
writer.print("<body>\n" +
"<div id=\"allcontent\">");
context.getRequest().getRequestDispatcher("pages/common/header.jsp").
include(context.getRequest(), context.getResponse());
writer.print(" <div id=\"actual_content\">");
context.getResponse().flushBuffer();
context.dispatch("/biggestAsyncRequestTest2");
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}
}
The second worker thread
public class BiggestAsyncSecondWorkerThread implements Runnable {
private AsyncContext context;
public BiggestAsyncSecondWorkerThread(AsyncContext context) {
this.context = context;
}
#Override
public void run() {
//The idea is to write something to the response and then dispatch.
try {
AsyncRequestProcessor.waitingTime(6000);
PrintWriter writer = context.getResponse().getWriter();
context.getRequest().getRequestDispatcher("pages/common/cr_leftnav.jsp").
include(context.getRequest(), context.getResponse());
writer.print(" <div id=\"content-body\">\n" +
" <h3>The external app</h3>");
writer.print("<p>This is the page you have been waiting so patiently for. After one round of asynchronous processing" +
"here you are. I love you..!!</p>");
writer.print(" </div>\n" +
" </div>\n" +
"</div>\n" +
"</body>\n" +
"</html>");
context.getResponse().flushBuffer();
//context.complete();
context.dispatch();
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}
}
And finally the initial call from the jsp that triggered this request in the first place
<div id="sidebar">
<ul id="parent_nav">
<li>Checking everything async does</li>
</ul>
</div>
Note: I have an async listener too. But the error seems to have nothing to do with it, so leaving it
Some additional info
Before the error I have mentioned at the top prints, the following lines are printed in the logs, suggesting that it's going wrong on line 23 of the Second Servlet.
<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter
Hi I'm the servlet BiggestAsyncFirstServlet and my job is done
<<AsyncRequestLoggingFilter>> This is AFTER returning from the doFilter call
<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter during the ASYNC dispatching
My apologies for such a long question. Any help I can get to understand why it's saying "A filter or servlet of the current chain does not support asynchronous operations." despite all the components being marked with asyncSupported=true, is deeply appreciated.
Thanks,
Kingshuk
Lately, during my research about asynchronous processing in Servlets, I came across at at least three ways to implement
some functionality using this approach.
The questions are:
Which one is the best?
Maybe some of these approaches are not recommended?
Maybe there is another one approach better than all of mentioned below?
Found approaches:
Using AsyncContext.start(Runnable).
This approach is quite simple and straightforward. But many serwers executes such a job in thread pool created for HTTP requests
(more about it here http://www.nurkiewicz.com/2012/05/javaxservletservletrequeststartasync.html)
Using custom threads pool created during Servlet context initialization
(sample here: http://www.journaldev.com/2008/async-servlet-feature-of-servlet-3).
But can I create my own threads in Servlet container? It was not recommended (or even prohibited) in EJB (before JavaEE7).
Can I use JavaSE Executors or should I use ManagedExecutors from JavaEE7 (assuming that I use JavaEE7)?
Using EJB and #Asynchronious annotation
(example here: https://github.com/wildfly/quickstart/tree/master/servlet-async/src/main/java/org/jboss/as/quickstarts/servlet/async).
But here I have no control over threads executing my task (i.e. how many thread should by created etc.)
I would by glad to hear your thoughts on this issue and your experience with AsyncContext.
All will have the same performance, at the backend all threads are replacing the request processing thread to another thread, so that more requests can be served.
Below you'll find a simple implementation:
#WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true)
public class AsyncLongRunningServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
System.out.println("Request Processing Thread "+Thread.currentThread().getName());
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
response.setContentType("text/html");
PrintWriter printWriter=response.getWriter();
printWriter.println("<html><head><title>Asynchronous servlet</title></head><body>");
printWriter.println("Request Processing Thread "+Thread.currentThread().getName());
printWriter.println("<br>");
printWriter.println("<progress id='progress' max='100')></progress>");
printWriter.println("<br>");
AsyncContext asyncCtx = request.startAsync();
asyncCtx.addListener(new AppAsyncListener());
asyncCtx.setTimeout(12000);
//release of request processing thread
asyncCtx.start(() ->{
printWriter.println("<br>");
printWriter.println("Async thread Name "+Thread.currentThread().getName());
printWriter.println("<br>");
int i=0;
while(i<100)
{
printWriter.println("<script>document.getElementById('progress').value=\""+i+"\";</script>");
printWriter.flush();
try {
Thread.sleep(100);
} catch (Exception e) {
}
i++;
}
printWriter.println("</body></html>");
asyncCtx.complete();
}
);
printWriter.println("<br>");
printWriter.println("End of response");
}
}
package com.journaldev.servlet.async;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebListener;
#WebListener
public class AppAsyncListener implements AsyncListener {
#Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onComplete");
// we can do resource cleanup activity here
}
#Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onError");
//we can return error response to client
}
#Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onStartAsync");
//we can log the event here
}
#Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onTimeout");
//we can send appropriate response to client
ServletResponse response = asyncEvent.getAsyncContext().getResponse();
PrintWriter out = response.getWriter();
out.write("TimeOut Error in Processing");
}
}
I need to get the ServletContext from inside a #ServerEndpoint in order to find Spring ApplicationContext and lookup for a Bean.
For the moment my best approach is to bind that bean in the JNDI naming context and lookup it in the Endpoint. Any better solution is welcome.
I'm also looking for a reasonable way to sync servlet's HttpSession with websocket's Session.
The servlet HttpSession is in JSR-356 available by HandshakeRequest#getHttpSession() which is in turn available when a handshake request is made right before #OnOpen of a #ServerEndpoint. The ServletContext is in turn just available via HttpSession#getServletContext(). That's two birds with one stone.
In order to capture the handshake request, implement a ServerEndpointConfig.Configurator and override the modifyHandshake() method. The HandshakeRequest is here available as method argument. You can put the HttpSession into EndpointConfig#getUserProperties(). The EndpointConfig is in turn available as method argument #OnOpen.
Here's a kickoff example of the ServerEndpointConfig.Configurator implementation:
public class ServletAwareConfig extends ServerEndpointConfig.Configurator {
#Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
config.getUserProperties().put("httpSession", httpSession);
}
}
Here's how you can use it, note the configurator attribute of the #ServerEndpoint:
#ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {
private EndpointConfig config;
#OnOpen
public void onOpen(Session websocketSession, EndpointConfig config) {
this.config = config;
}
#OnMessage
public void onMessage(String message) {
HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
ServletContext servletContext = httpSession.getServletContext();
// ...
}
}
As a design hint, it's the best to keep your #ServerEndpoint fully free of servlet API dependencies. You'd in the modifyHandshake() implementation better immediately extract exactly that information (usually a mutable Javabean) you need from the servlet session or context and put them in the user properties map instead. If you don't do that, then you should keep in mind that a websocket session can live longer than the HTTP session. So when you still carry around HttpSession into the endpoint, then you may run into IllegalStateException when you try to access it while it's being expired.
In case you happen to have CDI (and perhaps JSF) at hands, you may get inspiration from the source code of OmniFaces <o:socket> (links are at very bottom of showcase).
See also:
Real time updates from database using JSF/Java EE
Notify only specific user(s) through WebSockets, when something is modified in the database
Updated code for BalusC's answer, the onOpen method needs to be decorated with #OnOpen. Then there is no need anymore to extend the Endpoint class:
#ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {
private EndpointConfig config;
#OnOpen
public void onOpen(Session websocketSession, EndpointConfig config) {
this.config = config;
}
#OnMessage
public void onMessage(String message) {
HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
ServletContext servletContext = httpSession.getServletContext();
// ...
}
}
I tried out BalusC's answer on Tomcat (Versions 7.0.56 and 8.0.14). On both containers, the modifyHandshake's request parameter does not contain a HttpSession (and thus no servletContext).
As I needed the servlet context only to access "global" variables (web-application global, that is), I just stored these variables in an ordinary static field of a holder class. This is inelegant, but it worked.
That ooks like a bug in this specific tomcat versions - has anyone out there also seen this?
Somtimes we can't get session with above ServletAwareConfig of BalusC, this is because that the session is still not created. since we are not seek for session but servletContext, in tomcat we can do as below:
public static class ServletAwareConfig extends ServerEndpointConfig.Configurator {
#Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
try {
Field reqfld = request.getClass().getDeclaredField("request");
reqfld.setAccessible(true);
HttpServletRequest req = (HttpServletRequest) reqfld.get(request);
ServletContext ctxt = req.getServletContext();
Map<String, Object> up = config.getUserProperties();
up.put("servletContext", ctxt);
} catch (NoSuchFieldException e) {
} catch (SecurityException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
}
if we want init session immediately, we can call request.getSession().
Ref: Websocket - httpSession returns null
I have a tomcat6 servlet that manages incoming HttpPosts this way:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.getParameter("cmd") != null) {
eventPool.addEvent(new CommandEvent(new String[] { request.getParameter("cmd"),
request.getParameter("json") }, response));
}
}
The request will now be processed. When this is done, I want to write the result to the requesting client this way:
protected void sendResponse(HttpServletResponse httpResponse, String content){
try {
httpResponse.getWriter().println(CMD + "#" + content);
httpResponse.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
}
}
But it fails to flush and I get a NullPointerException because the HttpResponse was already closed.
How can I prevent the HttpResponse from flushing before I want it to?
You'll need to use Tomcat 7 (or any other container that supports Servlet 3.0 onwards) to use that style of programming. Look at the asynchronous request processing parts of the Servlet 3.0 specification.
Prior to Servlet 3.0, request/response processing is synchronous. i.e. you cannot 'park' a request/response pair and then handle them later in a different thread. Pretty much as soon as your doPost() method exits, Tomcat will recycle the request and response objects ready to use them to handle a new request.