Bug 23950

Summary: [PATCH] Context.listBindings(...) broken?
Product: Tomcat 4 Reporter: Eli Miller <emiller>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal    
Priority: P3    
Version: Nightly Build   
Target Milestone: ---   
Hardware: All   
OS: All   
Attachments: Possible patch to address this issue
server.xml snippet to support test case
jar file containing test ObjectFactory and Object classes and sources
JSP to support test case

Description Eli Miller 2003-10-20 17:56:35 UTC
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);
     }
Comment 1 Mark Thomas 2005-12-31 19:13:13 UTC
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.
Comment 2 Eli Miller 2006-01-03 16:34:49 UTC
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.
Comment 3 Eli Miller 2006-01-03 16:35:45 UTC
Created attachment 17316 [details]
server.xml snippet to support test case
Comment 4 Eli Miller 2006-01-03 16:37:04 UTC
Created attachment 17317 [details]
jar file containing test ObjectFactory and Object classes and sources
Comment 5 Eli Miller 2006-01-03 16:37:39 UTC
Created attachment 17318 [details]
JSP to support test case
Comment 6 Eli Miller 2006-01-03 16:46:50 UTC
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!
Comment 7 Mark Thomas 2006-01-03 22:41:11 UTC
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.
Comment 8 Eli Miller 2006-01-04 14:41:33 UTC
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.
Comment 9 Mark Thomas 2006-01-05 22:44:20 UTC
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