Start & Stop a ScheduledExecutorService in Java EE environment using servlet - java

We have a requirement where in we need to monitor remote JVM details via JMX using a simple servlet application. So things done till now in a standalone application is
1) Creat a JMX connector & get the Memory data --> done
2) We need to constantly monitor & get the records
(2.1 > which can be considered as scheduled task at constant delay & insert the records into DB Or 2.2> does the JMX gives the history if yes which MBean to access for the info?).
Here I am planning to use an interface to register the domain , followed to it. Have start & stop button from JSP. The functionality been when we click start the system will run the scheduler (ScheduledExecutorService ) & capture the records at background to give the history. When the uses clicks stop the scheduler has to stop the background process. The question is how can we control & get the object of a scheduler ?
1) In other words how can we start & stop a ScheduledExecutorService via servlets ? start a thread from one servlet & stop a thread from another servlet for a particular task ?
2) What if we have a clustered/load balanced environment ?
Currently am thinking of adding each ScheduledExecutorService into HashMap , key been the task object & value been the ScheduledExecutorService using SingleTon desgin pattern. Is there any default approach. The loop whole with SingleTon is in clustered/load balanced environment we may not be able to get the appropriate update objects.
Looking forward for your valuable suggestion.

If on java ee 7, try using the javax.enterprise.concurrent.ManagedScheduledExecutorService
Then you can do a resource injection and start the task with code similiar to below.
#Resource
ManagedScheduledExecutorService mses;
public void startTask() {
mses.scheduleAtFixedRate(runnable, 10, 10, SECONDS);
}
In Java EE 6 you could have a servlet create/remove using the timerservice API

ServletContext
A ServletContext represents your entire web app as running in the Servlet container. The context is established before the first HTTP request is processed by the servlet, as promised by the Servlet spec. While a HttpSession represents each user’s work session (technically, a thread through your servlet code), a ServletContext represents the scope across all those users.
To hook into the set-up and tear-down of the servlet context, implement a ServletContextListener. Tip: Auto-deploy your listener by marking it with #WebListener annotation. That interface requires a pair of methods, each called when your web app is being set-up before the first Servlet request is handled and when your web app is being torn-down.
Tip: That tear-down method is a good place to shut down your ScheduledExecutorService. The threads associated with your executor service may survive after your web app ends. You likely do not want that to happen.
See this Question: How to get and set a global object in Java servlet context
See also this nice summary of Servlet scope by BalusC.
Fetch the servlet context
You can access the current servlet’s ServletContext by first accessing its ServletConfig.
// … in your Servlet, such as its request handler methods like `doGet` …
ServletContext sc = this.getServletConfig().getServletContext() ;
And how about in a ServletContextListener, how do we access the servlet context? When either of the two methods on the listener is called, a ServletContextEvent is passed. From there call ServletContextEvent::getServletContext().
Store objects as ”attributes” on servlet context
So where to store your web app’s global variables such as your ScheduledExecutorService? The servlet context has a built in map of String to Object. These are called “attributes”. Call setAttribute( String , Object ) to store an attribute mapping. So make up a name for your ScheduledExecutorService to use a key into this map.
ScheduledExecutorService sec = … ; // Instantiated somewhere in your code.
…
String key = "myScheduledExecutorServiceForXYZ" ;
sc.setAttribute( key , sec ); // Storing the executor service as a generic `Object` for use later.
Later you can fetch your ScheduledExecutorService in the same manner. You will need to cast from Object to the known class, in this case ScheduledExecutorService.
Object o = sc.getAttribute( key ); // Fetch the `ScheduledExecutorService` from the servlet context’s built-in map of attributes.
// Cast to known class. If in doubt, test first with [`instanceOf`][11].
ScheduledExecutorService sec = ( ScheduledExecutorService ) o ;
You can ask for a list of all stored attribute names by calling ServletContext::getAttributeNames.
Diagram of scope
Here is a diagram of mine to get an idea of the hierarchy of scope in a Servlet environment. Note how each layer of scope has its set of attributes, its own map of String to Object. As you go down the diagram, each set of attributes has a shorter lifespan.

Related

Is running a long-running-process always bad on a servlet thread having a single HTTP request? [duplicate]

This question already has answers here:
How to run a background task in a servlet based web application?
(5 answers)
Closed 6 years ago.
I have a Java application (running on WAS 8.5) which acts as a kind of server inventory for the client. The application has a servlet that triggers a long running process.
The process: fetches data from a third party DB, executes Java logic, writes records back to the application's own DB (these DB connections are pooled ones) .
The servlet is not load-on-startup and is manually triggered only once a month by a single Operations guy (on some particular date based on the client's choice each month). The servlet had been historically using Timer and TimerTask in this way:
public class SyncMissingServlet extends HttpServlet implements Servlet{
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
try{
SyncMissing.runSync();
}catch(Exception ex){
logger.error(new LogMessage("ERROR: "), ex);
this.sendReply(printWriter, "ERROR: " + ex.toString());
}
}
}
public class SyncMissing
{
public static void runSync() throws Exception
{
Timer t = new Timer(true);
SyncMissingTask task = new SyncMissingTask(); //SyncMissingTask is an instance of TimerTask
// Start the synchronization 5 secs from now, and run it every 30 days.
t.schedule(task, 5000, 2592000000l); //this 30 day timings never really worked out for the client,
//since the app server is restarted frequently for deployments.
}
}
There is no use of Timer.close() or TimerTask.close() in the current code.
Recently this Servlet seems to have got auto-trigerred, after a system reboot and restart of the WAS services on the system...and that's the worry.
While I couldn't explain the auto-trigger to my client, I proposed the following options:
1. drop off the use of Timer and TimerTask (the long-running process then runs on the servlet's thread itself)
2. instead of TimerTask, make it a regular Runnable and run it in a separate thread within the servlet thread.
3. make use of Java's Executor Service
4. migrate to Servlet 3.0 and turn the servlet into a Async servlet.
5. drop off the servlet altogether, and replace it with a batch job.
I understand that options 3 and 4 are really the recommended ones (or possibly option 5). But I have a feeling, that in my business scenario - Options 3 & 4 may be an overkill.
If the need is really a manual invocation of the servlet by only one user per month, are options 1 and 2 that bad?
(my client wants the quickest solution and would certainly not fund option 5)
Well, if the servlet is supposed to be run only once in a month and there is only one client triggering it, it is fine to run it in the servlet's thread itself or create a new thread inside the servlet and let that do the task. The question of load and response times arises when you have a lot of clients making simultaneous requests, at which point you might want to use an Executor service or an async servlet.
There is no need to activate a background task by invoking a servlet. Your web app has its own lifecycle. The Servlet spec provides hooks for your web app getting set-up and torn-down. Perfect place to launch and quit your background task without ever invoking a servlet by a client user.
No need to depend on a human user remembering to start the background task. Let your web app technology do the work for you.
Also, you may often hear/read "never launch threads from JSP or Servlet". This is worthy advice with regard to processing each incoming request for generating a web page. But background tasks (not directly related to a single servlet request) is a different animal; perfectly okay to have threads for background tasks as long as you handle them properly. By 'properly' I mean you explicitly end those threads appropriately, and you handle thread-safety issues. An example of a background task might be regularly polling a web service or database to refresh a cache of data.
ServletContextListener
If you want an automated task to be performed regularly within your web app, use a ServletContextListener.
That interface defines a pair of methods. One, contextInitialized, is called automatically when the web app launches, guaranteed to run before any HTTP requests are handled. The other method, contextDestroyed, runs when the web app is being torn down.
Tip: Marking your listener with a #WebListener annotation will cause your Servlet container to automatically notice it and instantiate when the web app is launched.
Beware of a nasty bug when doing development with NetBeans & Tomcat (development problem only, not a problem in deployment) where the web app does a double launch.
ScheduledExecutorService
In your custom class implementing that interface, in contextInitialized, establish a ScheduledExecutorService object to run your task repeatedly. In contextDestroyed, shutdown that executor. This is very important as the thread(s) of that executor will survive the shutdown of your web app and even the servlet container.
The ScheduledExecutorService technology supplants the Timer and TimerTask classes. These classes are especially not recommended for use in a Java Servlet environment.
You can store a reference to the executor in your listener object.
#WebListener
class MonthlyTaskRunner implements ServletContextListener {
private ScheduledExecutorService scheduledExecutorService;
void contextInitialized(ServletContextEvent see) {
// initialize your ScheduledExecutorService.
// The ScheduledExecutorService will use one or more threads for its work outside of this thread running now.
this.scheduledExecutorService = … ;
}
void contextInitialized(ServletContextEvent see) {
// Shutdown the executor along with its thread(s).
this.scheduledExecutorService.shutDown();
}
}
I and others have posted on this extensively here on Stack Overflow, such as this. So search Stack Overflow. I have posted extensive examples in the context of Vaadin web apps, but the principles apply to any servlet web app. And see the Oracle Tutorial on Executors.
Where to store a reference to your ScheduledExecutorService once instantiated? You could store in a member variable on your context listener. But a more accessible place would be as an "attribute" on the servlet context. I describe this in detail along with example code and a nifty diagram in my Answer to another Question: Start & Stop a ScheduledExecutorService in Java EE environment using servlet
YearMonth
In that executor task, get the year-month of the current date for the time zone of your business context. Compare that year-month to one recorded when the task was last performed. Record that year-month somewhere, in a file, in a database, someplace.
Schedule your ScheduledExecutorService to run more often than necessary. Rather than worry about scheduling out a month, just let it run everyday. The check to compare current YearMonth with stored year-month requires nearly no execution time. KISS.
Java includes a YearMonth class.
YearMonth ymThen = YearMonth.parse( "2016-11" ); // Retrieve that string from storage.
ZoneId z = ZoneId.of( "America/Montreal" );
YearMonth ymNow = YearMonth.now( z );
if( ymNow.isAfter( ymThen ) ) {
// … run the task
String ymOutput = ymNow.toString();
// … write that `ymOutput` string someplace in storage.
} // Else do nothing. Let the ScheduledExecutorService run again after its designated rest period.
Similar Questions
Background timer task in JSP/Servlet web application
How to run a background task in a servlet based web application?

request.getSession().setAttribute() & .getAttribute("reqid") thread safety [duplicate]

Suppose, I have a webserver which holds numerous servlets. For information passing among those servlets I am setting session and instance variables.
Now, if 2 or more users send request to this server then what happens to the session variables?
Will they all be common for all the users or they will be different for each user?
If they are different, then how was the server able to differentiate between different users?
One more similar question, if there are n users accessing a particular servlet, then this servlet gets instantiated only the first time the first user accessed it or does it get instantiated for all the users separately?
In other words, what happens to the instance variables?
ServletContext
When the servlet container (like Apache Tomcat) starts up, it will deploy and load all its web applications. When a web application is loaded, the servlet container creates the ServletContext once and keeps it in the server's memory. The web app's web.xml and all of included web-fragment.xml files is parsed, and each <servlet>, <filter> and <listener> found (or each class annotated with #WebServlet, #WebFilter and #WebListener respectively) will be instantiated once and be kept in the server's memory as well, registred via the ServletContext. For each instantiated filter, its init() method is invoked with a new FilterConfig argument which in turn contains the involved ServletContext.
When a Servlet has a <servlet><load-on-startup> or #WebServlet(loadOnStartup) value greater than 0, then its init() method is also invoked during startup with a new ServletConfig argument which in turn contains the involved ServletContext. Those servlets are initialized in the same order specified by that value (1 is 1st, 2 is 2nd, etc). If the same value is specified for more than one servlet, then each of those servlets is loaded in the same order as they appear in the web.xml, web-fragment.xml, or #WebServlet classloading. In the event the "load-on-startup" value is absent, the init() method will be invoked whenever the HTTP request hits that servlet for the very first time.
When the servlet container is finished with all of the above described initialization steps, then the ServletContextListener#contextInitialized() will be invoked with a ServletContextEvent argument which in turn contains the involved ServletContext. This will allow the developer the opportunity to programmatically register yet another Servlet, Filter or Listener.
When the servlet container shuts down, it unloads all web applications, invokes the destroy() method of all its initialized servlets and filters, and all Servlet, Filter and Listener instances registered via the ServletContext are trashed. Finally the ServletContextListener#contextDestroyed() will be invoked and the ServletContext itself will be trashed.
HttpServletRequest and HttpServletResponse
The servlet container is attached to a web server that listens for HTTP requests on a certain port number (port 8080 is usually used during development and port 80 in production). When a client (e.g. user with a web browser, or programmatically using URLConnection) sends an HTTP request, the servlet container creates new HttpServletRequest and HttpServletResponse objects and passes them through any defined Filter in the chain and, eventually, the Servlet instance.
In the case of filters, the doFilter() method is invoked. When the servlet container's code calls chain.doFilter(request, response), the request and response continue on to the next filter, or hit the servlet if there are no remaining filters.
In the case of servlets, the service() method is invoked. By default, this method determines which one of the doXxx() methods to invoke based off of request.getMethod(). If the determined method is absent from the servlet, then an HTTP 405 error is returned in the response.
The request object provides access to all of the information about the HTTP request, such as its URL, headers, query string and body. The response object provides the ability to control and send the HTTP response the way you want by, for instance, allowing you to set the headers and the body (usually with generated HTML content from a JSP file). When the HTTP response is committed and finished, both the request and response objects are recycled and made available for reuse.
HttpSession
When a client visits the webapp for the first time and/or the HttpSession is obtained for the first time via request.getSession(), the servlet container creates a new HttpSession object, generates a long and unique ID (which you can get by session.getId()), and stores it in the server's memory. The servlet container also sets a Cookie in the Set-Cookie header of the HTTP response with JSESSIONID as its name and the unique session ID as its value.
As per the HTTP cookie specification (a contract any decent web browser and web server must adhere to), the client (the web browser) is required to send this cookie back in subsequent requests in the Cookie header for as long as the cookie is valid (i.e. the unique ID must refer to an unexpired session and the domain and path are correct). Using your browser's built-in HTTP traffic monitor, you can verify that the cookie is valid (press F12 in Chrome / Firefox 23+ / IE9+, and check the Net/Network tab). The servlet container will check the Cookie header of every incoming HTTP request for the presence of the cookie with the name JSESSIONID and use its value (the session ID) to get the associated HttpSession from server's memory.
The HttpSession stays alive until it has been idle (i.e. not used in a request) for more than the timeout value specified in <session-timeout>, a setting in web.xml. The timeout value defaults to 30 minutes. So, when the client doesn't visit the web app for longer than the time specified, the servlet container trashes the session. Every subsequent request, even with the cookie specified, will not have access to the same session anymore; the servlet container will create a new session.
On the client side, the session cookie stays alive for as long as the browser instance is running. So, if the client closes the browser instance (all tabs/windows), then the session is trashed on the client's side. In a new browser instance, the cookie associated with the session wouldn't exist, so it would no longer be sent. This causes an entirely new HttpSession to be created, with an entirely new session cookie being used.
In a nutshell
The ServletContext lives for as long as the web app lives. It is shared among all requests in all sessions.
The HttpSession lives for as long as the client is interacting with the web app with the same browser instance, and the session hasn't timed out at the server side. It is shared among all requests in the same session.
The HttpServletRequest and HttpServletResponse live from the time the servlet receives an HTTP request from the client, until the complete response (the web page) has arrived. It is not shared elsewhere.
All Servlet, Filter and Listener instances live as long as the web app lives. They are shared among all requests in all sessions.
Any attribute that is defined in ServletContext, HttpServletRequest and HttpSession will live as long as the object in question lives. The object itself represents the "scope" in bean management frameworks such as JSF, CDI, Spring, etc. Those frameworks store their scoped beans as an attribute of its closest matching scope.
Thread Safety
That said, your major concern is possibly thread safety. You should now know that servlets and filters are shared among all requests. That's the nice thing about Java, it's multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise be too expensive to recreate, init() and destroy() them for every single request.
You should also realize that you should never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's not thread-safe! The below example illustrates this:
public class ExampleServlet extends HttpServlet {
private Object thisIsNOTThreadSafe;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object thisIsThreadSafe;
thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}
}
See also:
What is the difference between JSF, Servlet and JSP?
Best option for Session management in Java
Difference between / and /* in servlet mapping url pattern
doGet and doPost in Servlets
Servlet seems to handle multiple concurrent browser requests synchronously
Why Servlets are not thread Safe?
Sessions
In short: the web server issues a unique identifier to each visitor on his first visit. The visitor must bring back that ID for him to be recognised next time around. This identifier also allows the server to properly segregate objects owned by one session against that of another.
Servlet Instantiation
If load-on-startup is false:
If load-on-startup is true:
Once he's on the service mode and on the groove, the same servlet will work on the requests from all other clients.
Why isn't it a good idea to have one instance per client? Think about this: Will you hire one pizza guy for every order that came? Do that and you'd be out of business in no time.
It comes with a small risk though. Remember: this single guy holds all the order information in his pocket: so if you're not cautious about thread safety on servlets, he may end up giving the wrong order to a certain client.
Session in Java servlets is the same as session in other languages such as PHP. It is unique to the user. The server can keep track of it in different ways such as cookies, url rewriting etc. This Java doc article explains it in the context of Java servlets and indicates that exactly how session is maintained is an implementation detail left to the designers of the server. The specification only stipulates that it must be maintained as unique to a user across multiple connections to the server. Check out this article from Oracle for more information about both of your questions.
Edit There is an excellent tutorial here on how to work with session inside of servlets. And here is a chapter from Sun about Java Servlets, what they are and how to use them. Between those two articles, you should be able to answer all of your questions.
When the servlet container (like Apache Tomcat) starts up, it will read from the web.xml file (only one per application) if anything goes wrong or shows up an error at container side console, otherwise, it will deploy and load all web applications by using web.xml (so named it as deployment descriptor).
During instantiation phase of the servlet, servlet instance is ready but it cannot serve the client request because it is missing with two pieces of information:
1: context information
2: initial configuration information
Servlet engine creates servletConfig interface object encapsulating the above missing information into it
servlet engine calls init() of the servlet by supplying servletConfig object references as an argument. Once init() is completely executed servlet is ready to serve the client request.
Q) In the lifetime of servlet how many times instantiation and initialization happens ??
A)only once (for every client request a new thread is created)
only one instance of the servlet serves any number of the client request ie, after serving one client request server does not die. It waits for other client requests ie what CGI (for every client request a new process is created) limitation is overcome with the servlet (internally servlet engine creates the thread).
Q)How session concept works?
A)whenever getSession() is called on HttpServletRequest object
Step 1: request object is evaluated for incoming session ID.
Step 2: if ID not available a brand new HttpSession object is created and its corresponding session ID is generated (ie of HashTable) session ID is stored into httpservlet response object and the reference of HttpSession object is returned to the servlet (doGet/doPost).
Step 3: if ID available brand new session object is not created session ID is picked up from the request object search is made in the collection of sessions by using session ID as the key.
Once the search is successful session ID is stored into HttpServletResponse and the existing session object references are returned to the doGet() or doPost() of UserDefineservlet.
Note:
1)when control leaves from servlet code to client don't forget that session object is being held by servlet container ie, the servlet engine
2)multithreading is left to servlet developers people for implementing ie., handle the multiple requests of client nothing to bother about multithread code
Inshort form:
A servlet is created when the application starts (it is deployed on the servlet container) or when it is first accessed (depending on the load-on-startup setting)
when the servlet is instantiated, the init() method of the servlet is called
then the servlet (its one and only instance) handles all requests (its service() method being called by multiple threads). That's why it is not advisable to have any synchronization in it, and you should avoid instance variables of the servlet
when the application is undeployed (the servlet container stops), the destroy() method is called.
Sessions - what Chris Thompson said.
Instantiation - a servlet is instantiated when the container receives the first request mapped to the servlet (unless the servlet is configured to load on startup with the <load-on-startup> element in web.xml). The same instance is used to serve subsequent requests.
The Servlet Specification JSR-315 clearly defines the web container behavior in the service (and doGet, doPost, doPut etc.) methods (2.3.3.1 Multithreading Issues, Page 9):
A servlet container may send concurrent requests through the service
method of the servlet. To handle the requests, the Servlet Developer
must make adequate provisions for concurrent processing with multiple
threads in the service method.
Although it is not recommended, an alternative for the Developer is to
implement the SingleThreadModel interface which requires the container
to guarantee that there is only one request thread at a time in the
service method. A servlet container may satisfy this requirement by
serializing requests on a servlet, or by maintaining a pool of servlet
instances. If the servlet is part of a Web application that has been
marked as distributable, the container may maintain a pool of servlet
instances in each JVM that the application is distributed across.
For servlets not implementing the SingleThreadModel interface, if the
service method (or methods such as doGet or doPost which are
dispatched to the service method of the HttpServlet abstract class)
has been defined with the synchronized keyword, the servlet container
cannot use the instance pool approach, but must serialize requests
through it. It is strongly recommended that Developers not synchronize
the service method (or methods dispatched to it) in these
circumstances because of detrimental effects on performance
No. Servlets are not Thread safe
This is allows accessing more than one threads at a time
if u want to make it Servlet as Thread safe ., U can go for
Implement SingleThreadInterface(i)
which is a blank Interface there is no
methods
or we can go for synchronize methods
we can make whole service method as synchronized by using synchronized
keyword in front of method
Example::
public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException
or we can the put block of the code in the Synchronized block
Example::
Synchronized(Object)
{
----Instructions-----
}
I feel that Synchronized block is better than making the whole method
Synchronized
As is clear from above explanations, by implementing the SingleThreadModel, a servlet can be assured thread-safety by the servlet container. The container implementation can do this in 2 ways:
1) Serializing requests (queuing) to a single instance - this is similar to a servlet NOT implementing SingleThreadModel BUT synchronizing the service/ doXXX methods; OR
2) Creating a pool of instances - which's a better option and a trade-off between the boot-up/initialization effort/time of the servlet as against the restrictive parameters (memory/ CPU time) of the environment hosting the servlet.

Control of when do servlets die

I have some configuration stored in the servlet context, sharing among servlets. The current approach is to load the config into a single copy of static hashmap shared by the servlets when the init() method of any servlet is called. But the problem is the servlets just won't die, so the configuration just won't reset even if I changed the configuration in the servlet context. Because the servlet never call init() again, they will not reload the configuration into the hashmap.
I am using websphere 7, is there any place I can configure when does a servlet die, and a new servlet is born?
I think it is too clumsy to reload the configuration from the servlet context when there is a new request.
Edit:
I have a scheduler which will create a new thread to check for a specific data file, if the data file was new, it will create a hashmap(with content of the data file) as an attribute in the servlet context.
Now the servlets will check for the servlet context for the hashmap and copy it to a static hashmap for shared use among servlets when they are just created and call init(). But the problem is that the servlet won't die during the execution, it just make the scheduled action not working.
I am using websphere 7, is there any
place I can configure when does a
servlet die, and a new servlet is
born?
No.
Edit: I have a scheduler which will
create a new thread to check for a
specific data file, if the data file
was new, it will create a hashmap(with
content of the data file) as an
attribute in the servlet context.
Now the servlets will check for the
servlet context for the hashmap and
copy it to a static hashmap for shared
use among servlets when they are just
created and call init(). But the
problem is that the servlet won't die
during the execution, it just make the
scheduled action not working.
Refactor the settings into a separate data object (rather than a HashMap or servlet instance fields) to be shared by servlet instances. Change the scheduler/alarm to update the shared data object directly.
Why not store this data in JNDI, where the servlets can get it whenever they need it? That way, you have complete control of the data; the servlet lifecycle would no longer matter.

How do I get a list of all HttpSession objects in a web application?

Let's say I have a running Java-based web application with 0 or more valid HttpSession objects associated with it. I want a way to access the current list of valid HttpSession objects. I was thinking that I could implement an HttpSessionListener and use it to append to a list of session id values that are stored in an application-scoped attribute, but then I'm on the hook to update the list as sessions are invalidated and who knows what else.
Before I start baking my own solution I thought I should ask the question:
Does the servlet API provide some means of getting access to the complete list of non-invalidated session objects?
I am using Tomcat 6.x as my web application container, and the MyFaces 1.2.x (JSF) library.
SOLUTION
I followed an approach similar to what BalusC discussed in these existing questions:
How to easily implement "who is
online" in Grails or Java Application
?
JSF: How to invalidate an
user session when he logs twice with
the same credentials
I modified by SessionData class to implement HttpSessionBindingListener. When a binding event happens, the object will either add or remove itself from the set of all the SessionData objects.
#Override
public void valueBound(HttpSessionBindingEvent event) {
// Get my custom application-scoped attribute
ApplicationData applicationData = getApplicationData();
// Get the set of all SessionData objects and add myself to it
Set<SessionData> activeSessions = applicationData.getActiveSessions();
if (!activeSessions.contains(this)) {
activeSessions.add(this);
}
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
ApplicationData applicationData = getApplicationData();
Set<SessionData> activeSessions = applicationData.getActiveSessions();
if (activeSessions.contains(this)) {
activeSessions.remove(this);
}
}
The one thing that continues to irritate me is what happens when Tomcat is restarted. Unless Tomcat has been properly configured to NOT serialize sessions to disk, it will do so. When Tomcat starts up again, the HttpSession objects (and the SessionData objects along with them) are deserialized and the sessions are made valid again. However, the serialization/deserialization totally sidesteps the HttpSession listener events, so I do not have the opportunity to gracefully put the deserialized reference to the SessionData back in my managed Set of objects after the restart.
I don't have any control over the production configuration of Tomcat in my customer's organization, so I cannot assume that it will be done the way I expect it.
My workaround is to compare the HttpSession creation time with the application startup time when a request is received. If the session was created before the application startup time, then I call invalidate() and the user is sent to an error/warning page with an explanation of what happened.
I get the application startup time by implementing a ServletContextListener and storing the current time inside an application-scoped object from within the contextInitialized() method of my listener.
No, the Servlet API doesn't provide a way. You really have to get hold of them all with help of a HttpSessionListener. You can find several examples in the following answers:
How to find HttpSession by jsessionid?
How to find number of active sessions per IP?
How to check Who's Online?
How to invalidate another session when a user is logged in twice?
There is no straight forward way. It depends on deployment. Above will fail once you decide to introduce distributed deployment and load balancing.
Not really an answer, but in the good ol' days there was "javax.servlet.http.HttpSessionContext", but it was dropped as of version 2.1, explicitly with no replacement: https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSessionContext.html

Sharing a static object between a servlet and a webservice

I have a servlet which handles http get requests that I'd like to be able to share an object which is also used by a webservice im developing. They are both on the same tomcat server within the same web-app container. Im not sure best how to do it any ideas ?
You can share things across the webapp by storing them as attributes in the ServletContext (using setAttribute / getAttribute). You could create the object in an impelementation of ServletContextListener, store it in the ServletContext, and then retrieve it and use it from your web service and servlet.
I will expand on my comment here.
In the simplest case ServletContext.setAttribute/getAttribute would be fine. But some people rightly raised the questions about thread safety.
For this a better approach would be to store a shared POJO in a ServletContext during webapp initialization and get it wherever you need with ServletContext.getAttribute.
For this you need to implement ServletContextListener interface and declare it in your web.xml file.
<listener>
<listener-class>your.package.ServletContextListenerImpl</listener-class>
</listener>
This listener is called once when your webapp is loaded by the servlet container, and when it is about to be unloaded by the servlet container. In both cases it passes ServletContextEvent that has a handle to ServletContext. It is at that point that you want to set/removeAttribute that points to your shared object.
Because of this you may be certain that ServletContext.getAttribute will return a valid object when called from the Servlet.service or one of the do... methods.
As for attribute name, I would go with your Shared class classname. I would also add a static access method to your shared class to get it from the ServletContext, like this:
public class Shared
{
...
public static Shared fromServletContext ( final ServletContext context )
{
return (Shared) context.getAttribute( Shared.class.getName( ) );
}
}
The simplest option is create a Singleton - a class which allows only one instance in memory. Since you get it by calling a static method on the class itself it should be available to both the servlet and the WS
If my alternative understanding of the question is correct, the data which comes from request should be stored, and then retreived by the web-service. If this is supposed to run in a multi-user environment, you might consider using an in-memory database (HSQLDB) to temporarily store the data. Then you will be able to retrieve it with your web-service, based on some criteria I cannot foretell.
If this is application-specific data (accessible by all users (sessions)), then use ServletContext#set/getAttribute(). If this is user (session)-specific data, then use HttpSession#set/getAttribute().
Let the servlet class set the object in the desired scope (application or session) by a specific attribute key and let the webservice get the object from the desired scope by the same attribute key.
I think you rather need the HttpSession.
The singleton story makes no sense here.

Categories