001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.quercus.env;
031:
032: import com.caucho.quercus.QuercusModuleException;
033: import com.caucho.quercus.lib.UnserializeReader;
034: import com.caucho.util.CacheListener;
035:
036: import java.io.IOException;
037: import java.io.InputStream;
038: import java.io.OutputStream;
039: import java.io.Serializable;
040: import java.util.IdentityHashMap;
041: import java.util.Map;
042: import java.util.logging.Logger;
043:
044: /**
045: * Represents the $_SESSION
046: */
047: public class SessionArrayValue extends ArrayValueWrapper implements
048: CacheListener, Serializable {
049: static protected final Logger log = Logger
050: .getLogger(SessionArrayValue.class.getName());
051:
052: private String _id;
053:
054: private int _useCount;
055:
056: protected long _accessTime;
057: private long _maxInactiveInterval;
058:
059: private boolean _isValid;
060:
061: public SessionArrayValue(String id, long now,
062: long maxInactiveInterval) {
063: this (id, now, maxInactiveInterval, new ArrayValueImpl());
064: }
065:
066: public SessionArrayValue(String id, long now,
067: long maxInactiveInterval, ArrayValue array) {
068: super (array);
069:
070: _id = id;
071: _accessTime = now;
072: _maxInactiveInterval = maxInactiveInterval;
073: }
074:
075: /**
076: * Returns the session id.
077: */
078: public String getId() {
079: return _id;
080: }
081:
082: /**
083: * Changes the session id. Used by session_regenerate_id() where we want
084: * to change the session id, but keep the rest of the session information.
085: */
086: public void setId(String id) {
087: _id = id;
088: }
089:
090: /**
091: * Converts to an object.
092: */
093: public Object toObject() {
094: return null;
095: }
096:
097: /**
098: * Copy for serialization
099: */
100: public Value copy(Env env, IdentityHashMap<Value, Value> map) {
101: long accessTime = _accessTime;
102:
103: SessionArrayValue copy = new SessionArrayValue(_id, accessTime,
104: _maxInactiveInterval, (ArrayValue) getArray().copy(env,
105: map));
106:
107: return copy;
108: }
109:
110: /**
111: * Encoding for serialization.
112: */
113: public String encode() {
114: StringBuilder sb = new StringBuilder();
115: ArrayValue array = getArray();
116:
117: synchronized (array) {
118: for (Map.Entry<Value, Value> entry : array.entrySet()) {
119: sb.append(entry.getKey().toString());
120: sb.append("|");
121: entry.getValue().serialize(sb);
122: }
123: }
124:
125: return sb.toString();
126: }
127:
128: /**
129: * Decodes encoded values, adding them to this object.
130: */
131: public boolean decode(Env env, String encoded) {
132: ArrayValue array = getArray();
133:
134: try {
135: UnserializeReader is = new UnserializeReader(encoded);
136:
137: StringBuilder sb = new StringBuilder();
138:
139: synchronized (array) {
140: while (true) {
141: int ch;
142:
143: sb.setLength(0);
144:
145: while ((ch = is.read()) > 0 && ch != '|') {
146: sb.append((char) ch);
147: }
148:
149: if (sb.length() == 0)
150: return true;
151:
152: String key = sb.toString();
153:
154: array.put(env.createString(key), is
155: .unserialize(env));
156: }
157: }
158: } catch (IOException e) {
159: throw new QuercusModuleException(e);
160: }
161: }
162:
163: public synchronized boolean inUse() {
164: return _useCount > 0;
165: }
166:
167: public synchronized void addUse() {
168: _useCount++;
169: }
170:
171: public boolean load() {
172: return true;
173: }
174:
175: /**
176: * Saves the object to the output stream.
177: */
178: public void store(OutputStream out) throws IOException {
179: String encode = encode();
180:
181: int len = encode.length();
182:
183: out.write(len >> 24);
184: out.write(len >> 16);
185: out.write(len >> 8);
186: out.write(len);
187:
188: for (int i = 0; i < len; i++) {
189: char ch = encode.charAt(i);
190:
191: out.write(ch >> 8);
192: out.write(ch);
193: }
194: }
195:
196: public void load(Env env, InputStream in) throws IOException {
197: int len = (((in.read() & 0xff) << 24)
198: + ((in.read() & 0xff) << 16)
199: + ((in.read() & 0xff) << 8) + ((in.read() & 0xff)));
200:
201: StringBuilder sb = new StringBuilder();
202:
203: for (int i = 0; i < len; i++) {
204: char ch = (char) (((in.read() & 0xff) << 8) + (in.read() & 0xff));
205:
206: sb.append(ch);
207: }
208:
209: String encoded = sb.toString();
210:
211: decode(env, encoded);
212: }
213:
214: /**
215: * Cleaning up session stuff at the end of a request.
216: *
217: * <p>If the session data has changed and we have persistent sessions,
218: * save the session. However, if save-on-shutdown is true, only save
219: * on a server shutdown.
220: */
221: public void finish() {
222: int count;
223:
224: synchronized (this ) {
225: count = --_useCount;
226: }
227:
228: if (count > 0)
229: return;
230:
231: if (count < 0)
232: throw new IllegalStateException();
233:
234: store();
235: }
236:
237: /**
238: * Store on shutdown.
239: */
240: public void storeOnShutdown() {
241: store();
242: }
243:
244: protected void store() {
245: }
246:
247: public long getMaxInactiveInterval() {
248: return _maxInactiveInterval;
249: }
250:
251: /*
252: public void setClusterObject(ClusterObject clusterObject)
253: {
254: _clusterObject = clusterObject;
255: }
256: */
257:
258: public void reset(long now) {
259: setValid(true);
260: setAccess(now);
261: clear();
262: }
263:
264: public synchronized long getAccessTime() {
265: return _accessTime;
266: }
267:
268: public synchronized void setAccess(long now) {
269: _accessTime = now;
270: }
271:
272: public synchronized boolean isValid() {
273: return _isValid;
274: }
275:
276: public synchronized void setValid(boolean isValid) {
277: _isValid = isValid;
278: }
279:
280: /**
281: * Invalidates the session.
282: */
283: public void invalidate() {
284: if (!_isValid)
285: throw new IllegalStateException(
286: L
287: .l("Can't call invalidate() when session is no longer valid."));
288:
289: try {
290: remove();
291:
292: clear();
293: } finally {
294: _isValid = false;
295: }
296: }
297:
298: protected void remove() {
299: }
300:
301: public boolean isEmpty() {
302: return getSize() == 0;
303: }
304:
305: /**
306: * Callback when the session is removed from the session cache, generally
307: * because the session cache is full.
308: */
309: public void removeEvent() {
310: // XXX: logic doesn't make sense
311:
312: /*
313: boolean isValid = _isValid;
314:
315: if (log.isLoggable(Level.FINE)) {
316: log.fine("remove session " + _id);
317: }
318:
319: long now = Alarm.getCurrentTime();
320:
321: ClusterObject clusterObject = _clusterObject;
322:
323: if (_isValid && clusterObject != null) {
324: try {
325: clusterObject.update();
326: clusterObject.store(this);
327: } catch (Throwable e) {
328: log.log(Level.WARNING, "Can't serialize session", e);
329: }
330: }
331: */
332: }
333:
334: //
335: // Java serialization code
336: //
337:
338: private Object writeReplace() {
339: return new ArrayValueImpl(this);
340: }
341: }
|