[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH]: ClassLoader problem in Servlet environment (Tomcat)
Patch Proposal
==============
I've made changes in LocalDatabase, Env, ClassManager, and OzoneClassLoader to support
passing a parent ClassLoader into LocalDatabase for use by OzoneClassLoader.
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 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:
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
Index: org/ozoneDB/LocalDatabase.java
===================================================================
RCS file: /raid/Repository/ozone/org/ozoneDB/LocalDatabase.java,v
retrieving revision 1.31
diff -u -r1.31 LocalDatabase.java
--- org/ozoneDB/LocalDatabase.java 2001/01/26 19:56:06 1.31
+++ org/ozoneDB/LocalDatabase.java 2001/03/10 07:40:25
@@ -28,13 +28,27 @@
*/
public final class LocalDatabase extends ExternalDatabase {
+ private ClassLoader parentClassLoader;
+
public Env theEnv;
public String userName;
public LocalDatabase() {
+ this((ClassLoader) null);
}
+
+ /**
+ * Make a new LocalDatabase and pass in a ClassLoader. For use in
+ * managed environments such as servlet containers.
+ *
+ * @param parentClassLoader
+ */
+ public LocalDatabase(ClassLoader parentClassLoader) {
+/*XXX*/ System.out.println("LocalDatabase(ClassLoader): this.parentClassLoader = " + parentClassLoader);
+ this.parentClassLoader = parentClassLoader;
+ }
/**
@@ -73,6 +87,7 @@
public void open( String _dirName, int _debugLevel ) throws Exception {
+/*XXX*/ System.out.println("LocalDatabase.open(String _dirName, int _debugLevel)");
String username = System.getProperty( "user.name" );
open( _dirName, _debugLevel, username, username );
}
@@ -100,8 +115,10 @@
if (_props.get( PROP_DEBUG ) != null) {
debugLevel = ((Integer)_props.get( PROP_DEBUG )).intValue();
}
-
- theEnv = new Env( dirName, debugLevel, false, true );
+/*XXX*/ System.out.println("LocalDatabase.open( Hashtable _props ): " +
+/*XXX*/ "theEnv = new Env(" + dirName + ", " + debugLevel + ", " +
+/*XXX*/ "false, true, " + parentClassLoader + ");");
+ theEnv = new Env( dirName, debugLevel, false, true, parentClassLoader );
// create the user if it doesn't exist yet; in local mode we cannot use
// the admin tool to do so
Index: org/ozoneDB/core/Env.java
===================================================================
RCS file: /raid/Repository/ozone/org/ozoneDB/core/Env.java,v
retrieving revision 1.77
diff -u -r1.77 Env.java
--- org/ozoneDB/core/Env.java 2001/02/28 10:08:15 1.77
+++ org/ozoneDB/core/Env.java 2001/03/10 07:40:51
@@ -30,35 +30,35 @@
public final class Env implements Observer {
// constant members ***********************************
- public final static String VERSION = "@version@";
- public final static String OS_DIR = "ostab";
- public final static String STATE_FILE = "state.properties";
- public final static String CONFIG_FILE = "config.properties";
- public final static String LOG_FILE = "log";
- public final static String DATA_DIR = "data" + File.separator;
- public final static String STATS_DIR = "stats";
+ public final static String VERSION = "@version@";
+ public final static String OS_DIR = "ostab";
+ public final static String STATE_FILE = "state.properties";
+ public final static String CONFIG_FILE = "config.properties";
+ public final static String LOG_FILE = "log";
+ public final static String DATA_DIR = "data" + File.separator;
+ public final static String STATS_DIR = "stats";
/**
* AdminPort and InvokeServer accepts and admin requests
*/
- public final static int ACCEPT_THREAD_PRIORITY = Thread.NORM_PRIORITY + 2;
+ public final static int ACCEPT_THREAD_PRIORITY = Thread.NORM_PRIORITY + 2;
/**
* Thread priority of normal transaction.
*/
- public final static int TRANSACTION_THREAD_PRIORITY = Thread.NORM_PRIORITY;
+ public final static int TRANSACTION_THREAD_PRIORITY = Thread.NORM_PRIORITY;
- public final static int TRANSACTION_MUTEX_PRIORITY = TRANSACTION_THREAD_PRIORITY + 1;
+ public final static int TRANSACTION_MUTEX_PRIORITY = TRANSACTION_THREAD_PRIORITY + 1;
/**
* Thread priority deadlock recognition.
*/
- public final static int DEADLOCK_THREAD_PRIORITY = Thread.NORM_PRIORITY;
+ public final static int DEADLOCK_THREAD_PRIORITY = Thread.NORM_PRIORITY;
/**
* Priority of the server thread (Server.main())
*/
- public final static int SERVER_THREAD_PRIORITY = Thread.NORM_PRIORITY + 2;
+ public final static int SERVER_THREAD_PRIORITY = Thread.NORM_PRIORITY + 2;
// class members **************************************
@@ -66,68 +66,73 @@
/**
* The one and only ozone environment of this VM.
*/
- public static Env theEnv;
+ public static Env theEnv;
protected static OzoneSecurityManager securityManager;
// instance members ***********************************
- public String dir;
+ public String dir;
/**
* Holds the content of the 'state.properties' file. After
* changing the content the state must be written to disk to make
* changes persistent.
*/
- public Setup state;
+ public Setup state;
/**
* Holds the content of the 'config.properties' config file.
*/
- public Setup config;
+ public Setup config;
- public LogWriter logWriter = new LogWriter();
+ public LogWriter logWriter = new LogWriter();
/**
* This indicates that we are about to shutdown.
*/
- public boolean shuttingdown = false;
+ public boolean shuttingdown = false;
- protected long idCount = 0;
- protected long idBorder = 0;
- protected long idRange = 5000;
+ protected long idCount = 0;
+ protected long idBorder = 0;
+ protected long idRange = 5000;
/*
* The total memory of this VM.
*/
- protected long totalMemory;
+ protected long totalMemory;
/*
* The amount of memory that should be kept free.
*/
- protected long keepMemory;
+ protected long keepMemory;
/**
* Interface for the database objects inside the server.
*/
- public Database database;
+ public Database database;
public AdminManager adminManager;
- public ClassManager classManager;
+ public ClassManager classManager;
- public TransactionManager transactionManager;
+ public TransactionManager transactionManager;
- public StoreManager storeManager;
+ public StoreManager storeManager;
- public UserManager userManager;
+ public UserManager userManager;
- protected InvokeServer invokeServer;
+ /**
+ * ClassLoader for OzoneClassLoader to set as its parent.
+ */
+ public ClassLoader parentClassLoader;
+
+ protected InvokeServer invokeServer;
- protected DeadlockThread deadlockThread;
+ protected DeadlockThread deadlockThread;
- protected DeadlockRecognition dr;
+ protected DeadlockRecognition dr;
// class methods **************************************
@@ -143,10 +148,10 @@
public static Env currentEnv() {
return theEnv;
}
-
-
+
+
// instance methods ***********************************
-
+
/**
* Construct a new ozone server environment.
*
@@ -155,7 +160,12 @@
* @param _local
*/
public Env( String _dirName, int _debugLevel, boolean _verbose, boolean _local ) throws Exception{
-
+ this( _dirName, _debugLevel, _verbose, _local, null );
+ }
+
+
+ public Env( String _dirName, int _debugLevel, boolean _verbose, boolean _local, ClassLoader _parentClassLoader ) throws Exception{
+/*XXX*/ System.out.println("Env( String _dirName, int _debugLevel, boolean _verbose, boolean _local, ClassLoader " + _parentClassLoader + " ) ");
if (theEnv != null) {
throw new Exception( "ozone environment (Env) already initialized for this VM" );
}
@@ -189,7 +199,11 @@
calcMemory();
database = new Database( this );
+
+/*XXX*/ System.out.println("Env(): parentClassLoader = " + _parentClassLoader + ";");
+ parentClassLoader = _parentClassLoader;
+/*XXX*/ System.out.println("Env(): classManager = new ClassManager( " + this + " );");
classManager = new ClassManager( this );
classManager.startup();
@@ -225,8 +239,8 @@
}
throw e;
}
- }
-
+ }
+
public void startExternalEventProcessing() throws Exception {
try {
Index: org/ozoneDB/core/OzoneClassLoader.java
===================================================================
RCS file: /raid/Repository/ozone/org/ozoneDB/core/OzoneClassLoader.java,v
retrieving revision 1.4
diff -u -r1.4 OzoneClassLoader.java
--- org/ozoneDB/core/OzoneClassLoader.java 2001/03/09 13:31:35 1.4
+++ org/ozoneDB/core/OzoneClassLoader.java 2001/03/10 10:07:54
@@ -27,10 +27,18 @@
public OzoneClassLoader() {
- super();
+ this((ClassLoader) null);
+/*XXX*/ System.out.println("OzoneClassLoader(): this((ClassLoader) null);");
+// super();
+// flushCache();
+ }
+
+ public OzoneClassLoader(ClassLoader parent) {
+ super(parent);
+/*XXX*/ System.out.println("OzoneClassLoader( ClassLoader ): super(" + parent + ");");
flushCache();
}
-
+
protected void flushCache() {
classTable = new DxHashMap( 100 );
@@ -38,6 +46,7 @@
protected Class findClass( String name ) throws ClassNotFoundException {
+/*XXX*/ System.out.println("OzoneClassLoader.findClass( String )");
if (name.startsWith( "java" )) {
throw new ClassNotFoundException( "I'm not here to load system classes." );
}
@@ -71,9 +80,10 @@
public Class loadClass( String name ) throws ClassNotFoundException {
return loadClass( name, false );
}
-
+
protected Class loadClass( String name, boolean resolve ) throws ClassNotFoundException {
+/*XXX*/ System.out.println("OzoneClassLoader.loadClass( String, boolean )");
if (name.startsWith( "java" )) {
return getSystemClassLoader().loadClass( name );
} else {
@@ -89,7 +99,13 @@
// this works around the compatibility problems but causes the reloadClasses()
// method to not work
- cl = getSystemClassLoader().loadClass( name );
+ //cl = getSystemClassLoader().loadClass( name );
+
+ // OK, let's try it this way...
+ // This allows for delegation up the ClassLoader chain.
+/*XXX*/ System.out.println("OzoneClassLoader.loadClass( String, boolean ): " +
+ "cl = super.loadClass(" + name + ", " + resolve + ");");
+ cl = super.loadClass(name, resolve);
}
classTable.addForKey( cl, name );
}
Index: org/ozoneDB/core/ClassManager.java
===================================================================
RCS file: /raid/Repository/ozone/org/ozoneDB/core/ClassManager.java,v
retrieving revision 1.24
diff -u -r1.24 ClassManager.java
--- org/ozoneDB/core/ClassManager.java 2001/03/09 13:31:34 1.24
+++ org/ozoneDB/core/ClassManager.java 2001/03/10 10:08:27
@@ -26,6 +26,7 @@
public ClassManager( Env _env ) {
+/*XXX*/ System.out.println("ClassManager( Env )");
env = _env;
}
@@ -50,8 +51,13 @@
// not allow to drop classes we can directly use the context loader
// of the thread to avoid problems in managed environments like
// servlets
- Class cl = Thread.currentThread().getContextClassLoader().loadClass( name );
- // Class cl = classLoader.loadClass( name );
+ //Class cl = Thread.currentThread().getContextClassLoader().loadClass( name );
+/*XXX*/ System.out.println("ClassManager.classForName( String ): " +
+ "Class cl = classLoader.loadClass(" + name + ");");
+
+ // We can use the OzoneClassLoader as long as we remember to set it's
+ // parentClassLoader through the LocalDatabase( ClassLoader ) constructor.
+ Class cl = classLoader.loadClass( name );
if (env.logWriter.hasTarget( LogWriter.DEBUG3 )) {
env.logWriter.newEntry( this, " class: " + cl.getName() + ", " + cl.hashCode(), LogWriter.DEBUG3 );
@@ -59,6 +65,7 @@
return cl;
}
catch (ClassNotFoundException e) {
+ e.printStackTrace();
throw new ClassNotFoundExc( e.getMessage() );
}
}
@@ -66,7 +73,16 @@
public void dropClasses() throws Exception {
env.logWriter.newEntry( this, "dropClasses()", LogWriter.DEBUG3 );
- classLoader = new OzoneClassLoader();
+ // Must call the correct constructor to ensure the correct parent is set.
+ if (env.parentClassLoader != null) {
+/*XXX*/ System.out.println("ClassManager.dropClasses(): " +
+ "classLoader = new OzoneClassLoader(" + env.parentClassLoader + ");");
+ classLoader = new OzoneClassLoader(env.parentClassLoader);
+ } else {
+/*XXX*/ System.out.println("ClassManager.dropClasses(): " +
+ "classLoader = new OzoneClassLoader();");
+ classLoader = new OzoneClassLoader();
+ }
}
}