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++;
       }
   }
  
  
  


Reply via email to