View Javadoc

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