View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.session.mgt.eis;
20  
21  import org.apache.shiro.session.Session;
22  import org.apache.shiro.session.UnknownSessionException;
23  import org.apache.shiro.session.mgt.SimpleSession;
24  
25  import java.io.Serializable;
26  
27  
28  /**
29   * An abstract {@code SessionDAO} implementation that performs some sanity checks on session creation and reading and
30   * allows for pluggable Session ID generation strategies if desired.  The {@code SessionDAO}
31   * {@link SessionDAO#update update} and {@link SessionDAO#delete delete} methods are left to
32   * subclasses.
33   * <h3>Session ID Generation</h3>
34   * This class also allows for plugging in a {@link SessionIdGenerator} for custom ID generation strategies.  This is
35   * optional, as the default generator is probably sufficient for most cases.  Subclass implementations that do use a
36   * generator (default or custom) will want to call the
37   * {@link #generateSessionId(org.apache.shiro.session.Session)} method from within their {@link #doCreate}
38   * implementations.
39   * <p/>
40   * Subclass implementations that rely on the EIS data store to generate the ID automatically (e.g. when the session
41   * ID is also an auto-generated primary key), they can simply ignore the {@code SessionIdGenerator} concept
42   * entirely and just return the data store's ID from the {@link #doCreate} implementation.
43   *
44   * @since 1.0
45   */
46  public abstract class AbstractSessionDAO implements SessionDAO {
47  
48      /**
49       * Optional SessionIdGenerator instance available to subclasses via the
50       * {@link #generateSessionId(org.apache.shiro.session.Session)} method.
51       */
52      private SessionIdGenerator sessionIdGenerator;
53  
54      /**
55       * Default no-arg constructor that defaults the {@link #setSessionIdGenerator sessionIdGenerator} to be a
56       * {@link org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator}.
57       */
58      public AbstractSessionDAO() {
59          this.sessionIdGenerator = new JavaUuidSessionIdGenerator();
60      }
61  
62      /**
63       * Returns the {@code SessionIdGenerator} used by the {@link #generateSessionId(org.apache.shiro.session.Session)}
64       * method.  Unless overridden by the {@link #setSessionIdGenerator(SessionIdGenerator)} method, the default instance
65       * is a {@link JavaUuidSessionIdGenerator}.
66       *
67       * @return the {@code SessionIdGenerator} used by the {@link #generateSessionId(org.apache.shiro.session.Session)}
68       *         method.
69       */
70      public SessionIdGenerator getSessionIdGenerator() {
71          return sessionIdGenerator;
72      }
73  
74      /**
75       * Sets the {@code SessionIdGenerator} used by the {@link #generateSessionId(org.apache.shiro.session.Session)}
76       * method.  Unless overridden by this method, the default instance ss a {@link JavaUuidSessionIdGenerator}.
77       *
78       * @param sessionIdGenerator the {@code SessionIdGenerator} to use in the
79       *                           {@link #generateSessionId(org.apache.shiro.session.Session)} method.
80       */
81      public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {
82          this.sessionIdGenerator = sessionIdGenerator;
83      }
84  
85      /**
86       * Generates a new ID to be applied to the specified {@code session} instance.  This method is usually called
87       * from within a subclass's {@link #doCreate} implementation where they assign the returned id to the session
88       * instance and then create a record with this ID in the EIS data store.
89       * <p/>
90       * Subclass implementations backed by EIS data stores that auto-generate IDs during record creation, such as
91       * relational databases, don't need to use this method or the {@link #getSessionIdGenerator() sessionIdGenerator}
92       * attribute - they can simply return the data store's generated ID from the {@link #doCreate} implementation
93       * if desired.
94       * <p/>
95       * This implementation uses the {@link #setSessionIdGenerator configured} {@link SessionIdGenerator} to create
96       * the ID.
97       *
98       * @param session the new session instance for which an ID will be generated and then assigned
99       * @return the generated ID to assign
100      */
101     protected Serializable generateSessionId(Session session) {
102         if (this.sessionIdGenerator == null) {
103             String msg = "sessionIdGenerator attribute has not been configured.";
104             throw new IllegalStateException(msg);
105         }
106         return this.sessionIdGenerator.generateId(session);
107     }
108 
109     /**
110      * Creates the session by delegating EIS creation to subclasses via the {@link #doCreate} method, and then
111      * asserting that the returned sessionId is not null.
112      *
113      * @param session Session object to create in the EIS and associate with an ID.
114      */
115     public Serializable create(Session session) {
116         Serializable sessionId = doCreate(session);
117         verifySessionId(sessionId);
118         return sessionId;
119     }
120 
121     /**
122      * Ensures the sessionId returned from the subclass implementation of {@link #doCreate} is not null and not
123      * already in use.
124      *
125      * @param sessionId session id returned from the subclass implementation of {@link #doCreate}
126      */
127     private void verifySessionId(Serializable sessionId) {
128         if (sessionId == null) {
129             String msg = "sessionId returned from doCreate implementation is null.  Please verify the implementation.";
130             throw new IllegalStateException(msg);
131         }
132     }
133 
134     /**
135      * Utility method available to subclasses that wish to
136      * assign a generated session ID to the session instance directly.  This method is not used by the
137      * {@code AbstractSessionDAO} implementation directly, but it is provided so subclasses don't
138      * need to know the {@code Session} implementation if they don't need to.
139      * <p/>
140      * This default implementation casts the argument to a {@link SimpleSession}, Shiro's default EIS implementation.
141      *
142      * @param session   the session instance to which the sessionId will be applied
143      * @param sessionId the id to assign to the specified session instance.
144      */
145     protected void assignSessionId(Session session, Serializable sessionId) {
146         ((SimpleSession) session).setId(sessionId);
147     }
148 
149     /**
150      * Subclass hook to actually persist the given <tt>Session</tt> instance to the underlying EIS.
151      *
152      * @param session the Session instance to persist to the EIS.
153      * @return the id of the session created in the EIS (i.e. this is almost always a primary key and should be the
154      *         value returned from {@link org.apache.shiro.session.Session#getId() Session.getId()}.
155      */
156     protected abstract Serializable doCreate(Session session);
157 
158     /**
159      * Retrieves the Session object from the underlying EIS identified by <tt>sessionId</tt> by delegating to
160      * the {@link #doReadSession(java.io.Serializable)} method.  If {@code null} is returned from that method, an
161      * {@link UnknownSessionException} will be thrown.
162      *
163      * @param sessionId the id of the session to retrieve from the EIS.
164      * @return the session identified by <tt>sessionId</tt> in the EIS.
165      * @throws UnknownSessionException if the id specified does not correspond to any session in the EIS.
166      */
167     public Session readSession(Serializable sessionId) throws UnknownSessionException {
168         Session s = doReadSession(sessionId);
169         if (s == null) {
170             throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
171         }
172         return s;
173     }
174 
175     /**
176      * Subclass implementation hook that retrieves the Session object from the underlying EIS or {@code null} if a
177      * session with that ID could not be found.
178      *
179      * @param sessionId the id of the <tt>Session</tt> to retrieve.
180      * @return the Session in the EIS identified by <tt>sessionId</tt> or {@code null} if a
181      *         session with that ID could not be found.
182      */
183     protected abstract Session doReadSession(Serializable sessionId);
184 
185 }