Context is read only

19,420

Solution 1

If you need to create a datasource dynamically is there really any need for a JNDI lookup? JNDI is designed to make the connection external to the application, while in your scenario its tightly coupled to the application due to a legitimate requirement. Why not just use a JDBC connection?

Solution 2

You need to create a ServletContextListener and there you can make the InitialContext writable - it's not the way it should be done, but if you really need it, this is one way you can do it.

This also works with 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();
    }
}

Solution 3

I haven't got this problem before since I usually defined JNDI in application server(tomcat, weblogic and etc). Just like what Kevin said, this is exactly what JNDI was designed for; separating datasource config from your source code and retrieving JNDI resources through lookup and inject;

Back to your question, I think tomcat has every strict rules on modifying JNDI at runtime. In another word, you cannot re-bind or remove jndi from Context. If you go through the tomcat specification you will probably see some thing about jndi lookup but no re-bind.

Solution 4

From section EE.5.3.4 of the EE 6 platform specification (JSR 316):

The container must ensure that the application component instances have only read access to their naming context. The container must throw the javax.naming.OperationNotSupportedException from all the methods of the javax.naming.Context interface that modify the environment naming context and its subcontexts.

Note that "their naming context" in this section is referring to java:comp.

Share:
19,420
rubenGL
Author by

rubenGL

Updated on August 15, 2022

Comments

  • rubenGL
    rubenGL over 1 year

    Helo masters, I have to create a JNDI Datasource dynamically, I tried to do it with a listener called SetupApplicationListener. Here is the beginning of 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>
    

    The code of the 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);
        }
    
    .....
    }
    

    And there is the error:

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

    Can I set the read-only properties of the Context to "true"? Thanks! :)

    Tomcat 6.0
    Oracle 11g
    jdk1.5
    

    EDIT: Don't need to be dynamically, i have to define a jndi datasource internally I can't modify the server files because it is a shared server. It must be jndi because other modules use it in that way, thanks.

  • rubenGL
    rubenGL over 11 years
    because there are some modules that ref the connetion with jndi. Due to security issues they dont want to place the conection values (user, pass, ...) in the server because it is a shared server.
  • rubenGL
    rubenGL over 11 years
    I need to define a jndi datasource in the app, internally, do u know another way to do it without a listener?
  • rubenGL
    rubenGL over 11 years
    There are some modules that ref the connetion with jndi. Due to security issues they dont want to place the conection values (user, pass, ...) in the server because it is a shared server. I need to define a jndi datasource in the app, internally, do u know another way to do it without a listener?
  • rubenGL
    rubenGL over 11 years
    Dont need to be dinamically
  • Kevin Bowersox
    Kevin Bowersox over 11 years
    @rubenGL but couldn't they just check the context.xml file to get these values?
  • rubenGL
    rubenGL over 11 years
    Dont need to be dinamically
  • rubenGL
    rubenGL over 11 years
    context.xml is a server file they dont want to put the connection values in the server
  • Anugoonj
    Anugoonj over 11 years
    +1 user2021142 , JNDI is meant to decouple the source code from the connectivity config. The JNDI configurations on the server can be restricted to only Admin users if that is what serves the purpose of security. Also, the database connection can be SSL enabled to avoid any malicious lookups.
  • Anugoonj
    Anugoonj over 11 years
    @rubenGL If the connection string is in the soruce code even then somone(assuming has access to tomcat) can extract the .class file and decompile to check the connection string, unless the class have been obfiscated with zero tolerance of getting the source code.
  • John Eipe
    John Eipe about 10 years
    I remember reading that in EE6, they brought java:global which is not-read-only. Any idea about this? I haven't used it before.