This is an exported version of the JIRA issue tracker. Please use the Google Code site to open new tickets or report updates to these existing tickets. Feel free to contact the mailing list with any questions.

[SJN-77] InitialContext.close() improperly throws exception when using JNDI Memory implementation separately
Created: Mon, 25 Dec 2006 12:47:13 -0800 (PST)  Updated: Sun, 6 Apr 2008 00:31:01 -0700 (PDT)

Status:Closed
Project:Simple-JNDI
Component/s:
Affects Version/s:0.11.1
Fix Version/s:0.11.3

Type:BugPriority: Major
Reporter:Ludovic OrbanAssignee:Henri Yandell
Resolution:Fixed 
Environment:


 Description   
According to this page http://www.osjava.org/simple-jndi/manual/MemoryContext.html it should be possible to use Simple JNDI as an in-memory context.

When I try with this jndi.properties in classpath:
        java.naming.factory.initial=org.osjava.sj.memory.MemoryContextFactory

this code:

Context ctx = new InitialContext();
        ctx.bind("aaa", "bbb");
        ctx.close();

throws this exception:

java.lang.ClassCastException
at org.osjava.sj.jndi.AbstractContext.close(AbstractContext.java:704)
at javax.naming.InitialContext.close(InitialContext.java:478)
        ...

and if you set this property in jndi.properties:

org.osjava.sj.jndi.shared=true

you get that exception instead:

javax.naming.NotContextException
at org.osjava.sj.jndi.AbstractContext.destroySubcontext(AbstractContext.java:529)
at org.osjava.sj.jndi.AbstractContext.close(AbstractContext.java:697)
at javax.naming.InitialContext.close(InitialContext.java:478)
Comment by bulenterdemir [ Sun, 17 Feb 2008 06:28:27 -0800 (PST) ]
Hi, the problem is in the AbstractContext's close() method. This method iterates through the internal HashTable (called table) in which it holds the JNDI bound objects. And while iterating it casts each object from this table to a Thread and tries to see it the thread's are alive by calling isAlive() on them.

So, obviously, if you've bound an object to the JNDI which is NOT a Thread, then the cast operation fails. Here's the code:

        while(table.size() > 0 || subContexts.size() > 0) {
            it = table.keySet().iterator();
            while(it.hasNext()) {
                Name name = (Name)it.next();
                if(!((Thread)table.get(name)).isAlive()) {
                    table.remove(name);
                }
            }

I don't understand why the author does this (Thread casting and calling isAlive()), however, this is the cause of your problem. And mine, too... :)

Here's a JUnit 4 test to reproduce the problem:

package org.falez.falez.jndi.test;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.junit.Before;
import org.junit.Test;

public class TestMemoryContext {
InitialContext ic;
@Before
public void setup() throws NamingException {
System.setProperty("java.naming.factory.initial", "org.osjava.sj.memory.MemoryContextFactory");
ic = new InitialContext();
}
@Test
public void testPut() throws NamingException {
String name = "falez:/hoba";
String value = "hoba1";
ic.bind(name, value);
String result = (String) ic.lookup(name);
ic.close();
}
}
Comment by bulenterdemir [ Sun, 17 Feb 2008 11:43:51 -0800 (PST) ]
Another problem with the MemoryContext is that, the close() method on the AbstractContext, literally closes everything. :)
This means, it releases all the resources related to the particular context: unbind objects, frees up the internal HashTable, etc.
This essentially destroys the Jndi registry.

However, the close() call should deallocate the resources the JNDI client uses to connect to the JNDI contect. For example, for a remote JNDI connection, the socket should be closed.
In other words, when close() is called, the registry should continue to live.

If you execute the code below:
Context ctx = new InitialContext();
ic.bind("falez:/hoba", "hoba");
ic.close();

the object bound is lost forever. If you create another InitialContext() and try to lookup for the object just bound, you won't find it there.

Normally, the resources used to connect to the registry should be freed. Not the JNDI registry itself.

Another implication is, if you use Spring's JndiTemplate to access the JNDI registry, you won't be able to use it all because JndiTemplate automatically closes the created InitialContext for every expression it executes. Therefore, after every call, the JNDI registry is destroyed.

As a result, AbstractContext's close() method should be empty.

Regards,
Bulent Erdemir
Comment by bayard [ Sat, 22 Mar 2008 13:21:56 -0700 (PDT) ]
svn ci -m "Applying Bulent Erdemir's unit test and protecting from the ClassCastException as per SJN-77"

Sending simple-jndi/src/java/org/osjava/sj/jndi/AbstractContext.java
Adding simple-jndi/src/test/org/osjava/sj/Sjn77Test.java
Transmitting file data ..
Committed revision 2657.
Comment by bayard [ Sat, 22 Mar 2008 13:22:23 -0700 (PDT) ]
Still need to deal with the question of what should be in the close() method.
Comment by bayard [ Sat, 22 Mar 2008 13:25:56 -0700 (PDT) ]
Doesn't fix the NotContextException.
Comment by bayard [ Sat, 22 Mar 2008 13:49:01 -0700 (PDT) ]
Latter problem is because StaticHashTable is used twice; which makes it the same table.

svn ci -m "Making it so there can be more than one StaticHashtable. This fixes the NotContextException reported in SJN-77"

Sending simple-jndi/src/java/org/osjava/sj/jndi/AbstractContext.java
Sending simple-jndi/src/java/org/osjava/sj/jndi/StaticHashtable.java
Sending simple-jndi/src/test/org/osjava/sj/Sjn77Test.java
Transmitting file data ...
Committed revision 2658.
Comment by bayard [ Sun, 6 Apr 2008 00:31:01 -0700 (PDT) ]
As for what should be in the close() method - that's the reason why the Static option exists. In that instance the registry exists beyond the mere object that was created.

So I believe this issue can be closed.