001: /*
002: * Copyright 2005 jWic group (http://www.jwic.de)
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: * de.jwic.base.SessionStorage
017: * Created on 08.11.2005
018: * $Id: SessionManager.java,v 1.3 2007/03/09 09:30:00 lordsam Exp $
019: */
020: package de.jwic.base;
021:
022: import java.io.File;
023: import java.io.FileInputStream;
024: import java.io.FileOutputStream;
025: import java.io.ObjectInputStream;
026: import java.io.ObjectOutputStream;
027: import java.util.Collection;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.Map;
031: import java.util.Timer;
032: import java.util.TimerTask;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036:
037: import de.jwic.events.SessionEvent;
038:
039: /**
040: * Manages the SessionContainer objects. It is used by the JWicRuntime
041: * to create, retrieve, serialize, deserialize and destroy applications.
042: *
043: * @author Florian Lippisch
044: * @version $Revision: 1.3 $
045: */
046: public class SessionManager {
047:
048: protected final Log log = LogFactory.getLog(getClass());
049:
050: private final static long DELAY = 1000 * 60 * 5; // wait 5 minutes before the first run.
051: private final static long INTERVALL = 1000 * 60 * 5; // run every 5 minutes.
052:
053: // client map
054: private Map clientSessions = new HashMap();
055: private File serDir = null;
056:
057: private long idCount = 10000;
058: private int storeTime = 0;
059:
060: private Timer timer = null;
061:
062: private class RefreshDeamon extends TimerTask {
063: /* (non-Javadoc)
064: * @see java.util.TimerTask#run()
065: */
066: public void run() {
067: storeOldSessions();
068: }
069: }
070:
071: /**
072: * Default Constructor.
073: */
074: public SessionManager(String tempDir) {
075: serDir = new File(tempDir);
076: if (!serDir.exists()) {
077: serDir.mkdirs();
078: }
079:
080: timer = new Timer(true);
081: timer.schedule(new RefreshDeamon(), DELAY, INTERVALL);
082: }
083:
084: /**
085: * Cancles the RefreshDeamon task and destroys.
086: * all open sessions.
087: */
088: public void destroy() {
089:
090: // stop timer.
091: timer.cancel();
092:
093: // destroy all clients.
094: for (Iterator it = getClientIDs().iterator(); it.hasNext();) {
095: String clientID = (String) it.next();
096: destroyClient(clientID);
097: }
098:
099: }
100:
101: /**
102: *
103: */
104: public void storeOldSessions() {
105:
106: if (storeTime > 0) { // serialization is not disabled
107: log.debug("Searching for out-timed sessions to store...");
108: long maxAge = System.currentTimeMillis()
109: - (storeTime * 1000 * 60);
110: for (Iterator it = getClientIDs().iterator(); it.hasNext();) {
111: String clientID = (String) it.next();
112: Collection c = getSessions(clientID);
113: if (c != null) {
114: for (Iterator itS = c.iterator(); itS.hasNext();) {
115: SessionContainer container = (SessionContainer) itS
116: .next();
117: if (container.getState() == SessionContainer.STATE_NORMAL
118: && container.getLastAccess() < maxAge) {
119: if (container.getSessionContext()
120: .getApplicationSetup()
121: .isSerializable()) {
122: try {
123: log
124: .debug("Auto-Serializing container "
125: + container);
126: serialize(container);
127: } catch (Exception e) {
128: log.error(
129: "Error serializing container "
130: + container, e);
131: }
132: }
133: }
134: }
135: }
136: }
137:
138: }
139:
140: }
141:
142: /**
143: * Create a new SessionStore object.
144: * @param clientID - String
145: * @param applicationID - String
146: * @param singleSession - boolean
147: * @return
148: */
149: public SessionContainer create(String clientID, String applicationID) {
150:
151: Map sessionMap = getSessionMap(clientID);
152:
153: long idNum = idCount++;
154: String id = idNum + "-" + System.currentTimeMillis();
155:
156: SessionContainer store = new SessionContainer(id, clientID);
157: store.setApplicationId(applicationID);
158:
159: sessionMap.put(id, store);
160:
161: return store;
162: }
163:
164: /**
165: * @param clientID
166: * @return
167: */
168: private Map getSessionMap(String clientID) {
169:
170: Map map = (Map) clientSessions.get(clientID);
171: if (map == null) {
172: synchronized (clientSessions) {
173: map = (Map) clientSessions.get(clientID);
174: if (map == null) {
175: map = new HashMap();
176: clientSessions.put(clientID, map);
177: }
178: }
179: }
180: return map;
181: }
182:
183: /**
184: * Returns the SessionStore with the specified id. Returns <code>null</code>
185: * if no SessionStore was found.
186: * @param clientID
187: * @param id
188: * @return
189: */
190: public SessionContainer get(String clientID, String id) {
191: Map sessionMap = getSessionMap(clientID);
192: return (SessionContainer) sessionMap.get(id);
193: }
194:
195: /**
196: * Returns the SessionStore for the specified applicationId
197: * @param clientID
198: * @param applicationID
199: * @return
200: */
201: public SessionContainer getByAppID(String clientID,
202: String applicationID) {
203: Map sessionMap = getSessionMap(clientID);
204:
205: // iterate through the sessions to find one with the specified appId
206: for (Iterator it = sessionMap.values().iterator(); it.hasNext();) {
207: SessionContainer store = (SessionContainer) it.next();
208: if (applicationID.equals(store.getApplicationId())) {
209: return store;
210: }
211: }
212:
213: return null;
214: }
215:
216: /**
217: * Removes the SessionStore.
218: * @param store
219: */
220: public void remove(SessionContainer store) {
221:
222: Map sessionMap = getSessionMap(store.getClientId());
223: sessionMap.remove(store.getId());
224: synchronized (clientSessions) {
225: if (sessionMap.size() == 0) {
226: clientSessions.remove(store.getClientId());
227: }
228: }
229: }
230:
231: /**
232: * Write the session to a temporary directory.
233: * @param container
234: */
235: public void serialize(SessionContainer container) {
236:
237: log.debug("Serializing container " + container);
238:
239: String filename = container.getClientId() + "_"
240: + container.getId() + ".ser";
241:
242: try {
243: SessionContext sc = container.getSessionContext();
244: sc.fireEvent(new SessionEvent(null),
245: SessionContext.BEFORE_SERIALIZATION);
246: FileOutputStream fos = new FileOutputStream(new File(
247: serDir, filename));
248: ObjectOutputStream oos = new ObjectOutputStream(fos);
249:
250: oos.writeObject(sc);
251: oos.close();
252:
253: container.setState(SessionContainer.STATE_STORED);
254: container.setSessionContext(null); // release SessionContext
255:
256: } catch (Exception e) {
257: log.error("Error storing session", e);
258: throw new JWicException(
259: "Serialization failed. Can not serialize session.",
260: e);
261: }
262:
263: }
264:
265: /**
266: * Reactivate the session.
267: * @param container
268: */
269: public void deserialize(SessionContainer container) {
270:
271: log.debug("Deserializing container " + container);
272:
273: try {
274: String filename = container.getClientId() + "_"
275: + container.getId() + ".ser";
276: File file = new File(serDir, filename);
277: if (file.exists()) {
278: FileInputStream fis = new FileInputStream(file);
279: ObjectInputStream ois = new ObjectInputStream(fis);
280:
281: SessionContext sc = (SessionContext) ois.readObject();
282: ois.close();
283: sc.fireEvent(new SessionEvent(null),
284: SessionContext.AFTER_DESERIALIZATION);
285:
286: container.setSessionContext(sc);
287: container.setState(SessionContainer.STATE_NORMAL);
288:
289: fis.close();
290: if (!file.delete()) { // remove the serialized file.
291: log.warn("Can't delete serialization file "
292: + file.getName());
293: }
294:
295: } else {
296: throw new JWicException(
297: "The session can not be desrialized because the data can not be found.");
298: }
299:
300: } catch (Exception e) {
301: log.error("Error deserializing session", e);
302: throw new JWicException(
303: "Deserialization failed. Can not deserialize session.",
304: e);
305: }
306:
307: }
308:
309: /**
310: * Destroys all sessions store for the specified clientID.
311: * @param clientID
312: */
313: public void destroyClient(String clientID) {
314:
315: Map map = (Map) clientSessions.get(clientID);
316: if (map != null) {
317:
318: // Iterate through all sessions and destroy them.
319: // 2005-12-06 JBO: replaced usage of Iterator to solve ConcurrentModificationException bug
320: while (map.size() > 0) {
321: Map.Entry entry = (Map.Entry) map.entrySet().iterator()
322: .next();
323: SessionContainer container = (SessionContainer) entry
324: .getValue();
325: try {
326: switch (container.getState()) {
327: // destroy the session
328: case SessionContainer.STATE_NORMAL: {
329: container.getSessionContext().destroy();
330: container
331: .setState(SessionContainer.STATE_DESTROYED);
332: break;
333: }
334: // desrialize and destroy the session.
335: case SessionContainer.STATE_STORED: {
336: deserialize(container);
337: container.getSessionContext().destroy();
338: container
339: .setState(SessionContainer.STATE_DESTROYED);
340: break;
341: }
342: default: // ignore others, just remove from map.
343: break;
344: }
345: } catch (Exception e) {
346: log.error("Exception while destroying session "
347: + container, e);
348: }
349: map.remove(entry.getKey());
350: clientSessions.remove(clientID);
351: }
352:
353: }
354:
355: }
356:
357: /**
358: * Returns the clientIDs that have one or more sessions.
359: * @return
360: */
361: public Collection getClientIDs() {
362: return clientSessions.keySet();
363: }
364:
365: /**
366: * Returns the sessions assigned to the specified client ID.
367: * @param clientID
368: * @return
369: */
370: public Collection getSessions(String clientID) {
371: Map map = (Map) clientSessions.get(clientID);
372: if (map != null) {
373: return map.values();
374: }
375: return null;
376: }
377:
378: /**
379: * Returns the time after a session is serialized.
380: * @return
381: */
382: public int getStoreTime() {
383: return storeTime;
384: }
385:
386: /**
387: * @param sessionStoreTime
388: */
389: public void setStoreTime(int sessionStoreTime) {
390: this.storeTime = sessionStoreTime;
391: }
392: }
|