View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.server.session;
20  
21  import java.security.SecureRandom;
22  import java.util.Random;
23  
24  import javax.servlet.http.HttpServletRequest;
25  
26  import org.eclipse.jetty.server.SessionIdManager;
27  import org.eclipse.jetty.util.component.AbstractLifeCycle;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  
31  public abstract class AbstractSessionIdManager extends AbstractLifeCycle implements SessionIdManager
32  {
33      private static final Logger LOG = Log.getLogger(AbstractSessionIdManager.class);
34  
35      private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId";
36  
37      protected Random _random;
38      protected boolean _weakRandom;
39      protected String _workerName;
40  
41      /* ------------------------------------------------------------ */
42      public AbstractSessionIdManager()
43      {
44      }
45  
46      /* ------------------------------------------------------------ */
47      public AbstractSessionIdManager(Random random)
48      {
49          _random=random;
50      }
51  
52  
53      /* ------------------------------------------------------------ */
54      /**
55       * Get the workname. If set, the workername is dot appended to the session
56       * ID and can be used to assist session affinity in a load balancer.
57       *
58       * @return String or null
59       */
60      public String getWorkerName()
61      {
62          return _workerName;
63      }
64  
65      /* ------------------------------------------------------------ */
66      /**
67       * Set the workname. If set, the workername is dot appended to the session
68       * ID and can be used to assist session affinity in a load balancer.
69       *
70       * @param workerName
71       */
72      public void setWorkerName(String workerName)
73      {
74          if (workerName.contains("."))
75              throw new IllegalArgumentException("Name cannot contain '.'");
76          _workerName=workerName;
77      }
78  
79      /* ------------------------------------------------------------ */
80      public Random getRandom()
81      {
82          return _random;
83      }
84  
85      /* ------------------------------------------------------------ */
86      public synchronized void setRandom(Random random)
87      {
88          _random=random;
89          _weakRandom=false;
90      }
91  
92      /* ------------------------------------------------------------ */
93      /**
94       * Create a new session id if necessary.
95       *
96       * @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long)
97       */
98      public String newSessionId(HttpServletRequest request, long created)
99      {
100         synchronized (this)
101         {
102             if (request!=null)
103             {
104                 // A requested session ID can only be used if it is in use already.
105                 String requested_id=request.getRequestedSessionId();
106                 if (requested_id!=null)
107                 {
108                     String cluster_id=getClusterId(requested_id);
109                     if (idInUse(cluster_id))
110                         return cluster_id;
111                 }
112 
113                 // Else reuse any new session ID already defined for this request.
114                 String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
115                 if (new_id!=null&&idInUse(new_id))
116                     return new_id;
117             }
118 
119             // pick a new unique ID!
120             String id = newSessionId(request.hashCode());
121 
122             request.setAttribute(__NEW_SESSION_ID,id);
123             return id;
124         }
125     }
126 
127     /* ------------------------------------------------------------ */
128     public String newSessionId(long seedTerm)
129     {
130         // pick a new unique ID!
131         String id=null;
132         while (id==null||id.length()==0||idInUse(id))
133         {
134             long r0=_weakRandom
135                     ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
136                     :_random.nextLong();
137                     if (r0<0)
138                         r0=-r0;
139                     long r1=_weakRandom
140                             ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
141                             :_random.nextLong();
142                             if (r1<0)
143                                 r1=-r1;
144                             id=Long.toString(r0,36)+Long.toString(r1,36);
145 
146                             //add in the id of the node to ensure unique id across cluster
147                             //NOTE this is different to the node suffix which denotes which node the request was received on
148                             if (_workerName!=null)
149                                 id=_workerName + id;
150         }
151         return id;
152     }
153 
154 
155     /* ------------------------------------------------------------ */
156     public abstract void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);
157 
158 
159     
160     /* ------------------------------------------------------------ */
161     @Override
162     protected void doStart() throws Exception
163     {
164        initRandom();
165     }
166 
167     /* ------------------------------------------------------------ */
168     @Override
169     protected void doStop() throws Exception
170     {
171     }
172 
173     /* ------------------------------------------------------------ */
174     /**
175      * Set up a random number generator for the sessionids.
176      *
177      * By preference, use a SecureRandom but allow to be injected.
178      */
179     public void initRandom ()
180     {
181         if (_random==null)
182         {
183             try
184             {
185                 _random=new SecureRandom();
186             }
187             catch (Exception e)
188             {
189                 LOG.warn("Could not generate SecureRandom for session-id randomness",e);
190                 _random=new Random();
191                 _weakRandom=true;
192             }
193         }
194         else
195             _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory());
196     }
197 
198 
199 }