I did a simple test, and executed my test servlet below from different browsers and command line tools. Regardless where I execute it, it displays the same "hohoho time". I am absolutly fine with this, I just want to know two things:
a) if I can rely on this on different versions of tomcat (today I use version 7.54)
b) does this mean a servlet is never executed parallel in different threads (i.e. two users are requesting the very same servlet at the very same time)
#WebServlet(name="test servlet 2", urlPatterns={"/test2"}, loadOnStartup=1)
public class Test2 extends HttpServlet {
private String test = "baaa .. :-(";
#Override
public void init(ServletConfig config) throws ServletException {
DEBUG("Server starts");
this.test = "hohoho " + System.currentTimeMillis();
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletOutputStream out = resp.getOutputStream();
out.write(test.getBytes());
out.flush();
out.close();
}
}
Indeed servlet container creates only one instance of each servlet. This helps you to hold state in servlet member variables. This however does not mean that servlet container cannot use the same servlet instance to process serveral requests simultaneously. This is what servlet contains (including Tomcat) does. Therefore your servlet must be thread safe. Please refer to this article for details: http://tutorials.jenkov.com/java-servlets/servlet-concurrency.html
I have below view on your question.
a) Yes,atleast from tomcat 7 onwards.For tomcat 6(annotation support),you will have to check the specs.
b) Regarding execution in parallel thread,servlet not threadsafe by default,to ensure thread safety,Servlet service() method should not access any member variables, unless these member variables are thread safe themselves.
For more on thread safety : Servlet Thread Safety
There is only one instance of each HttpServlet and its init() method is only called once when the HttpServlet is constructed. However the doGet() is called in a different thread for each request and can happen in parallel.
Related
This question already has answers here:
How do servlets work? Instantiation, sessions, shared variables and multithreading
(8 answers)
Passing an object from JSP page back to Servlet
(3 answers)
How can I store state for an individual browser tab/window?
(2 answers)
Closed 5 years ago.
This is the sample code, illustrating the usage of instance variable and request attribute:
#WebServlet(name = "Upload", urlPatterns = {"/upload"})
#MultipartConfig()
public class Upload extends HttpServlet {
private String txt;
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try{
String txt2 = (String) request.getAttribute("txt2");
//txt and txt2 variables are available for processing.
..........
} finally {
txt = null;//Prepare variable for next request.
}
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
.....
request.setAttribute("txt2", someValue);
//vs
txt = someValue;
processRequest(request, response);
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
.....
processRequest(request, response);
}
}
Now I know that instance variables should be never declared in servlets, because the same servlet is shared between concurent requests. But what about request attributes? Is it safe to use them?
Not sure what you mean by initializing variables. On a servlet you mean or where?
Depending on what you need to do, maybe there are APIs involved in which you don't have control about the contracts (since they are not your APIs).
For example, when requesting is being processed by a filter chain. Maybe one filter is interested on data being set by a filter executed previously in the chain. You don't have means to pass data on the filter chain contract. Request attribute might be an option. Playing with ThreadLocal might be another, but it has performance implications and is not always suitable.
Another example, maybe you forward one request from a servlet to another. Again, you will not have an option to play with whatever member variable you define.
Also, if you were thinking on adding member variables on a servlet, remember Servlets are not thread-safe. What would you do? synchronize request processing, only one request being processed per servlet?
Your servlet is going to serve hundreds on concurrent request each one of them will use same shared instance of the servlet, therefore using private instance variable will lead to inconsistent behavior. Unless you synchronize access to it, which will lead to greater contention on access to this private field, therefore slowing down response time, producing bottleneck.
While storing attribute within binded to single request will allow you to avoid such problem, moreover it was designed to overcome issues similar to yours.
How does the Tomcat container calls service(ServletRequest req, ServletResponse res) method?
Where can I look for implementation of this call?
How can I see how this req and res objects (that passed to service() method) are created?
Although the Tomcat Architecture page is in TODO status, there is at least a link to the UML sequence diagram of the request processing. Combining with Tomcat's source code, it is a good starting point.
If you want to know it, first clone apache tomcat source code
git clone https://github.com/apache/tomcat.git
Then inside the cloned repository, launch this command to search where it is invoked service method:
grep -H -n -r "\.service(" --include=*.java
You will find a short file list:
java/javax/servlet/jsp/PageContext.java:107: * in this PageContext until the return from the current Servlet.service()
java/org/apache/catalina/connector/Request.java:3128: // that set towards the start of CoyoyeAdapter.service()
java/org/apache/catalina/core/ApplicationFilterChain.java:231: servlet.service(request, response);
java/org/apache/catalina/servlets/DefaultServlet.java:411: super.service(req, resp);
java/org/apache/catalina/servlets/WebdavServlet.java:349: super.service(req, resp);
java/org/apache/coyote/ajp/AjpProcessor.java:403: getAdapter().service(request, response);
java/org/apache/coyote/AsyncStateMachine.java:41: * been called during a single Servlet.service() method. The
java/org/apache/coyote/AsyncStateMachine.java:58: * been called during a single Servlet.service() method. The
java/org/apache/coyote/http11/Http11Processor.java:498: getAdapter().service(request, response);
java/org/apache/coyote/http2/StreamProcessor.java:257: adapter.service(request, response);
java/org/apache/jasper/Constants.java:41: * HttpJspBase.service(). This is where most of the code generated
java/org/apache/jasper/servlet/JspServlet.java:385: wrapper.service(request, response, precompile);
java/org/apache/jasper/servlet/JspServletWrapper.java:440: servlet.service(request, response);
java/org/apache/jasper/servlet/JspServletWrapper.java:443: servlet.service(request, response);
The most intresting one is java/org/apache/catalina/core/ApplicationFilterChain.java. You wil find more coincidences, but much of them are because there is another interface into Tomcat source code that has a very similar method java/org/apache/coyote/Adapter.java ignore it.
Once you get java/org/apache/catalina/core/ApplicationFilterChain.java, you can edit, got to line 231 and see where the service method is called.
However, both req and res objects are not created in that place. Finding how those are created seems to be a bit more complex and requires more time.
Servlet lifecycle is controlled by the underlying container. Once the servlet has been initialized and there is a request, Tomcat will call the servlet's service method to process the request.
Service method will delegate request to your Servlet class where you can get access to req and res objects in doGet or doPost methods.
public void doGet(HttpServletRequest req, HttpServletResponse res){
}
Update :
1. Upon request from the client, Container creates two objects : HttpServletRequest and HttpServletResponse.
2. Based on the request, Container will find correct Servlet (as per URL mapping), creates new thread for that particular request(one-to-one mapping - new thread for each request) and calls Servlet's service method, passing in created HttpServletRequest and HttpServletResponse objects as arguments.
3. Based on request method (GET or POST) service() method will call doGet() or doPost() method in Servlet, again passing the same HttpServletRequest and HttpServletResponse objects as arguments.
Those are Servlet specifications in a nutshell. How does Tomcat act exactly is implementation specific, it is not controlled by specification. If you need to know how exactly it is implemented in Tomcat, you might check it's source code.
As far as I know Java Servlets are handling multiple requests concurrently and I've searched through StackOverflow as well as Google, and confirmed what I thought. However I am quite confused right now, I wrote a simple servlets that seem to show blocking behaviour.
so I have a simple Servlet:
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID = 2628320200587071622L;
private static final Logger logger = Logger.getLogger(MyServlet.class);
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
logger.info("[doGet] Test before");
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("[doGet] Test after");
resp.setContentType("text/plain");
resp.getWriter().write("OK");
}
}
Then I have 2 browser windows, I opened at the same time that hit my Servlet.
And the result is the first request blocking the 2nd one. The log also shows:
10:49:05,088 [http-8383-Processor14] INFO MyServlet - [doGet] Test before
10:50:05,096 [http-8383-Processor14] INFO MyServlet - [doGet] Test after
10:50:05,106 [http-8383-Processor22] INFO MyServlet - [doGet] Test before
10:51:05,112 [http-8383-Processor22] INFO MyServlet - [doGet] Test after
I feel like I am missing something ... Servlets supposed to be able to handle concurrent request, but it doesnt seem to be doing it. I also did the same as above on the service method instead of doGet and it does the same thing.
Any pointers?
Thanks
Your browser is apparently using the same HTTP connection in different windows. The servlet container uses a single thread per HTTP connection, not per HTTP request. You should run two physically different webbrowsers to test this properly. E.g. one Firefox and one Chrome.
Say, I have a TestServlet class which declared as a global string variable. If there are simultaneous calls to TestServlet class, will this 'message' string variable safe to print without crossing from concurrent threads?
public class TestServlet extends HttpServlet {
private String message;
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
OutputStream os = response.getOutputStream();
message = UUID.randomUUID().toString();
os.write(message.getBytes());
}
}
A servlet is shared across requests, so no, it is not thread-safe. A servlet object is scoped to a <servlet> declaration in web.xml.
The container creates an instance of the <servlet-class> for each servlet and calls its init method.
Some references here:
http://www.coderanch.com/t/473015/Servlets/java/Servlet-thread-safe
http://docs.oracle.com/javaee/5/tutorial/doc/bnafu.html
Generally, if you are storing state in a servlet, you should consider alternatives. Why store state there? What is the state tied to? Is it tied to the request? If so, use request.setAttribute. If it is meant to be shared across multiple requests by the same user, use request.getSession().setAttribute. If it should be shared by all requests, then request.getServletContext().setAttribute.
If the state is specific to a single request and only within the execution of this servlet, then it should just be a local variable in your method. For example:
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
OutputStream os = response.getOutputStream();
String message = UUID.randomUUID().toString();
os.write(message.getBytes());
}
Notice there are no non-local variables here. That is fairly thread-safe.
No, servlets are not thread-safe. Servlets should not expose any sort of mutable state in this manner.
Is there a simple way to change the default name that Tomcat gives its worker threads? Ideally, I'd like all the threads associated with a certain web application to have it in their name, perhaps as a prefix. For example, if I deploy app1.war and app2.war, I'd like all the threads for app1 to have app1 in their name.
From a forum, reposted here with a bit better formatting; a simple filter, that renames each thread to the request URI, in this example:
public class ThreadNameFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain fc) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest)req;
final Thread curThr = Thread.currentThread();
final String oldName = curThr.getName();
// change the name of the current thread to something related
// to the application (e.g. URI)
try {
curThr.setName(httpReq.getRequestURI());
fc.doFilter(req, resp);
} finally {
curThr.setName(oldName);
}
}
}
Threads are only temporarily "associated" with a particular webapp.
I can't imagine why this would be something useful at all:
Logging should include context information (e.g. what component is logging)
Profiling can easily reveal what code is being executed (and thus reveal the webapp that is "running")
Exceptions include full stack traces and can reveal what code was being executed (and thus reveal the webapp that was "running")
Long-running threads can be inspected via a thread-dump (and thus reveal the webapp that is "running" in the long-running request)