1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.catalina.session;
19
20 import java.beans.PropertyChangeListener;
21 import java.beans.PropertyChangeSupport;
22 import java.io.IOException;
23
24 import org.apache.catalina.Lifecycle;
25 import org.apache.catalina.LifecycleException;
26 import org.apache.catalina.LifecycleListener;
27 import org.apache.catalina.Manager;
28 import org.apache.catalina.Store;
29 import org.apache.catalina.util.LifecycleSupport;
30 import org.apache.catalina.util.StringManager;
31
32 /**
33 * Abstract implementation of the Store interface to
34 * support most of the functionality required by a Store.
35 *
36 * @author Bip Thelin
37 * @version $Revision: 467222 $, $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
38 */
39
40 public abstract class StoreBase
41 implements Lifecycle, Store {
42
43 // ----------------------------------------------------- Instance Variables
44
45 /**
46 * The descriptive information about this implementation.
47 */
48 protected static String info = "StoreBase/1.0";
49
50 /**
51 * Name to register for this Store, used for logging.
52 */
53 protected static String storeName = "StoreBase";
54
55 /**
56 * Has this component been started yet?
57 */
58 protected boolean started = false;
59
60 /**
61 * The lifecycle event support for this component.
62 */
63 protected LifecycleSupport lifecycle = new LifecycleSupport(this);
64
65 /**
66 * The property change support for this component.
67 */
68 protected PropertyChangeSupport support = new PropertyChangeSupport(this);
69
70 /**
71 * The string manager for this package.
72 */
73 protected StringManager sm = StringManager.getManager(Constants.Package);
74
75 /**
76 * The Manager with which this JDBCStore is associated.
77 */
78 protected Manager manager;
79
80 // ------------------------------------------------------------- Properties
81
82 /**
83 * Return the info for this Store.
84 */
85 public String getInfo() {
86 return(info);
87 }
88
89
90 /**
91 * Return the name for this Store, used for logging.
92 */
93 public String getStoreName() {
94 return(storeName);
95 }
96
97
98 /**
99 * Set the Manager with which this Store is associated.
100 *
101 * @param manager The newly associated Manager
102 */
103 public void setManager(Manager manager) {
104 Manager oldManager = this.manager;
105 this.manager = manager;
106 support.firePropertyChange("manager", oldManager, this.manager);
107 }
108
109 /**
110 * Return the Manager with which the Store is associated.
111 */
112 public Manager getManager() {
113 return(this.manager);
114 }
115
116
117 // --------------------------------------------------------- Public Methods
118
119 /**
120 * Add a lifecycle event listener to this component.
121 *
122 * @param listener The listener to add
123 */
124 public void addLifecycleListener(LifecycleListener listener) {
125 lifecycle.addLifecycleListener(listener);
126 }
127
128
129 /**
130 * Get the lifecycle listeners associated with this lifecycle. If this
131 * Lifecycle has no listeners registered, a zero-length array is returned.
132 */
133 public LifecycleListener[] findLifecycleListeners() {
134
135 return lifecycle.findLifecycleListeners();
136
137 }
138
139
140 /**
141 * Remove a lifecycle event listener from this component.
142 *
143 * @param listener The listener to add
144 */
145 public void removeLifecycleListener(LifecycleListener listener) {
146 lifecycle.removeLifecycleListener(listener);
147 }
148
149 /**
150 * Add a property change listener to this component.
151 *
152 * @param listener a value of type 'PropertyChangeListener'
153 */
154 public void addPropertyChangeListener(PropertyChangeListener listener) {
155 support.addPropertyChangeListener(listener);
156 }
157
158 /**
159 * Remove a property change listener from this component.
160 *
161 * @param listener The listener to remove
162 */
163 public void removePropertyChangeListener(PropertyChangeListener listener) {
164 support.removePropertyChangeListener(listener);
165 }
166
167 // --------------------------------------------------------- Protected Methods
168
169 /**
170 * Called by our background reaper thread to check if Sessions
171 * saved in our store are subject of being expired. If so expire
172 * the Session and remove it from the Store.
173 *
174 */
175 public void processExpires() {
176 long timeNow = System.currentTimeMillis();
177 String[] keys = null;
178
179 if(!started) {
180 return;
181 }
182
183 try {
184 keys = keys();
185 } catch (IOException e) {
186 manager.getContainer().getLogger().error("Error getting keys", e);
187 return;
188 }
189 if (manager.getContainer().getLogger().isDebugEnabled()) {
190 manager.getContainer().getLogger().debug(getStoreName()+ ": processExpires check number of " + keys.length + " sessions" );
191 }
192
193 for (int i = 0; i < keys.length; i++) {
194 try {
195 StandardSession session = (StandardSession) load(keys[i]);
196 if (session == null) {
197 continue;
198 }
199 if (session.isValid()) {
200 continue;
201 }
202 if (manager.getContainer().getLogger().isDebugEnabled()) {
203 manager.getContainer().getLogger().debug(getStoreName()+ ": processExpires expire store session " + keys[i] );
204 }
205 if ( ( (PersistentManagerBase) manager).isLoaded( keys[i] )) {
206 // recycle old backup session
207 session.recycle();
208 } else {
209 // expire swapped out session
210 session.expire();
211 }
212 remove(session.getIdInternal());
213 } catch (Exception e) {
214 manager.getContainer().getLogger().error("Session: "+keys[i]+"; ", e);
215 try {
216 remove(keys[i]);
217 } catch (IOException e2) {
218 manager.getContainer().getLogger().error("Error removing key", e2);
219 }
220 }
221 }
222 }
223
224
225 // --------------------------------------------------------- Thread Methods
226
227
228 /**
229 * Prepare for the beginning of active use of the public methods of this
230 * component. This method should be called after <code>configure()</code>,
231 * and before any of the public methods of the component are utilized.
232 *
233 * @exception LifecycleException if this component detects a fatal error
234 * that prevents this component from being used
235 */
236 public void start() throws LifecycleException {
237 // Validate and update our current component state
238 if (started)
239 throw new LifecycleException
240 (sm.getString(getStoreName()+".alreadyStarted"));
241 lifecycle.fireLifecycleEvent(START_EVENT, null);
242 started = true;
243
244 }
245
246
247 /**
248 * Gracefully terminate the active use of the public methods of this
249 * component. This method should be the last one called on a given
250 * instance of this component.
251 *
252 * @exception LifecycleException if this component detects a fatal error
253 * that needs to be reported
254 */
255 public void stop() throws LifecycleException {
256 // Validate and update our current component state
257 if (!started)
258 throw new LifecycleException
259 (sm.getString(getStoreName()+".notStarted"));
260 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
261 started = false;
262
263 }
264
265
266 }