View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.gcloud.session;
20  
21  import java.util.Random;
22  
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpSession;
25  
26  import org.eclipse.jetty.server.Handler;
27  import org.eclipse.jetty.server.Server;
28  import org.eclipse.jetty.server.SessionManager;
29  import org.eclipse.jetty.server.handler.ContextHandler;
30  import org.eclipse.jetty.server.session.AbstractSession;
31  import org.eclipse.jetty.server.session.AbstractSessionIdManager;
32  import org.eclipse.jetty.server.session.SessionHandler;
33  import org.eclipse.jetty.util.log.Log;
34  import org.eclipse.jetty.util.log.Logger;
35  
36  import com.google.gcloud.datastore.Datastore;
37  import com.google.gcloud.datastore.DatastoreFactory;
38  import com.google.gcloud.datastore.Entity;
39  import com.google.gcloud.datastore.Key;
40  import com.google.gcloud.datastore.KeyFactory;
41  
42  
43  
44  /**
45   * GCloudSessionIdManager
46   *
47   * 
48   * 
49   */
50  public class GCloudSessionIdManager extends AbstractSessionIdManager
51  {
52      private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
53      public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
54      public static final String KIND = "GCloudSessionId";
55      private Server _server;
56      private Datastore _datastore;
57      private KeyFactory _keyFactory;
58      private GCloudConfiguration _config;
59      
60      
61      
62   
63      /**
64       * @param server
65       */
66      public GCloudSessionIdManager(Server server)
67      {
68          super();
69          _server = server;
70      }
71  
72      /**
73       * @param server
74       * @param random
75       */
76      public GCloudSessionIdManager(Server server, Random random)
77      {
78         super(random);
79         _server = server;
80      }
81  
82  
83  
84      /** 
85       * Start the id manager.
86       * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart()
87       */
88      @Override
89      protected void doStart() throws Exception
90      {
91          if (_config == null)
92              throw new IllegalStateException("No gcloud configuration specified");       
93          
94  
95          _datastore = DatastoreFactory.instance().get(_config.getDatastoreOptions());
96          _keyFactory = _datastore.newKeyFactory().kind(KIND);
97    
98          super.doStart();
99      }
100 
101 
102     
103     /** 
104      * Stop the id manager
105      * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStop()
106      */
107     @Override
108     protected void doStop() throws Exception
109     {
110         super.doStop();
111     }
112 
113     
114    
115 
116     
117     /** 
118      * Check to see if the given session id is being
119      * used by a session in any context.
120      * 
121      * This method will consult the cluster.
122      * 
123      * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
124      */
125     @Override
126     public boolean idInUse(String id)
127     {
128         if (id == null)
129             return false;
130         
131         String clusterId = getClusterId(id);
132         
133         //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
134         //keeping it valid
135         try
136         {
137             return exists(clusterId);
138         }
139         catch (Exception e)
140         {
141             LOG.warn("Problem checking inUse for id="+clusterId, e);
142             return false;
143         }
144         
145     }
146 
147     /** 
148      * Remember a new in-use session id.
149      * 
150      * This will save the in-use session id to the cluster.
151      * 
152      * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
153      */
154     @Override
155     public void addSession(HttpSession session)
156     {
157         if (session == null)
158             return;
159 
160         //insert into the store
161         insert (((AbstractSession)session).getClusterId());
162     }
163 
164   
165 
166     
167     public GCloudConfiguration getConfig()
168     {
169         return _config;
170     }
171 
172     public void setConfig(GCloudConfiguration config)
173     {
174         _config = config;
175     }
176 
177     
178     
179     /** 
180      * Remove a session id from the list of in-use ids.
181      * 
182      * This will remvove the corresponding session id from the cluster.
183      * 
184      * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
185      */
186     @Override
187     public void removeSession(HttpSession session)
188     {
189         if (session == null)
190             return;
191 
192         //delete from the cache
193         delete (((AbstractSession)session).getClusterId());
194     }
195 
196     /** 
197      * Remove a session id. This compels all other contexts who have a session
198      * with the same id to also remove it.
199      * 
200      * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
201      */
202     @Override
203     public void invalidateAll(String id)
204     {
205         //delete the session id from list of in-use sessions
206         delete (id);
207 
208 
209         //tell all contexts that may have a session object with this id to
210         //get rid of them
211         Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
212         for (int i=0; contexts!=null && i<contexts.length; i++)
213         {
214             SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
215             if (sessionHandler != null)
216             {
217                 SessionManager manager = sessionHandler.getSessionManager();
218 
219                 if (manager != null && manager instanceof GCloudSessionManager)
220                 {
221                     ((GCloudSessionManager)manager).invalidateSession(id);
222                 }
223             }
224         }
225 
226     }
227 
228     /** 
229      * Change a session id. 
230      * 
231      * Typically this occurs when a previously existing session has passed through authentication.
232      * 
233      * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
234      */
235     @Override
236     public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
237     {
238         //generate a new id
239         String newClusterId = newSessionId(request.hashCode());
240 
241         delete(oldClusterId);
242         insert(newClusterId);
243 
244 
245         //tell all contexts to update the id 
246         Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
247         for (int i=0; contexts!=null && i<contexts.length; i++)
248         {
249             SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
250             if (sessionHandler != null) 
251             {
252                 SessionManager manager = sessionHandler.getSessionManager();
253 
254                 if (manager != null && manager instanceof GCloudSessionManager)
255                 {
256                     ((GCloudSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
257                 }
258             }
259         }
260 
261     }
262 
263     
264     
265     /**
266      * Ask the datastore if a particular id exists.
267      * 
268      * @param id
269      * @return
270      */
271     protected boolean exists (String id)
272     {
273         if (_datastore == null)
274             throw new IllegalStateException ("No DataStore");
275         Key key = _keyFactory.newKey(id);
276         return _datastore.get(key) != null;
277     }
278     
279 
280     /**
281      * Put a session id into the cluster.
282      * 
283      * @param id
284      */
285     protected void insert (String id)
286     {        
287         if (_datastore == null)
288             throw new IllegalStateException ("No DataStore");
289 
290         Entity entity = Entity.builder(makeKey(id))
291                         .set("id", id).build();
292         _datastore.put(entity);
293     }
294 
295    
296    
297     
298     /**
299      * Remove a session id from the cluster.
300      * 
301      * @param id
302      */
303     protected void delete (String id)
304     {
305         if (_datastore == null)
306             throw new IllegalStateException ("No DataStore");
307         
308         _datastore.delete(makeKey(id));
309     }
310     
311     
312 
313     /**
314      * Generate a unique key from the session id.
315      * 
316      * @param id
317      * @return
318      */
319     protected Key makeKey (String id)
320     {
321         return _keyFactory.newKey(id);
322     }
323 }