Servlet thread safety: instance variables vs. request scope attributes [duplicate] - java

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.

Related

forward from doPost() to doGet() [duplicate]

This question already has answers here:
HttpServletResponse sendRedirect permanent
(2 answers)
Closed 7 years ago.
I am making a servlet for attendance. So in the doGet() method all the front end is displayed and if any error is generated ; i.e., something is left blank then the doPost() method should call the doGet() again for completing the blank spaces.
How can I call doGet() method from the same servlet's doPost()?
If I take your question literally (i.e. invoke doGet() from doPost()), you can just invoke the doGet() method... it's a standard method like any other.
Here's a tip: When the doPost() and doGet() methods share a common set of logic, it's a good practice to isolate that logic into a separate (private) method that is to be invoked by all relevant do***() methods. For example:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// GET-based logic
processCommonLogic();
// Other GET-based logic
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// POST-based logic
processCommonLogic();
// Other POST-based logic
}
private void processCommonLogic() /* throws ServletException and/or IOException if needed */ {
// Common logic
}
You can use this pattern to create a processError() method that can be invoked wherever you need it.
However, if the scope your question goes beyond invoking doGet() from doPost(), I suggest you have a look at the references pointed by Alain O'Dea.
You can do that, it's a simple
this.doGet(req, resp);
return;
However, it's not a best practice. Generally better to implement view logic as a JSP, and dispatch to it from the post logic...
this. getServletConfig().getRequestDispatcher("my_view.jsp")
.forward(req,resp);;
return;
Or use include(), or an MVC framework like Struts...

Is there only one servlet object per tomcat server?

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.

passing data btw classes using request.setParameter, request.getParameter

I have 2 java classes and I want to transfer data between them.
I take user id as parameter in a previous jsp form, and in a java class, using setAttribute I create a atribute named st_id.
then in another java clas I want to retrieve this data, but I get null.pointer exception.
first java file;
public class Signin implements Action {
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
Student stu = new StDAO().getUser(request.getParameter("st_id").toString());
request.setAttribute("st_id", request.getParameter("st_id").toString());
...
second;
public class addCourseStu implements Action{
#Override
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
TakeCourseDAO pf = new TakeCourseDAO();
String s= (String) request.getAttribute("st_id");
So s is null, it's not my intention.
A request exists from the time the web browser sends it to the web server until the web server (via the servlet) has made its response.Every request for a servlet has its own accessibilty scope. From a servlet, you can:
add new attributes to the request's scope
obtain exisiting attributes from the request's scope
remove exisiting attributes from the request's scope
As you are getting null it is quite obvious that the attribute is not accessed within the scope.
You can try alternatives like Session scope or Application scopes which ever suits you
It is not entirely clear what you want to do but I gather that you want to maintain some state on the server between two requests right?
Look into sessions & cookies for this.
What you do here is weird as it seems you are setting an attribute on an incoming request in the first file.

Is HttpServlet.class running as thread safe?

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.

Accessing a HashMap of custom objects in request scope after a redirect

I have a HashMap of custom objects being passed to a JSP using RequestDispatcher and I am able to access the object and its properties using JSTL.
However the code fails in case the parameter is sent using response.sendRedirect() .
I am not sure what the reason is and how to make it work?
The response.sendRedirect() basically instructs the client (the webbrowser) to send a new request on the given URL. You'll also see this being reflected by a change in the browser address bar.
A new request does of course not contain the attribtues of the previous (or any other) request. That would otherwise have broken the whole concept of "request scope".
To preprocess a GET request, you need to do the job in doGet() method of a servlet and then redirect to the URL of that servlet instead.
E.g.
response.sendRedirect(request.getContextPath() + "/foo");
and
#WebServlet("/foo")
public class FooServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, Foo> foos = fooService.map();
request.setAttribute("foos", foos);
request.getRequestDispatcher("/WEB-INF/foo.jsp").forward(request, response);
}
}
Note that this problem is in no way related to having a hashmap of custom objects in the request scope.
See also:
Our servlets wiki page
You can not share a request attribute in response.sendRedirect as it creates a new request.
But, if you want that HashMap, in response.sendRedirect, you can put that in session like
request.getSession().setAttribute("myMap", [HashMap object]);
and can share between the servlet and JSP. This works in both RequestDispatcher and sendRedirect.

Categories

Resources