Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/axis/handlers/SimpleSessionHandler.java


1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.axis.handlers;
18  
19  import org.apache.axis.AxisEngine;
20  import org.apache.axis.AxisFault;
21  import org.apache.axis.Constants;
22  import org.apache.axis.Message;
23  import org.apache.axis.MessageContext;
24  import org.apache.axis.components.logger.LogFactory;
25  import org.apache.axis.message.SOAPEnvelope;
26  import org.apache.axis.message.SOAPHeaderElement;
27  import org.apache.axis.session.SimpleSession;
28  import org.apache.axis.utils.Messages;
29  import org.apache.axis.utils.SessionUtils;
30  import org.apache.commons.logging.Log;
31  
32  import javax.xml.namespace.QName;
33  import javax.xml.rpc.server.ServiceLifecycle;
34  import java.util.Enumeration;
35  import java.util.HashSet;
36  import java.util.Hashtable;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.Set;
40  
41  /** This handler uses SOAP headers to do simple session management.
42   *
43   * <p>Essentially, you install it on both the request and response chains of
44   * your service, on both the client and the server side.</p>
45   *
46   * <p>ON THE SERVER:</p>
47   * <ul>
48   * <li>The REQUEST is checked for a session ID header.  If present, we
49   *     look up the correct SimpleSession.  If not, we create a new session.
50   *     In either case, we install the session into the MessageContext, and
51   *     put its ID in the SESSION_ID property.
52   * <li>The RESPONSE gets a session ID header tacked on, assuming we found a
53   *     SESSION_ID property in the MessageContext.
54   * </ul>
55   * <p>ON THE CLIENT:</p>
56   * <ul>
57   * <li>The RESPONSE messages are checked for session ID headers.  If present,
58   *     we pull the ID out and insert it into an option in the AxisClient.
59   *     This works because a given Call object is associated with a single
60   *     AxisClient.  However, we might want to find a way to put it into the
61   *     Call object itself, which would make a little more sense.  This would
62   *     mean being able to get to the Call from the MC, i.e. adding a getCall()
63   *     API (which would only work on the client side)....
64   * <li>When REQUESTS are generated, we look to see if an ID option is present
65   *     in the AxisClient associated with the MessageContext.  If so, we
66   *     insert a session ID header with the appropriate ID.
67   * </ul>
68   *
69   * <p>SimpleSessions are "reaped" periodically via a very simplistic
70   * mechanism.  Each time the handler is invoke()d we check to see if more
71   * than <b>reapPeriodicity</b> milliseconds have elapsed since the last
72   * reap.  If so, we walk the collection of active Sessions, and for each
73   * one, if it hasn't been "touched" (i.e. had a getProperty() or setProperty()
74   * performed) in longer than its timeout, we remove it from the collection.</p>
75   *
76   * @author Glen Daniels (gdaniels@apache.org)
77   */
78  public class SimpleSessionHandler extends BasicHandler
79  {
80      protected static Log log =
81          LogFactory.getLog(SimpleSessionHandler.class.getName());
82  
83      public static final String SESSION_ID = "SimpleSession.id";
84      public static final String SESSION_NS = "http://xml.apache.org/axis/session";
85      public static final String SESSION_LOCALPART = "sessionID";
86      public static final QName sessionHeaderName = new QName(SESSION_NS,
87                                                              SESSION_LOCALPART);
88  
89      private Hashtable activeSessions = new Hashtable();
90  
91      // Reap timed-out sessions on the first request after this many
92      // seconds.
93      private long reapPeriodicity = 30;
94      private long lastReapTime = 0;
95  
96      // By default, sessions time out after 1 minute of inactivity (60 sec)
97      private int defaultSessionTimeout = 60;
98  
99      /**
100      * Process a MessageContext.
101      */
102     public void invoke(MessageContext context) throws AxisFault
103     {
104         // Should we reap timed out sessions?
105         long curTime = System.currentTimeMillis();
106         boolean reap = false;
107         
108         // Minimize synchronicity, just check in here, do reap later.
109         synchronized (this) {
110             if (curTime > lastReapTime + (reapPeriodicity * 1000)) {
111                 reap = true;
112                 lastReapTime = curTime;
113             }
114         }
115         
116         if (reap) {
117             Set entries = activeSessions.entrySet();
118             Set victims = new HashSet();
119             Object key;
120             Iterator i;
121             for (i = entries.iterator(); i.hasNext();) {
122                 Map.Entry entry = (Map.Entry) i.next();
123                 key = entry.getKey();
124                 SimpleSession session = (SimpleSession) entry.getValue();
125                 if ((curTime - session.getLastAccessTime()) >
126                      (session.getTimeout() * 1000)) {
127                     log.debug(Messages.getMessage("timeout00",
128                                                         key.toString()));
129 
130                     // Don't modify the hashtable while we're iterating.
131                     victims.add(key);
132                 }
133             }
134 
135             // Now go remove all the victims we found during the iteration.
136             for (i = victims.iterator(); i.hasNext();) {
137                 key = i.next();
138                 SimpleSession session = (SimpleSession)activeSessions.get(key);
139                 activeSessions.remove(key);
140 
141                 // For each victim, swing through the data looking for
142                 // ServiceLifecycle objects, and calling destroy() on them.
143                 // FIXME : This cleanup should probably happen on another
144                 //         thread, as it might take a little while.
145                 Enumeration keys = session.getKeys();
146                 while (keys != null && keys.hasMoreElements()) {
147                     String keystr = (String)keys.nextElement();
148                     Object obj = session.get(keystr);
149                     if (obj != null && obj instanceof ServiceLifecycle) {
150                         ((ServiceLifecycle)obj).destroy();
151                     }
152                 }
153             }
154         }
155         
156         if (context.isClient()) {
157             doClient(context);
158         } else {
159             doServer(context);
160         }
161     }
162 
163     /**
164      * Client side of processing.
165      */
166     public void doClient(MessageContext context) throws AxisFault
167     {
168         if (context.getPastPivot()) {
169             // This is a response.  Check it for the session header.
170             Message msg = context.getResponseMessage();
171             if (msg == null)
172                 return;
173             SOAPEnvelope env = msg.getSOAPEnvelope();
174             SOAPHeaderElement header = env.getHeaderByName(SESSION_NS,
175                                                            SESSION_LOCALPART);
176             if (header == null)
177                 return;
178             
179             // Got one!
180             try {
181                 Long id = (Long)header.
182                              getValueAsType(Constants.XSD_LONG);
183                 // Store it away.
184                 AxisEngine engine = context.getAxisEngine();
185                 engine.setOption(SESSION_ID, id);
186                 // Note that we processed this header!
187                 header.setProcessed(true);
188             } catch (Exception e) {
189                 throw AxisFault.makeFault(e);
190             }
191         } else {
192             AxisEngine engine = context.getAxisEngine();
193             Long id = (Long)engine.getOption(SESSION_ID);
194             if (id == null)
195                 return;
196             
197             // We have a session ID, so insert the header
198             Message msg = context.getRequestMessage();
199             if (msg == null)
200                 throw new AxisFault(Messages.getMessage("noRequest00"));
201             
202             SOAPEnvelope env = msg.getSOAPEnvelope();
203             SOAPHeaderElement header = new SOAPHeaderElement(SESSION_NS,
204                                                              SESSION_LOCALPART,
205                                                              id);
206             env.addHeader(header);
207         }
208     }
209 
210     /**
211      * Server side of processing.
212      */
213     public void doServer(MessageContext context) throws AxisFault
214     {
215         if (context.getPastPivot()) {
216             // This is a response.  Add the session header if we have an
217             // ID.
218             Long id = (Long)context.getProperty(SESSION_ID);
219             if (id == null)
220                 return;
221             
222             Message msg = context.getResponseMessage();
223             if (msg == null)
224                 return;
225             SOAPEnvelope env = msg.getSOAPEnvelope();
226             SOAPHeaderElement header = new SOAPHeaderElement(SESSION_NS,
227                                                              SESSION_LOCALPART,
228                                                              id);
229             env.addHeader(header);
230         } else {
231             // Request.  Set up the session if we find the header.
232             Message msg = context.getRequestMessage();
233             if (msg == null)
234                 throw new AxisFault(Messages.getMessage("noRequest00"));
235             
236             SOAPEnvelope env = msg.getSOAPEnvelope();
237             SOAPHeaderElement header = env.getHeaderByName(SESSION_NS,
238                                                            SESSION_LOCALPART);
239             Long id;
240             
241             if (header != null) {
242                 // Got one!
243                 try {
244                     id = (Long)header.
245                             getValueAsType(Constants.XSD_LONG);
246                 } catch (Exception e) {
247                     throw AxisFault.makeFault(e);
248                 }
249             } else {
250                 id = getNewSession();
251             }
252             
253             SimpleSession session = (SimpleSession)activeSessions.get(id);
254             if (session == null) {
255                 // Must have timed out, get a new one.
256                 id = getNewSession();
257                 session = (SimpleSession)activeSessions.get(id);
258             }
259 
260             // This session is still active...
261             session.touch();
262             
263             // Store it away in the MessageContext.
264             context.setSession(session);
265             context.setProperty(SESSION_ID, id);
266         }
267     }
268     
269     /**
270      * Generate a new session, register it, and return its ID.
271      *
272      * @return the new session's ID for later lookup.
273      */
274     private synchronized Long getNewSession()
275     {
276         Long id = SessionUtils.generateSession();
277         SimpleSession session = new SimpleSession();
278         session.setTimeout(defaultSessionTimeout);
279         activeSessions.put(id, session);
280         return id;
281     }
282 
283     /**
284      * Set the reaper periodicity in SECONDS
285      *
286      * Convenience method for testing.
287      *
288      * !!! TODO: Should be able to set this via options on the Handler
289      * or perhaps the engine.
290      */
291     public void setReapPeriodicity(long reapTime)
292     {
293         reapPeriodicity = reapTime;
294     }
295 
296     /**
297      * Set the default session timeout in SECONDS
298      *
299      * Again, for testing.
300      */
301     public void setDefaultSessionTimeout(int defaultSessionTimeout) {
302         this.defaultSessionTimeout = defaultSessionTimeout;
303     }
304 }