Reputation: 21
I have upgraded tomcat from version 7.0.34 to version 8.0.33, and since then I have been facing a problem to share the web application context and Junit context.
I have a web application with singleton class that gathers statistic data about the web application. I also have Junit that runs the web application in embedded tomcat. the Junit queries the web application and then checks the statistic data.
I try to make a simple example:
the singleton:
public class Counter {
private static Counter instance;
private AtomicLong counter;
private Counter(){}
public static Counter getInstance(){
if(instance == null){
synchronized (Counter.class) {
if(instance == null){
instance = new Counter();
}
}
}
return instance;
}
public long incrementAndGet(){
return counter.incrementAndGet();
}
public long getValue(){
return counter.get();
}
}
the servlet:
@WebServlet(name="servlet",loadOnStartup=1, urlPatterns="/servletTest")
public class Servlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hi, you are the #" + Counter.getInstance().incrementAndGet() + " visitor");
}
}
contextListener:
public class MyContextListener implements ServletContextListener{
@Override
public void contextDestroyed(ServletContextEvent arg0) {}
@Override
public void contextInitialized(ServletContextEvent arg0) {
Counter.getInstance().incrementAndGet();
}
}
Test unit:
public void mainTest() throws ServletException, LifecycleException{
Tomcat tomcat = new Tomcat();
tomcat.setPort(50000);
StandardContext ctx = (StandardContext) tomcat.addWebapp("/fe", System.getProperty("FEBaseDir")); //The FEBaseDir property is supposed to be taken from Maven build using 'test' profile
tomcat.start();
Counter.getInstance().getValue();
}
when I used Tomcat 7, everything worked fine. but since I upgraded tomcat to tomcat 8.0.33, It hasn't been working. the singleton class with the static data loads twice. first by the tomcat and then by the Junit itself.
I have tried to pass tomcat a classloader but it doesn't work.
public void mainTest() throws ServletException, LifecycleException{
Tomcat tomcat = new Tomcat();
tomcat.setPort(50000);
StandardContext ctx = (StandardContext) tomcat.addWebapp("/fe", System.getProperty("FEBaseDir")); //The FEBaseDir property is supposed to be taken from Maven build using 'test' profile
ctx.setCrossContext(true);
ctx.setLoader((Loader) new WebappLoader(Thread.currentThread().getContextClassLoader()));
ctx.setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getEngine().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getHost().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getService().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.getServer().setParentClassLoader(Thread.currentThread().getContextClassLoader());
tomcat.start();
Counter.getInstance().getValue();
}
What am I doing wrong?
Upvotes: 1
Views: 1627
Reputation: 6538
You could try using the setDelegate method in StandardContext
to prevent the web-app classloader from reloading the Counter
class, but this impacts security in a bad manner so I advice against that.
The usual way to expose statistics is to use JMX (MBeans). You enable this by calling the setUseNaming method in StandardContext
with value true
.
You can register a mbean like this (copied from here):
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName beanPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")");
mBeanServer.registerMBean(hikariPool, beanPoolName);
And you can retrieve a value like this (copied from here):
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (foo)");
HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(mBeanServer, poolName, HikariPoolMXBean.class);
int idleConnections = poolProxy.getIdleConnections();
See also this SO question and you'll probably have to read some more documentation (in my experience, it takes a while to understand the whole JMX thing and get it to work). I have not tried this in combination with unit-tests though, so YMMV.
Upvotes: 2