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-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to