It looks a weird classloading or security/permission issue. Below is a workaround.
The main idea: Since the ServletContextListener
could call the new InitialContext()
without the ClassNotFoundException
get it in the listener and pass it to the constructor of the MBean object before you register the MBean. I used a simple web application and I have not modified tomcat/conf/web.xml
.
Resource configuration in the tomcat/conf/context.xml
:
<Context>
...
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="..." driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest?autoReconnect=true"/>
...
<Context>
The web.xml
resource configuration:
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
The ServletContextListener
which registers the MBean:
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class ContextListener implements ServletContextListener {
private ObjectName objectName;
public void contextInitialized(final ServletContextEvent sce) {
System.out.println("---> bean context listener started");
final MBeanServer mbeanServer =
ManagementFactory.getPlatformMBeanServer();
try {
final InitialContext initialContext = new InitialContext();
final Context envContext =
(Context) initialContext.lookup("java:/comp/env");
objectName = new ObjectName("com.example:type=Hello");
final Hello helloMbean = new Hello(envContext);
mbeanServer.registerMBean(helloMbean, objectName);
System.out.println("---> registerMBean ok");
} catch (final Exception e) {
e.printStackTrace();
}
}
public void contextDestroyed(final ServletContextEvent sce) {
System.out.println("---> bean context listener destroyed");
final MBeanServer mbeanServer =
ManagementFactory.getPlatformMBeanServer();
try {
mbeanServer.unregisterMBean(objectName);
System.out.println("---> unregisterMBean ok");
} catch (final Exception e) {
e.printStackTrace();
}
}
}
MBean interface:
public interface HelloMBean {
void sayHello();
}
MBean implementation:
import java.sql.Connection;
import javax.naming.Context;
import javax.sql.DataSource;
public class Hello implements HelloMBean {
private final Context envContext;
public Hello(final Context envContext) {
this.envContext = envContext;
System.out.println("new hello " + envContext);
}
@Override
public void sayHello() {
System.out.println("sayHello()");
try {
final DataSource ds =
(DataSource) envContext.lookup("jdbc/TestDB");
final Connection conn = ds.getConnection();
System.out.println(" conn: " + conn);
// more JDBC code
} catch (final Exception e) {
e.printStackTrace();
}
}
}
The MBean Descriptor How To says a mbeans-descriptor.xml
is required but it's worked without it well. I could connect to the HelloMBean with jconsole
. Calling sayHello()
through jconsole
printed the following:
conn: jdbc:mysql://localhost:3306/javatest?autoReconnect=true, \
UserName=root@localhost, MySQL-AB JDBC Driver
Sources: