You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bu...@apache.org on 2009/07/12 08:09:35 UTC

DO NOT REPLY [Bug 47512] New: Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

           Summary: Binding java.lang.reflect.Proxy to JNDI directory
                    raises java.lang.ClassCastException
           Product: Tomcat 6
           Version: 6.0.18
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Catalina
        AssignedTo: dev@tomcat.apache.org
        ReportedBy: mrrtnn@gmail.com


I've created a custom javax.naming.spi.ObjectFactory that is able to bind
javax.naming.Reference(s) into the global JNDI naming directory during startup
time using a org.apache.catalina.LifecycleListener.

I want two separate web contexts invoke the custom
ObjectFactory#getObjectInstance method indirectly, getting through the local to
the global JNDI context with a ResourceLink by invoking the local
InitialContext#lookup method. Since both contexts have different classloaders
my intention is to create a java.lang.reflect.Proxy given the current thread
classloader.

For the first web context, everything goes Ok. The thread reaches the
ObjectFactory, the object factory peeks the current thread classloader (always
a webapp classloader), peeks the name of the interface to use for
Proxy#newProxyInstance method, and the object to proxify for an internal
collection and returns the proxy.

The problem arises for the second web context. The InitialContext#lookup method
resolves the name and, although the thread never reaches the ObjectFactory, the
object is resolved. Surprisingly, the returned object is the proxy created for
the first web context. This proxy implements an interface available in the
first web context's classloader. This interface is present in the second
context classpath, but is loaded by a different webapp classloader. Thus, a
java.lang.ClassCastException is raised.

Digging into the tomcat codebase, I found in org.apache.naming.NamingContext
that once a reference is resolved, the resolved object is sort of rebound to
the original name. Thus, when the second lookup invocation arrives, the name is
associated to an object (not a reference) and the ObjectFactory is never
invoked.

Here's the org.apache.naming.NamingContext code, lines 791 to 799:

 } else if (entry.type == NamingEntry.REFERENCE) {
       try {
          Object obj = NamingManager.getObjectInstance(entry.value, name, this,
env);
             if (obj != null) {
               entry.value = obj;
               entry.type = NamingEntry.ENTRY;
             }
          return obj;

When a NamingEntry.REFERENCE arrive, the reference is resolved with the help of
a javax.naming.spi.NamingManager. If the resolved object is null, the entry is
converted to a NamingEntry.ENTRY instead of leaving it as a
NamingEntry.REFERENCE.

In my opinion, the entry should be left as a REFERENCE, so the next invocation
to the method, no matter who is the invoker nor its classloader, resolves the
reference again with the javax.naming.spi.NamingManager's help. This "cache"
policy of references should be left to the ObjectFactory itself, which will
have all the necessary information to apply such a policy.

The code change would only imply the removal of the if I've shown above.

Regards,

Martin.

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

--- Comment #9 from Martín Schonaker <mr...@gmail.com> 2010-01-06 06:51:50 UTC ---
(In reply to comment #8)
> Whilst binding objects into the global JNDI directory where the classes are
> only available to the web application class loader may be possible, this is
> asking from all sorts of class loading pain and memory leaks.
> 
> On that basis I am marking this as WONTFIX.
> 
> If you want this functionality in your environment the simplest way to achieve
> it is to patch org.apache.naming.NamingContext, compile the class and then
> place the class file in CATALINA_HOME/lib/org/apache/naming/NamingContext.class
> I'd also put the .java file alongside it so people can easily see what you
> changed.

I've found another workaround. Thanks for your time, guys.

Please review these ideas when you have the time. As I said, NamingContext
implementation is flawed and *that's* a cause for class loading pain. Please
allow me to recommend you to check Sun's JNDI/RMI Service Provider (Object
Factory implementation code available here
http://www.docjar.com/html/api/com/sun/jndi/rmi/registry/RegistryContextFactory.java.html)
or JBoss JNP client naming implementation as a reference.

Best Regards.

Martin.

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

Mark Thomas <ma...@apache.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |WONTFIX

--- Comment #8 from Mark Thomas <ma...@apache.org> 2009-12-10 13:24:48 GMT ---
Whilst binding objects into the global JNDI directory where the classes are
only available to the web application class loader may be possible, this is
asking from all sorts of class loading pain and memory leaks.

On that basis I am marking this as WONTFIX.

If you want this functionality in your environment the simplest way to achieve
it is to patch org.apache.naming.NamingContext, compile the class and then
place the class file in CATALINA_HOME/lib/org/apache/naming/NamingContext.class
I'd also put the .java file alongside it so people can easily see what you
changed.

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512





--- Comment #2 from MartinS <mr...@gmail.com>  2009-07-12 18:34:01 PST ---
Created an attachment (id=23963)
 --> (https://issues.apache.org/bugzilla/attachment.cgi?id=23963)
added the patch

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

--- Comment #3 from Tim Funk <fu...@apache.org> 2009-11-03 17:21:20 UTC ---
in trunk as http://svn.apache.org/viewvc?rev=832638&view=rev

updated STATUS.txt for tomcat 6 inclusion

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

--- Comment #7 from Martín Schonaker <mr...@gmail.com> 2009-11-05 19:01:45 UTC ---
(In reply to comment #4)
> So, Martin wants to have a global JNDI entry, that behaves differently
> depending on the caller.
> 
> I think that relying on ObjectFactory to do the trick is wrong. Factories, as
> the name implies, are supposed to be called to create an object. Creation is
> something that is done once. It would be wrong to do it on every access. The
> Javadoc for NamingManager#getObjectInstance() clearly states that it "Creates
> an instance of an object".
> 
> Thus caching the result, as NamingContext#lookup(Name,boolean) does, is valid.
> 
> 
> I think that implementing a custom javax.naming.Context would be one of
> possible proper ways to implement what you do want. Though I have not tried so
> myself, so I do not really know.

Konstantin,

when you say "So, Martin wants to have a global JNDI entry, that behaves
differently depending on the caller." I think you might be forgetting that
ObjectFactories classes are solved in the invoking classloader. Thus, the
behavior will depend on the local ObjectFactory implementation.

For Tomcat programming purposes, it would seem to be enough to put the
ObjectFactory class in the common classloader. Nevertheless, nothing prohibits
someone to fill the classname field (see javax.naming.Rerefence constructor and
#getClassName() method) with name of classes that don't exist in the common
classloader.

So for instance, suppose that I fill classname with "xyz.MyClass" in a
Reference published in the global Tomcat JNDI directory. Suppose that in the
the web context "A" (with its own classloader as you surely already know) that
class exists. When performing a lookup, the class will be successfully
resolved, and an instance returned (depending on object class implementation).

In the same scenario, another lookup but in web context "B" (also containing
xyz.MyClass) happens. org.apache.naming.NamingContext will return the cached
object from context A. Which in turn, cannot be cast to the local xyz.MyClass,
since its class belongs to a different classloader, producing a
ClassCastException.

I reported the issue for java.lang.reflect.Proxy because I was working with
proxies, but this might happen with any Object.

Why I'm trying to implement? to share objects among contexts whose classes are
not know until JNDI-name-resolving time, although present in invoking
classloaders.

The link to customer resource factories won't help. Those factories are always
local to the context they're declared.

I would love to customize naming context implementation, but it's deeply
hardcoded into Tomcat's codebase. Besides, I think the implementation of
Context is just flawed, not wrong.

So, please guys, proof wrong the concept of ObjectFactories are resolved/ran in
the invoking classloader/thread before withdrawing this issue.

Best regards.

Martin.

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

--- Comment #6 from Tim Funk <fu...@apache.org> 2009-11-04 09:53:09 UTC ---
Would this do what you need? (This might be worth moving the the tomcat user
list if you need ideas/help)

http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html#Adding%20Custom%20Resource%20Factories

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512





--- Comment #1 from MartinS <mr...@gmail.com>  2009-07-11 23:26:20 PST ---
errata: in "If the resolved object is null, the entry is
converted to a NamingEntry.ENTRY instead of leaving it as a
NamingEntry.REFERENCE." I meant "if the resolved object is not null".

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

--- Comment #4 from Konstantin Kolinko <kn...@gmail.com> 2009-11-04 01:50:51 UTC ---
So, Martin wants to have a global JNDI entry, that behaves differently
depending on the caller.

I think that relying on ObjectFactory to do the trick is wrong. Factories, as
the name implies, are supposed to be called to create an object. Creation is
something that is done once. It would be wrong to do it on every access. The
Javadoc for NamingManager#getObjectInstance() clearly states that it "Creates
an instance of an object".

Thus caching the result, as NamingContext#lookup(Name,boolean) does, is valid.


I think that implementing a custom javax.naming.Context would be one of
possible proper ways to implement what you do want. Though I have not tried so
myself, so I do not really know.

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


DO NOT REPLY [Bug 47512] Binding java.lang.reflect.Proxy to JNDI directory raises java.lang.ClassCastException

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=47512

--- Comment #5 from Tim Funk <fu...@apache.org> 2009-11-04 08:42:38 UTC ---
patch withdrawn based on feedback

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org