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.nosql;
20  
21  import java.util.ArrayList;
22  import java.util.concurrent.ConcurrentHashMap;
23  import java.util.concurrent.ConcurrentMap;
24  import java.util.concurrent.TimeUnit;
25  
26  import javax.servlet.http.HttpServletRequest;
27  
28  import org.eclipse.jetty.server.SessionManager;
29  import org.eclipse.jetty.server.session.AbstractSession;
30  import org.eclipse.jetty.server.session.AbstractSessionManager;
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  
34  /**
35   * NoSqlSessionManager
36   *
37   * Base class for SessionManager implementations using nosql frameworks
38   */
39  public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager
40  {
41      private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
42  
43      protected final ConcurrentMap<String,NoSqlSession> _sessions=new ConcurrentHashMap<String,NoSqlSession>();
44  
45      private int _stalePeriod=0;
46      private int _savePeriod=0;
47      private int _idlePeriod=-1;
48      private boolean _deidleBeforeExpiry = true;
49      private boolean _invalidateOnStop;
50      private boolean _preserveOnStop = true;
51      private boolean _saveAllAttributes;
52      
53      /* ------------------------------------------------------------ */
54      /**
55       * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
56       */
57      @Override
58      public void doStart() throws Exception
59      {
60          super.doStart();
61         
62      }
63      
64      /* ------------------------------------------------------------ */
65      @Override
66      protected void addSession(AbstractSession session)
67      {
68          if (isRunning())
69          {
70              //add into memory
71              _sessions.put(session.getClusterId(),(NoSqlSession)session);
72              //add into db
73              ((NoSqlSession)session).save(true);
74          }
75      }
76  
77      /* ------------------------------------------------------------ */
78      @Override
79      public AbstractSession getSession(String idInCluster)
80      {
81          NoSqlSession session = _sessions.get(idInCluster);
82          __log.debug("getSession {} ", session );
83          
84          if (session==null)
85          {
86              __log.debug("Session {} is not in memory", idInCluster);
87              
88              //session not in this node's memory, load it
89              session=loadSession(idInCluster);
90              
91              if (session!=null)
92              {
93                  //session exists, check another request thread hasn't loaded it too
94                  NoSqlSession race=_sessions.putIfAbsent(idInCluster,session);
95                  if (race!=null)
96                  {
97                      session.willPassivate();
98                      session.clearAttributes();
99                      session=race;
100                 }
101                 else
102                     __log.debug("session loaded ", idInCluster);
103                 
104                 //check if the session we just loaded has actually expired, maybe while we weren't running
105                 if (session.getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((session.getMaxInactiveInterval()*1000L)+session.getAccessed()) < System.currentTimeMillis())
106                 {
107                     __log.debug("session expired ", idInCluster);
108                     expire(idInCluster);
109                     session = null;
110                 }
111             }
112             else
113                 __log.debug("session does not exist {}", idInCluster);
114         }
115         else
116             session.deIdle();
117 
118         return session;
119     }
120     
121     /* ------------------------------------------------------------ */
122     @Override
123     protected void shutdownSessions() throws Exception
124     {
125         //If we are stopping, and we're preserving sessions, then we want to
126         //save all of the sessions (including those that have been added during this method call)
127         //and then just remove them from memory.
128         
129         //If we don't wish to preserve sessions and we're stopping, then we should invalidate
130         //the session (which may remove it).
131         long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
132         long stopTime = 0;
133         if (gracefulStopMs > 0)
134             stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));        
135         
136         ArrayList<NoSqlSession> sessions=new ArrayList<NoSqlSession>(_sessions.values());
137 
138         // loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop
139         while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0)))
140         {
141             for (NoSqlSession session : sessions)
142             {
143                 if (isPreserveOnStop())
144                 {
145                     //we don't want to delete the session, so save the session
146                     //and remove from memory
147                     session.save(false);
148                     _sessions.remove(session.getClusterId());
149                 }
150                 else
151                 {
152                   //invalidate the session so listeners will be called and also removes the session
153                   session.invalidate();
154                 }
155             }
156             
157             //check if we should terminate our loop if we're not using the stop timer
158             if (stopTime == 0)
159             {
160                 break;
161             }
162             // Get any sessions that were added by other requests during processing and go around the loop again
163             sessions=new ArrayList<NoSqlSession>(_sessions.values());
164         }
165     }
166     
167 
168     /* ------------------------------------------------------------ */
169     @Override
170     protected AbstractSession newSession(HttpServletRequest request)
171     {
172         return new NoSqlSession(this,request);
173     }
174 
175     /* ------------------------------------------------------------ */
176     /** Remove the session from the in-memory list for this context.
177      * Also remove the context sub-document for this session id from the db.
178      * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
179      */
180     @Override
181     protected boolean removeSession(String idInCluster)
182     {
183         NoSqlSession session = _sessions.remove(idInCluster);
184 
185         try
186         {
187             if (session != null)
188             {
189                 return remove(session);
190             }
191         }
192         catch (Exception e)
193         {
194             __log.warn("Problem deleting session {}", idInCluster,e);
195         }
196 
197         return session != null;
198 
199     }
200 
201     /* ------------------------------------------------------------ */
202     protected void expire( String idInCluster )
203     {
204         //get the session from memory
205         NoSqlSession session = _sessions.get(idInCluster);
206 
207         try
208         {
209             if (session == null)
210             {
211                 //we need to expire the session with its listeners, so load it
212                 session = loadSession(idInCluster);
213             }
214             else
215             {
216                 //deidle if the session was idled
217                 if (isDeidleBeforeExpiry())
218                     session.deIdle();
219             }
220 
221             //check that session is still valid after potential de-idle
222             if (session != null && session.isValid())
223                 session.timeout();
224         }
225         catch (Exception e)
226         {
227             __log.warn("Problem expiring session {}", idInCluster,e);
228         }
229     }
230 
231 
232     public void invalidateSession (String idInCluster)
233     {
234         NoSqlSession session = _sessions.get(idInCluster);
235         try
236         {
237             __log.debug("invalidating session {}", idInCluster);
238             if (session != null)
239             {
240                 session.invalidate();
241             }
242         }
243         catch (Exception e)
244         {
245             __log.warn("Problem invalidating session {}", idInCluster,e); 
246         }
247     }
248 
249     
250     /* ------------------------------------------------------------ */
251     /**
252      * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
253      * <ul>  
254      * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
255      * <li>If the state period is set to a value &lt; 0, then no staleness check will be made.</li>
256      * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li>
257      * </ul>
258      * @return the stalePeriod in seconds
259      */
260     public int getStalePeriod()
261     {
262         return _stalePeriod;
263     }
264 
265     /* ------------------------------------------------------------ */
266     /**
267      * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
268      * <ul>  
269      * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
270      * <li>If the state period is set to a value &lt; 0, then no staleness check will be made.</li>
271      * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li>
272      * </ul>
273      * @param stalePeriod the stalePeriod in seconds
274      */
275     public void setStalePeriod(int stalePeriod)
276     {
277         _stalePeriod = stalePeriod;
278     }
279 
280     /* ------------------------------------------------------------ */
281     /**
282      * The Save Period is the time in seconds between saves of a dirty session to the DB.  
283      * When this period is exceeded, the a dirty session will be written to the DB: <ul>
284      * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
285      * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
286      * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li>
287      * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li>
288      * <li>a save period of &gt; 1 means the session is written after that period in seconds of being dirty.</li>
289      * </ul>
290      * @return the savePeriod -2,-1,0,1 or the period in seconds &gt;=2 
291      */
292     public int getSavePeriod()
293     {
294         return _savePeriod;
295     }
296 
297     /* ------------------------------------------------------------ */
298     /**
299      * The Save Period is the time in seconds between saves of a dirty session to the DB.  
300      * When this period is exceeded, the a dirty session will be written to the DB: <ul>
301      * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
302      * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
303      * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li>
304      * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li>
305      * <li>a save period of &gt; 1 means the session is written after that period in seconds of being dirty.</li>
306      * </ul>
307      * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds &gt;=2 
308      */
309     public void setSavePeriod(int savePeriod)
310     {
311         _savePeriod = savePeriod;
312     }
313 
314     /* ------------------------------------------------------------ */
315     /**
316      * The Idle Period is the time in seconds before an in memory session is passivated.
317      * When this period is exceeded, the session will be passivated and removed from memory.  If the session was dirty, it will be written to the DB.
318      * If the idle period is set to a value &lt; 0, then the session is never idled.
319      * @return the idlePeriod
320      */
321     public int getIdlePeriod()
322     {
323         return _idlePeriod;
324     }
325 
326     /* ------------------------------------------------------------ */
327     /**
328      * The Idle Period is the time in seconds before an in memory session is passivated.
329      * When this period is exceeded, the session will be passivated and removed from memory.  If the session was dirty, it will be written to the DB.
330      * If the idle period is set to a value &lt; 0, then the session is never idled.
331      * @param idlePeriod the idlePeriod in seconds
332      */
333     public void setIdlePeriod(int idlePeriod)
334     {
335         _idlePeriod = idlePeriod;
336     }
337 
338     /* ------------------------------------------------------------ */
339     /**
340      * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
341      * @return the invalidateOnStop
342      */
343     public boolean isInvalidateOnStop()
344     {
345         return _invalidateOnStop;
346     }
347 
348     /* ------------------------------------------------------------ */
349     /**
350      * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
351      * @return the removeOnStop
352      */
353     public boolean isPreserveOnStop()
354     {
355         return _preserveOnStop;
356     }
357 
358     /* ------------------------------------------------------------ */
359     /**
360      * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
361      * @param invalidateOnStop the invalidateOnStop to set
362      */
363     public void setInvalidateOnStop(boolean invalidateOnStop)
364     {
365         _invalidateOnStop = invalidateOnStop;
366     }
367 
368     /* ------------------------------------------------------------ */
369     /**
370      * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
371      * @param preserveOnStop the preserveOnStop to set
372      */
373     public void setPreserveOnStop(boolean preserveOnStop)
374     {
375         _preserveOnStop = preserveOnStop;
376     }
377 
378     /* ------------------------------------------------------------ */
379     /**
380      * Save all attributes of a session or only update the dirty attributes.
381      * @return the saveAllAttributes
382      */
383     public boolean isSaveAllAttributes()
384     {
385         return _saveAllAttributes;
386     }
387 
388     /* ------------------------------------------------------------ */
389     /**
390      * Save all attributes of a session or only update the dirty attributes.
391      * @param saveAllAttributes the saveAllAttributes to set
392      */
393     public void setSaveAllAttributes(boolean saveAllAttributes)
394     {
395         _saveAllAttributes = saveAllAttributes;
396     }
397     
398     /* ------------------------------------------------------------ */
399     public boolean isDeidleBeforeExpiry()
400     {
401         return _deidleBeforeExpiry;
402     }
403     
404     /* ------------------------------------------------------------ */
405     public void setDeidleBeforeExpiry(boolean deidleBeforeExpiry)
406     {
407         _deidleBeforeExpiry = deidleBeforeExpiry;
408     }
409     
410     /* ------------------------------------------------------------ */
411     @Override
412     public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
413     {
414         
415         // Take the old session out of the list of sessions
416         // Change to the new id
417         // Put it back into the list of sessions
418         // Update permanent storage
419 
420         synchronized (this)
421         {
422             try
423             {
424                 NoSqlSession session = _sessions.remove(oldClusterId);
425                 update (session, newClusterId, newNodeId);
426                 session.setClusterId(newClusterId);
427                 session.setNodeId(newNodeId);
428                 _sessions.put(newClusterId, session);
429             }
430             catch (Exception e)
431             {
432                 __log.warn(e);
433             }
434         }
435         super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
436     }
437 
438     
439     /* ------------------------------------------------------------ */
440     abstract protected NoSqlSession loadSession(String clusterId);
441     
442     /* ------------------------------------------------------------ */
443     abstract protected Object save(NoSqlSession session,Object version, boolean activateAfterSave);
444 
445     /* ------------------------------------------------------------ */
446     abstract protected Object refresh(NoSqlSession session, Object version);
447 
448     /* ------------------------------------------------------------ */
449     abstract protected boolean remove(NoSqlSession session);
450 
451     /* ------------------------------------------------------------ */
452     abstract protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception;
453     
454 }