Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
HttpSessionImpl |
|
| 2.1333333333333333;2.133 |
1 | /* | |
2 | * Copyright 1999,2004 The Apache Software Foundation. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | ||
18 | package org.apache.commons.messagelet.impl; | |
19 | ||
20 | ||
21 | import java.beans.PropertyChangeSupport; | |
22 | import java.io.IOException; | |
23 | import java.io.NotSerializableException; | |
24 | import java.io.ObjectInputStream; | |
25 | import java.io.ObjectOutputStream; | |
26 | import java.io.Serializable; | |
27 | import java.security.Principal; | |
28 | import java.util.ArrayList; | |
29 | import java.util.Enumeration; | |
30 | import java.util.HashMap; | |
31 | import java.util.Iterator; | |
32 | ||
33 | import javax.servlet.ServletContext; | |
34 | import javax.servlet.http.HttpSession; | |
35 | import javax.servlet.http.HttpSessionActivationListener; | |
36 | import javax.servlet.http.HttpSessionBindingEvent; | |
37 | import javax.servlet.http.HttpSessionBindingListener; | |
38 | import javax.servlet.http.HttpSessionContext; | |
39 | import javax.servlet.http.HttpSessionEvent; | |
40 | ||
41 | import org.apache.commons.collections.iterators.IteratorEnumeration; | |
42 | ||
43 | /** | |
44 | * Based on the Catalina StandardSession class. | |
45 | * Standard implementation of the <b>HttpSession</b> interface. This object is | |
46 | * serializable, so that it can be stored in persistent storage or transferred | |
47 | * to a different JVM for distributable session support. | |
48 | * <p> | |
49 | * | |
50 | * @author Craig R. McClanahan | |
51 | * @author Sean Legassick | |
52 | * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> | |
53 | * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> | |
54 | * @version $Revision: 155459 $ $Date: 2005-02-26 13:24:44 +0000 (Sat, 26 Feb 2005) $ | |
55 | */ | |
56 | ||
57 | public class HttpSessionImpl implements HttpSession, Serializable { | |
58 | ||
59 | ||
60 | ||
61 | // ----------------------------------------------------- Instance Variables | |
62 | ||
63 | ||
64 | /** | |
65 | * The dummy attribute value serialized when a NotSerializableException is | |
66 | * encountered in <code>writeObject()</code>. | |
67 | */ | |
68 | private static final String NOT_SERIALIZED = | |
69 | "___NOT_SERIALIZABLE_EXCEPTION___"; | |
70 | ||
71 | ||
72 | /** | |
73 | * The collection of user data attributes associated with this Session. | |
74 | */ | |
75 | 0 | private HashMap attributes = new HashMap(); |
76 | ||
77 | ||
78 | /** | |
79 | * The authentication type used to authenticate our cached Principal, | |
80 | * if any. NOTE: This value is not included in the serialized | |
81 | * version of this object. | |
82 | */ | |
83 | 0 | private transient String authType = null; |
84 | ||
85 | ||
86 | /** | |
87 | * The time this session was created, in milliseconds since midnight, | |
88 | * January 1, 1970 GMT. | |
89 | */ | |
90 | 0 | private long creationTime = 0L; |
91 | ||
92 | ||
93 | /** | |
94 | * The debugging detail level for this component. NOTE: This value | |
95 | * is not included in the serialized version of this object. | |
96 | */ | |
97 | 0 | private transient int debug = 0; |
98 | ||
99 | ||
100 | /** | |
101 | * We are currently processing a session expiration, so bypass | |
102 | * certain IllegalStateException tests. NOTE: This value is not | |
103 | * included in the serialized version of this object. | |
104 | */ | |
105 | 0 | private transient boolean expiring = false; |
106 | ||
107 | ||
108 | /** | |
109 | * The session identifier of this Session. | |
110 | */ | |
111 | 0 | private String id = null; |
112 | ||
113 | ||
114 | ||
115 | /** | |
116 | * The last accessed time for this Session. | |
117 | */ | |
118 | 0 | private long lastAccessedTime = creationTime; |
119 | ||
120 | ||
121 | /** | |
122 | * The session event listeners for this Session. | |
123 | */ | |
124 | 0 | private transient ArrayList listeners = new ArrayList(); |
125 | ||
126 | ||
127 | ||
128 | /** | |
129 | * The maximum time interval, in seconds, between client requests before | |
130 | * the servlet container may invalidate this session. A negative time | |
131 | * indicates that the session should never time out. | |
132 | */ | |
133 | 0 | private int maxInactiveInterval = -1; |
134 | ||
135 | ||
136 | /** | |
137 | * Flag indicating whether this session is new or not. | |
138 | */ | |
139 | 0 | private boolean isNew = false; |
140 | ||
141 | ||
142 | /** | |
143 | * Flag indicating whether this session is valid or not. | |
144 | */ | |
145 | 0 | private boolean isValid = false; |
146 | ||
147 | ||
148 | /** | |
149 | * Internal notes associated with this session by Catalina components | |
150 | * and event listeners. <b>IMPLEMENTATION NOTE:</b> This object is | |
151 | * <em>not</em> saved and restored across session serializations! | |
152 | */ | |
153 | 0 | private transient HashMap notes = new HashMap(); |
154 | ||
155 | ||
156 | /** | |
157 | * The authenticated Principal associated with this session, if any. | |
158 | * <b>IMPLEMENTATION NOTE:</b> This object is <i>not</i> saved and | |
159 | * restored across session serializations! | |
160 | */ | |
161 | 0 | private transient Principal principal = null; |
162 | ||
163 | ||
164 | /** | |
165 | * The property change support for this component. NOTE: This value | |
166 | * is not included in the serialized version of this object. | |
167 | */ | |
168 | 0 | private transient PropertyChangeSupport support = |
169 | new PropertyChangeSupport(this); | |
170 | ||
171 | ||
172 | /** | |
173 | * The current accessed time for this session. | |
174 | */ | |
175 | 0 | private long thisAccessedTime = creationTime; |
176 | ||
177 | /** | |
178 | * The ServletContext | |
179 | */ | |
180 | protected ServletContext servletContext; | |
181 | ||
182 | /** | |
183 | * Is this session distributable. If so then | |
184 | * its values must be Serializable. | |
185 | */ | |
186 | 0 | private boolean distributable = false; |
187 | ||
188 | ||
189 | 0 | public HttpSessionImpl(ServletContext servletContext) { |
190 | 0 | this.servletContext = servletContext; |
191 | 0 | } |
192 | ||
193 | // ----------------------------------------------------- Session Properties | |
194 | ||
195 | ||
196 | /** | |
197 | * Return the authentication type used to authenticate our cached | |
198 | * Principal, if any. | |
199 | */ | |
200 | public String getAuthType() { | |
201 | ||
202 | 0 | return (this.authType); |
203 | ||
204 | } | |
205 | ||
206 | ||
207 | /** | |
208 | * Set the authentication type used to authenticate our cached | |
209 | * Principal, if any. | |
210 | * | |
211 | * @param authType The new cached authentication type | |
212 | */ | |
213 | public void setAuthType(String authType) { | |
214 | ||
215 | 0 | String oldAuthType = this.authType; |
216 | 0 | this.authType = authType; |
217 | 0 | support.firePropertyChange("authType", oldAuthType, this.authType); |
218 | ||
219 | 0 | } |
220 | ||
221 | ||
222 | /** | |
223 | * Set the creation time for this session. This method is called by the | |
224 | * Manager when an existing Session instance is reused. | |
225 | * | |
226 | * @param time The new creation time | |
227 | */ | |
228 | public void setCreationTime(long time) { | |
229 | ||
230 | 0 | this.creationTime = time; |
231 | 0 | this.lastAccessedTime = time; |
232 | 0 | this.thisAccessedTime = time; |
233 | ||
234 | 0 | } |
235 | ||
236 | ||
237 | /** | |
238 | * Return the session identifier for this session. | |
239 | */ | |
240 | public String getId() { | |
241 | ||
242 | 0 | return (this.id); |
243 | ||
244 | } | |
245 | ||
246 | ||
247 | /** | |
248 | * Set the session identifier for this session. | |
249 | * | |
250 | * @param id The new session identifier | |
251 | */ | |
252 | public void setId(String id) { | |
253 | ||
254 | 0 | this.id = id; |
255 | ||
256 | // Notify interested session event listeners | |
257 | ///fireSessionEvent(Session.SESSION_CREATED_EVENT, null); | |
258 | ||
259 | 0 | } |
260 | ||
261 | ||
262 | /** | |
263 | * Return the last time the client sent a request associated with this | |
264 | * session, as the number of milliseconds since midnight, January 1, 1970 | |
265 | * GMT. Actions that your application takes, such as getting or setting | |
266 | * a value associated with the session, do not affect the access time. | |
267 | */ | |
268 | public long getLastAccessedTime() { | |
269 | ||
270 | 0 | return (this.lastAccessedTime); |
271 | ||
272 | } | |
273 | ||
274 | ||
275 | /** | |
276 | * Return the maximum time interval, in seconds, between client requests | |
277 | * before the servlet container will invalidate the session. A negative | |
278 | * time indicates that the session should never time out. | |
279 | * | |
280 | * @exception IllegalStateException if this method is called on | |
281 | * an invalidated session | |
282 | */ | |
283 | public int getMaxInactiveInterval() { | |
284 | ||
285 | 0 | if (!isValid) { |
286 | 0 | throw new IllegalStateException( "Cannot call getMaxInactiveInterval on an invalidated session" ); |
287 | } | |
288 | ||
289 | 0 | return (this.maxInactiveInterval); |
290 | ||
291 | } | |
292 | ||
293 | ||
294 | /** | |
295 | * Set the maximum time interval, in seconds, between client requests | |
296 | * before the servlet container will invalidate the session. A negative | |
297 | * time indicates that the session should never time out. | |
298 | * | |
299 | * @param interval The new maximum interval | |
300 | */ | |
301 | public void setMaxInactiveInterval(int interval) { | |
302 | ||
303 | 0 | this.maxInactiveInterval = interval; |
304 | ||
305 | 0 | } |
306 | ||
307 | ||
308 | /** | |
309 | * Set the <code>isNew</code> flag for this session. | |
310 | * | |
311 | * @param isNew The new value for the <code>isNew</code> flag | |
312 | */ | |
313 | public void setNew(boolean isNew) { | |
314 | ||
315 | 0 | this.isNew = isNew; |
316 | ||
317 | 0 | } |
318 | ||
319 | ||
320 | /** | |
321 | * Return the authenticated Principal that is associated with this Session. | |
322 | * This provides an <code>Authenticator</code> with a means to cache a | |
323 | * previously authenticated Principal, and avoid potentially expensive | |
324 | * <code>Realm.authenticate()</code> calls on every request. If there | |
325 | * is no current associated Principal, return <code>null</code>. | |
326 | */ | |
327 | public Principal getPrincipal() { | |
328 | ||
329 | 0 | return (this.principal); |
330 | ||
331 | } | |
332 | ||
333 | ||
334 | /** | |
335 | * Set the authenticated Principal that is associated with this Session. | |
336 | * This provides an <code>Authenticator</code> with a means to cache a | |
337 | * previously authenticated Principal, and avoid potentially expensive | |
338 | * <code>Realm.authenticate()</code> calls on every request. | |
339 | * | |
340 | * @param principal The new Principal, or <code>null</code> if none | |
341 | */ | |
342 | public void setPrincipal(Principal principal) { | |
343 | ||
344 | 0 | Principal oldPrincipal = this.principal; |
345 | 0 | this.principal = principal; |
346 | 0 | support.firePropertyChange("principal", oldPrincipal, this.principal); |
347 | ||
348 | 0 | } |
349 | ||
350 | ||
351 | ||
352 | /** | |
353 | * Return the <code>isValid</code> flag for this session. | |
354 | */ | |
355 | public boolean isValid() { | |
356 | ||
357 | 0 | return (this.isValid); |
358 | ||
359 | } | |
360 | ||
361 | ||
362 | /** | |
363 | * Set the <code>isValid</code> flag for this session. | |
364 | * | |
365 | * @param isValid The new value for the <code>isValid</code> flag | |
366 | */ | |
367 | public void setValid(boolean isValid) { | |
368 | ||
369 | 0 | this.isValid = isValid; |
370 | 0 | } |
371 | ||
372 | ||
373 | // ------------------------------------------------- Session Public Methods | |
374 | ||
375 | ||
376 | /** | |
377 | * Update the accessed time information for this session. This method | |
378 | * should be called by the context when a request comes in for a particular | |
379 | * session, even if the application does not reference it. | |
380 | */ | |
381 | public void access() { | |
382 | ||
383 | 0 | this.isNew = false; |
384 | 0 | this.lastAccessedTime = this.thisAccessedTime; |
385 | 0 | this.thisAccessedTime = System.currentTimeMillis(); |
386 | ||
387 | 0 | } |
388 | ||
389 | ||
390 | ||
391 | /** | |
392 | * Perform the internal processing required to invalidate this session, | |
393 | * without triggering an exception if the session has already expired. | |
394 | */ | |
395 | public void expire() { | |
396 | ||
397 | // Mark this session as "being expired" if needed | |
398 | 0 | if (expiring) |
399 | 0 | return; |
400 | 0 | expiring = true; |
401 | 0 | setValid(false); |
402 | ||
403 | // Unbind any objects associated with this session | |
404 | 0 | String keys[] = keys(); |
405 | 0 | for (int i = 0; i < keys.length; i++) |
406 | 0 | removeAttribute(keys[i]); |
407 | ||
408 | // Notify interested session event listeners | |
409 | //fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null); | |
410 | ||
411 | // We have completed expire of this session | |
412 | 0 | expiring = false; |
413 | 0 | } |
414 | ||
415 | ||
416 | /** | |
417 | * Perform the internal processing required to passivate | |
418 | * this session. | |
419 | */ | |
420 | public void passivate() { | |
421 | ||
422 | // Notify ActivationListeners | |
423 | 0 | HttpSessionEvent event = null; |
424 | 0 | String keys[] = keys(); |
425 | 0 | for (int i = 0; i < keys.length; i++) { |
426 | 0 | Object attribute = getAttribute(keys[i]); |
427 | 0 | if (attribute instanceof HttpSessionActivationListener) { |
428 | 0 | if (event == null) |
429 | 0 | event = new HttpSessionEvent(this); |
430 | // FIXME: Should we catch throwables? | |
431 | 0 | ((HttpSessionActivationListener)attribute).sessionWillPassivate(event); |
432 | } | |
433 | } | |
434 | ||
435 | 0 | } |
436 | ||
437 | ||
438 | /** | |
439 | * Perform internal processing required to activate this | |
440 | * session. | |
441 | */ | |
442 | public void activate() { | |
443 | ||
444 | // Notify ActivationListeners | |
445 | 0 | HttpSessionEvent event = null; |
446 | 0 | String keys[] = keys(); |
447 | 0 | for (int i = 0; i < keys.length; i++) { |
448 | 0 | Object attribute = getAttribute(keys[i]); |
449 | 0 | if (attribute instanceof HttpSessionActivationListener) { |
450 | 0 | if (event == null) |
451 | 0 | event = new HttpSessionEvent(this); |
452 | // FIXME: Should we catch throwables? | |
453 | 0 | ((HttpSessionActivationListener)attribute).sessionDidActivate(event); |
454 | } | |
455 | } | |
456 | ||
457 | 0 | } |
458 | ||
459 | ||
460 | /** | |
461 | * Return the object bound with the specified name to the internal notes | |
462 | * for this session, or <code>null</code> if no such binding exists. | |
463 | * | |
464 | * @param name Name of the note to be returned | |
465 | */ | |
466 | public Object getNote(String name) { | |
467 | ||
468 | 0 | synchronized (notes) { |
469 | 0 | return (notes.get(name)); |
470 | 0 | } |
471 | ||
472 | } | |
473 | ||
474 | ||
475 | /** | |
476 | * Return an Iterator containing the String names of all notes bindings | |
477 | * that exist for this session. | |
478 | */ | |
479 | public Iterator getNoteNames() { | |
480 | ||
481 | 0 | synchronized (notes) { |
482 | 0 | return (notes.keySet().iterator()); |
483 | 0 | } |
484 | ||
485 | } | |
486 | ||
487 | ||
488 | /** | |
489 | * Release all object references, and initialize instance variables, in | |
490 | * preparation for reuse of this object. | |
491 | */ | |
492 | public void recycle() { | |
493 | ||
494 | // Reset the instance variables associated with this Session | |
495 | 0 | attributes.clear(); |
496 | 0 | setAuthType(null); |
497 | 0 | creationTime = 0L; |
498 | 0 | expiring = false; |
499 | 0 | id = null; |
500 | 0 | lastAccessedTime = 0L; |
501 | 0 | maxInactiveInterval = -1; |
502 | 0 | setPrincipal(null); |
503 | 0 | isNew = false; |
504 | 0 | isValid = false; |
505 | 0 | } |
506 | ||
507 | ||
508 | /** | |
509 | * Remove any object bound to the specified name in the internal notes | |
510 | * for this session. | |
511 | * | |
512 | * @param name Name of the note to be removed | |
513 | */ | |
514 | public void removeNote(String name) { | |
515 | ||
516 | 0 | synchronized (notes) { |
517 | 0 | notes.remove(name); |
518 | 0 | } |
519 | ||
520 | 0 | } |
521 | ||
522 | ||
523 | ||
524 | /** | |
525 | * Bind an object to a specified name in the internal notes associated | |
526 | * with this session, replacing any existing binding for this name. | |
527 | * | |
528 | * @param name Name to which the object should be bound | |
529 | * @param value Object to be bound to the specified name | |
530 | */ | |
531 | public void setNote(String name, Object value) { | |
532 | ||
533 | 0 | synchronized (notes) { |
534 | 0 | notes.put(name, value); |
535 | 0 | } |
536 | ||
537 | 0 | } |
538 | ||
539 | ||
540 | /** | |
541 | * Return a string representation of this object. | |
542 | */ | |
543 | public String toString() { | |
544 | ||
545 | 0 | StringBuffer sb = new StringBuffer(); |
546 | 0 | sb.append("StandardSession["); |
547 | 0 | sb.append(id); |
548 | 0 | sb.append("]"); |
549 | 0 | return (sb.toString()); |
550 | ||
551 | } | |
552 | ||
553 | ||
554 | // ------------------------------------------------ Session Package Methods | |
555 | ||
556 | ||
557 | /** | |
558 | * Read a serialized version of the contents of this session object from | |
559 | * the specified object input stream, without requiring that the | |
560 | * StandardSession itself have been serialized. | |
561 | * | |
562 | * @param stream The object input stream to read from | |
563 | * | |
564 | * @exception ClassNotFoundException if an unknown class is specified | |
565 | * @exception IOException if an input/output error occurs | |
566 | */ | |
567 | void readObjectData(ObjectInputStream stream) | |
568 | throws ClassNotFoundException, IOException { | |
569 | ||
570 | 0 | readObject(stream); |
571 | ||
572 | 0 | } |
573 | ||
574 | ||
575 | /** | |
576 | * Write a serialized version of the contents of this session object to | |
577 | * the specified object output stream, without requiring that the | |
578 | * StandardSession itself have been serialized. | |
579 | * | |
580 | * @param stream The object output stream to write to | |
581 | * | |
582 | * @exception IOException if an input/output error occurs | |
583 | */ | |
584 | void writeObjectData(ObjectOutputStream stream) | |
585 | throws IOException { | |
586 | ||
587 | 0 | writeObject(stream); |
588 | ||
589 | 0 | } |
590 | ||
591 | ||
592 | // ------------------------------------------------- HttpSession Properties | |
593 | ||
594 | ||
595 | /** | |
596 | * Return the time when this session was created, in milliseconds since | |
597 | * midnight, January 1, 1970 GMT. | |
598 | * | |
599 | * @exception IllegalStateException if this method is called on an | |
600 | * invalidated session | |
601 | */ | |
602 | public long getCreationTime() { | |
603 | ||
604 | 0 | if (!isValid) { |
605 | 0 | throw new IllegalStateException( "Cannot call getCreationTime() on invalidated session" ); |
606 | } | |
607 | ||
608 | 0 | return (this.creationTime); |
609 | ||
610 | } | |
611 | ||
612 | ||
613 | /** | |
614 | * Return the ServletContext to which this session belongs. | |
615 | */ | |
616 | public ServletContext getServletContext() { | |
617 | 0 | return servletContext; |
618 | ||
619 | } | |
620 | ||
621 | ||
622 | /** | |
623 | * Return the session context with which this session is associated. | |
624 | * | |
625 | * @deprecated As of Version 2.1, this method is deprecated and has no | |
626 | * replacement. It will be removed in a future version of the | |
627 | * Java Servlet API. | |
628 | */ | |
629 | public HttpSessionContext getSessionContext() { | |
630 | ||
631 | 0 | return null; |
632 | ||
633 | } | |
634 | ||
635 | ||
636 | // ----------------------------------------------HttpSession Public Methods | |
637 | ||
638 | ||
639 | /** | |
640 | * Return the object bound with the specified name in this session, or | |
641 | * <code>null</code> if no object is bound with that name. | |
642 | * | |
643 | * @param name Name of the attribute to be returned | |
644 | * | |
645 | * @exception IllegalStateException if this method is called on an | |
646 | * invalidated session | |
647 | */ | |
648 | public Object getAttribute(String name) { | |
649 | ||
650 | 0 | if (!isValid) { |
651 | 0 | throw new IllegalStateException( "Cannot call getAttribute() on invalidated session" ); |
652 | } | |
653 | ||
654 | 0 | synchronized (attributes) { |
655 | 0 | return (attributes.get(name)); |
656 | 0 | } |
657 | ||
658 | } | |
659 | ||
660 | ||
661 | /** | |
662 | * Return an <code>Enumeration</code> of <code>String</code> objects | |
663 | * containing the names of the objects bound to this session. | |
664 | * | |
665 | * @exception IllegalStateException if this method is called on an | |
666 | * invalidated session | |
667 | */ | |
668 | public Enumeration getAttributeNames() { | |
669 | ||
670 | 0 | if (!isValid) { |
671 | 0 | throw new IllegalStateException( "Cannot call getAttributeNames() on invalidated session" ); |
672 | } | |
673 | ||
674 | 0 | synchronized (attributes) { |
675 | 0 | return (new IteratorEnumeration(attributes.keySet().iterator())); |
676 | 0 | } |
677 | ||
678 | } | |
679 | ||
680 | ||
681 | /** | |
682 | * Return the object bound with the specified name in this session, or | |
683 | * <code>null</code> if no object is bound with that name. | |
684 | * | |
685 | * @param name Name of the value to be returned | |
686 | * | |
687 | * @exception IllegalStateException if this method is called on an | |
688 | * invalidated session | |
689 | * | |
690 | * @deprecated As of Version 2.2, this method is replaced by | |
691 | * <code>getAttribute()</code> | |
692 | */ | |
693 | public Object getValue(String name) { | |
694 | ||
695 | 0 | return (getAttribute(name)); |
696 | ||
697 | } | |
698 | ||
699 | ||
700 | /** | |
701 | * Return the set of names of objects bound to this session. If there | |
702 | * are no such objects, a zero-length array is returned. | |
703 | * | |
704 | * @exception IllegalStateException if this method is called on an | |
705 | * invalidated session | |
706 | * | |
707 | * @deprecated As of Version 2.2, this method is replaced by | |
708 | * <code>getAttributeNames()</code> | |
709 | */ | |
710 | public String[] getValueNames() { | |
711 | ||
712 | 0 | if (!isValid) { |
713 | 0 | throw new IllegalStateException( "Cannot call getValueNames() on invalidated session" ); |
714 | } | |
715 | ||
716 | 0 | return (keys()); |
717 | ||
718 | } | |
719 | ||
720 | ||
721 | /** | |
722 | * Invalidates this session and unbinds any objects bound to it. | |
723 | * | |
724 | * @exception IllegalStateException if this method is called on | |
725 | * an invalidated session | |
726 | */ | |
727 | public void invalidate() { | |
728 | ||
729 | 0 | if (!isValid) { |
730 | 0 | throw new IllegalStateException( "Cannot call invalidate() on invalidated session" ); |
731 | } | |
732 | ||
733 | // Cause this session to expire | |
734 | 0 | expire(); |
735 | ||
736 | 0 | } |
737 | ||
738 | ||
739 | /** | |
740 | * Return <code>true</code> if the client does not yet know about the | |
741 | * session, or if the client chooses not to join the session. For | |
742 | * example, if the server used only cookie-based sessions, and the client | |
743 | * has disabled the use of cookies, then a session would be new on each | |
744 | * request. | |
745 | * | |
746 | * @exception IllegalStateException if this method is called on an | |
747 | * invalidated session | |
748 | */ | |
749 | public boolean isNew() { | |
750 | ||
751 | 0 | if (!isValid) { |
752 | 0 | throw new IllegalStateException( "Cannot call isNew() on invalidated session" ); |
753 | } | |
754 | ||
755 | 0 | return (this.isNew); |
756 | ||
757 | } | |
758 | ||
759 | ||
760 | /** | |
761 | * Bind an object to this session, using the specified name. If an object | |
762 | * of the same name is already bound to this session, the object is | |
763 | * replaced. | |
764 | * <p> | |
765 | * After this method executes, and if the object implements | |
766 | * <code>HttpSessionBindingListener</code>, the container calls | |
767 | * <code>valueBound()</code> on the object. | |
768 | * | |
769 | * @param name Name to which the object is bound, cannot be null | |
770 | * @param value Object to be bound, cannot be null | |
771 | * | |
772 | * @exception IllegalStateException if this method is called on an | |
773 | * invalidated session | |
774 | * | |
775 | * @deprecated As of Version 2.2, this method is replaced by | |
776 | * <code>setAttribute()</code> | |
777 | */ | |
778 | public void putValue(String name, Object value) { | |
779 | ||
780 | 0 | setAttribute(name, value); |
781 | ||
782 | 0 | } |
783 | ||
784 | ||
785 | /** | |
786 | * Remove the object bound with the specified name from this session. If | |
787 | * the session does not have an object bound with this name, this method | |
788 | * does nothing. | |
789 | * <p> | |
790 | * After this method executes, and if the object implements | |
791 | * <code>HttpSessionBindingListener</code>, the container calls | |
792 | * <code>valueUnbound()</code> on the object. | |
793 | * | |
794 | * @param name Name of the object to remove from this session. | |
795 | * | |
796 | * @exception IllegalStateException if this method is called on an | |
797 | * invalidated session | |
798 | */ | |
799 | public void removeAttribute(String name) { | |
800 | ||
801 | // Validate our current state | |
802 | 0 | if (!expiring && !isValid) { |
803 | 0 | throw new IllegalStateException( "Cannot call removeAttribute() on an invalidated session" ); |
804 | } | |
805 | ||
806 | // Remove this attribute from our collection | |
807 | 0 | Object value = null; |
808 | 0 | boolean found = false; |
809 | 0 | synchronized (attributes) { |
810 | 0 | found = attributes.containsKey(name); |
811 | 0 | if (found) { |
812 | 0 | value = attributes.get(name); |
813 | 0 | attributes.remove(name); |
814 | } else { | |
815 | 0 | return; |
816 | } | |
817 | 0 | } |
818 | /* | |
819 | // Call the valueUnbound() method if necessary | |
820 | HttpSessionBindingEvent event = | |
821 | new HttpSessionBindingEvent((HttpSession) this, name, value); | |
822 | if ((value != null) && | |
823 | (value instanceof HttpSessionBindingListener)) | |
824 | ((HttpSessionBindingListener) value).valueUnbound(event); | |
825 | ||
826 | // Notify interested application event listeners | |
827 | StandardContext context = (StandardContext) manager.getContainer(); | |
828 | Object listeners[] = context.getApplicationListeners(); | |
829 | if (listeners == null) | |
830 | return; | |
831 | for (int i = 0; i < listeners.length; i++) { | |
832 | if (!(listeners[i] instanceof HttpSessionAttributeListener)) | |
833 | continue; | |
834 | HttpSessionAttributeListener listener = | |
835 | (HttpSessionAttributeListener) listeners[i]; | |
836 | try { | |
837 | context.fireContainerEvent("beforeSessionAttributeRemoved", | |
838 | listener); | |
839 | listener.attributeRemoved(event); | |
840 | context.fireContainerEvent("afterSessionAttributeRemoved", | |
841 | listener); | |
842 | } catch (Throwable t) { | |
843 | context.fireContainerEvent("afterSessionAttributeRemoved", | |
844 | listener); | |
845 | // FIXME - should we do anything besides log these? | |
846 | log( "Exception firing attribute event", t); | |
847 | } | |
848 | } | |
849 | */ | |
850 | 0 | } |
851 | ||
852 | ||
853 | /** | |
854 | * Remove the object bound with the specified name from this session. If | |
855 | * the session does not have an object bound with this name, this method | |
856 | * does nothing. | |
857 | * <p> | |
858 | * After this method executes, and if the object implements | |
859 | * <code>HttpSessionBindingListener</code>, the container calls | |
860 | * <code>valueUnbound()</code> on the object. | |
861 | * | |
862 | * @param name Name of the object to remove from this session. | |
863 | * | |
864 | * @exception IllegalStateException if this method is called on an | |
865 | * invalidated session | |
866 | * | |
867 | * @deprecated As of Version 2.2, this method is replaced by | |
868 | * <code>removeAttribute()</code> | |
869 | */ | |
870 | public void removeValue(String name) { | |
871 | ||
872 | 0 | removeAttribute(name); |
873 | ||
874 | 0 | } |
875 | ||
876 | ||
877 | /** | |
878 | * Bind an object to this session, using the specified name. If an object | |
879 | * of the same name is already bound to this session, the object is | |
880 | * replaced. | |
881 | * <p> | |
882 | * After this method executes, and if the object implements | |
883 | * <code>HttpSessionBindingListener</code>, the container calls | |
884 | * <code>valueBound()</code> on the object. | |
885 | * | |
886 | * @param name Name to which the object is bound, cannot be null | |
887 | * @param value Object to be bound, cannot be null | |
888 | * | |
889 | * @exception IllegalArgumentException if an attempt is made to add a | |
890 | * non-serializable object in an environment marked distributable. | |
891 | * @exception IllegalStateException if this method is called on an | |
892 | * invalidated session | |
893 | */ | |
894 | public void setAttribute(String name, Object value) { | |
895 | ||
896 | // Name cannot be null | |
897 | 0 | if (name == null) { |
898 | 0 | throw new IllegalArgumentException( "Attribute name cannot be null" ); |
899 | } | |
900 | ||
901 | // Null value is the same as removeAttribute() | |
902 | 0 | if (value == null) { |
903 | 0 | removeAttribute(name); |
904 | 0 | return; |
905 | } | |
906 | ||
907 | // Validate our current state | |
908 | 0 | if (!isValid) { |
909 | 0 | throw new IllegalStateException( "You cannot call this method on an invalidated session" ); |
910 | } | |
911 | 0 | if (distributable && |
912 | !(value instanceof Serializable)) { | |
913 | 0 | throw new IllegalArgumentException( "Attribute value must be Serializable" ); |
914 | } | |
915 | ||
916 | // Replace or add this attribute | |
917 | 0 | Object unbound = null; |
918 | 0 | synchronized (attributes) { |
919 | 0 | unbound = attributes.get(name); |
920 | 0 | attributes.put(name, value); |
921 | 0 | } |
922 | ||
923 | // Call the valueUnbound() method if necessary | |
924 | 0 | if ((unbound != null) && |
925 | (unbound instanceof HttpSessionBindingListener)) { | |
926 | 0 | ((HttpSessionBindingListener) unbound).valueUnbound |
927 | (new HttpSessionBindingEvent((HttpSession) this, name)); | |
928 | } | |
929 | ||
930 | // Call the valueBound() method if necessary | |
931 | 0 | HttpSessionBindingEvent event = null; |
932 | 0 | if (unbound != null) |
933 | 0 | event = new HttpSessionBindingEvent |
934 | ((HttpSession) this, name, unbound); | |
935 | else | |
936 | 0 | event = new HttpSessionBindingEvent |
937 | ((HttpSession) this, name, value); | |
938 | 0 | if (value instanceof HttpSessionBindingListener) |
939 | 0 | ((HttpSessionBindingListener) value).valueBound(event); |
940 | ||
941 | // Notify interested application event listeners | |
942 | /* | |
943 | StandardContext context = (StandardContext) manager.getContainer(); | |
944 | Object listeners[] = context.getApplicationListeners(); | |
945 | if (listeners == null) | |
946 | return; | |
947 | for (int i = 0; i < listeners.length; i++) { | |
948 | if (!(listeners[i] instanceof HttpSessionAttributeListener)) | |
949 | continue; | |
950 | HttpSessionAttributeListener listener = | |
951 | (HttpSessionAttributeListener) listeners[i]; | |
952 | try { | |
953 | if (unbound != null) { | |
954 | context.fireContainerEvent("beforeSessionAttributeReplaced", | |
955 | listener); | |
956 | listener.attributeReplaced(event); | |
957 | context.fireContainerEvent("afterSessionAttributeReplaced", | |
958 | listener); | |
959 | } else { | |
960 | context.fireContainerEvent("beforeSessionAttributeAdded", | |
961 | listener); | |
962 | listener.attributeAdded(event); | |
963 | context.fireContainerEvent("afterSessionAttributeAdded", | |
964 | listener); | |
965 | } | |
966 | } catch (Throwable t) { | |
967 | if (unbound != null) | |
968 | context.fireContainerEvent("afterSessionAttributeReplaced", | |
969 | listener); | |
970 | else | |
971 | context.fireContainerEvent("afterSessionAttributeAdded", | |
972 | listener); | |
973 | // FIXME - should we do anything besides log these? | |
974 | log( "Exception firing attribute event", t); | |
975 | } | |
976 | } | |
977 | */ | |
978 | 0 | } |
979 | ||
980 | ||
981 | // -------------------------------------------- HttpSession Private Methods | |
982 | ||
983 | ||
984 | /** | |
985 | * Read a serialized version of this session object from the specified | |
986 | * object input stream. | |
987 | * <p> | |
988 | * <b>IMPLEMENTATION NOTE</b>: The reference to the owning Manager | |
989 | * is not restored by this method, and must be set explicitly. | |
990 | * | |
991 | * @param stream The input stream to read from | |
992 | * | |
993 | * @exception ClassNotFoundException if an unknown class is specified | |
994 | * @exception IOException if an input/output error occurs | |
995 | */ | |
996 | private void readObject(ObjectInputStream stream) | |
997 | throws ClassNotFoundException, IOException { | |
998 | ||
999 | // Deserialize the scalar instance variables (except Manager) | |
1000 | 0 | authType = null; // Transient only |
1001 | 0 | creationTime = ((Long) stream.readObject()).longValue(); |
1002 | 0 | lastAccessedTime = ((Long) stream.readObject()).longValue(); |
1003 | 0 | maxInactiveInterval = ((Integer) stream.readObject()).intValue(); |
1004 | 0 | isNew = ((Boolean) stream.readObject()).booleanValue(); |
1005 | 0 | isValid = ((Boolean) stream.readObject()).booleanValue(); |
1006 | 0 | thisAccessedTime = ((Long) stream.readObject()).longValue(); |
1007 | 0 | principal = null; // Transient only |
1008 | 0 | setId((String) stream.readObject()); |
1009 | 0 | if (debug >= 2) |
1010 | 0 | log("readObject() loading session " + id); |
1011 | ||
1012 | // Deserialize the attribute count and attribute values | |
1013 | 0 | if (attributes == null) |
1014 | 0 | attributes = new HashMap(); |
1015 | 0 | int n = ((Integer) stream.readObject()).intValue(); |
1016 | 0 | boolean isValidSave = isValid; |
1017 | 0 | isValid = true; |
1018 | 0 | for (int i = 0; i < n; i++) { |
1019 | 0 | String name = (String) stream.readObject(); |
1020 | 0 | Object value = (Object) stream.readObject(); |
1021 | 0 | if ((value instanceof String) && (value.equals(NOT_SERIALIZED))) |
1022 | 0 | continue; |
1023 | 0 | if (debug >= 2) |
1024 | 0 | log(" loading attribute '" + name + |
1025 | "' with value '" + value + "'"); | |
1026 | 0 | synchronized (attributes) { |
1027 | 0 | attributes.put(name, value); |
1028 | 0 | } |
1029 | } | |
1030 | 0 | isValid = isValidSave; |
1031 | ||
1032 | 0 | } |
1033 | ||
1034 | ||
1035 | /** | |
1036 | * Write a serialized version of this session object to the specified | |
1037 | * object output stream. | |
1038 | * <p> | |
1039 | * <b>IMPLEMENTATION NOTE</b>: The owning Manager will not be stored | |
1040 | * in the serialized representation of this Session. After calling | |
1041 | * <code>readObject()</code>, you must set the associated Manager | |
1042 | * explicitly. | |
1043 | * <p> | |
1044 | * <b>IMPLEMENTATION NOTE</b>: Any attribute that is not Serializable | |
1045 | * will be unbound from the session, with appropriate actions if it | |
1046 | * implements HttpSessionBindingListener. If you do not want any such | |
1047 | * attributes, be sure the <code>distributable</code> property of the | |
1048 | * associated Manager is set to <code>true</code>. | |
1049 | * | |
1050 | * @param stream The output stream to write to | |
1051 | * | |
1052 | * @exception IOException if an input/output error occurs | |
1053 | */ | |
1054 | private void writeObject(ObjectOutputStream stream) throws IOException { | |
1055 | ||
1056 | // Write the scalar instance variables (except Manager) | |
1057 | 0 | stream.writeObject(new Long(creationTime)); |
1058 | 0 | stream.writeObject(new Long(lastAccessedTime)); |
1059 | 0 | stream.writeObject(new Integer(maxInactiveInterval)); |
1060 | 0 | stream.writeObject(new Boolean(isNew)); |
1061 | 0 | stream.writeObject(new Boolean(isValid)); |
1062 | 0 | stream.writeObject(new Long(thisAccessedTime)); |
1063 | 0 | stream.writeObject(id); |
1064 | 0 | if (debug >= 2) |
1065 | 0 | log("writeObject() storing session " + id); |
1066 | ||
1067 | // Accumulate the names of serializable and non-serializable attributes | |
1068 | 0 | String keys[] = keys(); |
1069 | 0 | ArrayList saveNames = new ArrayList(); |
1070 | 0 | ArrayList saveValues = new ArrayList(); |
1071 | 0 | ArrayList unbinds = new ArrayList(); |
1072 | 0 | for (int i = 0; i < keys.length; i++) { |
1073 | 0 | Object value = null; |
1074 | 0 | synchronized (attributes) { |
1075 | 0 | value = attributes.get(keys[i]); |
1076 | 0 | } |
1077 | 0 | if (value == null) |
1078 | 0 | continue; |
1079 | 0 | else if (value instanceof Serializable) { |
1080 | 0 | saveNames.add(keys[i]); |
1081 | 0 | saveValues.add(value); |
1082 | } else | |
1083 | 0 | unbinds.add(keys[i]); |
1084 | } | |
1085 | ||
1086 | // Serialize the attribute count and the Serializable attributes | |
1087 | 0 | int n = saveNames.size(); |
1088 | 0 | stream.writeObject(new Integer(n)); |
1089 | 0 | for (int i = 0; i < n; i++) { |
1090 | 0 | stream.writeObject((String) saveNames.get(i)); |
1091 | try { | |
1092 | 0 | stream.writeObject(saveValues.get(i)); |
1093 | 0 | if (debug >= 2) |
1094 | 0 | log(" storing attribute '" + saveNames.get(i) + |
1095 | "' with value '" + saveValues.get(i) + "'"); | |
1096 | 0 | } catch (NotSerializableException e) { |
1097 | 0 | log( "Session is not serializable for attribute name: " + saveNames.get(i) + " and session id: " + id, e); |
1098 | 0 | stream.writeObject(NOT_SERIALIZED); |
1099 | 0 | if (debug >= 2) |
1100 | 0 | log(" storing attribute '" + saveNames.get(i) + |
1101 | "' with value NOT_SERIALIZED"); | |
1102 | 0 | unbinds.add(saveNames.get(i)); |
1103 | 0 | } |
1104 | } | |
1105 | ||
1106 | // Unbind the non-Serializable attributes | |
1107 | 0 | Iterator names = unbinds.iterator(); |
1108 | 0 | while (names.hasNext()) { |
1109 | 0 | removeAttribute((String) names.next()); |
1110 | } | |
1111 | ||
1112 | 0 | } |
1113 | ||
1114 | ||
1115 | // -------------------------------------------------------- Private Methods | |
1116 | ||
1117 | ||
1118 | /** | |
1119 | * Notify all session event listeners that a particular event has | |
1120 | * occurred for this Session. The default implementation performs | |
1121 | * this notification synchronously using the calling thread. | |
1122 | * | |
1123 | * @param type Event type | |
1124 | * @param data Event data | |
1125 | */ | |
1126 | public void fireSessionEvent(String type, Object data) { | |
1127 | /* | |
1128 | if (listeners.size() < 1) | |
1129 | return; | |
1130 | SessionEvent event = new SessionEvent(this, type, data); | |
1131 | SessionListener list[] = new SessionListener[0]; | |
1132 | synchronized (listeners) { | |
1133 | list = (SessionListener[]) listeners.toArray(list); | |
1134 | } | |
1135 | for (int i = 0; i < list.length; i++) | |
1136 | ((SessionListener) list[i]).sessionEvent(event); | |
1137 | */ | |
1138 | 0 | } |
1139 | ||
1140 | ||
1141 | /** | |
1142 | * Return the names of all currently defined session attributes | |
1143 | * as an array of Strings. If there are no defined attributes, a | |
1144 | * zero-length array is returned. | |
1145 | */ | |
1146 | private String[] keys() { | |
1147 | ||
1148 | 0 | String results[] = new String[0]; |
1149 | 0 | synchronized (attributes) { |
1150 | 0 | return ((String[]) attributes.keySet().toArray(results)); |
1151 | 0 | } |
1152 | ||
1153 | } | |
1154 | ||
1155 | ||
1156 | /** | |
1157 | * Log a message to the current ServletContext | |
1158 | * | |
1159 | * @param message Message to be logged | |
1160 | */ | |
1161 | protected void log(String message) { | |
1162 | ||
1163 | 0 | servletContext.log(message); |
1164 | ||
1165 | 0 | } |
1166 | ||
1167 | ||
1168 | /** | |
1169 | * Log a message to the current ServletContext | |
1170 | * | |
1171 | * @param message Message to be logged | |
1172 | * @param throwable Associated exception | |
1173 | */ | |
1174 | protected void log(String message, Throwable throwable) { | |
1175 | ||
1176 | 0 | servletContext.log(message, throwable); |
1177 | ||
1178 | 0 | } |
1179 | ||
1180 | ||
1181 | } |