Spring 使用 Apache CXF 和 JNDI 查找 javax.naming.NameNotFoundException 使用嵌入式 Tomcat 启动

Spring Boot with embedded Tomcat with Apache CXF and JNDI lookup javax.naming.NameNotFoundException

我在将遗留 Java 应用程序迁移到 Spring Boot 时遇到了障碍。具体来说,该应用程序将 Apache CXF 用于 SOAP Web 服务,这些服务具有从 Oracle 数据库获取数据的操作。我看到的问题是,一旦 Web 服务尝试进行 JNDI 查找,就会抛出以下异常:

Name [java:comp/env/jdbc/myDataSource] is not bound in this Context. Unable to find [java:comp].

NamingException: javax.naming.NameNotFoundException: Name [java:comp/env/jdbc/myDataSource] is not bound in this Context. Unable to find [java:comp].

作为参考,我在此处检查了一个问题示例: https://github.com/pmercer/spring-boot-sample-tomcat-jndi-cxf-ws

因此,如果有人可以向我指出一个工作示例 and/or 提供解决问题的建议,那么将不胜感激。

添加更多详细信息...

以下是在现有应用程序中执行查找的方式,该应用程序作为 .war 文件部署在外部 Tomcat 服务器上 ...

/**
 * Returns a database connection for the specified datasource
 * @param db - ex. "jdbc/DatasourceXyz"
 * @return db Connection
 * @throws Exception
 */
public static Connection getDbConnection(String db) throws Exception {
    java.sql.Connection conn = null;

    try {
        LOG.debug("getting db connection for " + db + " ...");

        javax.naming.Context initCtx = new javax.naming.InitialContext();
        javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env");
        javax.sql.DataSource ds = (javax.sql.DataSource) envCtx.lookup(db);
        conn = ds.getConnection();

        initCtx.close();
        LOG.debug("got db connection for " + db);
        LOG.debug("url = " + conn.getMetaData().getURL());

    } catch (Exception e) {
        String message = String.format("Exception thrown while creating a JDBC connection for %s: %s", db,
                e.getMessage());
        throw new Exception(message);
    }

    return conn;
}

并且数据库属性存储在 Tomcat 服务器的 context.xml 文件中...

<Context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- environment-specific jdbc data sources: -->
<Resource name="jdbc/DatasourceXyz" auth="Container" type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@hostname:1521:database" username="someUser" password="***" maxTotal="20" maxIdle="10" maxWaitMillis="60000"/>
<!-- set other jdbc data sources below: -->

此外,由于现有的 getDbConnection 方法调用很普遍,而且应用程序的数据库 CRUD 代码很广泛,因此目标是保持该代码不变。所以基本上我只需要能够创建数据库连接,就像这样...

public static Connection getDbConnection(String db) throws Exception {
    java.sql.Connection conn = null;

    try {
        LOG.debug("getting db connection for " + db + " ...");

        // Get the SampleUtilService from the Bridge
        SampleUtilService sampleUtilService = SpringContextBridge.services().getSampleUtilService();

        // Get DataSource from the JndiObjectFactoryBean
        javax.sql.DataSource ds = sampleUtilService.getDataSourcefromFactoryBean(db);

        conn = ds.getConnection();

        LOG.debug("got db connection for " + db);
        LOG.debug("url = " + conn.getMetaData().getURL());

    } catch (Exception e) {
        String message = String.format("Exception thrown while creating a JDBC connection for %s: %s", db,
                e.getMessage());
        throw new Exception(message);
    }

    return conn;
}

因此,借助 SpringContextBridge 功能并使用嵌入式 Tomcat 服务器配置 JNDI,正如我的示例项目 README 中所述,基本上这就是我的计划。另外,由于我是 Spring Boot 的新手,所以我不确定这是否是最好的方法。

此外,在我最初的 post 之后做了一些额外的测试后,看来我看到的问题可能只是由于将 JndiObjectFactoryBean.setLookupOnStartup 属性 设置为 false 引起的,即。 ,

    @Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
    bean.setJndiName("java:comp/env/jdbc/myDataSource");
    bean.setProxyInterface(DataSource.class);
    bean.setLookupOnStartup(false);
    bean.afterPropertiesSet();
    return (DataSource)bean.getObject();
}

当我将 bean 的 setLookupOnStartup 属性 设置为 true 时,应用程序能够成功获得数据库连接。但是,当应用程序尝试通过 JNDI 进行查找时,我仍然看到问题,即

        DataSource dataSource =
            (DataSource) new InitialContext().lookup("java:comp/env/jdbc/myDataSource");

因此,如果使用该 bean 的额外测试证明是成功的,那么我可能会继续使用该 bean 进行查找。另外,如果有更好的方法,我愿意接受建议。

此外,据我所知,数据源配置应存储在应用程序的 application.properties 文件中...

jdbc.xyz.datasource.name=jdbc/DatasourceXyz
jdbc.xyz.datasource.driver=oracle.jdbc.OracleDriver
jdbc.xyz.datasource.url=jdbc:oracle:thin:@hostname:1521:database
jdbc.xyz.datasource.username=someUser
jdbc.xyz.datasource.password=***

我不确定为什么在 Apache CXF SOAP Web 服务调用的上下文中执行直接 JNDI 查找会失败。但是,使用 JndiObjectFactoryBean bean 完成的查找正在运行,所以这就是我要 运行 使用的内容。