Tomcat 中的自定义 MBean - 创建 InitialContext 时找不到 javaURLContextFactory

发布于 2024-12-06 15:32:23 字数 526 浏览 1 评论 0原文

我编写了一个部署在 Tomcat 6 中的自定义 MBean。它的任务之一是查询数据库值。我通过使用 JNDI 加载数据库资源来完成此操作 - 该资源是在 Tomcat 的 server.xml 中定义的。

问题是,当我创建 javax.naming.InitialContext 实例时,它会抛出 ClassNotFoundException,因为它找不到 org.apache.naming.java。 javaURLContextFactory。 该类位于 catalina.jar 中并由公共类加载器加载。包含我的 MBean 代码的 jar 由共享类加载器加载。

关于如何解决这个问题有什么想法吗?

注意:我的 MBean 是由我在 tomcat/conf/web.xml 中定义的 ContextListener 加载的。我还在 webapp web.xml 中定义了它,这没有什么区别。我无法真正移动我的 jar 以便由公共类加载器加载,因为它依赖于共享类加载器加载的类。

预先致谢,

威尔

I've written a custom MBean that deploys in Tomcat 6. One of its tasks is to query a database value. I'm doing this by loading up the database resource using JNDI - the resource is defined in Tomcat's server.xml.

The problem is that when I create an instance of javax.naming.InitialContext it throws a ClassNotFoundException as it can't find org.apache.naming.java.javaURLContextFactory.
This class is in catalina.jar and loaded by the common classloader. The jar containing my MBean code is loaded by a shared classloader.

Any ideas as to how I can get around this?

To note: my MBean is loaded by a ContextListener which I've defined in the tomcat/conf/web.xml. I've also defined it in a webapp web.xml which makes no difference. I can't really move my jar so as to be loaded by the common classloader as it relies on classes loaded by the shared classloader.

Thanks in advance,

Will

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ゝ偶尔ゞ 2024-12-13 15:32:23

这看起来是一个奇怪的类加载或安全/权限问题。下面是一个解决方法。

主要思想:由于 ServletContextListener 可以调用 new InitialContext() 而无需 ClassNotFoundException 在侦听器中获取它并将其传递给构造函数在注册 MBean 之前先获取 MBean 对象。我使用了一个简单的 Web 应用程序,并且没有修改 tomcat/conf/web.xml

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>

web.xml 资源配置:

<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>

注册 MBean 的 ServletContextListener

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 接口:

public interface HelloMBean {
    void sayHello();
}

MBean 实现:

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();
        }
    }
}

MBean 描述符操作方法mbeans-descriptor.xml 是必需的,但没有它也能正常工作。我可以使用 jconsole 连接到 HelloMBean。通过 jconsole 调用 sayHello() 打印以下内容:

conn: jdbc:mysql://localhost:3306/javatest?autoReconnect=true, \
UserName=root@localhost, MySQL-AB JDBC Driver

Sources:

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:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文