JSEC-46 Implemented functionality for issue, also added minor updates to reference documentation

git-svn-id: https://svn.apache.org/repos/asf/incubator/jsecurity/trunk@737634 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/org/jsecurity/mgt/DefaultSecurityManager.java b/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
index f3d9fd6..87d165c 100644
--- a/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
@@ -476,7 +476,7 @@
      * @see org.jsecurity.authz.HostUnauthorizedException

      * @since 1.0

      */

-    protected Subject getSubjectBySessionId(Serializable sessionId) throws InvalidSessionException, AuthorizationException {

+    private Subject getSubjectBySessionId(Serializable sessionId) throws InvalidSessionException, AuthorizationException {

         if (!isValid(sessionId)) {

             String msg = "Specified id [" + sessionId + "] does not correspond to a valid Session  It either " +

                     "does not exist or the corresponding session has been stopped or expired.";

diff --git a/core/src/org/jsecurity/mgt/SessionsSecurityManager.java b/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
index fff3568..758a72b 100644
--- a/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
@@ -25,6 +25,7 @@
 import org.jsecurity.session.SessionListener;
 import org.jsecurity.session.SessionListenerRegistrar;
 import org.jsecurity.session.mgt.AbstractSessionManager;
+import org.jsecurity.session.mgt.AbstractValidatingSessionManager;
 import org.jsecurity.session.mgt.DefaultSessionManager;
 import org.jsecurity.session.mgt.SessionManager;
 import org.jsecurity.util.LifecycleUtils;
@@ -153,9 +154,9 @@
                     "underlying SessionManager instance is a part of the " +
                     "[" + requiredType.getName() + "] class hierarchy.  " +
                     "The current SessionManager is of type [" + this.sessionManager.getClass().getName() + "].  " +
-                    "This might occur for example if you're trying to set the validation interval " +
-                    "in a servlet container-backed session environment ('http' session mode).  If that is the case " +
-                    "however, that property is only useful when using 'jsecurity' session mode and using " +
+                    "This might occur for example if you're trying to set the validation interval or auto session " +
+                    "creation in a servlet container-backed session environment ('http' session mode).  If that is " +
+                    "the case however, that property is only useful when using 'jsecurity' session mode and using " +
                     "JSecurity enterprise sessions which do not rely on a servlet container.";
             throw new IllegalStateException(msg);
         }
@@ -194,6 +195,47 @@
     }
 
     /**
+     * Passthrough configuration property to the wrapped {@link AbstractValidatingSessionManager} - if it should
+     * automatically create a new session when an invalid session is referenced.  The default value unless
+     * overridden by this method is <code>true</code> for developer convenience and to match what most people are
+     * accustomed based on years of servlet container behavior.
+     * <p/>
+     * When true (the default), the wrapped {@link AbstractValidatingSessionManager} implementation throws an
+     * {@link org.jsecurity.session.ReplacedSessionException ReplacedSessionException} to the caller whenever a new
+     * session is created so the caller can receive the new session ID and react accordingly for future
+     * {@link SessionManager SessionManager} method invocations.
+     *
+     * @param autoCreate if the wrapped {@link AbstractValidatingSessionManager} should automatically create a new
+     *                   session when an invalid session is referenced
+     * @see org.jsecurity.session.mgt.AbstractValidatingSessionManager#setAutoCreateAfterInvalidation(boolean)
+     */
+    public void setAutoCreateSessionAfterInvalidation(boolean autoCreate) {
+        assertSessionManager(AbstractValidatingSessionManager.class);
+        ((AbstractValidatingSessionManager) this.sessionManager).setAutoCreateAfterInvalidation(autoCreate);
+    }
+
+    /**
+     * Passthrough configuration property that returns <code>true</code> if the wrapped
+     * {@link AbstractValidatingSessionManager AbstractValidatingSessionManager} should automatically create a
+     * new session when an invalid session is referenced, <code>false</code> otherwise.  Unless overridden by the
+     * {@link #setAutoCreateSessionAfterInvalidation(boolean)} method, the default value is <code>true</code> for
+     * developer convenience and to match what most people are accustomed based on years of servlet container behavior.
+     * <p/>
+     * When true (the default), the wrapped {@link AbstractValidatingSessionManager AbstractValidatingSessionManager}
+     * implementation throws an {@link org.jsecurity.session.ReplacedSessionException ReplacedSessionException} to
+     * the caller whenever a new session is created so the caller can receive the new session ID and react accordingly
+     * for future {@link SessionManager SessionManager} method invocations.
+     *
+     * @return <code>true</code> if this session manager should automatically create a new session when an invalid
+     *         session is referenced, <code>false</code> otherwise.
+     * @see org.jsecurity.session.mgt.AbstractValidatingSessionManager#isAutoCreateAfterInvalidation()
+     */
+    public boolean isAutoCreateSessionAfterInvalidation() {
+        assertSessionManager(AbstractValidatingSessionManager.class);
+        return ((AbstractValidatingSessionManager) this.sessionManager).isAutoCreateAfterInvalidation();
+    }
+
+    /**
      * Ensures the internal SessionManager instance is an <code>instanceof</code>
      * {@link org.jsecurity.session.SessionListenerRegistrar SessionListenerRegistrar} to ensure that any
      * listeners attempting to be registered can actually do so with the internal delegate instance.
diff --git a/core/src/org/jsecurity/session/ProxiedSession.java b/core/src/org/jsecurity/session/ProxiedSession.java
index c3b6b77..5bafecc 100644
--- a/core/src/org/jsecurity/session/ProxiedSession.java
+++ b/core/src/org/jsecurity/session/ProxiedSession.java
@@ -16,25 +16,6 @@
  * specific language governing permissions and limitations

  * under the License.

  */

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *     http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

 package org.jsecurity.session;

 

 import java.io.Serializable;

diff --git a/core/src/org/jsecurity/session/ReplacedSessionException.java b/core/src/org/jsecurity/session/ReplacedSessionException.java
new file mode 100644
index 0000000..9ea18f7
--- /dev/null
+++ b/core/src/org/jsecurity/session/ReplacedSessionException.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jsecurity.session;
+
+import java.io.Serializable;
+
+/**
+ * Exception thrown when a {@link #getOriginalSessionId() referenced session} has been determined to be invalid and
+ * a new session was created automatically to replace it.  This exception is thrown if the {@code SessionManager} has
+ * been configured to auto-recreate sessions when it encounters an invalid session reference.
+ *
+ * @author Les Hazlewood
+ * @since 1.0
+ */
+public class ReplacedSessionException extends InvalidSessionException {
+
+    private Serializable newSessionId = null;
+
+    public ReplacedSessionException() {
+        super();
+    }
+
+    public ReplacedSessionException(String msg, Serializable originalSessionId, Serializable newSessionId) {
+        super(msg, originalSessionId);
+        this.newSessionId = newSessionId;
+    }
+
+    public Serializable getOriginalSessionId() {
+        return super.getSessionId();
+    }
+
+    public Serializable getNewSessionId() {
+        return newSessionId;
+    }
+}
diff --git a/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java b/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java
index 355d0d2..46fdee1 100644
--- a/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java
+++ b/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java
@@ -51,6 +51,7 @@
     private long globalSessionTimeout = DEFAULT_GLOBAL_SESSION_TIMEOUT;
     private Collection<SessionListener> listeners = new ArrayList<SessionListener>();
 
+
     public AbstractSessionManager() {
     }
 
diff --git a/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java b/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java
index 0209c7e..c77f436 100644
--- a/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java
+++ b/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java
@@ -59,6 +59,12 @@
 
     protected long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
 
+    /**
+     * Whether or not to automatically create a new session transparently when a referenced session has expired.
+     * True by default, for developer convenience.
+     */
+    private boolean autoCreateAfterInvalidation = true;
+
 
     public AbstractValidatingSessionManager() {
     }
@@ -108,6 +114,41 @@
         return sessionValidationInterval;
     }
 
+    /**
+     * Returns <code>true</code> if this session manager should automatically create a new session when an invalid
+     * session is referenced, <code>false</code> otherwise.  Unless overridden by the
+     * {@link #setAutoCreateAfterInvalidation(boolean)} method, the default value is <code>true</code> for developer
+     * convenience and to match what most people are accustomed based on years of servlet container behavior.
+     * <p/>
+     * When true (the default), this {@code SessionManager} implementation throws an
+     * {@link org.jsecurity.session.ReplacedSessionException ReplacedSessionException} to the caller whenever a new session is created so
+     * the caller can receive the new session ID and react accordingly for future {@code SessionManager SessionManager}
+     * method invocations.
+     *
+     * @return <code>true</code> if this session manager should automatically create a new session when an invalid
+     *         session is referenced, <code>false</code> otherwise.
+     */
+    public boolean isAutoCreateAfterInvalidation() {
+        return autoCreateAfterInvalidation;
+    }
+
+    /**
+     * Sets if this session manager should automatically create a new session when an invalid
+     * session is referenced.  The default value unless overridden by this method is <code>true</code> for developer
+     * convenience and to match what most people are accustomed based on years of servlet container behavior.
+     * <p/>
+     * When true (the default), this {@code SessionManager} implementation throws an
+     * {@link org.jsecurity.session.ReplacedSessionException ReplacedSessionException} to the caller whenever a new session is created so
+     * the caller can receive the new session ID and react accordingly for future {@code SessionManager SessionManager}
+     * method invocations.
+     *
+     * @param autoCreateAfterInvalidation if this session manager should automatically create a new session when an
+     *                                    invalid session is referenced
+     */
+    public void setAutoCreateAfterInvalidation(boolean autoCreateAfterInvalidation) {
+        this.autoCreateAfterInvalidation = autoCreateAfterInvalidation;
+    }
+
     protected final Session doGetSession(Serializable sessionId) throws InvalidSessionException {
         enableSessionValidationIfNecessary();
         return retrieveSession(sessionId);
diff --git a/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java b/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java
index a4a765a..602a2e6 100644
--- a/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java
+++ b/core/src/org/jsecurity/session/mgt/DefaultSessionManager.java
@@ -23,6 +23,7 @@
 import org.jsecurity.cache.CacheManager;
 import org.jsecurity.cache.CacheManagerAware;
 import org.jsecurity.session.InvalidSessionException;
+import org.jsecurity.session.ReplacedSessionException;
 import org.jsecurity.session.Session;
 import org.jsecurity.session.mgt.eis.MemorySessionDAO;
 import org.jsecurity.session.mgt.eis.SessionDAO;
@@ -109,7 +110,22 @@
             log.trace("Retrieving session with id [" + sessionId + "]");
         }
         Session s = sessionDAO.readSession(sessionId);
-        validate(s);
+        if ( isAutoCreateAfterInvalidation() ) {
+            //save the host address in case the session will be invalidated.  We want to retain it for the
+            //replacement session:
+            InetAddress hostAddress = s.getHostAddress();
+            try {
+                validate(s);
+            } catch (InvalidSessionException e) {
+                Serializable newId = start(hostAddress);
+                String msg = "Session with id [" + sessionId + "] is invalid.  The SessionManager " +
+                        "has been configured to automatically re-create sessions upon invalidation.  Returnining " +
+                        "new session id [" + newId + "] with exception so the caller may react accordingly.";
+                throw new ReplacedSessionException(msg, sessionId, newId);
+            }
+        } else {
+            validate(s);
+        }
         return s;
     }
 
diff --git a/core/src/org/jsecurity/session/mgt/DelegatingSession.java b/core/src/org/jsecurity/session/mgt/DelegatingSession.java
index feb491e..f221be9 100644
--- a/core/src/org/jsecurity/session/mgt/DelegatingSession.java
+++ b/core/src/org/jsecurity/session/mgt/DelegatingSession.java
@@ -19,6 +19,7 @@
 package org.jsecurity.session.mgt;
 
 import org.jsecurity.session.InvalidSessionException;
+import org.jsecurity.session.ReplacedSessionException;
 import org.jsecurity.session.Session;
 
 import java.io.Serializable;
@@ -121,7 +122,12 @@
      */
     public Date getStartTimestamp() {
         if (startTimestamp == null) {
-            startTimestamp = sessionManager.getStartTimestamp(id);
+            try {
+                startTimestamp = sessionManager.getStartTimestamp(id);
+            } catch (ReplacedSessionException e) {
+                this.id = e.getNewSessionId();
+                startTimestamp = sessionManager.getStartTimestamp(id);
+            }
         }
         return startTimestamp;
     }
@@ -131,15 +137,30 @@
      */
     public Date getLastAccessTime() {
         //can't cache - only business pojo knows the accurate time:
-        return sessionManager.getLastAccessTime(id);
+        try {
+            return sessionManager.getLastAccessTime(id);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            return sessionManager.getLastAccessTime(id);
+        }
     }
 
     public long getTimeout() throws InvalidSessionException {
-        return sessionManager.getTimeout(id);
+        try {
+            return sessionManager.getTimeout(id);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            return sessionManager.getTimeout(id);
+        }
     }
 
     public void setTimeout(long maxIdleTimeInMillis) throws InvalidSessionException {
-        sessionManager.setTimeout(id, maxIdleTimeInMillis);
+        try {
+            sessionManager.setTimeout(id, maxIdleTimeInMillis);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            sessionManager.setTimeout(id, maxIdleTimeInMillis);
+        }
     }
 
     /**
@@ -147,7 +168,12 @@
      */
     public InetAddress getHostAddress() {
         if (hostAddress == null) {
-            hostAddress = sessionManager.getHostAddress(id);
+            try {
+                hostAddress = sessionManager.getHostAddress(id);
+            } catch (ReplacedSessionException e) {
+                this.id = e.getNewSessionId();
+                hostAddress = sessionManager.getHostAddress(id);
+            }
         }
         return hostAddress;
     }
@@ -156,28 +182,48 @@
      * @see org.jsecurity.session.Session#touch()
      */
     public void touch() throws InvalidSessionException {
-        sessionManager.touch(id);
+        try {
+            sessionManager.touch(id);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            sessionManager.touch(id);
+        }
     }
 
     /**
      * @see org.jsecurity.session.Session#stop()
      */
     public void stop() throws InvalidSessionException {
-        sessionManager.stop(id);
+        try {
+            sessionManager.stop(id);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            sessionManager.stop(id);
+        }
     }
 
     /**
      * @see org.jsecurity.session.Session#getAttributeKeys
      */
     public Collection<Object> getAttributeKeys() throws InvalidSessionException {
-        return sessionManager.getAttributeKeys(id);
+        try {
+            return sessionManager.getAttributeKeys(id);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            return sessionManager.getAttributeKeys(id);
+        }
     }
 
     /**
      * @see Session#getAttribute(Object key)
      */
     public Object getAttribute(Object key) throws InvalidSessionException {
-        return sessionManager.getAttribute(id, key);
+        try {
+            return sessionManager.getAttribute(id, key);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            return sessionManager.getAttribute(id, key);
+        }
     }
 
     /**
@@ -187,7 +233,12 @@
         if (value == null) {
             removeAttribute(key);
         } else {
-            sessionManager.setAttribute(id, key, value);
+            try {
+                sessionManager.setAttribute(id, key, value);
+            } catch (ReplacedSessionException e) {
+                this.id = e.getNewSessionId();
+                sessionManager.setAttribute(id, key, value);
+            }
         }
     }
 
@@ -195,6 +246,11 @@
      * @see Session#removeAttribute(Object key)
      */
     public Object removeAttribute(Object key) throws InvalidSessionException {
-        return sessionManager.removeAttribute(id, key);
+        try {
+            return sessionManager.removeAttribute(id, key);
+        } catch (ReplacedSessionException e) {
+            this.id = e.getNewSessionId();
+            return sessionManager.removeAttribute(id, key);
+        }
     }
 }
diff --git a/core/src/org/jsecurity/subject/DelegatingSubject.java b/core/src/org/jsecurity/subject/DelegatingSubject.java
index f6f1e8f..af5166d 100644
--- a/core/src/org/jsecurity/subject/DelegatingSubject.java
+++ b/core/src/org/jsecurity/subject/DelegatingSubject.java
@@ -238,8 +238,8 @@
     }
 
     public void login(AuthenticationToken token) throws AuthenticationException {
-        Subject authcSecCtx = securityManager.login(token);
-        PrincipalCollection principals = authcSecCtx.getPrincipals();
+        Subject subject = securityManager.login(token);
+        PrincipalCollection principals = subject.getPrincipals();
         if (principals == null || principals.isEmpty()) {
             String msg = "Principals returned from securityManager.login( token ) returned a null or " +
                     "empty value.  This value must be non null and populated with one or more elements.  " +
@@ -248,7 +248,7 @@
             throw new IllegalStateException(msg);
         }
         this.principals = principals;
-        Session session = authcSecCtx.getSession(false);
+        Session session = subject.getSession(false);
         if (session != null) {
             if (session instanceof StoppingAwareProxiedSession) {
                 this.session = session;
diff --git a/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java b/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java
index 1ecfba9..254b76a 100644
--- a/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java
+++ b/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java
@@ -22,6 +22,7 @@
 import org.jsecurity.authc.UsernamePasswordToken;
 import org.jsecurity.realm.text.PropertiesRealm;
 import org.jsecurity.session.Session;
+import org.jsecurity.session.mgt.AbstractValidatingSessionManager;
 import org.jsecurity.subject.Subject;
 import org.jsecurity.util.ThreadContext;
 import org.junit.After;
@@ -29,6 +30,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.Serializable;
+
 /**
  * @author Les Hazlewood
  * @since 0.2
@@ -70,4 +73,34 @@
         assertNull(subject.getPrincipal());
         assertNull(subject.getPrincipals());
     }
+
+    /**
+     * Test that validates functionality for issue
+     * <a href="https://issues.apache.org/jira/browse/JSEC-46">JSEC-46</a>
+     */
+    @Test
+    public void testAutoCreateSessionAfterInvalidation() {
+        Subject subject = sm.getSubject();
+        Session session = subject.getSession();
+        Serializable origSessionId = session.getId();
+
+        String key = "foo";
+        String value1 = "bar";
+        session.setAttribute(key, value1);
+        assertEquals(value1, session.getAttribute(key) );
+
+        //now test auto creation:
+        session.setTimeout(100);
+        try {
+            Thread.sleep(150);
+        } catch (InterruptedException e) {
+            //ignored
+        }
+        session.setTimeout(AbstractValidatingSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT);
+        Serializable newSessionId = session.getId();
+        assertFalse(origSessionId.equals(newSessionId));
+
+        Object aValue = session.getAttribute(key);
+        assertNull(aValue);
+    }
 }
diff --git a/docs/reference/src/index.xml b/docs/reference/src/index.xml
index 33ba1f7..41f3bb2 100644
--- a/docs/reference/src/index.xml
+++ b/docs/reference/src/index.xml
@@ -37,7 +37,7 @@
 <book>
     <bookinfo>
         <title>JSecurity Reference Documentation</title>
-        <releaseinfo>0.9.0</releaseinfo>
+        <releaseinfo>1.0.0</releaseinfo>
         <authorgroup>
             <author>
                 <firstname>Les</firstname>
@@ -45,7 +45,8 @@
             </author>
         </authorgroup>
         <legalnotice>
-            <para>Copies of this document may be made for your own use and for
+            <para>
+                Copies of this document may be made for your own use and for
                 distribution to others, provided that you do not charge any
                 fee for such copies and further provided that each copy
                 contains this Copyright Notice, whether distributed in print
@@ -56,17 +57,35 @@
     <!-- front matter -->
     <toc/>
     &preface;
-    &terminology;
-    &overview;
+    <part id="jsecurity-quickstart">
+        <title>JSecurity Quickstart</title>
+        <partintro id="jsecurity-quickstart-intro">
+            <para>
+                This initial part of the reference documentation walks through the simplest quickstart application
+                so that you may become familiarized JSecurity's programming API and general concepts. While this
+                application is probably the absolute simplest JSecurity-enabled application that we know of,
+                it should make you comfortable understanding what JSecurity can do, and how it can help you create
+                more secure applications. The rest of the documentation will serve as your guide to help you move
+                from this simple example to even the most complicated enterprise applications.
+            </para>
+
+            <note>
+                <para>
+                    The code for this quickstart application is in the <filename>samples/quickstart</filename>
+                    directory of the source distribution.
+                </para>
+            </note>
+        </partintro>
+    </part>
     <part id="jsecurity-core">
         <title>JSecurity Core</title>
         <partintro id="jsecurity-core-intro">
             <para>
-                This initial part of the reference documentation covers
+                This part of the reference documentation covers
                 JSecurity's core architectural components that support
                 functionality in all environments, from
                 constrained Applets and cell phones to full n-tier
-                enterprise clustered applications.
+                clustered enterprise applications.
             </para>
             <para>
                 Most important among these are the Subject and
@@ -78,10 +97,7 @@
                 <interfacename>Authorizer</interfacename>.
             </para>
             <para>
-                Coverage of Spring's integration with AspectJ (currently
-                the richest - in terms of features - and certainly most
-                mature AOP implementation in the Java enterprise space)
-                is also provided.
+
             </para>
             <para>
                 Finally, the adoption of the test-driven-development (TDD)
diff --git a/docs/reference/src/preface.xml b/docs/reference/src/preface.xml
index 5d9cf1e..4f7cdd0 100644
--- a/docs/reference/src/preface.xml
+++ b/docs/reference/src/preface.xml
@@ -17,64 +17,66 @@
   ~ under the License.

   -->

 <preface>

-  <title>Preface</title>

+    <title>Preface</title>

 

-  <para>

-    Securing a software application is a task that is almost always a necessity, but isn't

-    something that we like to worry about very often. We're more focused on programming business logic,

-    deadlines, performance, and many other things that are more directly related to things clients

-    can see. Application security is often an afterthought and something that we do when we

-    <quote>get around to it</quote>.

-  </para>

+    <para>

+        Securing a software application is a task that is almost always a necessity, but isn't

+        something that we like to worry about very often. We're more focused on programming business logic,

+        deadlines, performance, and many other things that are more directly related to things clients

+        can see. Application security is often an afterthought and something that we do when we

+        <quote>get around to it</quote>.

+    </para>

 

-  <para>

-    While application security is usually a necessity, it would be nice if we could implement it whenever

-    we want, and without it being too difficult. Actually, it

-    <emphasis>shouldn't</emphasis>

-    be difficult.

-    In fact, it

-    <emphasis>should</emphasis>

-    be downright easy. It should also be as

-    transparent and unintrusive as possible, so you don't have to change a lot of code to secure what you want.

-    And above all, it should be extremely easy to understand, so when you do actually have to look at security

-    code, it just makes sense.

-  </para>

+    <para>

+        While application security is usually a necessity, it would be nice if we could implement it whenever

+        we want, and without it being too difficult. Actually, it

+        <emphasis>shouldn't</emphasis>

+        be difficult.

+        In fact, it

+        <emphasis>should</emphasis>

+        be downright easy. It should also be as transparent and

+        unintrusive as possible, so you don't have to change a lot of code to secure what you want.

+        And above all, it should be extremely easy to understand, so when you do actually have to look at security

+        code, it just makes sense.

+    </para>

 

-  <para>

-    JSecurity is a Java security framework that attempts to achieve these goals. The framework tries to

-    give as much power and flexibility as possible, while still being

-    <emphasis>really</emphasis>

-    easy to understand and easy to use. It tries to be the most comprehensive and feature-rich security

-    framework in the Java world so that you have everything you need at your fingertips.

-  </para>

+    <para>

+        JSecurity is a Java security framework that attempts to achieve these goals. The framework tries to

+        give as much power and flexibility as possible, while still being

+        <emphasis>really</emphasis>

+        easy to understand and easy to use. It tries to be the most comprehensive and feature-rich security

+        framework in the Java world so that you have everything you need at your fingertips.

+    </para>

 

-  <para>

-    This document is the official refrence manual for the JSecurity framework, and it aims to give you the

-    most complete documentation on JSecurity and all of its features. It is

-    very much a work-in-progress, and we welcome suggestions and recommendations. If you have any,

-    we'd very much appreciate your feedback on our forums at

-    <ulink url="http://www.jsecurity.org/forum">http://www.jsecurity.org/forum</ulink>

-    or

-    mailing lists at<ulink url="http://www.jsecurity.org/mailinglists">http://www.jsecurity.org/mailinglists</ulink>.

-  </para>

+    <para>

+        This document is the official refrence manual for the JSecurity framework, and it aims to give you the

+        most complete documentation on JSecurity and all of its features. It is

+        very much a work-in-progress, and we welcome suggestions and recommendations. If you have any,

+        we'd very much appreciate your feedback on our forums at

+        <ulink url="http://www.jsecurity.org/forum">http://www.jsecurity.org/forum</ulink>

+        or

+        mailing lists at

+        <ulink url="http://www.jsecurity.org/mailinglists">http://www.jsecurity.org/mailinglists</ulink>

+        .

+    </para>

 

-  <para>

-    Before continuing on, we'd like to give credit where it is due for the format of this

-    book. We sincerely appreciate all the work that Christian Bauer from the

-    <ulink url="http://www.hibernate.org">Hibernate</ulink>

-    team and

-    Juergen Hoeller of the

-    <ulink url="http://www.springframework.org">Spring</ulink>

-    team did in ensuring this

-    document could be created in PDF and HTML formats. Without their efforts, it would have been an agnoizing

-    task for us to do ourselves.

-    Thank you!

-  </para>

+    <para>

+        Before continuing on, we'd like to give credit where it is due for the format of this

+        book. We sincerely appreciate all the work that Christian Bauer from the

+        <ulink url="http://www.hibernate.org">Hibernate</ulink>

+        team and

+        Juergen Hoeller of the

+        <ulink url="http://www.springframework.org">Spring</ulink>

+        team did in ensuring this

+        document could be created in PDF and HTML formats. Without their efforts, it would have been an agnoizing

+        task for us to do ourselves.

+        Thank you!

+    </para>

 

-  <para>

-    So, without further adieu, let's move on to the documentation. Feel

-    free knowing you can incorporate JSecurity's rich features quickly and easily now, or perhaps maybe

-    later when you<quote>get around to it</quote>.

-  </para>

+    <para>

+        So, without further adieu, let's move on to the documentation. Feel

+        free knowing you can incorporate JSecurity's rich features quickly and easily now, or perhaps maybe

+        later when you<quote>get around to it</quote>.

+    </para>

 

 </preface>
\ No newline at end of file
diff --git a/docs/reference/styles/fopdf.xsl b/docs/reference/styles/fopdf.xsl
index bfca48b..b958c1c 100644
--- a/docs/reference/styles/fopdf.xsl
+++ b/docs/reference/styles/fopdf.xsl
@@ -64,7 +64,7 @@
                 <xsl:value-of select="bookinfo/title"/>
               </fo:block>
               <fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
-                <xsl:text>Version</xsl:text>
+                <xsl:text>Version </xsl:text>
                 <xsl:value-of select="bookinfo/releaseinfo"/>
               </fo:block>
             </fo:table-cell>
@@ -79,7 +79,7 @@
           <fo:table-row>
             <fo:table-cell text-align="center">
               <fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
-                <xsl:text>Copyright &copyright; 2004-2008</xsl:text>
+                <xsl:text>Copyright &copyright; 2004-2009 </xsl:text>
                 <xsl:for-each select="bookinfo/authorgroup/author">
                   <xsl:if test="position() > 1">
                     <xsl:text>,</xsl:text>
diff --git a/web/src/org/jsecurity/web/session/DefaultWebSessionManager.java b/web/src/org/jsecurity/web/session/DefaultWebSessionManager.java
index 8cb9cf2..761b836 100644
--- a/web/src/org/jsecurity/web/session/DefaultWebSessionManager.java
+++ b/web/src/org/jsecurity/web/session/DefaultWebSessionManager.java
@@ -259,8 +259,8 @@
             session = doGetSession(request, response);
         } catch (InvalidSessionException ise) {
             if (log.isTraceEnabled()) {
-                log.trace("Request Session is invalid, message: [" + ise.getMessage() + "].  Removing any " +
-                        "associated session cookie...");
+                log.trace("Request Session with id [" + ise.getSessionId() + "] is invalid, message: [" +
+                        ise.getMessage() + "].  Removing any associated session cookie...");
             }
             getSessionIdCookieAttribute().removeValue(request, response);