Search code examples
javajndi

infinite recursion on new InitialContext()


I am trying to setup JNDI for a Java SE console application.

I have the following code:

public class FooMain {

    public static void main (String args[]) throws NamingException {
        Context context = new InitialContext();
        context.bind("foo", "bar");
    }
}

… and the context factory is defined as:

public class MyContextFactory implements InitialContextFactory {

    private static Hashtable store = new Hashtable();

    @Override
    public Context getInitialContext(Hashtable environment) throws NamingException {
        return new InitialContext() {

            @Override
            public void bind(String name, Object obj) {
                store.put(name, obj);
            }

            @Override
            public Object lookup(String name) {
                return store.get(name);
            }    
        };
    }
}

When I invoke my FooMain class using:

java -Djava.naming.factory.initial=MyContextFactory -cp ... FooMain

I get infinite recursion and an eventual StackOverflow Exception:

Exception in thread "main" java.lang.StackOverflowError
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.naming.internal.VersionHelper12.getContextClassLoader(VersionHelper12.java:185)
at com.sun.naming.internal.ResourceManager.getApplicationResources(ResourceManager.java:549)
at com.sun.naming.internal.ResourceManager.getInitialEnvironment(ResourceManager.java:244)
at javax.naming.InitialContext.init(InitialContext.java:240)
at javax.naming.InitialContext.<init>(InitialContext.java:192)
at MyContextFactory$1.<init>(MyContextFactory.java:20)
at MyContextFactory.getInitialContext(MyContextFactory.java:20)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
at javax.naming.InitialContext.init(InitialContext.java:244)
at javax.naming.InitialContext.<init>(InitialContext.java:192)
at MyContextFactory$1.<init>(MyContextFactory.java:20)
at MyContextFactory.getInitialContext(MyContextFactory.java:20)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
at javax.naming.InitialContext.init(InitialContext.java:244)
at javax.naming.InitialContext.<init>(InitialContext.java:192)
at MyContextFactory$1.<init>(MyContextFactory.java:20)
at MyContextFactory.getInitialContext(MyContextFactory.java:20)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
at javax.naming.InitialContext.init(InitialContext.java:244)
at javax.naming.InitialContext.<init>(InitialContext.java:192)
...

I can make the code work by creating an "environment" Hashtable, placing the name of the MyContextFactory class there (under the "java.naming.factory.initial" key) and then creating the InitialContext using the constructor that accepts a Hashtable environment:

Context context = new InitialContext(environment);

But my question is: how can I make this work using the no-args constructor and supplying the name of the factory class using "-Djava.naming.factory.initial" at the invocation of the JVM ?


Solution

  • Thanks to the suggestion by Jim Garrison, here's the answer. The crux of the matter is the call super(true); in the constructor of the MyContext class.

    class MyContext extends InitialContext {
    
        private Hashtable store;
    
        public MyContext(Hashtable store) throws NamingException {
            super(true);
            this.store = store;
        }
    
        @Override
        public void bind(String name, Object obj) {
            store.put(name, obj);
        }
    
        @Override
        public Object lookup(String name) {
            return store.get(name);
        }    
    
    }
    
    
    
    public class FooMain {
    
        private static final int    ANSWER    = 42;
        private static final String JNDI_NAME = "/config/theAnswerToEverything";
    
        public static void main (String args[]) throws NamingException {
            Context context = new InitialContext();
            putInContext(                    JNDI_NAME, ANSWER);
    
            int answer = retrieveFromContext(JNDI_NAME);
            Assert.assertEquals(ANSWER, answer);
            System.out.printf("%d\n", answer);
        }
    
    
        private static void putInContext(final String key, final Object value) throws NamingException {
            Context context = new InitialContext();
            context.bind(key, value);
        }
    
        private static int retrieveFromContext(final String key) throws NamingException {
            Context context = new InitialContext();
            return (int) context.lookup(key);
        }
    }
    
    
    public class MyContextFactory implements InitialContextFactory {
    
        private static Hashtable store = new Hashtable();
    
        /*
          Do not confuse [store] with [environment]. They serve different
          purposes.
         */
    
        @Override
        public Context getInitialContext(Hashtable environment) throws NamingException {
            return new MyContext(store);
        }
    }
    

    Invoke from the command line:

    $ java -Djava.naming.factory.initial=MyContextFactory -cp [actual classpath] FooMain
    42