views:

456

answers:

4

I'm trying to implement a Singleton in Tomcat 6.24 on Linux with x86_64 OpenJDK 1.6.

My application is just a bunch of JSPs and some static content and the JSPs make calls to my Java code. Currently the web.xml just looks like this:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   version="2.5">

<description>
    App Name
</description>
<display-name>App Name</display-name>

<!-- The Usual Welcome File List -->
<welcome-file-list>
    <welcome-file>pages/index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

Before when I was trying to load my Singleton it was getting instantiated twice since the class was getting loaded by two different class loaders (I'm not sure why) and each loader would create an instance of the singleton which is not acceptable for my application. I finally figured out if I exported my code as a jar and put it in $CATALINA_HOME/lib then there was only one instance, but this is not an elegant solution.

I've been googling for hours, but I haven't come up with anything yet. I'm wondering if there is some other solution. Currently I'm not precompling my JSPs, could this be part of the problem? Could I write a servlet to ensure the singleton is created? If so how do I do that?

A: 

When is the singleton created first? You could force it to be created when the index.jsp is loaded by including a:

<%! MySingleton instance = MySingleton.getInstance(); %>

line.

If you precompile the jsp's and configure tomcat to never auto-reload classes or jsp's the singleton should stay single :-)

rsp
Several JSPs mae calls the method of a class which in process of executing the method uses the singleton. I'll try precompiling the JSPs and see if that solves it.
jwegan
@jwegan, by adding this to the first jsp that gets loaded, you introduce a known first-use preventing possible races later in the application flow. The index.jsp does not have to use the singleton, just force it to be instantiated at a known point.
rsp
@rsp I tried putting <%! MySingleton instance = MySingleton.getInstance(); %> in index.jsp and when index.jsp gets loaded it looks the constructor is getting called twice (confirmed via print to stderr in the constructor). I create the singleton as part of the static initialization of the class so it appears that for some reason two classloaders are involved in serving up index.jsp
jwegan
@jwegan, can you print the stacktraces up to that point? Btw, for singletons it is better to create the singleton instance in the synchronised getInstance() method to prevent all initialisation to take part at once at application start.
rsp
A: 

To ensure correct initialisation of the Singleton, you should check that you are using the default parent-first classloader, see http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html.

Then define a ServletContextListener and intialise your Singleton in the method:

contextInitialized(ServletContextEvent sce)  

which is guaranteed to be called before any of your servlets or filters.

All ServletContextListeners are notified of context initialization before any filter or servlet in the web application is initialized.

Define your ServletContextListener in your web.xml

crowne
Thanks, your post about contexts made me realize the issue was because I was accessing the application through <server>/ and also <server>/<appname>
jwegan
+1  A: 

Ok, I finally figured out the problem.

I had made my application the default application for the server by adding a to server.xml and setting the path to "". However, when I was accessing it through the URL http://localhost/somepage.jsp for somethings, but also the URL http://localhost/appname/anotherpage.jsp for other things.

Once I changed all the URLs to use http://localhost/ instead of http://localhost/appname the problem was fixed.

jwegan
A: 

Once I changed all the URLs to use http://localhost/ instead of http://localhost/appname the problem was fixed.

This implies that you've 2 deployed webapplications and expected them to use the same classloader. This is untrue. If you want a shared library which is used by all webapplications, then you should put it in the path as denoted by "Tomcat Shared" (which is configureable by shared.loader in catalina.properties file). You only need to ensure that you do not have this library in the webapp's own library or in Tomcat/lib.

BalusC