You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Christopher Schultz <ch...@christopherschultz.net> on 2018/03/06 15:43:45 UTC

ClassNotFoundException when calling custom MBean operation

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

All,

I think I already know the answer to this question, but I'm going to
ask anyway in case it helps others understand what's going on.

I have a custom MBean for a loadable-object in my web application.
This is an object that lives in the application-scope and needs to be
periodically re-loaded from our relational database when we add new
data to it.

The bean looks like this:

    public static class ReloadableObjectBean
        implements ReloadableObjectBeanMBean
    {
        private ReloadableObject _rlo;
        private Date _loadTime;

        public ReloadableObjectBean()
        {
            _rlo = null;
            _loadTime = null;
        }

        public ReloadableObjectBean(ReloadableObject dxe)
        {
            _loadTime = new Date();
            _rlo = dxe;
        }

        @Override
        public int getItemCount() {
            return _rlo.getItems().size();
        }

        protected Date getLoadTimeInternal()
        {
            return _loadTime;
        }

        @Override
        public Date getLoadTime() {
            return (Date)getLoadTimeInternal().clone();
        }

        @Override
        public void reload() throws ReloadableObjectException,
ServiceException
        {
            ReloadableObject rlo =
ReloadableObjectHelper.newReloadableObject();
            _loadTime = new Date();
            _rlo = rlo;
        }
    }

This is an inner class defined in one of my ServletContextListeners,
and it's inserted into the MBeanServer like this:

        ReloadableObject rlo =
ReloadableObjectHelper.newReloadableObject();

        try {
            MBeanServer mbs = getServer();
            ObjectName objectName = new
ObjectName("com.chadis:type=ReloadableObject");

            if(mbs.isRegistered(objectName))
                mbs.unregisterMBean(objectName);

            mbs.registerMBean(new ReloadableObjectBean(rlo), objectName)
;
        } catch (MBeanException mbe) {
            logger.warn("Cannot register MBean", mbe);
        } catch (InstanceAlreadyExistsException iaee) {
            logger.warn("Cannot register MBean", iaee);
        } catch (NotCompliantMBeanException ncme) {
            logger.warn("Cannot register MBean", ncme);
        } catch (MalformedObjectNameException mone) {
            logger.warn("Cannot register MBean", mone);
        } catch (InstanceNotFoundException infe) {
            logger.warn("Cannot de-register MBean", infe);
        }

        application.setAttribute("reloadbleObject", rlo);

Everything goes well until I try to invoke the "reload" operation on
this MBean from a JMX client, where I get ClassNotFoundException:

2018-03-06 10:26:07,271 [RMI TCP Connection(1)-169.254.211.40] FATAL
ReloadableObjectHelper- Could not load ReloadableObject:
ServiceException: Cannot obtain database connection
	at ReloadableObjectHelper.getConnection(ReloadableObjectHelper.java:147
)
	at
ReloadableObjectHelper.newDiagnosisEngine(ReloadableObjectHelper.java:55
)
	at InitListener$ReloadableObjectBean.reloadEngine(InitListener.java:159
)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.jav
a:62)
    ....
Caused by:
Caused by: javax.naming.NoInitialContextException: Cannot instantiate
class: org.apache.naming.java.javaURLContextFactory [Root exception is
java.lang.ClassNotFoundException:
org.apache.naming.java.javaURLContextFactory]
	at
javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:674)
	at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:31
3)
	at javax.naming.InitialContext.init(InitialContext.java:244)
	at javax.naming.InitialContext.<init>(InitialContext.java:192)
	at ReloadbleObjectHelper.getConnection(ReloadableObjectHelper.java:136)
	... 39 more
Caused by: java.lang.ClassNotFoundException:
org.apache.naming.java.javaURLContextFactory
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at
com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:7
2)
	at
com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:6
1)
	at
javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:672)
	... 43 more


The error I get on VisualVM (my JMX client, here) is that it can't
load my ServiceException class -- one that is defined only within the
web application.

I suspect the problem is that the thread's context class loader (TCCL)
is set to Tomcat's ClassLoader, since the request is being handled by
Tomcat's internal JMX server. If I were to invoke this operation via
the Manager's JMXProxyServlet, I'd probably be dealing with the
Manager's WebappClassLoader, instead, but the problem would be the
same: those ClassLoaders are unaware of my application's classes.

I *believe* the solution is to change the TCCL in this "reload"
method, but that means I'll need to capture the TCCL during the
invocation of the MBean itself and hang on to it... something like this:

        ClassLoader originalCL =
Thread.currentThread().getContextClassLoader();
        ReloadableObject rlo =
ReloadableObjectHelper.newReloadableObject();

        try {
            MBeanServer mbs = getServer();
            ObjectName objectName = new
ObjectName("com.chadis:type=ReloadableObject");

            if(mbs.isRegistered(objectName))
                mbs.unregisterMBean(objectName);

            mbs.registerMBean(new ReloadableObjectBean(rlo,
originalCL), objectName);
        }

Then, later, in the reload() method:


        @Override
        public void reload() throws ReloadableObjectException,
ServiceException
        {
            ClassLoader tccl =
Thread.currentThread().getContextClassLoader();

            try {
                Thread.currentThread().setContextClassLoader(originalCL)
;

                ReloadableObject rlo =
ReloadableObjectHelper.newReloadableObject();
                _loadTime = new Date();
                _rlo = rlo;
            } finally {
                Thread.currentThread().setContextClassLoader(tccl);
            }
        }

Does that sound about right?

I'll probably want to future-proof it by wrapping those calls into
PrivilegedActions, etc. but is this the right approach for what I'm
trying to do, here? Or am I missing something?

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQJRBAEBCAA7FiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAlqetzEdHGNocmlzQGNo
cmlzdG9waGVyc2NodWx0ei5uZXQACgkQHPApP6U8pFgN8RAAwaaFjruUtxisKCrl
SvCxe+8UYbVjOq889x8UdBirnt0fQI6q53W4q51l72pvJ4yi83R1XrtjjcltbQ+4
A/OXS6VKg0L9gY6heM38LRp6Yp0Jjy9jIMjNMxCdjU9hqqfT+7jWinvxelFpMfFj
QXMILFHD0BRkwpc83ntyFGG/9i0cGdzaYb28GhvCff+48gk0CF7T2IO4VNN+UvYU
TnWbfiPhO8ng+Cqja4QwrMv2CcBvTp4OuFU7rg/hBAZ545H2hBIPhzaVMCTzqBdO
z1UOHhluBc2srvoVzXg7OlX4w4PRShu4QqvYECm/JQBKa8kWlgfLQgeKJbms6xc8
4Y+tuEZMaROPVITpncyj24gur+gD3PjZh4+JRKucdZg5Bt/ci7Rb9lpvwJSL9pmq
pV05I4tXakU5JibTNFydZNEn6vHWEz9DuQeYr2EQb4JeJ81FlOAuuqF6kgBVRINQ
F0RCdW5D8BXh2QLs9/ijUnnUbk83rmbah/4zKYdHGtlsiaJ1lmhZmmfAvZRek1vK
8NZ6A0/YaGw34LK2bYEctllZYSBs6G2AMQgO0KPU09jDXe4bVfjz00XR96A+1cX1
RNL/jRhcZlfizPbLtg4GDwxWzJj664p+1F236Zg8haUjQXI4WrV6UaeCf5X3SIdQ
rxhOvK+OGoSOl6TWP5OaqMHrKaQ=
=snnR
-----END PGP SIGNATURE-----

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


Re: ClassNotFoundException when calling custom MBean operation

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Konstantin,

On 3/6/18 8:26 PM, Konstantin Kolinko wrote:
> 2018-03-06 18:43 GMT+03:00 Christopher Schultz
> <ch...@christopherschultz.net>:
>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256
>> 
>> All,
>> 
>> I think I already know the answer to this question, but I'm going
>> to ask anyway in case it helps others understand what's going
>> on.
>> 
>> I have a custom MBean for a loadable-object in my web
>> application. This is an object that lives in the
>> application-scope and needs to be periodically re-loaded from our
>> relational database when we add new data to it.
>> 
>> The bean looks like this:
>> 
>> public static class ReloadableObjectBean implements
>> ReloadableObjectBeanMBean { private ReloadableObject _rlo; 
>> private Date _loadTime;
>> 
>> public ReloadableObjectBean() { _rlo = null; _loadTime = null; }
>> 
>> public ReloadableObjectBean(ReloadableObject dxe) { _loadTime =
>> new Date(); _rlo = dxe; }
>> 
>> @Override public int getItemCount() { return
>> _rlo.getItems().size(); }
>> 
>> protected Date getLoadTimeInternal() { return _loadTime; }
>> 
>> @Override public Date getLoadTime() { return
>> (Date)getLoadTimeInternal().clone(); }
>> 
>> @Override public void reload() throws ReloadableObjectException, 
>> ServiceException { ReloadableObject rlo = 
>> ReloadableObjectHelper.newReloadableObject(); _loadTime = new
>> Date(); _rlo = rlo; } }
>> 
>> This is an inner class defined in one of my
>> ServletContextListeners, and it's inserted into the MBeanServer
>> like this:
>> 
>> ReloadableObject rlo = 
>> ReloadableObjectHelper.newReloadableObject();
>> 
>> try { MBeanServer mbs = getServer(); ObjectName objectName = new 
>> ObjectName("com.chadis:type=ReloadableObject");
>> 
>> if(mbs.isRegistered(objectName)) 
>> mbs.unregisterMBean(objectName);
>> 
>> mbs.registerMBean(new ReloadableObjectBean(rlo), objectName) ; }
>> catch (MBeanException mbe) { logger.warn("Cannot register MBean",
>> mbe); } catch (InstanceAlreadyExistsException iaee) { 
>> logger.warn("Cannot register MBean", iaee); } catch
>> (NotCompliantMBeanException ncme) { logger.warn("Cannot register
>> MBean", ncme); } catch (MalformedObjectNameException mone) { 
>> logger.warn("Cannot register MBean", mone); } catch
>> (InstanceNotFoundException infe) { logger.warn("Cannot
>> de-register MBean", infe); }
>> 
>> application.setAttribute("reloadbleObject", rlo);
>> 
>> Everything goes well until I try to invoke the "reload" operation
>> on this MBean from a JMX client, where I get
>> ClassNotFoundException:
>> 
>> 2018-03-06 10:26:07,271 [RMI TCP Connection(1)-169.254.211.40]
>> FATAL ReloadableObjectHelper- Could not load ReloadableObject: 
>> ServiceException: Cannot obtain database connection at
>> ReloadableObjectHelper.getConnection(ReloadableObjectHelper.java:147
>>
>> 
)
>> at 
>> ReloadableObjectHelper.newDiagnosisEngine(ReloadableObjectHelper.java
:55
>>
>> 
)
>> at
>> InitListener$ReloadableObjectBean.reloadEngine(InitListener.java:159
>>
>> 
)
>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
>> at 
>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
jav
>>
>> 
a:62)
>> .... Caused by: Caused by:
>> javax.naming.NoInitialContextException: Cannot instantiate class:
>> org.apache.naming.java.javaURLContextFactory [Root exception is 
>> java.lang.ClassNotFoundException: 
>> org.apache.naming.java.javaURLContextFactory] at 
>> javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:6
74)
>>
>> 
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:31
>> 3) at javax.naming.InitialContext.init(InitialContext.java:244) 
>> at javax.naming.InitialContext.<init>(InitialContext.java:192) at
>> ReloadbleObjectHelper.getConnection(ReloadableObjectHelper.java:136)
>>
>> 
... 39 more
>> Caused by: java.lang.ClassNotFoundException: 
>> org.apache.naming.java.javaURLContextFactory at
>> java.net.URLClassLoader.findClass(URLClassLoader.java:381) at
>> java.lang.ClassLoader.loadClass(ClassLoader.java:424) at
>> sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at
>> java.lang.ClassLoader.loadClass(ClassLoader.java:357) at
>> java.lang.Class.forName0(Native Method) at
>> java.lang.Class.forName(Class.java:348) at 
>> com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.jav
a:7
>>
>> 
2)
>> at 
>> com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.jav
a:6
>>
>> 
1)
>> at 
>> javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:6
72)
>>
>> 
... 43 more
>> 
>> 
>> The error I get on VisualVM (my JMX client, here) is that it
>> can't load my ServiceException class -- one that is defined only
>> within the web application.
>> 
>> I suspect the problem is that the thread's context class loader
>> (TCCL) is set to Tomcat's ClassLoader, since the request is being
>> handled by Tomcat's internal JMX server. If I were to invoke this
>> operation via the Manager's JMXProxyServlet, I'd probably be
>> dealing with the Manager's WebappClassLoader, instead, but the
>> problem would be the same: those ClassLoaders are unaware of my
>> application's classes.
>> 
>> I *believe* the solution is to change the TCCL in this "reload" 
>> method, but that means I'll need to capture the TCCL during the 
>> invocation of the MBean itself and hang on to it... something
>> like this:
>> 
>> ClassLoader originalCL = 
>> Thread.currentThread().getContextClassLoader(); ReloadableObject
>> rlo = ReloadableObjectHelper.newReloadableObject();
>> 
>> try { MBeanServer mbs = getServer(); ObjectName objectName = new 
>> ObjectName("com.chadis:type=ReloadableObject");
>> 
>> if(mbs.isRegistered(objectName)) 
>> mbs.unregisterMBean(objectName);
>> 
>> mbs.registerMBean(new ReloadableObjectBean(rlo, originalCL),
>> objectName); }
>> 
>> Then, later, in the reload() method:
>> 
>> 
>> @Override public void reload() throws ReloadableObjectException, 
>> ServiceException { ClassLoader tccl = 
>> Thread.currentThread().getContextClassLoader();
>> 
>> try { Thread.currentThread().setContextClassLoader(originalCL) ;
>> 
>> ReloadableObject rlo = 
>> ReloadableObjectHelper.newReloadableObject(); _loadTime = new
>> Date(); _rlo = rlo; } finally { 
>> Thread.currentThread().setContextClassLoader(tccl); } }
>> 
>> Does that sound about right?
>> 
>> I'll probably want to future-proof it by wrapping those calls
>> into PrivilegedActions, etc. but is this the right approach for
>> what I'm trying to do, here? Or am I missing something?
>> 
> 
> From those error messages, it looks like your code does a JNDI
> lookup.

Yes.

> And the lookup fails with a NamingException (a 
> javax.naming.NoInitialContextException to be specific, but that is 
> just a detail).
> 
> A JNDI tree is local to a web application. Each web application has
> an independent JNDI tree. Selecting the correct one uses TCCL as
> the key.

That's what I thought. So, I just have to play with the TCCL.

> So if you TCCL class loader is not set correctly, you won't be able
> to access JNDI.
> 
> If you are interested in implementation details, see 
> org.apache.naming.SelectorContext 
> org.apache.naming.ContextBindings 
> org.apache.naming.java.javaURLContextFactory
> 
> 
> On the question on what is the best way to change and restore
> TCCL: Current code uses a helper API method, StandardContext.bind()
> / unbind().

This bean is being defined by my application and loaded from within
it. I'd prefer not to have a compile-time dependency on Tomcat itself.

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQIzBAEBCAAdFiEEMmKgYcQvxMe7tcJcHPApP6U8pFgFAlqf+ikACgkQHPApP6U8
pFgV7xAAmeQIBYhVlMvsjACJT2NuuxPTe7pdUuiczGe/5GSIpJ/gqfLMRu4nwzns
2IG0uTiC6vH4q3PIvbuNa0VWmy9q0+hF9lISQSj4Pt8bb6PICfqW+6gJPt2IKHDx
Rt6cWwJZQy53Nn6UGRY8eIzr/oZXkmSg0wEOq/rcqBSLUPk7SGnxBd2gW9Fy6KYz
gzqB/esgsMRZqw++DH4AKDoGkm1nD7z1qUTLvDlqzKwupEs8M+XA6F+QkBGCo6PP
sc2tghn2P0yuOrcrNdE9Typr5UMcHerDzT3KtZ1rHi36RMTLWoWza7/w2xC+VIJi
Ofmezhi8PlLhFtiw13T1NFY+gHs23eLpJ7P9v/Vx8zpUVqIje8y7dm/RjikgKCF7
L+IMz6yQv3QRMVYgsW7+3HYw8tTEqlkdl2H3HyU+ac6jUButUqvsVlTPNRssNwRq
xTAwjAUa2HDLIxq0wtpds76VzmWSgBWSmCby8NDCJuHR6fQtGamjR/yxeufyXoKy
+lZla6U4kPuBdo6iNqwSlIluvJyvVpCdkNh1R89bew9QyqK1bSlGg0tLVUXlVeK/
XBQWgOqFB6Ge2ulbTAVto0clKaHu2/93nIyGMz3UnO0xRMOgqoAs8PXYOcGozV5D
ouFCeDo5e3bovL3Obdg49CtLZNve7gEwSuPpakJuvDBhQ1+/J74=
=gg2l
-----END PGP SIGNATURE-----

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


Re: ClassNotFoundException when calling custom MBean operation

Posted by Konstantin Kolinko <kn...@gmail.com>.
2018-03-06 18:43 GMT+03:00 Christopher Schultz <ch...@christopherschultz.net>:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA256
>
> All,
>
> I think I already know the answer to this question, but I'm going to
> ask anyway in case it helps others understand what's going on.
>
> I have a custom MBean for a loadable-object in my web application.
> This is an object that lives in the application-scope and needs to be
> periodically re-loaded from our relational database when we add new
> data to it.
>
> The bean looks like this:
>
>     public static class ReloadableObjectBean
>         implements ReloadableObjectBeanMBean
>     {
>         private ReloadableObject _rlo;
>         private Date _loadTime;
>
>         public ReloadableObjectBean()
>         {
>             _rlo = null;
>             _loadTime = null;
>         }
>
>         public ReloadableObjectBean(ReloadableObject dxe)
>         {
>             _loadTime = new Date();
>             _rlo = dxe;
>         }
>
>         @Override
>         public int getItemCount() {
>             return _rlo.getItems().size();
>         }
>
>         protected Date getLoadTimeInternal()
>         {
>             return _loadTime;
>         }
>
>         @Override
>         public Date getLoadTime() {
>             return (Date)getLoadTimeInternal().clone();
>         }
>
>         @Override
>         public void reload() throws ReloadableObjectException,
> ServiceException
>         {
>             ReloadableObject rlo =
> ReloadableObjectHelper.newReloadableObject();
>             _loadTime = new Date();
>             _rlo = rlo;
>         }
>     }
>
> This is an inner class defined in one of my ServletContextListeners,
> and it's inserted into the MBeanServer like this:
>
>         ReloadableObject rlo =
> ReloadableObjectHelper.newReloadableObject();
>
>         try {
>             MBeanServer mbs = getServer();
>             ObjectName objectName = new
> ObjectName("com.chadis:type=ReloadableObject");
>
>             if(mbs.isRegistered(objectName))
>                 mbs.unregisterMBean(objectName);
>
>             mbs.registerMBean(new ReloadableObjectBean(rlo), objectName)
> ;
>         } catch (MBeanException mbe) {
>             logger.warn("Cannot register MBean", mbe);
>         } catch (InstanceAlreadyExistsException iaee) {
>             logger.warn("Cannot register MBean", iaee);
>         } catch (NotCompliantMBeanException ncme) {
>             logger.warn("Cannot register MBean", ncme);
>         } catch (MalformedObjectNameException mone) {
>             logger.warn("Cannot register MBean", mone);
>         } catch (InstanceNotFoundException infe) {
>             logger.warn("Cannot de-register MBean", infe);
>         }
>
>         application.setAttribute("reloadbleObject", rlo);
>
> Everything goes well until I try to invoke the "reload" operation on
> this MBean from a JMX client, where I get ClassNotFoundException:
>
> 2018-03-06 10:26:07,271 [RMI TCP Connection(1)-169.254.211.40] FATAL
> ReloadableObjectHelper- Could not load ReloadableObject:
> ServiceException: Cannot obtain database connection
>         at ReloadableObjectHelper.getConnection(ReloadableObjectHelper.java:147
> )
>         at
> ReloadableObjectHelper.newDiagnosisEngine(ReloadableObjectHelper.java:55
> )
>         at InitListener$ReloadableObjectBean.reloadEngine(InitListener.java:159
> )
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.jav
> a:62)
>     ....
> Caused by:
> Caused by: javax.naming.NoInitialContextException: Cannot instantiate
> class: org.apache.naming.java.javaURLContextFactory [Root exception is
> java.lang.ClassNotFoundException:
> org.apache.naming.java.javaURLContextFactory]
>         at
> javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:674)
>         at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:31
> 3)
>         at javax.naming.InitialContext.init(InitialContext.java:244)
>         at javax.naming.InitialContext.<init>(InitialContext.java:192)
>         at ReloadbleObjectHelper.getConnection(ReloadableObjectHelper.java:136)
>         ... 39 more
> Caused by: java.lang.ClassNotFoundException:
> org.apache.naming.java.javaURLContextFactory
>         at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
>         at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
>         at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
>         at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
>         at java.lang.Class.forName0(Native Method)
>         at java.lang.Class.forName(Class.java:348)
>         at
> com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:7
> 2)
>         at
> com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:6
> 1)
>         at
> javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:672)
>         ... 43 more
>
>
> The error I get on VisualVM (my JMX client, here) is that it can't
> load my ServiceException class -- one that is defined only within the
> web application.
>
> I suspect the problem is that the thread's context class loader (TCCL)
> is set to Tomcat's ClassLoader, since the request is being handled by
> Tomcat's internal JMX server. If I were to invoke this operation via
> the Manager's JMXProxyServlet, I'd probably be dealing with the
> Manager's WebappClassLoader, instead, but the problem would be the
> same: those ClassLoaders are unaware of my application's classes.
>
> I *believe* the solution is to change the TCCL in this "reload"
> method, but that means I'll need to capture the TCCL during the
> invocation of the MBean itself and hang on to it... something like this:
>
>         ClassLoader originalCL =
> Thread.currentThread().getContextClassLoader();
>         ReloadableObject rlo =
> ReloadableObjectHelper.newReloadableObject();
>
>         try {
>             MBeanServer mbs = getServer();
>             ObjectName objectName = new
> ObjectName("com.chadis:type=ReloadableObject");
>
>             if(mbs.isRegistered(objectName))
>                 mbs.unregisterMBean(objectName);
>
>             mbs.registerMBean(new ReloadableObjectBean(rlo,
> originalCL), objectName);
>         }
>
> Then, later, in the reload() method:
>
>
>         @Override
>         public void reload() throws ReloadableObjectException,
> ServiceException
>         {
>             ClassLoader tccl =
> Thread.currentThread().getContextClassLoader();
>
>             try {
>                 Thread.currentThread().setContextClassLoader(originalCL)
> ;
>
>                 ReloadableObject rlo =
> ReloadableObjectHelper.newReloadableObject();
>                 _loadTime = new Date();
>                 _rlo = rlo;
>             } finally {
>                 Thread.currentThread().setContextClassLoader(tccl);
>             }
>         }
>
> Does that sound about right?
>
> I'll probably want to future-proof it by wrapping those calls into
> PrivilegedActions, etc. but is this the right approach for what I'm
> trying to do, here? Or am I missing something?
>

From those error messages, it looks like your code does a JNDI lookup.

And the lookup fails with a NamingException (a
javax.naming.NoInitialContextException to be specific, but that is
just a detail).

A JNDI tree is local to a web application. Each web application has an
independent JNDI tree. Selecting the correct one uses TCCL as the key.

So if you TCCL class loader is not set correctly, you won't be able to
access JNDI.

If you are interested in implementation details, see
org.apache.naming.SelectorContext
org.apache.naming.ContextBindings
org.apache.naming.java.javaURLContextFactory


On the question on what is the best way to change and restore TCCL:
Current code uses a helper API method, StandardContext.bind() / unbind().


Best regards,
Konstantin Kolinko

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