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()*1000L)+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         NoSqlSession session = _sessions.remove(idInCluster);
180 
181         try
182         {
183             if (session != null)
184             {
185                 return remove(session);
186             }
187         }
188         catch (Exception e)
189         {
190             __log.warn("Problem deleting session {}", idInCluster,e);
191         }
192 
193         return session != null;
194 
195     }
196 
197     /* ------------------------------------------------------------ */
198     protected void expire( String idInCluster )
199     {
200         //get the session from memory
201         NoSqlSession session = _sessions.get(idInCluster);
202 
203         try
204         {
205             if (session == null)
206             {
207                 //we need to expire the session with its listeners, so load it
208                 session = loadSession(idInCluster);
209             }
210 
211             if (session != null)
212                 session.timeout();
213         }
214         catch (Exception e)
215         {
216             __log.warn("Problem expiring session {}", idInCluster,e);
217         }
218     }
219 
220 
221     public void invalidateSession (String idInCluster)
222     {
223         NoSqlSession session = _sessions.get(idInCluster);
224         try
225         {
226             __log.debug("invalidating session {}", idInCluster);
227             if (session != null)
228             {
229                 session.invalidate();
230             }
231         }
232         catch (Exception e)
233         {
234             __log.warn("Problem invalidating session {}", idInCluster,e); 
235         }
236     }
237 
238     
239     /* ------------------------------------------------------------ */
240     /**
241      * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
242      * <ul>  
243      * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
244      * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
245      * <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>
246      * </ul>
247      * @return the stalePeriod in seconds
248      */
249     public int getStalePeriod()
250     {
251         return _stalePeriod;
252     }
253 
254     /* ------------------------------------------------------------ */
255     /**
256      * The State Period is the maximum time in seconds that an in memory session is allows to be stale:
257      * <ul>  
258      * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li>
259      * <li>If the state period is set to a value < 0, then no staleness check will be made.</li>
260      * <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>
261      * </ul>
262      * @param stalePeriod the stalePeriod in seconds
263      */
264     public void setStalePeriod(int stalePeriod)
265     {
266         _stalePeriod = stalePeriod;
267     }
268 
269     /* ------------------------------------------------------------ */
270     /**
271      * The Save Period is the time in seconds between saves of a dirty session to the DB.  
272      * When this period is exceeded, the a dirty session will be written to the DB: <ul>
273      * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
274      * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
275      * <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>
276      * <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>
277      * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
278      * </ul>
279      * @return the savePeriod -2,-1,0,1 or the period in seconds >=2 
280      */
281     public int getSavePeriod()
282     {
283         return _savePeriod;
284     }
285 
286     /* ------------------------------------------------------------ */
287     /**
288      * The Save Period is the time in seconds between saves of a dirty session to the DB.  
289      * When this period is exceeded, the a dirty session will be written to the DB: <ul>
290      * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li>
291      * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li>
292      * <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>
293      * <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>
294      * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li>
295      * </ul>
296      * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds >=2 
297      */
298     public void setSavePeriod(int savePeriod)
299     {
300         _savePeriod = savePeriod;
301     }
302 
303     /* ------------------------------------------------------------ */
304     /**
305      * The Idle Period is the time in seconds before an in memory session is passivated.
306      * 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.
307      * If the idle period is set to a value < 0, then the session is never idled.
308      * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
309      * @return the idlePeriod
310      */
311     public int getIdlePeriod()
312     {
313         return _idlePeriod;
314     }
315 
316     /* ------------------------------------------------------------ */
317     /**
318      * The Idle Period is the time in seconds before an in memory session is passivated.
319      * 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.
320      * If the idle period is set to a value < 0, then the session is never idled.
321      * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0.
322      * @param idlePeriod the idlePeriod in seconds
323      */
324     public void setIdlePeriod(int idlePeriod)
325     {
326         _idlePeriod = idlePeriod;
327     }
328 
329     /* ------------------------------------------------------------ */
330     /**
331      * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
332      * @return the invalidateOnStop
333      */
334     public boolean isInvalidateOnStop()
335     {
336         return _invalidateOnStop;
337     }
338 
339     /* ------------------------------------------------------------ */
340     /**
341      * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
342      * @return the removeOnStop
343      */
344     public boolean isPreserveOnStop()
345     {
346         return _preserveOnStop;
347     }
348 
349     /* ------------------------------------------------------------ */
350     /**
351      * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
352      * @param invalidateOnStop the invalidateOnStop to set
353      */
354     public void setInvalidateOnStop(boolean invalidateOnStop)
355     {
356         _invalidateOnStop = invalidateOnStop;
357     }
358 
359     /* ------------------------------------------------------------ */
360     /**
361      * Preserve sessions when the session manager is stopped otherwise remove them from the DB.
362      * @param removeOnStop the removeOnStop to set
363      */
364     public void setPreserveOnStop(boolean preserveOnStop)
365     {
366         _preserveOnStop = preserveOnStop;
367     }
368 
369     /* ------------------------------------------------------------ */
370     /**
371      * Save all attributes of a session or only update the dirty attributes.
372      * @return the saveAllAttributes
373      */
374     public boolean isSaveAllAttributes()
375     {
376         return _saveAllAttributes;
377     }
378 
379     /* ------------------------------------------------------------ */
380     /**
381      * Save all attributes of a session or only update the dirty attributes.
382      * @param saveAllAttributes the saveAllAttributes to set
383      */
384     public void setSaveAllAttributes(boolean saveAllAttributes)
385     {
386         _saveAllAttributes = saveAllAttributes;
387     }
388     
389     /* ------------------------------------------------------------ */
390     @Override
391     public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
392     {
393         
394         // Take the old session out of the list of sessions
395         // Change to the new id
396         // Put it back into the list of sessions
397         // Update permanent storage
398 
399         synchronized (this)
400         {
401             try
402             {
403                 NoSqlSession session = _sessions.remove(oldClusterId);
404                 update (session, newClusterId, newNodeId);
405                 session.setClusterId(newClusterId);
406                 session.setNodeId(newNodeId);
407                 _sessions.put(newClusterId, session);
408             }
409             catch (Exception e)
410             {
411                 __log.warn(e);
412             }
413         }
414         super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
415     }
416 
417     
418     /* ------------------------------------------------------------ */
419     abstract protected NoSqlSession loadSession(String clusterId);
420     
421     /* ------------------------------------------------------------ */
422     abstract protected Object save(NoSqlSession session,Object version, boolean activateAfterSave);
423 
424     /* ------------------------------------------------------------ */
425     abstract protected Object refresh(NoSqlSession session, Object version);
426 
427     /* ------------------------------------------------------------ */
428     abstract protected boolean remove(NoSqlSession session);
429 
430     /* ------------------------------------------------------------ */
431     abstract protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception;
432     
433 }