View Javadoc

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