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