gdaniels 2002/10/16 10:11:04 Modified: java/src/org/apache/axis/providers/java JavaProvider.java java/src/org/apache/axis/session Session.java SimpleSession.java java/src/org/apache/axis/transport/http AxisHttpSession.java Log: Improve synchronization/locking when getting and making new session-based service objects. This should deal with Doug's problem of constructors calling back into the engine. Revision Changes Path 1.88 +78 -22 xml-axis/java/src/org/apache/axis/providers/java/JavaProvider.java Index: JavaProvider.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/providers/java/JavaProvider.java,v retrieving revision 1.87 retrieving revision 1.88 diff -u -r1.87 -r1.88 --- JavaProvider.java 10 Oct 2002 17:22:54 -0000 1.87 +++ JavaProvider.java 16 Oct 2002 17:11:04 -0000 1.88 @@ -65,8 +65,6 @@ import org.apache.axis.description.ServiceDesc; import org.apache.axis.encoding.TypeMapping; import org.apache.axis.enum.Scope; -import org.apache.axis.enum.Style; -import org.apache.axis.enum.Use; import org.apache.axis.handlers.soap.SOAPService; import org.apache.axis.message.SOAPEnvelope; import org.apache.axis.providers.BasicProvider; @@ -149,16 +147,8 @@ // look in incoming session Session session = msgContext.getSession(); if (session != null) { - // This part isn't thread safe... - synchronized (session) { - // store service objects in session, indexed by class name - Object obj = session.get(serviceName); - if (obj == null) { - obj = getNewServiceObject(msgContext, clsName); - session.set(serviceName, obj); - } - return obj; - } + return getSessionServiceObject(session, serviceName, + msgContext, clsName); } else { // was no incoming session, sigh, treat as request scope scopeHolder.value = Scope.DEFAULT.getValue(); @@ -169,16 +159,8 @@ AxisEngine engine = msgContext.getAxisEngine(); Session appSession = engine.getApplicationSession(); if (appSession != null) { - // This part isn't thread safe - synchronized (appSession) { - // store service objects in session, indexed by class name - Object obj = appSession.get(serviceName); - if (obj == null) { - obj = getNewServiceObject(msgContext, clsName); - appSession.set(serviceName, obj); - } - return obj; - } + return getSessionServiceObject(appSession, serviceName, + msgContext, clsName); } else { // was no application session, sigh, treat as request scope // FIXME : Should we bomb in this case? @@ -189,6 +171,80 @@ // NOTREACHED return null; } + } + + /** + * Simple utility class for dealing with synchronization issues. + */ + class LockObject { + private boolean completed = false; + + synchronized void waitUntilComplete() throws InterruptedException { + while (!completed) { + wait(); + } + } + + synchronized void complete() { + completed = true; + notifyAll(); + } + } + + /** + * Get a service object from a session. Handles threading / locking + * issues when multiple threads might be accessing the same session + * object, and ensures only one thread gets to create the service + * object if there isn't one already. + */ + private Object getSessionServiceObject(Session session, + String serviceName, + MessageContext msgContext, + String clsName) throws Exception { + Object obj = null; + boolean makeNewObject = false; + + // This is a little tricky. + synchronized (session.getLockObject()) { + // store service objects in session, indexed by class name + obj = session.get(serviceName); + + // If nothing there, put in a placeholder object so + // other threads wait for us to create the real + // service object. + if (obj == null) { + obj = new LockObject(); + makeNewObject = true; + session.set(serviceName, obj); + } + } + + // OK, we DEFINITELY have something in obj at this point. Either + // it's the service object or it's a LockObject (ours or someone + // else's). + + if (LockObject.class == obj.getClass()) { + LockObject lock = (LockObject)obj; + + // If we were the lucky thread who got to install the + // placeholder, create a new service object and install it + // instead, then notify anyone waiting on the LockObject. + if (makeNewObject) { + obj = getNewServiceObject(msgContext, clsName); + session.set(serviceName, obj); + lock.complete(); + } else { + // It's someone else's LockObject, so wait around until + // it's completed. + lock.waitUntilComplete(); + + // Now we are guaranteed there is a service object in the + // session, so this next part doesn't need syncing + obj = session.get(serviceName); + } + } + + return obj; } /** 1.7 +10 -0 xml-axis/java/src/org/apache/axis/session/Session.java Index: Session.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/session/Session.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- Session.java 6 May 2002 19:41:46 -0000 1.6 +++ Session.java 16 Oct 2002 17:11:04 -0000 1.7 @@ -109,4 +109,14 @@ * "Touch" the session (mark it recently used) */ public void touch(); + + /** + * Get an Object suitable for synchronizing the session. This method + * exists because different session implementations might provide + * different ways of getting at shared data. For a simple hashtable- + * based session, this would just be the hashtable, but for sessions + * which use database connections, etc. it might be an object wrapping + * a table ID or somesuch. + */ + public Object getLockObject(); } 1.7 +15 -0 xml-axis/java/src/org/apache/axis/session/SimpleSession.java Index: SimpleSession.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/session/SimpleSession.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- SimpleSession.java 6 May 2002 19:41:46 -0000 1.6 +++ SimpleSession.java 16 Oct 2002 17:11:04 -0000 1.7 @@ -153,4 +153,19 @@ { return lastTouched; } + + /** + * Get an Object suitable for synchronizing the session. This method + * exists because different session implementations might provide + * different ways of getting at shared data. For a simple hashtable- + * based session, this would just be the hashtable, but for sessions + * which use database connections, etc. it might be an object wrapping + * a table ID or somesuch. + */ + public Object getLockObject() { + if (rep == null) { + rep = new Hashtable(); + } + return rep; + } } 1.11 +13 -0 xml-axis/java/src/org/apache/axis/transport/http/AxisHttpSession.java Index: AxisHttpSession.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/transport/http/AxisHttpSession.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- AxisHttpSession.java 13 Aug 2002 00:38:46 -0000 1.10 +++ AxisHttpSession.java 16 Oct 2002 17:11:04 -0000 1.11 @@ -171,4 +171,17 @@ rep = req.getSession(); } } + + /** + * Get an Object suitable for synchronizing the session. This method + * exists because different session implementations might provide + * different ways of getting at shared data. For a simple hashtable- + * based session, this would just be the hashtable, but for sessions + * which use database connections, etc. it might be an object wrapping + * a table ID or somesuch. + */ + public Object getLockObject() { + ensureSession(); + return rep; + } }