001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: DiskPagedSessionHome.java,v 1.3 2007-10-19 10:05:39 sinisa Exp $
022: */
023:
024: package com.lutris.appserver.server.sessionEnhydra;
025:
026: import java.util.Enumeration;
027: import java.util.Hashtable;
028:
029: import com.lutris.appserver.server.Enhydra;
030: import com.lutris.appserver.server.session.SessionException;
031: import com.lutris.logging.Logger;
032: import com.lutris.util.Config;
033: import com.lutris.util.ConfigException;
034: import com.lutris.util.FilePersistentStore;
035: import com.lutris.util.PersistentStore;
036: import com.lutris.util.PersistentStoreException;
037:
038: /**
039: * The StandardSessionManager uses PagedSessionHome to manage
040: * a collection of sessions that can be paged to disk.<p>
041: *
042: * PagedSessionHome will page sessions to disk
043: * as soon as a pre-configured threshold of sessions has been
044: * reached. Only sessions in the 'passive' state will be
045: * paged to disk. If all sessions are in the 'active' state
046: * and the threshold has been reached, then a request to create
047: * a new session will block until one of the 'active' sessions
048: * goes into the 'passive' state. At this point the session
049: * that just became 'passive' is paged to disk and a new
050: * session is created.<p>
051: *
052: * Sessions are paged to disk by serializing
053: * the all data (excluding the session manager) that is
054: * associated with a session. This requires that
055: * the session data and user associated with a session
056: * are serializable.<p>
057: *
058: * The following parameters can be used to configure
059: * the PagedSessionHome. They should be grouped together in a section,
060: * normally <code>SessionManager.SessionHome</code>, which is specified to
061: * the constructor.<p>
062: *
063: * <ul>
064: *
065: * <li><code>PageThreshold: {int}</code><p>
066: * Sessions are paged if the total number of sessions exceeds PageThreshold.
067: * If not set, defaults to 100.<p>
068: *
069: * <li><code>PageWait: {int}</code><p>
070: * Specifies the maximum time (in milliseconds) that a thread will
071: * wait for a session to be created or retrieved. If the
072: * page threshold has been reached, then a thread has to wait for
073: * an existing session to be paged before a new session can be
074: * created or a pre-existing session (that has been paged to
075: * disk) can be activated. If not set, defaults to 1 minute.
076: * If this time is exceeded then an exception is thrown - this
077: * prevents a possible deadlock situation.<p>
078: *
079: * <li><code>PageDir: {String}</code><p>
080: * The directory where sessions will be paged. This setting is
081: * required.<p>
082: *
083: * <li><code>MaxSessions: {int}</code><p>
084: * Specifies the maximum number of in use concurrent sessions. If this value
085: * is exceeded then CreateSession() will throw a CreateSessionException.
086: * -1 indicates unlimited session. If MaxSessions is not set
087: * in the application's configuration then MaxSessions defaults to
088: * unlimited sessions.<p>
089: *
090: * <li><code>SaveOnRestart: (true|false}</code><p>
091: * Indicate that shold we saves all (active and passive) sessions on disk
092: * when restart application.
093: * If we use <code>SessionManager.MemoryPersistence = true</code> then ignore
094: * <code>SaveOnRestart</code> parameter.
095: *
096: * </ul>
097: *
098: * @see StandardSession
099: * @see StandardSessionManager
100: * @version $Revision: 1.3 $
101: * @author Kyle Clark
102: */
103: public class DiskPagedSessionHome extends PagedSessionHome {
104:
105: /**
106: * The paged cache contains handles to passive
107: * sessions that have been paged to disk.
108: */
109: private Hashtable pagedCache = new Hashtable();
110:
111: /**
112: * The persistent storage interface.
113: */
114: private PersistentStore store;
115:
116: /**
117: * Configuration keys.
118: */
119: private static final String PAGE_DIR_KEY = "PageDir";
120:
121: /**DACHA & TUFA
122: * variables which determine should we do page sessions
123: * during restart application
124: */
125: private static final String SAVE_ON_RESTART_KEY = "SaveOnRestart";
126: private boolean saveSessions = false;
127: String saveSess = "false";
128:
129: /**
130: * @param sessionMgr
131: * The session manager associated with this session home.
132: * @param config
133: * Object parsed from configuration file. This should be
134: * for the section containing the standard session home configuration.
135: * @param loader
136: * The class load to use when load objects from persistent store.
137: * @exception ConfigException
138: * signifies a problem in the configuration file.
139: * @exception SessionException
140: * if the initialization fails.
141: */
142: public DiskPagedSessionHome(StandardSessionManager sessionMgr,
143: Config config, ClassLoader loader) throws SessionException,
144: ConfigException {
145: super (sessionMgr, config, loader);
146: String pageDir = config.getString(PAGE_DIR_KEY);
147: /**DACHA & TUFA
148: * read from config file SAVE_ON_RESTART key
149: */
150: if (config.containsKey(SAVE_ON_RESTART_KEY)) {
151: saveSess = config.getString(SAVE_ON_RESTART_KEY);
152: if (saveSess.equals("true"))
153: saveSessions = true;
154: }
155: debug(PAGE_DIR_KEY + " = " + pageDir);
156: try {
157: store = new FilePersistentStore(pageDir, loader);
158: /**DACHA & TUFA
159: * load all paged sessions from disk
160: */
161: if (saveSessions)
162: loadPagedSessions();
163: } catch (Exception e) {
164: throw new SessionException(e);
165: }
166: }
167:
168: /**
169: * Creates a new session object. This method is intended to be
170: * overriden by classes that extend PagedSessionHome.
171: *
172: * @return a new session.
173: */
174: protected PagedSession newSession(StandardSessionManager mgr,
175: String sessionKey) throws SessionException {
176: return new PagedSession(mgr, sessionKey);
177: }
178:
179: /**
180: * Deletes a paged session. If the session doesn't exist then this
181: * is a noop.
182: *
183: * @param sessionKey the key identifying the session
184: * that should be deleted.
185: */
186: protected void deleteSession(String sessionKey)
187: throws SessionException {
188: PagedSessionHandle pagedSession = (PagedSessionHandle) pagedCache
189: .remove(sessionKey);
190: if (pagedSession != null) {
191: pagedSession.delete();
192: }
193: }
194:
195: /**
196: * Pages a session to disk.
197: *
198: * @param session the session to page.
199: * @exception SessionException if the paged session could not be
200: * paged out.
201: */
202: protected synchronized void pageOut(PagedSession s)
203: throws SessionException {
204: // v. strahinja, 01 okt 2002 debug(3, "page: write session to disk: " + s.getSessionKey());
205: debug("page: write session to disk: " + s.getSessionKey());
206: PagedSessionHandle page = new PagedSessionHandle(s, store);
207: page.write();
208: pagedCache.put(s.getSessionKey(), page);
209: }
210:
211: /**
212: * Reads a paged session from disk.
213: *
214: * @param sessionKey the key identifying the session that should
215: * be paged in.
216: * @return the paged session that was read in.
217: * @exception SessionException if the paged session could not be
218: * read in or does not exist.
219: */
220: protected synchronized PagedSession pageIn(String sessionKey)
221: throws SessionException {
222: PagedSessionHandle sessHandle = (PagedSessionHandle) pagedCache
223: .get(sessionKey);
224: PagedSession session = null;
225: try {
226: if (sessHandle != null) {
227: // v. strahinja, 01 okt 2002 debug(3, "page: read session from disk: " + sessHandle.getSessionKey());
228: debug("page: read session from disk: "
229: + sessHandle.getSessionKey());
230: session = sessHandle.read();
231: // v. strahinja, 01 okt 2002 debug(3, "page: read data: " + session);
232: debug("page: read data: " + session);
233: // delete paged version from disk...
234: sessHandle.delete();
235: }
236: } catch (SessionException e) {
237: Enhydra.getLogChannel().write(Logger.ALERT,
238: "Session not found on disk.", e);
239: }
240: pagedCache.remove(sessionKey);
241: return session;
242: }
243:
244: /**
245: * Returns the number of paged sessions.
246: * @exception SessionException if the paged session count
247: * cannot be retrieved.
248: */
249: protected synchronized int getPagedSessionCount()
250: throws SessionException {
251: return pagedCache.size();
252: }
253:
254: /**
255: * Returns true if the specified session key is in use
256: * by a session that has been paged out.
257: *
258: * @param sessionKey the session key to test.
259: * @return true if the session key is in use by a paged session.
260: */
261: protected boolean pagedSessionKeyExists(String sessionKey)
262: throws SessionException {
263: return pagedCache.containsKey(sessionKey);
264: }
265:
266: /**
267: * Returns an enumeration of the keys of all the sessions that have
268: * been paged out to persistent storage.
269: *
270: * @return the session key enumeration.
271: * @exception SessionException if an error occurs.
272: */
273: protected Enumeration getPagedSessionKeys() throws SessionException {
274: return pagedCache.keys();
275: }
276:
277: /**
278: * Removes a session that is new and paged.
279: *
280: * @exception SessionException if an error occurs.
281: */
282: protected boolean cleanupNewPagedSession() throws SessionException {
283: long oldestTime = -1;
284: String key = null;
285: Enumeration e = pagedCache.keys();
286: while (e.hasMoreElements()) {
287: PagedSessionHandle s = (PagedSessionHandle) pagedCache
288: .get(e.nextElement());
289: if (s.isNew()) {
290: if ((oldestTime < 0)
291: || (s.getTimeCreated() < oldestTime)) {
292: oldestTime = s.getTimeCreated();
293: key = s.getSessionKey();
294: }
295: }
296: }
297: if (key != null) {
298: removeSession(key);
299: return true;
300: }
301: return false;
302: }
303:
304: /**
305: * Shuts dows the session home. Removes paged sessions
306: * from disk or page all sessions if key SaveOnRestart = true.
307: */
308:
309: public void shutdown() {
310: /**DACHA & TUFA
311: * method PagedSessionHome.shutdown() responsible for
312: * paging all (active and passive) sessions to disk
313: */
314: if (saveSessions) {
315: super .shutdown();
316: } else {
317: Enumeration enumeration = pagedCache.elements();
318: for (int i = 0; i < pagedCache.size(); i++) {
319: try {
320: ((PagedSessionHandle) enumeration.nextElement())
321: .delete();
322: } catch (Exception e) {
323: // ignore
324: }
325: }
326: }
327: }
328:
329: /**DACHA & TUFA
330: * method for loading paged sessions when application start
331: */
332: private void loadPagedSessions() throws PersistentStoreException {
333: Enumeration enumeration = store.keys();
334: while (enumeration.hasMoreElements()) {
335: String sessionKey = (String) enumeration.nextElement();
336: PagedSession session = (PagedSession) store
337: .retrieve(sessionKey);
338: Object[] obj = { sessionMgr };
339: session.restoreTransientData(obj);
340: PagedSessionHandle psh = new PagedSessionHandle(session,
341: store);
342: session = null;
343: pagedCache.put(sessionKey, psh);
344: }
345: }
346:
347: }
|