[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH]: ClassLoader problem in Servlet environment (Tomcat)



Falko, thank you for your detailed and to-the-point response.  I must admit, I am still
learning tons every day about the intricacies of Java (and esp. ClassLoaders)!  After
re-reading you message a few times and reading a few other resources on ClassLoaders, I
realized that - with your patch - all I needed to do was call
      Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
in each servlet, before the first access to ExternalDatabase or LocalDatabase.  This
approach seems to work with both.

Thanks for your patience with me,
Nathan Probst

P.S.  What is the purpose for having a specialized OzoneClassLoader?  If you could explain
it to me, I feel that I now posess sufficient understanding of ClassLoaders to attempt a
solution.

Falko Braeutigam wrote:

> On Sat, 10 Mar 2001, Nathan Eric Probst wrote:
> > >%_Patch Proposal
> > ==============
> >
> > I've  made changes in LocalDatabase, Env, ClassManager, and OzoneClassLoader to support
> > passing a parent ClassLoader into LocalDatabase for use by OzoneClassLoader.
>
> Honestly I don't like the idea to give LocalDatabase responsibility or even
> knowledge about the ClassLoader. All ClassLoader related things clearly
> should be done by the ClassManager.
>
> >
> > I believe this approach is the most flexible solution to the problem at hand.  Please
> > note that these changes are not well tested and should be reviewed by someone (Falko?)
> > more familiar with the code than I.
>
> I don't see why that much changes are needed. (finding the actually changes in
> your diffs is difficult anyway, please please please have your editor to not
> change lines that are not changed!)
>
> Ok, there are two versions to get a ClassLoader - from a class or from a
> thread. My patch uses the threads ClassLoader, which doesn't work for Tomcat.
> The only line you need to change this is ClassManager.java:53 to use the
> ClassLoader of the current class.
>
> A servlet engine _should_ set the proper context ClassLoader. But there are
> differences. Therefore I will not checkin the above mentioned patch. Please do
> yourself if needed.
>
> >
> > I have this patched version of ozone working well in a Tomcat 3.2.1 environment with no
> > mention of ozone.jar or my application classes in the system classpath.  (It also works
> > with ozone.jar in the classpath.)  All jars and classes are contained in the webapp
> > context.
> >
> > Now for the bad news.  I now get a ClassCastException when I test this with a
> > RemoteDatabase.  This doesn't matter to me at the momment, but I image that it would be
> > a problem for many of you if these changes were commited to CVS!  ;-)  Here's the stack
> > trace:
>
> This exactly is the reason why I simply commented out the use of the
> OzoneClassLoader to fix your problem - is doesn't work! And therefore the
> reloadCLasses() method doesn't work yet.
> The only way to drop classes is to drop their ClassLoader. Hmmm... ok, seems
> to be straight forward. It isn't! Classes that are loaded by different
> CLassLoaders are not compatible. That is, if your com.java...NewsImpl class is
> loaded by ozone's ClassLoader and org.ozoneDB.OzoneCompatible are loader by the
> system's ClassLoader, then (NewsImpl instanceof OzoneCompatible) is false. This
> is the reason why you get this exception. I did not realy try to tackle this
> one because it needs more than just two or threee hours to come up with a
> general solution that fits into current ozone code and model. But this is my
> impression. I would be happy if someone proves me wrong. ;)
>
> Falko
>
> >
> >     java.lang.ClassCastException: com.javawebguy.smg.db.ozone.NewsImpl
> >         at org.ozoneDB.core.AbstractObjectContainer.createTarget(AbstractObje
> >             ctContainer.java:227)
> >         at org.ozoneDB.core.Transaction.createObject(Transaction.java:395)
> >         at org.ozoneDB.core.DbRemote.DbCreateObj.perform(DbCreateObj.java:48)
> >         at org.ozoneDB.core.Transaction.performCommand(Transaction.java:273)
> >         at org.ozoneDB.core.TransactionManager.performCommand(TransactionMana
> >             ger.java:366)
> >         at org.ozoneDB.core.TransactionManager.completeTransaction(Transactio
> >             nManager.java:334)
> >         at org.ozoneDB.core.TransactionManager.handleCommand(TransactionManag
> >             er.java:249)
> >         at org.ozoneDB.core.InvokeServer.handleClientEvent(InvokeServer.java:
> >             76)
> >         at org.ozoneDB.DxLib.net.DxMultiServerClient.run(DxMultiServerClient.
> >             java:44)
> >         at java.lang.Thread.run(Thread.java:484)
> >
> >
> > I would appreciate some help in figuring this one out.
> >
> > Thanks,
> > Nathan Probst
> >
> > Nathan Eric Probst wrote:
> >
> > > Unfortunately, this didn't fix the problem.
> > >
> > > I still get this exception:
> > > org.ozoneDB.ClassNotFoundExc: com.javawebguy.smg.db.ozone.NewsImpl
> > >         at org.ozoneDB.ExternalDatabase.sendCommand(ExternalDatabase.java:508)
> > >         at org.ozoneDB.ExternalDatabase.sendCommand(ExternalDatabase.java:476)
> > >         at org.ozoneDB.ExternalDatabase.createObject(ExternalDatabase.java:708)
> > >         at org.ozoneDB.ExternalDatabase.createObject(ExternalDatabase.java:702)
> > >         at
> > > com.javawebguy.smg.actions.OzoneNewsAction.perform(OzoneNewsAction.java:99)
> > >         at
> > > org.apache.struts.action.ActionServlet.processActionPerform(ActionServlet.java:1620)
> > >
> > >         at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1430)
> > >         at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:464)
> > >         at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
> > >         at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
> > >         at org.apache.tomcat.core.ServletWrapper.doService(ServletWrapper.java:404)
> > >
> > >         at org.apache.tomcat.core.Handler.service(Handler.java:286)
> > >
> > > So, I assumed that the Thread's ContextClassLoader doesnt' work either.  To test
> > > this, I used the folloeing code:
> > >    LocalDatabase db = new LocalDatabase();
> > >    db.open("/tmp/ozoneDB", 3);
> > >    System.out.println( "Connected ..." );
> > >    db.reloadClasses();
> > >
> > >    try {
> > >     System.out.println("Testing `this` class loader...");
> > >     News foo = (News) this.getClass()
> > >                           .getClassLoader()
> > >                           .loadClass(NewsImpl.class.getName())
> > >                           .newInstance();
> > >    } catch (ClassNotFoundException e) {
> > >     e.printStackTrace(System.out);
> > >    }
> > >
> > >    try {
> > >     System.out.println("Testing Thread class loader...");
> > >     News bar = (News) Thread.currentThread()
> > >                             .getContextClassLoader()
> > >                             .loadClass(NewsImpl.class.getName())
> > >                             .newInstance();
> > >    } catch (ClassNotFoundException e) {
> > >     e.printStackTrace(System.out);
> > >    }
> > >
> > > And received the following as a result:
> > > Testing `this` class loader...
> > > Testing Thread class loader...
> > > java.lang.ClassNotFoundException: com.javawebguy.smg.db.ozone.NewsImpl
> > >         at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
> > >         at java.security.AccessController.doPrivileged(Native Method)
> > >         at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
> > >         at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
> > >         at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:286)
> > >         at java.lang.ClassLoader.loadClass(ClassLoader.java:253)
> > >         at
> > > com.javawebguy.smg.actions.OzoneNewsAction.perform(OzoneNewsAction.java:110)
> > >
> > > It seems that using the servlet's ClassLoader would work.  Or, better yet, setting
> > > the servler's ClassLoader as the parent of the OzoneClassLoader, so that when the
> > > OzoneClassLoader delegates up the chain, the required resources are found.  So, I
> > > propose that LocalDatabase be modified to accept a ClassLoader in it's constructor
> > > so that users may optionally choose to specify the parent ClassLoader for the ozone
> > > Env.
> > >
> > > Falko, what do you think?  I've started hacking at this solution, but have had no
> > > luck yet.  Do you know where changes would need to be made to support this
> > > behavior?
> > >
> > > Thanks,
> > > Nathan Probst
> > >
> > > Falko Braeutigam wrote:
> > >
> > > > On Fri, 09 Mar 2001, Falko Braeutigam wrote:
> > > > > On Fri, 09 Mar 2001, Nathan Eric Probst wrote:
> > > >   :
> > > > > > I can make it work with RemoteDatabase by putting my WEB-INF/classes
> > > > > > directory in the classpath when I start ozone, but this is not a good
> > > > > > solution.
> > > > >
> > > > > There are two ClassLoaders in ozone, one for jdk1.1 and one for jdk1.2+. By
> > > > > default the 1.1 is used. This one calls Class.forName() to actually get the
> > > > > class. I'm not sure if Class.forName() uses the threads ClassLoader which is
> > > > > needed to get the servlet ClassLoader which probably has access to the WEB-INF
> > > > > classes. I will check this. Stay tuned.
> > > >
> > > > I've committed a small patch. ClassManager now uses context loader directly
> > > > instead of the ozone loader. Thus the ozone server should now have access to
> > > > all the classes that the servlet engine wants to make available to your servlet
> > > > (WEB-INF/classes).
> > > >
> > > > Falko
> > > >
> > > > >
> > > > >
> > > > > Falko
> > > > >
> > > > > >
> > > > > > In fact, I need to be able to use only a LocalDatabase and no special
> > > > > > classpath treatments.  The app I'm developing will be deployed to a
> > > > > > third-party hosting provider where I will not have control over the
> > > > > > environment.  So, everything need to work from within the servlet
> > > > > > context.
> > > > > >
> > > > > > Please help!
> > > > > >
> > > > > > Thanks,
> > > > > > Nathan Probst
> > > > > --
> > > > > ______________________________________________________________________
> > > > > Falko Braeutigam                             mailto:falko@smb-tec.com
> > > > > SMB GmbH                                       http://www.smb-tec.com
> > > > --
> > > > ______________________________________________________________________
> > > > Falko Braeutigam                             mailto:falko@smb-tec.com
> > > > SMB GmbH                                       http://www.smb-tec.com
> >
>
> ----------------------------------------
> Content-Type: text/plain; name="LocalDatabase.java.udiff"
> Content-Transfer-Encoding: 7bit
> Content-Description:
> ----------------------------------------
>
> ----------------------------------------
> Content-Type: text/plain; name="Env.java.udiff"
> Content-Transfer-Encoding: 7bit
> Content-Description:
> ----------------------------------------
>
> ----------------------------------------
> Content-Type: text/plain; name="OzoneClassLoader.java.udiff"
> Content-Transfer-Encoding: 7bit
> Content-Description:
> ----------------------------------------
>
> ----------------------------------------
> Content-Type: text/plain; name="ClassManager.java.udiff"
> Content-Transfer-Encoding: 7bit
> Content-Description:
> ----------------------------------------
>
> --
> ______________________________________________________________________
> Falko Braeutigam                              mailto:falko@smb-tec.com
> SMB GmbH                                        http://www.smb-tec.com