【问题标题】:Context is read only上下文是只读的
【发布时间】:2013-01-12 20:51:06
【问题描述】:

Helo 大师,我必须动态创建一个 JNDI 数据源,我尝试使用一个名为 SetupApplicationListener 的侦听器来完成它。这里是WEB-LIB/web.xml的开头

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee">

    <display-name>pri-web</display-name>

    <!-- Listeners -->
    <listener>
        <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
    </listener>
    <listener>
        <listener-class>myapp.SetupApplicationListener</listener-class>
    </listener>

监听器的代码:

public class SetupApplicationListener implements ServletContextListener {

    public static Log LOG = null;

    public void contextInitialized(ServletContextEvent ctx){
        try {            
            createOracleDataSource();
.....
        }
    }

    private void createOracleDataSource() throws SQLException, NamingException {
        OracleDataSource ds = new OracleDataSource();
        ds.setDriverType(...);
        ds.setServerName(...);
        ds.setPortNumber(...);
        ds.setDatabaseName(...);
        ds.setUser(...);
        ds.setPassword(...);

        new InitialContext().bind("java:comp/env/jdbc/myDS", ds);
    }

.....
}

还有错误:

[ERROR] 29/01/2013 09:44:50,517 (SetupApplicationListener.java:86) -> Error
javax.naming.NamingException: Context is read only
    at org.apache.naming.NamingContext.checkWritable(NamingContext.java:903)
    at org.apache.naming.NamingContext.bind(NamingContext.java:831)
    at org.apache.naming.NamingContext.bind(NamingContext.java:171)
    at org.apache.naming.NamingContext.bind(NamingContext.java:187)
    at org.apache.naming.SelectorContext.bind(SelectorContext.java:186)
    at javax.naming.InitialContext.bind(InitialContext.java:359)
    at myapp.SetupApplicationListener.createOracleDataSource(SetupApplicationListener.java:102)

我可以将 Context 的只读属性设置为“true”吗?谢谢! :)

Tomcat 6.0
Oracle 11g
jdk1.5

编辑:不需要动态,我必须在内部定义一个 jndi 数据源我不能修改服务器文件,因为它是一个共享服务器。一定是 jndi,因为其他模块都是这样使用的,谢谢。

【问题讨论】:

标签: java spring tomcat servlets jndi


【解决方案1】:

如果您需要动态创建数据源,是否真的需要 JNDI 查找? JNDI 旨在建立应用程序外部的连接,而在您的场景中,由于合法需求,它与应用程序紧密耦合。为什么不直接使用 JDBC 连接?

【讨论】:

  • 因为有些模块引用了 jndi 的连接。由于安全问题,他们不想将连接值(用户、密码、...)放在服务器中,因为它是共享服务器。
  • 我需要在应用程序内部定义一个 jndi 数据源,你知道没有监听器的另一种方法吗?
  • 不需要动态
  • @rubenGL 但他们不能只检查 context.xml 文件来获取这些值吗?
  • context.xml 是一个服务器文件,他们不想将连接值放在服务器中
【解决方案2】:

我以前没有遇到过这个问题,因为我通常在应用程序服务器(tomcat、weblogic 等)中定义 JNDI。正如 Kevin 所说,这正是 JNDI 的设计目的;将数据源配置与源代码分离,并通过查找和注入检索 JNDI 资源;

回到你的问题,我认为 tomcat 对在运行时修改 JNDI 有严格的规定。换句话说,您不能从 Context 中重新绑定或删除 jndi。如果您仔细阅读 tomcat 规范,您可能会看到一些关于 jndi 查找但没有重新绑定的内容。

【讨论】:

  • 有些模块引用了 jndi 的连接。由于安全问题,他们不想将连接值(用户、密码、...)放在服务器中,因为它是共享服务器。我需要在应用程序内部定义一个 jndi 数据源,你知道没有监听器的另一种方法吗?
  • 不需要动态
  • +1 user2021142 ,JNDI 旨在将源代码与连接配置分离。如果出于安全目的,服务器上的 JNDI 配置可以仅限于管理员用户。此外,数据库连接可以启用 SSL 以避免任何恶意查找。
【解决方案3】:

来自 EE 6 平台规范 (JSR 316) 的 EE.5.3.4 部分:

容器必须确保应用程序组件实例 对其命名上下文只有读取权限。容器必须 从所有的抛出 javax.naming.OperationNotSupportedException javax.naming.Context 接口的方法修改 环境命名上下文及其子上下文。

请注意,本节中的“他们的命名上下文”指的是java:comp

【讨论】:

  • 我记得在 EE6 中读过,他们带来了非只读的 java:global。对此有任何想法吗?我以前没用过。
【解决方案4】:

当我发现我正在关闭 environmentContext 对象时,我解决了这个问题 例如:

Context context=new InitialContext();
Context environmentContext=(Context) context.lookup("java:comp/env");

我的代码是:

environmentContext.close();

从 environmentContext 中删除关闭功能后,问题被卖给了我;

【讨论】:

    【解决方案5】:

    您需要创建一个 ServletContextListener 并在那里您可以使 InitialContext 可写 - 这不是应该这样做的方式,但如果您真的需要它,这是您可以做到的一种方式。

    这也适用于 Java Melody!

    protected void makeJNDIContextWritable(ServletContextEvent sce) {
        try {
            Class<?> contextAccessControllerClass = sce.getClass().getClassLoader().loadClass("org.apache.naming.ContextAccessController");
            Field readOnlyContextsField = contextAccessControllerClass.getDeclaredField("readOnlyContexts");
            readOnlyContextsField.setAccessible(true);
            Hashtable readOnlyContexts = (Hashtable) readOnlyContextsField.get(null);
            String context = null;
            for (Object key : readOnlyContexts.keySet()) {
                String keyString = key + "";
                if (keyString.endsWith(sce.getServletContext().getContextPath())) {
                    context = keyString;
                }
            }
            readOnlyContexts.remove(context);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    

    【讨论】:

      【解决方案6】:

      我也遇到过这个问题,但是对于 Tomee 来说是新手,我不知道有一个简单的解决方案。当我将我的 Web 应用程序部署到 webapps 文件夹时,该应用程序运行良好,但是当我将它部署到服务文件夹时,我得到了同样的中止。问题是文件夹名称与战争名称不匹配(减去 .war)。一旦我解决了这个问题,该应用程序就可以正常工作了。确保战争名称、文件夹名称和服务名称相同。这个问题会产生几个不同的错误,包括 Context is read only 和 Error merging Java EE JNDI entries。

      【讨论】:

        【解决方案7】:

        我通过在 context.xml 中设置 useNaming="false" 解决了这个问题。

        From the documentation:

        useNaming :设置为 true(默认值)以让 Catalina 为该 Web 应用程序启用与 Java2 Enterprise Edition (J2EE) 平台约定兼容的 JNDI InitialContext。

        【讨论】:

          猜你喜欢
          • 2019-08-17
          • 2019-12-05
          • 1970-01-01
          • 1970-01-01
          • 2014-08-25
          • 1970-01-01
          • 2021-07-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多