I know one Servlets instance is shared by multiple threads for handling concurrent requests. Inside the servlets, I will call other thread-safe classes: ExternalClassOne which in turn calls ExternalClassTwo.
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ExternalClassOne cOne = new ExternalClassOne();
cOne.doSomething();
//doSomething() will also use other classes like ExternalClassTwo, ExternalClassThree...
}
}
I have some questions:
How many instances of the classes ExternalClassOne, ExternalClassTwo will be created?
If they are created per thread for each request (e.g., 100 concurrent requests = 100 instances of ExternalClassOne), does making them singleton increase the performance? Does Tomcat have any "magic" to reuse thread-safe instance where possible?
Instances are created on each execution of new, as already stated in the comment above.
Careful with singletons: To obtain the instance needs a synchronized method invocation, and this ruins the responsiveness of your application.
Tomcat does not provide any such means afaik, but the Java library. You may use ThreadLocals.
Otherwise, create the (thread-safe) classes in a ContextListener on application startup and put them in the app context, so each servlet can get them from there.
Related
Before adding CDI into our application I had created a resource that used the #Suspended AsyncResponse object to implement long polling for a chat client. What I did was create a new newSingleThreadExecutor() and submit a Runnable to it that used .wait(30000) on a message list until notification that a new message was sent. Inside that task I used the HttpServletRequest which was obtained using #Context and everything worked perfectly.
However once we added CDI to our application and even without making the resource class a bean (scanning only annotated beans and we didn't give it any scope annotation) I got a runtime exception that the request object INSIDE the Runnable task couldn't be accessed because of an illegal state exception:
Method threw 'java.lang.IllegalStateException' exception. Cannot evaluate com.sun.proxy.$Proxy74.toString()
I'm not really sure why this happens but I know it is CDI related since it refers to a proxy object. One guess is that the resource class itself has become CDI scoped and that scope can't be accessed from a different thread? I read somewhere that manually started threads are not managed and thus can't have access to any scope related objects. However how did this use to work until CDI was implemented?
Right now I THINK I've solved the issue (that is releasing the thread servicing request I/O and having a worker take over the waiting until notified) using jersey's #ManagedAsync annotation which supposedly has the whole method be run in an internal jersey executor service. Is this correct? Also in that case, is there any need of the AsyncResponse object?
EDIT: I have NOT solved the issue. #ManagedAsync worked when the resource class was not defined as a CDI bean. After making it #RequestScoped, whenever I try to call the method I get the following exception
org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
I think this is because the request can end before the async thread has finished which means all scope objects (like HttpServletRequest) will be destroyed and thus we won't have access to them. Is there a way to used #ManagedAsync in a #RequestScoped bean and make use of #Context HttpServletRequest??
TL;DR:
How can I have access to a context resource inside a manually started thread?
Why did I have access to the request object before CDI was implemented?
Is it possible to use #ManagedAsync in a #RequestScoped cdi bean?
Old method:
#GET
#Path("method")
public void method(#Context HttpServletRequest request, #Suspended AsyncResponse ar) {
//request object was accessible here
Executors.newSingleTHreadExecutor().submit(() -> {
//request object was also accessible here but lost access after implementing CDI.
Object o = foo.bar(request);
ar.resume(Response.ok(o).build());
});
}
Current non-working method:
#GET
#Path("method")
#ManagedAsync
public void method(#Context HttpServletRequest request, #Suspended AsyncResponse ar) {
Object o = foo.bar(request);
ar.resume(Response.ok(o).build()); //Is there any point to this?
}
To answer your question - no. You cannot use async and request scoped objects. Async support is lacking in CDI - see also https://issues.jboss.org/browse/CDI-452
I have copied some code (the example is taken from
http://hmkcode.com/java-servlet-send-receive-json-using-jquery-ajax/
it works but there is something I do not understand:
I cannot understand how the "articles" retains provious values
Is it not reinitialized with each call of the servlet ?
public class JSONServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// This will store all received articles
List<Article> articles = new LinkedList<Article>();
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// 1. get received JSON data from request
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String jsonString = "";
if (br != null){
jsonString = br.readLine();
}
ObjectMapper mapper = new ObjectMapper();
Article article = mapper.readValue(jsonString, Article.class);
response.setContentType("application/json");
articles.add(article);
mapper.writeValue(response.getOutputStream(), articles);
}
}
The application server usually maintains a single instance of a servlet, so declaring the List<Article> articles as attribute in your servlet class will work as a container for the entire application for your articles.
Note that this approach should be for testing purposes only. An real world application designed like this will fail because a servlet is accessed by multiple threads at the same time and several requests on the same URL attended by your servlet that try to add the data into this unsynchronized list will raise ConcurrentModificationException.
In case you want/need to store data per client (browser), use session scope. In case you want/need to store data per application (available to all clients of your application), use application scope.
More info:
How do servlets work? Instantiation, sessions, shared variables and multithreading
How to pass parameter to jsp:include via c:set? What are the scopes of the variables in JSP? (which contains an explanation on variable scopes in web applications)
Only one servlet instance is created at any given time in a web app. Because the articles field is stored as a member variable, (not local to the method), it is shared among all requests (and all threads). This is typically a very bad practice as you could run into concurrency issues with multiple threads attempting to access the same data structure at the same time and security issues with users able to access data they perhaps shouldn't have access to.
The correct way to do this would be to use session to store data that should be private to each user, or use something like Spring's SessionScopedProxy support.
I am not an expert on Java servlets, but once the servlet is initialized (a.k.a your JSONServlet class), the articles List is initialized, and subsequent calls of doPost via clients are appending to the articles list, it is not being re-initialized every time a POST is called. It will only be deleted and re-initialized when you restart your servlet.
I have a single-instance class, implementing ExceptionMapper. It's not a static class, but it's a class for which I know only single instance is created (I checked - constructor is called only once).
My class uses #Context HttpServletRequest, and I can clearly observe that when my ExceptionMapper.toResponse() method is called, the #Context 'request' parameter has a value which is relevant for a request where the exception is thrown.
The doc says this is indeed by-design supported feature and that it's done by using "proxies".
I wonder how exactly this is implemented - how a single instance can have different member variable values simultaneously?
Thank you,
AG
P.S.: here's the test code:
#Provider
public class MyExceptionMapper implements ExceptionMapper<Exception> {
public MyExceptionMapper() {
System.out.println("CTOR!!");
}
#Context HttpServletRequest req;
public static boolean done = false;
public Response toResponse(Exception ex) {
if (!done) {
done = true;
Thread.sleep(10000);
}
System.out.println(req.getRequestURI());
return null;
}
}
My REST handler method throws exception, so when I execute the following 2 requests "in parallel" (the sleep above makes sure first one is not finished when second one arrives and IMHO should modify the one-and-only 'req' field):
- http://localhost/app/one
- http://localhost/app/two
my program prints:
CTOR!
http://localhost/app/one
http://localhost/app/two
The simplest method of achieving the effect you observe is for the injected HttpServletRequest object to actually be a proxy object, a thread-aware delegate for the real HttpServletRequest. When you call methods on the delegate, all they do is look up the correct real object (e.g., via a thread local variable) and pass the call onto that. This strategy is relatively simple to get right, and as it is an interface we definitely don't have to worry about field accesses (which are quite a bit trickier to proxy for).
There's a few different ways to construct such a proxy object. In particular, it could be done by directly implementing the HttpServletRequest interface, or it could be done more generically via the Java general dynamic proxy mechanism (which can construct a proxy for any interface). There are other more elaborate possibilities such as runtime code generation, but they're unnecessary here. OTOH, I wouldn't be at all surprised if HttpServletRequest was directly implemented; it's a somewhat important class for a JAX-RS implementation…
Talking Java Servlets here... I'm working on creating my own "Per Request Context" and I was looking to tie the "Per Request Context" object to the Thread.currentThread().getId() value.
Instead of passing around this context object everywhere I was planning on checking the current threadid when a user calls a function that is Per Request based and automatically getting the Context object out of a hashtable for that threadId.
I would use the code this like..
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
MyFramework.EnterContext();
try {
// do stuff here that leads to other classes on the same thread
// Access current context via static MyFramework.getCurrentContext()
}
finally { MyFramework.ExitContext(); }
}
However I would like to protect my application automatically from any potential user that does not call ExitContext(). In C# there is an event handler on the thread object for onexit...(think I wrong on this) is there some way to detect or poll when a thread exits? I'm currently storing only the threadId (long).
Any ideas?
unfortunatelly, there is no such feature built in for threads in Java. Besides, thread id is only guaranteed to be unique at any one time, but may be reused eventually when the thread dies (from the docs). however, the servlet framework that you are using may be implementing such feature (just a speculation).
i would recommend you implement a servlet filter, and tell your users to include it in their web.xml. with this you can be sure the client code always gets correctly wraped in your thread context.
A ThreadLocal seems to fit your use perfectly. A ThreadLocal object can provide a way to store a variable per thread. The internal workings of this class are very much of what you describe, it uses a map to give thread-local variables.
Something like this should do the trick:
private static final ThreadLocal<UserContext> userContext = new ThreadLocal<UserContext>();
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MyFramework.EnterContext();
try {
UserContext context = userContext.get();
//if you used the set method in this thread earlier
//a thread local context would be returned using get
}
finally { MyFramework.ExitContext(); }
}
As for your other problem, you can use an observer pattern and notify when the thread completes its task.
I have a class public class GAE_SERVLETREQUESTServlet extends HttpServlet {
Not sure what the spec says about recycling of the HTTPServlet: Should the servlet container create new instance of this class on each incoming request or can the implementation reuse classes between requests?
I'm investigating a funny issue where it seems that a Map created on the GAE_SERVLETREQUESTServlet instance maintains state between requests.
For the general case - non-distributed, multi-threaded, it is guaranteed that there will be only one instance of the servlet. From the Servlet 3.0 specification:
2.1 Request Handling Methods
The basic Servlet interface defines a service method for handling client requests.
This method is called for each request that the servlet container routes to an instance
of a servlet.
The handling of concurrent requests to a Web application generally requires that the
Web Developer design servlets that can deal with multiple threads executing within
the service method at a particular time.
Generally the Web container handles concurrent requests to the same servlet by
concurrent execution of the service method on different threads.
2.2 Number of Instances
The servlet declaration which is either via the annotation as described in Chapter 8,
“Annotations and pluggability” or part of the deployment descriptor of the Web
application containing the servlet, as described in Chapter 14, “Deployment
Descriptor”, controls how the servlet container provides instances of the servlet.
For a servlet not hosted in a distributed environment (the default), the servlet
container must use only one instance per servlet declaration. However, for a servlet
implementing the SingleThreadModel interface, the servlet container may
instantiate multiple instances to handle a heavy request load and serialize requests
to a particular instance.
In the case where a servlet was deployed as part of an application marked in the
deployment descriptor as distributable, a container may have only one instance per
servlet declaration per Java Virtual Machine (JVM™)1. However, if the servlet in a
distributable application implements the SingleThreadModel interface, the container
may instantiate multiple instances of that servlet in each JVM of the container.
If you are saving data that is relevant to each user, you should store it in the HTTP Session. As stated by skaffman, do not store data in the servlet class that you expect to be different for each each user.
Here is a quick example.
class MyServlet extends HttpServlet
{
private Object ThisIsTheWrongPlaceToStorePerUserData;
... stuff ... doPut(HttpServletRequest httpRequest, ... more stuff ...)
{
Object iAmGood = new Object();
HttpSession session = httpRequest.getSession(true);
session.setAttribute("GoodPlaceToStorePerUserData", iAmGood);
... stuff ...
}
}