gdaniels 02/05/06 12:41:47
Modified: java/src/org/apache/axis AxisEngine.java
java/src/org/apache/axis/handlers SimpleSessionHandler.java
java/src/org/apache/axis/providers/java EJBProvider.java
JavaProvider.java
java/src/org/apache/axis/session Session.java
SimpleSession.java
java/src/org/apache/axis/transport/http AxisHttpSession.java
AxisServlet.java
java/src/org/apache/axis/utils resources.properties
java/test/session TestSimpleSession.java
Log:
Implement the JAX-RPC ServiceLifecycle functionality.
* JavaProvider now calls init() on each new service object which
implements ServiceLifecycle. Request-scoped objects also receive
a destroy() call after the JavaProvider has called processMessage().
* SimpleSessionHandler now calls destroy() on any service objects it
finds inside a timed-out session.
* AxisEngine now implements cleanup(), during which it calls destroy()
on any application-scoped service objects it finds.
* AxisServlet now noticed when the servlet destroy() method is called,
and calls cleanup() on its AxisEngine.
* TestSimpleSession now tests the ServiceLifecycle methods as well,
by confirming the correct # of init() and destroy() calls have
occurred during the course of the test.
TODO :
- Catch ^c in SimpleAxisServer, and cleanup the engine there.
- Move session cleanup into another thread, since there might be a lot
of data to scan through in a Session object.
Revision Changes Path
1.76 +19 -0 xml-axis/java/src/org/apache/axis/AxisEngine.java
Index: AxisEngine.java
===================================================================
RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/AxisEngine.java,v
retrieving revision 1.75
retrieving revision 1.76
diff -u -r1.75 -r1.76
--- AxisEngine.java 29 Mar 2002 23:25:01 -0000 1.75
+++ AxisEngine.java 6 May 2002 19:41:46 -0000 1.76
@@ -66,8 +66,10 @@
import org.apache.commons.logging.LogFactory;
import javax.xml.rpc.namespace.QName;
+import javax.xml.rpc.server.ServiceLifecycle;
import java.util.ArrayList;
import java.util.Hashtable;
+import java.util.Enumeration;
/**
* An <code>AxisEngine</code> is the base class for AxisClient and
@@ -194,6 +196,23 @@
log.debug(JavaUtils.getMessage("exit00", "AxisEngine::init"));
}
+ }
+
+ public void cleanup() {
+ super.cleanup();
+
+ // Let any application-scoped service objects know that we're going
+ // away...
+ Enumeration keys = session.getKeys();
+ if (keys != null) {
+ while (keys.hasMoreElements()) {
+ String key = (String)keys.nextElement();
+ Object obj = session.get(key);
+ if (obj != null && obj instanceof ServiceLifecycle) {
+ ((ServiceLifecycle)obj).destroy();
+ }
+ }
+ }
}
/** Write out our engine configuration.
1.20 +18 -1
xml-axis/java/src/org/apache/axis/handlers/SimpleSessionHandler.java
Index: SimpleSessionHandler.java
===================================================================
RCS file:
/home/cvs/xml-axis/java/src/org/apache/axis/handlers/SimpleSessionHandler.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- SimpleSessionHandler.java 12 Apr 2002 09:46:36 -0000 1.19
+++ SimpleSessionHandler.java 6 May 2002 19:41:46 -0000 1.20
@@ -69,11 +69,13 @@
import org.apache.commons.logging.LogFactory;
import javax.xml.rpc.namespace.QName;
+import javax.xml.rpc.server.ServiceLifecycle;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
+import java.util.Enumeration;
/** This handler uses SOAP headers to do simple session management.
*
@@ -172,7 +174,22 @@
// Now go remove all the victims we found during the iteration.
for (i = victims.iterator(); i.hasNext();) {
- activeSessions.remove(i.next());
+ key = i.next();
+ SimpleSession session = (SimpleSession)activeSessions.get(key);
+ activeSessions.remove(key);
+
+ // For each victim, swing through the data looking for
+ // ServiceLifecycle objects, and calling destroy() on them.
+ // FIXME : This cleanup should probably happen on another
+ // thread, as it might take a little while.
+ Enumeration keys = session.getKeys();
+ while (keys != null && keys.hasMoreElements()) {
+ String keystr = (String)keys.nextElement();
+ Object obj = session.get(keystr);
+ if (obj != null && obj instanceof ServiceLifecycle) {
+ ((ServiceLifecycle)obj).destroy();
+ }
+ }
}
}
1.17 +1 -1
xml-axis/java/src/org/apache/axis/providers/java/EJBProvider.java
Index: EJBProvider.java
===================================================================
RCS file:
/home/cvs/xml-axis/java/src/org/apache/axis/providers/java/EJBProvider.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- EJBProvider.java 30 Apr 2002 19:07:31 -0000 1.16
+++ EJBProvider.java 6 May 2002 19:41:46 -0000 1.17
@@ -105,7 +105,7 @@
* @param clsName The JNDI name of the EJB home class
* @return an object that implements the service
*/
- protected Object getNewServiceObject(MessageContext msgContext,
+ protected Object makeNewServiceObject(MessageContext msgContext,
String clsName)
throws Exception
{
1.51 +63 -18
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.50
retrieving revision 1.51
diff -u -r1.50 -r1.51
--- JavaProvider.java 30 Apr 2002 19:07:31 -0000 1.50
+++ JavaProvider.java 6 May 2002 19:41:46 -0000 1.51
@@ -74,6 +74,8 @@
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
+import javax.xml.rpc.server.ServiceLifecycle;
+import javax.xml.rpc.holders.IntHolder;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
@@ -91,13 +93,16 @@
protected static Log log =
LogFactory.getLog(JavaProvider.class.getName());
-
// from the original stubbed-out JavaProvider...
// not quite sure what these are for but it is to do with WSDD... -- RobJ
public static final String OPTION_CLASSNAME = "className";
public static final String OPTION_IS_STATIC = "isStatic";
public static final String OPTION_CLASSPATH = "classPath";
+ public static final int SCOPE_REQUEST = 0;
+ public static final int SCOPE_SESSION = 1;
+ public static final int SCOPE_APPLICATION = 2;
+
private String classNameOption = "className";
private String allowedMethodsOption = "allowedMethods";
@@ -107,7 +112,8 @@
*/
public Object getServiceObject (MessageContext msgContext,
Handler service,
- String clsName)
+ String clsName,
+ IntHolder scopeHolder)
throws Exception
{
String serviceName = msgContext.getService().getName();
@@ -121,12 +127,16 @@
}
if (scope.equalsIgnoreCase("Request")) {
+ // Convey the scope upwards
+ scopeHolder.value = SCOPE_REQUEST;
// make a one-off
return getNewServiceObject(msgContext, clsName);
} else if (scope.equalsIgnoreCase("Session")) {
-
+ // Convey the scope upwards
+ scopeHolder.value = SCOPE_SESSION;
+
// What do we do if serviceName is null at this point???
if (serviceName == null)
serviceName = msgContext.getService().toString();
@@ -145,10 +155,13 @@
}
} else {
// was no incoming session, sigh, treat as request scope
+ scopeHolder.value = SCOPE_REQUEST;
return getNewServiceObject(msgContext, clsName);
}
} else if (scope.equalsIgnoreCase("Application")) {
+ scopeHolder.value = SCOPE_APPLICATION;
+
// MUST be AxisEngine here!
AxisEngine engine = msgContext.getAxisEngine();
if (engine.getApplicationSession() != null) {
@@ -164,7 +177,9 @@
return obj;
}
} else {
- // was no incoming session, sigh, treat as request scope
+ // was no application session, sigh, treat as request scope
+ // FIXME : Should we bomb in this case?
+ scopeHolder.value = SCOPE_REQUEST;
return getNewServiceObject(msgContext, clsName);
}
@@ -177,6 +192,25 @@
}
/**
+ * Return a new service object which, if it implements the ServiceLifecycle
+ * interface, has been init()ed. Right now we pass "null" as the context
+ * argument to init() - this might want to be the MessageContext?
+ *
+ * @param msgContext the MessageContext
+ * @param clsName the name of the class to instantiate
+ * @return an initialized service object
+ */
+ private Object getNewServiceObject(MessageContext msgContext,
+ String clsName) throws Exception {
+ Object serviceObject = makeNewServiceObject(msgContext, clsName);
+ if (serviceObject != null &&
+ serviceObject instanceof ServiceLifecycle) {
+ ((ServiceLifecycle)serviceObject).init(null);
+ }
+ return serviceObject;
+ }
+
+ /**
* Process the current message. Side-effect resEnv to create return value.
*
* @param msgContext self-explanatory
@@ -233,9 +267,11 @@
allowedMethods = null;
try {
+ IntHolder scope = new IntHolder();
Object obj = getServiceObject(msgContext,
service,
- clsName);
+ clsName,
+ scope);
JavaClass jc = JavaClass.find(obj.getClass());
Message reqMsg = msgContext.getRequestMessage();
@@ -246,9 +282,8 @@
getSOAPConstants()) :
(SOAPEnvelope)resMsg.getSOAPEnvelope();
- // get the response message again! It may have been explicitly set!
- // (by, say, a proxy service :-) -- RobJ
- if (msgContext.getResponseMessage() == null) {
+ // If we didn't have a response message, make sure we set one up
+ if (resMsg == null) {
resMsg = new Message(resEnv);
msgContext.setResponseMessage( resMsg );
}
@@ -261,8 +296,19 @@
allowedMethods = axisConfig.getAllowedMethods();
}
- processMessage(msgContext, clsName, allowedMethods, reqEnv,
- resEnv, jc, obj);
+ try {
+ processMessage(msgContext, clsName, allowedMethods, reqEnv,
+ resEnv, jc, obj);
+ } catch (Exception exp) {
+ throw exp;
+ } finally {
+ // If this is a request scoped service object which implements
+ // ServiceLifecycle, let it know that it's being destroyed now.
+ if (scope.value == SCOPE_REQUEST &&
+ obj instanceof ServiceLifecycle) {
+ ((ServiceLifecycle)obj).destroy();
+ }
+ }
}
catch( Exception exp ) {
log.info( JavaUtils.getMessage("toAxisFault00"), exp);
@@ -371,7 +417,7 @@
* class wrapped in jc
*
*/
- protected Object getNewServiceObject(MessageContext msgContext,
+ protected Object makeNewServiceObject(MessageContext msgContext,
String clsName)
throws Exception
{
@@ -402,13 +448,12 @@
/**
* Returns the Class info about the service class.
*/
- protected Class getServiceClass(MessageContext msgContext, String clsName)
throws Exception {
- Handler service = msgContext.getService();
- Object obj = getServiceObject(msgContext,
- service,
- clsName);
-
- return obj.getClass();
+ protected Class getServiceClass(MessageContext msgContext, String clsName)
+ throws Exception {
+ ClassLoader cl = msgContext.getClassLoader();
+ ClassCache cache = msgContext.getAxisEngine().getClassCache();
+ JavaClass jc = cache.lookup(clsName, cl);
+ return jc.getJavaClass();
}
/**
1.6 +7 -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.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- Session.java 30 Oct 2001 16:46:39 -0000 1.5
+++ Session.java 6 May 2002 19:41:46 -0000 1.6
@@ -55,6 +55,8 @@
package org.apache.axis.session;
+import java.util.Enumeration;
+
/**
* An abstract interface to provide session storage to Axis services.
*
@@ -82,6 +84,11 @@
* @param key the name of the property desired.
*/
public void remove(String key);
+
+ /**
+ * Get an enumeration of the keys in this session
+ */
+ public Enumeration getKeys();
/** Set the session's time-to-live.
*
1.6 +11 -1 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.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- SimpleSession.java 30 Oct 2001 16:46:39 -0000 1.5
+++ SimpleSession.java 6 May 2002 19:41:46 -0000 1.6
@@ -56,6 +56,7 @@
package org.apache.axis.session;
import java.util.Hashtable;
+import java.util.Enumeration;
/**
* A trivial session implementation.
@@ -115,7 +116,16 @@
rep.remove(key);
lastTouched = System.currentTimeMillis();
}
-
+
+ /**
+ * Get an enumeration of the keys in this session
+ */
+ public Enumeration getKeys() {
+ if (rep != null)
+ return rep.keys();
+ return null;
+ }
+
/** Set the session's time-to-live.
*
* This is implementation-specific, but basically should be the #
1.9 +10 -1
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.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- AxisHttpSession.java 3 Dec 2001 18:18:20 -0000 1.8
+++ AxisHttpSession.java 6 May 2002 19:41:47 -0000 1.9
@@ -59,6 +59,7 @@
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
+import java.util.Enumeration;
/**
* An HTTP/Servlet implementation of Axis sessions.
@@ -119,7 +120,15 @@
ensureSession();
rep.removeAttribute(key);
}
-
+
+ /**
+ * Get an enumeration of the keys in this session
+ */
+ public Enumeration getKeys() {
+ ensureSession();
+ return rep.getAttributeNames();
+ }
+
/** Set the session's time-to-live.
*
* This is implementation-specific, but basically should be the #
1.98 +14 -1
xml-axis/java/src/org/apache/axis/transport/http/AxisServlet.java
Index: AxisServlet.java
===================================================================
RCS file:
/home/cvs/xml-axis/java/src/org/apache/axis/transport/http/AxisServlet.java,v
retrieving revision 1.97
retrieving revision 1.98
diff -u -r1.97 -r1.98
--- AxisServlet.java 3 May 2002 14:42:39 -0000 1.97
+++ AxisServlet.java 6 May 2002 19:41:47 -0000 1.98
@@ -162,7 +162,20 @@
jwsClassDir = webInfPath + File.separator + "jwsClasses";
}
}
-
+
+ /**
+ * Destroy method is called when the servlet is going away. Pass this
+ * down to the AxisEngine to let it clean up.
+ */
+ public void destroy() {
+ super.destroy();
+ try {
+ getEngine().cleanup();
+ } catch (AxisFault fault) {
+ log.error(JavaUtils.getMessage("faultDuringCleanup"), fault);
+ }
+ }
+
public AxisServer getEngine() throws AxisFault { return getEngine(this); }
/**
1.95 +1 -3 xml-axis/java/src/org/apache/axis/utils/resources.properties
Index: resources.properties
===================================================================
RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/utils/resources.properties,v
retrieving revision 1.94
retrieving revision 1.95
diff -u -r1.94 -r1.95
--- resources.properties 1 May 2002 19:52:45 -0000 1.94
+++ resources.properties 6 May 2002 19:41:47 -0000 1.95
@@ -807,6 +807,4 @@
implAlreadySet=Attempt to set implementation class on a ServiceDesc which has
already been configured
cantCreateBean00=Unable to create JavaBean of type {0}. Missing default
constructor? Error was: {1}.
-
-
-
+faultDuringCleanup=AxisEngine faulted during cleanup!
1.16 +49 -5 xml-axis/java/test/session/TestSimpleSession.java
Index: TestSimpleSession.java
===================================================================
RCS file: /home/cvs/xml-axis/java/test/session/TestSimpleSession.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- TestSimpleSession.java 28 Mar 2002 03:15:18 -0000 1.15
+++ TestSimpleSession.java 6 May 2002 19:41:47 -0000 1.16
@@ -4,7 +4,6 @@
import org.apache.axis.session.SimpleSession;
import org.apache.axis.session.Session;
import org.apache.axis.handlers.soap.SOAPService;
-import org.apache.axis.handlers.EchoHandler;
import org.apache.axis.handlers.SimpleSessionHandler;
import org.apache.axis.client.Service;
import org.apache.axis.client.Call;
@@ -18,16 +17,16 @@
import org.apache.axis.deployment.wsdd.WSDDConstants;
import org.apache.axis.providers.java.RPCProvider;
-import javax.xml.rpc.namespace.QName;
+import javax.xml.rpc.server.ServiceLifecycle;
+import javax.xml.rpc.ServiceException;
-
-/**
+/**
* Test the SimpleSession implementation (using SOAP headers for session
* maintenance)
*
* @author Glen Daniels ([EMAIL PROTECTED])
*/
-public class TestSimpleSession extends TestCase {
+public class TestSimpleSession extends TestCase implements ServiceLifecycle {
static final String clientWSDD =
"<deployment xmlns=\"http://xml.apache.org/axis/wsdd/\" " +
"xmlns:java=\"" + WSDDConstants.WSDD_JAVA + "\">\n" +
@@ -112,11 +111,17 @@
assertNotNull("count was null!", count);
assertEquals("count was wrong", 1, count.intValue());
+ // We should have init()ed a single service object
+ assertEquals("Wrong # of calls to init()!", 1, initCalls);
+
// Next invocation should return 2, assuming the session-based
// counter is working.
count = (Integer)call.invoke("sessionTest", "counter", null);
assertEquals("count was wrong", 2, count.intValue());
+ // We should still have 1
+ assertEquals("Wrong # of calls to init()!", 1, initCalls);
+
// Now start fresh and confirm a new session
Service svc2 = new Service(clientProvider);
Call call2 = (Call)svc2.createCall();
@@ -129,12 +134,21 @@
assertEquals("New session count was incorrect", 1,
count.intValue());
+ // We should have init()ed 2 service objects now
+ assertEquals("Wrong # of calls to init()!", 2, initCalls);
+ // And no destroy()s yet...
+ assertEquals("Shouldn't have called destroy() yet!", 0, destroyCalls);
+
// Wait around a few seconds to let the first session time out
Thread.sleep(4000);
// And now we should get a new session, therefore going back to 1
count = (Integer)call.invoke("sessionTest", "counter", null);
assertEquals("count after timeout was incorrect", 1, count.intValue());
+
+ // Check init/destroy counts
+ assertEquals("Wrong # of calls to init()!", 3, initCalls);
+ assertEquals("Wrong # of calls to destroy()!", 2, destroyCalls);
}
/**
@@ -160,5 +174,35 @@
TestSimpleSession test = new TestSimpleSession("test");
test.testSessionAPI();
test.testSessionService();
+ }
+
+ private static int initCalls = 0;
+ private static int destroyCalls = 0;
+
+ /**
+ * After a service endpoint object (an instance of a service
+ * endpoint class) is instantiated, the JAX-RPC runtime system
+ * invokes the init method.The service endpoint class uses the
+ * init method to initialize its configuration and setup access
+ * to any external resources.
+ * @param context Initialization context for a JAX-RPC service
+ endpoint; Carries javax.servlet.ServletContext
+ for the servlet based JAX-RPC endpoints
+ * @throws ServiceException If any error in initialization of the
+ service endpoint; or if any illegal context has
+ been provided in the init method
+ */
+ public void init(Object context) throws ServiceException {
+ initCalls++;
+ }
+
+ /**
+ * JAX-RPC runtime system ends the lifecycle of a service endpoint
+ * object by invoking the destroy method. The service endpoint
+ * releases its resourcesin the implementation of the destroy
+ * method.
+ */
+ public void destroy() {
+ destroyCalls++;
}
}