I believe that the Context.listBindings(...) methods should return an Enumeration that returns resolved Objects, not references. The current implementation does not make an effort to resolve References to Objects prior to returning the Binding. It also should hold Exceptions until the enumeration has been fully traversed. A possible patch is noted below (verified against 1.4, spot-checked for pre-1.4). Index: NamingContextBindingsEnumeration.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/NamingContextBindingsEnumeration.java,v retrieving revision 1.1 diff -u -r1.1 NamingContextBindingsEnumeration.java --- NamingContextBindingsEnumeration.java 2 Nov 2000 06:14:16 -0000 1.1 +++ NamingContextBindingsEnumeration.java 20 Oct 2003 14:58:28 -0000 @@ -67,9 +67,12 @@ import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; +import javax.naming.CompositeName; +import javax.naming.Context; import javax.naming.NamingException; import javax.naming.NamingEnumeration; import javax.naming.Binding; +import javax.naming.spi.NamingManager; /** * Naming enumeration implementation. @@ -85,13 +88,17 @@ // ----------------------------------------------------------- Constructors - public NamingContextBindingsEnumeration(Vector entries) { + public NamingContextBindingsEnumeration(Vector entries, Context ctx, Hashtable env) { enum = entries.elements(); + this.ctx = ctx; + this.env = env; } - public NamingContextBindingsEnumeration(Enumeration enum) { + public NamingContextBindingsEnumeration(Enumeration enum, Context ctx, Hashtable env) { this.enum = enum; + this.ctx = ctx; + this.env = env; } @@ -103,6 +110,11 @@ */ protected Enumeration enum; + private Hashtable env; + private Context ctx; + private Binding next; + private Exception trouble; + private boolean runtimeExc; // --------------------------------------------------------- Public Methods @@ -110,9 +122,10 @@ /** * Retrieves the next element in the enumeration. */ - public Object next() - throws NamingException { - return nextElement(); + public Object next() { + Object ret = next; + next = null; + return ret; } @@ -121,7 +134,35 @@ */ public boolean hasMore() throws NamingException { - return enum.hasMoreElements(); + if (next != null) { + return true; + } + if (enum.hasMoreElements()) { + try { + NamingEntry entry = (NamingEntry)enum.nextElement(); + String name = entry.name; + Object obj = NamingManager.getObjectInstance(entry.value, new CompositeName(name), ctx, env); + next = new Binding(name, entry.value.getClass().getName(), obj, true); + return true; + } catch (NamingException e) { + trouble = e; + } catch (RuntimeException e) { + trouble = e; + runtimeExc = true; + } catch (Exception e) { + NamingException ne = new NamingException(); + ne.setRootCause(e); + trouble = ne; + } + } + if (trouble != null) { + if (runtimeExc) { + throw (RuntimeException)trouble; + } else { + throw (NamingException)trouble; + } + } + return false; } @@ -133,16 +174,17 @@ } - public boolean hasMoreElements() { - return enum.hasMoreElements(); - } - - - public Object nextElement() { - NamingEntry entry = (NamingEntry) enum.nextElement(); - return new Binding(entry.name, entry.value.getClass().getName(), - entry.value, true); - } + public boolean hasMoreElements() { + try { + return hasMore(); + } catch (NamingException e) { + return false; + } + } + + public Object nextElement() { + return next(); + } } Index: NamingContext.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/NamingContext.java,v retrieving revision 1.8 diff -u -r1.8 NamingContext.java --- NamingContext.java 8 Nov 2001 21:02:04 -0000 1.8 +++ NamingContext.java 20 Oct 2003 14:58:38 -0000 @@ -429,7 +429,7 @@ while ((!name.isEmpty()) && (name.get(0).length() == 0)) name = name.getSuffix(1); if (name.isEmpty()) { - return new NamingContextBindingsEnumeration(bindings.elements()); + return new NamingContextBindingsEnumeration(bindings.elements(), this, env); } NamingEntry entry = (NamingEntry) bindings.get(name.get(0)); Index: resources/FileDirContext.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/resources/FileDirContext.java,v retrieving revision 1.15 diff -u -r1.15 FileDirContext.java --- resources/FileDirContext.java 6 Nov 2002 09:57:17 -0000 1.15 +++ resources/FileDirContext.java 20 Oct 2003 14:59:01 -0000 @@ -389,7 +389,7 @@ Vector entries = list(file); - return new NamingContextBindingsEnumeration(entries); + return new NamingContextBindingsEnumeration(entries, this, env); } Index: resources/WARDirContext.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/resources/WARDirContext.java,v retrieving revision 1.5 diff -u -r1.5 WARDirContext.java --- resources/WARDirContext.java 28 Feb 2002 07:04:36 -0000 1.5 +++ resources/WARDirContext.java 20 Oct 2003 14:59:10 -0000 @@ -374,12 +374,12 @@ public NamingEnumeration listBindings(Name name) throws NamingException { if (name.isEmpty()) - return new NamingContextBindingsEnumeration(list(entries)); + return new NamingContextBindingsEnumeration(list(entries), this, env); Entry entry = treeLookup(name); if (entry == null) throw new NamingException (sm.getString("resources.notFound", name)); - return new NamingContextBindingsEnumeration(list(entry)); + return new NamingContextBindingsEnumeration(list(entry), this, env); }
I have tested list() and listBindings() for NamingContext and FileDirContext and do not see anything in the values returned to suggest that the values returned do not conform to the Context.list() and Context.listBindings() interface. Given that the behaviour is as expected, I see no need to apply the suggested patch. If you have a test case that does demonstrate a problem, please provide it. Without any such test case no further action will be taken on this issue which will be marked as invalid. Also, it should be noted that the patch as is has a problem in that next() can no longer be called consecutively. A call to hasMore() is required before next() can be called.
Created attachment 17315 [details] Possible patch to address this issue Here is an updated (and less ambitious) patch that should address the listBinding issue noted.
Created attachment 17316 [details] server.xml snippet to support test case
Created attachment 17317 [details] jar file containing test ObjectFactory and Object classes and sources
Created attachment 17318 [details] JSP to support test case
Sorry about the lack of clarification on this one. The problem seems to arise when using a custom ObjectFactory. To reproduce the problem: place uploaded JSP into webapp place uploaded (ObjectFactory) jar file in WEB-INF/lib edit server.xml to uploaded snippet: <Resource name="list/foo" type="TestObject"/> <ResourceParams name="list/foo"> <parameter><name>factory</name><value>factory.TestObjectFactory</value></paramet er> </ResourceParams> <Environment name="list/num" type="java.lang.Integer" value="42"/> The JSP verifies that the listBinding method return an appropriate class type for the foo object in the list Context. The current implementation returns an instance of org.apache.naming.ResourceRef instead of TestObject. The listBindings method seems to work fine for primitives defined as Environment objects. The patch included is against the latest branch of Tomcat (I'm not sure how the naming subpackage is distributed and whether the changes can apply to less recent versions). In an effort to maintain pre-1.4 compatibility some compromises are made in the exception handling of the nextElement and next methods -- it would be preferable for it use a 1.4 nested Exception. I hope this helps. Thanks!
Thanks for the clarification and a special thanks for the test case. I think I can see what the problem is. Have a quick read of section 5.1.5 of this link first http://java.sun.com/j2se/1.4.2/docs/guide/jndi/spec/jndi/jndi.5.html Some of the objects being returned, including those created by a custom ObjectFactory are References. To quote part of the doc referenced above "When the result of an operation such as Context.lookup() or Binding.getObject() is a Reference object, JNDI attempts to convert the reference into the object that it represents before returning it to the client." As far as I can see, there is no code in Binding.getObject() that will do this. Tomcat implements it's own Context and there is code in NamingContext.lookup() that resolves references (aka external links). It isn't completely clear to me right now where this lookup should occur in the case of Context.listBindings() but as far as I can tell, if we add a <code>if (entry.value instanceof Reference)</code> test in NamingContextBindingsEnumeration and resolve any References we find it should fix the problem. I'll work on a patch to do this.
Upon further investigation it seems like the resolution from Reference to Object should always be handled by NamingContext. This allows NamingContext to replace its internally stored Reference with the resolved Object, which would not happen if resolution was managed directly by NamingContextBindingsEnumeration (including my patch). If it is not implemented this way then Objects with the same name returned from listBindings (...) may not equal those returned by lookup(...). I don't know if this is contractually required by the JNDI SPI but it seems safer to ensure equality.
I have applied a fix for this to TC4 and TC5 that uses NamingContext.lookup() to resolve references. See http://marc.theaimsgroup.com/?l=tomcat-dev&m=113649730824960&w=2