You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by lh...@apache.org on 2010/04/23 00:49:22 UTC

svn commit: r937094 [1/2] - in /incubator/shiro/trunk: core/src/main/java/org/apache/shiro/crypto/ core/src/main/java/org/apache/shiro/mgt/ core/src/main/java/org/apache/shiro/subject/ core/src/main/java/org/apache/shiro/subject/support/ core/src/test/...

Author: lhazlewood
Date: Thu Apr 22 22:49:21 2010
New Revision: 937094

URL: http://svn.apache.org/viewvc?rev=937094&view=rev
Log:
SHIRO-141: 

Refactored SecurityManager.createSubject and SubjectFactory.createSubject methods to take in a new interface: SubjectContext.  SubjectContext is essentially a Map with a few extra type-safe getters/setters and some resolve* methods.  The resolve methods use heuristics to acquire data used to construct subject instances. 

This logic used to be entirely in the SubjectFactory implementations, but it was also needed by the RememberMeManager implementations, which didn't have access to/knowledge of a SubjectFactory (and shouldn't).  So that logic was extracted to a SubjectContext implementation that could be shared across both RememberMeManager and SubjectFactory implementations.

Also removed two Exceptions that were never used.  Updated test cases to reflect the new interface addition.

Finally, the default CipherService used by the AbstractRememberMeManager is now an AesCipherService instead of the previous BlowfishCipherService default.  AES is more commonly used in most end-user environments.  Existing RememberMe cookies will therefore fail with this configuration, but this is a non-critical problem - the next login w/ rememberMe enabled will create an AES-encrypted cookie and work from that point on.

Added:
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/SubjectContext.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java   (contents, props changed)
      - copied, changed from r935228, incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/DelegatingSubject.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/subject/WebSubjectContext.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/subject/support/DefaultWebSubjectContext.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/subject/support/WebDelegatingSubject.java
      - copied, changed from r935228, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/subject/WebDelegatingSubject.java
Removed:
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/DelegatingSubject.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/InvalidSubjectException.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/SubjectException.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/subject/WebDelegatingSubject.java
Modified:
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/DefaultBlockCipherService.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/RememberMeManager.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SecurityManager.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SubjectFactory.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java
    incubator/shiro/trunk/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java
    incubator/shiro/trunk/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/DefaultWebSecurityManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/WebRememberMeManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSubjectFactory.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/subject/WebSubject.java
    incubator/shiro/trunk/web/src/test/java/org/apache/shiro/web/WebRememberMeManagerTest.java

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/DefaultBlockCipherService.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/DefaultBlockCipherService.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/DefaultBlockCipherService.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/DefaultBlockCipherService.java Thu Apr 22 22:49:21 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.util.StringUtils
  *
  * <h2>Usage</h2>
  * Note that this class exists mostly to simplify algorithm-specific subclasses.  Unless you understand the concepts of
- * cipher modes of operations, block sizes, and padding schemes, and you want direct control of these things, you should
+ * cipher modes of operation, block sizes, and padding schemes, and you want direct control of these things, you should
  * typically not uses instances of this class directly.  Instead, algorithm-specific subclasses, such as
  * {@link AesCipherService}, {@link BlowfishCipherService}, and others are usually better suited for regular use.
  * <p/>
@@ -319,18 +319,18 @@ public class DefaultBlockCipherService e
 
     /**
      * Returns the block cipher's block size to be used when constructing
-     * {@link javax.crypto.Cipher Cipher} transformation string or {@code -1} if the JCA Provider default block size
-     * the specified {@link #getAlgorithmName() algorithm} should be used.
+     * {@link javax.crypto.Cipher Cipher} transformation string or {@code 0} if the JCA Provider default block size
+     * for the specified {@link #getAlgorithmName() algorithm} should be used.
      * <p/>
      * This attribute is used <em>only</em> when constructing the transformation string for block (byte array)
      * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}).  The
      * {@link #getStreamingBlockSize() streamingBlockSize} attribute is used when the block cipher is used for
      * streaming operations.
      * <p/>
-     * The default value is {@code -1} to retain the JCA Provider default.
+     * The default value is {@code 0} which retains the JCA Provider default.
      *
      * @return the block cipher block size to be used when constructing the
-     *         {@link javax.crypto.Cipher Cipher} transformation string, or {@code -1} if the JCA Provider default
+     *         {@link javax.crypto.Cipher Cipher} transformation string, or {@code 0} if the JCA Provider default
      *         block size for the specified {@link #getAlgorithmName() algorithm} should be used.
      */
     public int getBlockSize() {
@@ -339,7 +339,7 @@ public class DefaultBlockCipherService e
 
     /**
      * Sets the block cipher's block size to be used when constructing
-     * {@link javax.crypto.Cipher Cipher} transformation string.  {@code -1} indicates that the JCA Provider default
+     * {@link javax.crypto.Cipher Cipher} transformation string.  {@code 0} indicates that the JCA Provider default
      * block size for the specified {@link #getAlgorithmName() algorithm} should be used.
      * <p/>
      * This attribute is used <em>only</em> when constructing the transformation string for block (byte array)
@@ -347,13 +347,13 @@ public class DefaultBlockCipherService e
      * {@link #getStreamingBlockSize() streamingBlockSize} attribute is used when the block cipher is used for
      * streaming operations.
      * <p/>
-     * The default value is {@code -1} to retain the JCA Provider default.
+     * The default value is {@code 0} which retains the JCA Provider default.
      * <p/>
      * <b>NOTE:</b> block cipher block sizes are very algorithm-specific.  If you change this value, ensure that it
      * will work with the specified {@link #getAlgorithmName() algorithm}.
      *
      * @param blockSize the block cipher block size to be used when constructing the
-     *                  {@link javax.crypto.Cipher Cipher} transformation string, or {@code -1} if the JCA Provider
+     *                  {@link javax.crypto.Cipher Cipher} transformation string, or {@code 0} if the JCA Provider
      *                  default block size for the specified {@link #getAlgorithmName() algorithm} should be used.
      */
     public void setBlockSize(int blockSize) {

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java Thu Apr 22 22:49:21 2010
@@ -30,12 +30,11 @@ import org.apache.shiro.io.DefaultSerial
 import org.apache.shiro.io.Serializer;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.SubjectContext;
 import org.apache.shiro.util.ByteSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Map;
-
 /**
  * Abstract implementation of the {@code RememberMeManager} interface that handles
  * {@link #setSerializer(org.apache.shiro.io.Serializer) serialization} and
@@ -44,8 +43,8 @@ import java.util.Map;
  * The remembered identity storage location and details are left to subclasses.
  * <h2>Default encryption key</h2>
  * This implementation uses an {@link AesCipherService AesCipherService} for strong encryption by default.  It also
- * uses a default generated symmetric key to both encrypt and decrypt data.Shiro's default symmetric block Cipher using the Blowfish algorithm.  As it is a symmetric Cipher, it uses the
- * same <tt>Key</tt> to both encrypt and decrypt data, BUT NOTE:
+ * uses a default generated symmetric key to both encrypt and decrypt data.  As AES is a symmetric cipher, the same
+ * {@code key} is used to both encrypt and decrypt data, BUT NOTE:
  * <p/>
  * Because Shiro is an open-source project, if anyone knew that you were using Shiro's default
  * {@code key}, they could download/view the source, and with enough effort, reconstruct the {@code key}
@@ -362,13 +361,12 @@ public abstract class AbstractRememberMe
 
     /**
      * Forgets (removes) any remembered identity data for the subject being built by the specified {@code context}
-     * argument.  The context map is usually populated by a {@link Subject.Builder} implementation.  See the
-     * {@link SubjectFactory} class constants for Shiro's known map keys.
+     * argument.  The context map is usually populated by a {@link Subject.Builder} implementation.
      *
      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
      *                       is being used to construct a {@link Subject} instance.
      */
-    protected abstract void forgetIdentity(Map subjectContext);
+    protected abstract void forgetIdentity(SubjectContext subjectContext);
 
     /**
      * Forgets (removes) any remembered identity data for the specified {@link Subject} instance.
@@ -485,7 +483,7 @@ public abstract class AbstractRememberMe
 
     /**
      * Persists the identity bytes to a persistent store for retrieval later via the
-     * {@link #getRememberedSerializedIdentity(java.util.Map)} method.
+     * {@link #getRememberedSerializedIdentity(SubjectContext)} method.
      *
      * @param subject    the Subject for which the identity is being serialized.
      * @param serialized the serialized bytes to be persisted.
@@ -493,12 +491,12 @@ public abstract class AbstractRememberMe
     protected abstract void rememberSerializedIdentity(Subject subject, byte[] serialized);
 
     /**
-     * Implements the interface method by first {@link #getRememberedSerializedIdentity(java.util.Map) acquiring}
-     * the remembered serialized byte array.  Then it {@link #convertBytesToPrincipals(byte[], java.util.Map) converts}
+     * Implements the interface method by first {@link #getRememberedSerializedIdentity(SubjectContext) acquiring}
+     * the remembered serialized byte array.  Then it {@link #convertBytesToPrincipals(byte[], SubjectContext) converts}
      * them and returns the re-constituted {@link PrincipalCollection}.  If no remembered principals could be
      * obtained, {@code null} is returned.
      * <p/>
-     * If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, java.util.Map)} method
+     * If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, SubjectContext)} method
      * is called to allow any necessary post-processing (such as immediately removing any previously remembered
      * values for safety).
      *
@@ -506,7 +504,7 @@ public abstract class AbstractRememberMe
      *                       is being used to construct a {@link Subject} instance.
      * @return the remembered principals or {@code null} if none could be acquired.
      */
-    public PrincipalCollection getRememberedPrincipals(Map subjectContext) {
+    public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
         PrincipalCollection principals = null;
         try {
             byte[] bytes = getRememberedSerializedIdentity(subjectContext);
@@ -532,7 +530,7 @@ public abstract class AbstractRememberMe
      * @return the previously persisted serialized identity, or {@code null} if there is no available data for the
      *         Subject.
      */
-    protected abstract byte[] getRememberedSerializedIdentity(Map subjectContext);
+    protected abstract byte[] getRememberedSerializedIdentity(SubjectContext subjectContext);
 
     /**
      * If a {@link #getCipherService() cipherService} is available, it will be used to first decrypt the byte array.
@@ -543,7 +541,7 @@ public abstract class AbstractRememberMe
      *                       is being used to construct a {@link Subject} instance.
      * @return the de-serialized and possibly decrypted principals
      */
-    protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, Map subjectContext) {
+    protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
         if (getCipherService() != null) {
             bytes = decrypt(bytes);
         }
@@ -553,7 +551,7 @@ public abstract class AbstractRememberMe
     /**
      * Called when an exception is thrown while trying to retrieve principals.  The default implementation logs a
      * debug message and forgets ('unremembers') the problem identity by calling
-     * {@link #forgetIdentity(java.util.Map) forgetIdentity(context)} and then immediately re-throws the
+     * {@link #forgetIdentity(SubjectContext) forgetIdentity(context)} and then immediately re-throws the
      * exception to allow the calling component to react accordingly.
      * <p/>
      * This method implementation never returns an
@@ -567,7 +565,7 @@ public abstract class AbstractRememberMe
      *                is being used to construct a {@link Subject} instance.
      * @return nothing - the original {@code RuntimeException} is propagated in all cases.
      */
-    protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, Map context) {
+    protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context) {
         if (log.isDebugEnabled()) {
             log.debug("There was a failure while trying to retrieve remembered principals.  This could be due to a " +
                     "configuration problem or corrupted principals.  This could also be due to a recently " +

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java Thu Apr 22 22:49:21 2010
@@ -28,13 +28,21 @@ import org.apache.shiro.session.SessionE
 import org.apache.shiro.session.mgt.DelegatingSession;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.SubjectContext;
+import org.apache.shiro.subject.support.DefaultSubjectContext;
+import org.apache.shiro.util.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.Serializable;
+import java.lang.Exception;
+import java.lang.IllegalArgumentException;
+import java.lang.IllegalStateException;
+import java.lang.String;
+import java.lang.SuppressWarnings;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+
+import org.apache.shiro.mgt.SecurityManager;
 
 /**
  * The Shiro framework's default concrete implementation of the {@link SecurityManager} interface,
@@ -177,6 +185,10 @@ public class DefaultSecurityManager exte
         return new DelegatingSession(this, id);
     }
 
+    protected SubjectContext createSubjectContext() {
+        return new DefaultSubjectContext();
+    }
+
     /**
      * Creates a {@code Subject} instance for the user represented by the given method arguments.
      *
@@ -187,12 +199,12 @@ public class DefaultSecurityManager exte
      *         authenticated subject.
      */
     protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
-        Map<String, Object> context = new HashMap<String, Object>();
-        context.put(SubjectFactory.AUTHENTICATED, Boolean.TRUE);
-        context.put(SubjectFactory.AUTHENTICATION_TOKEN, token);
-        context.put(SubjectFactory.AUTHENTICATION_INFO, info);
+        SubjectContext context = createSubjectContext();
+        context.setAuthenticated(true);
+        context.setAuthenticationToken(token);
+        context.setAuthenticationInfo(info);
         if (existing != null) {
-            context.put(SubjectFactory.SUBJECT, existing);
+            context.setSubject(existing);
         }
         return createSubject(context);
     }
@@ -212,21 +224,21 @@ public class DefaultSecurityManager exte
         PrincipalCollection principals = subject.getPrincipals();
         if (principals != null && !principals.isEmpty()) {
             Session session = subject.getSession();
-            session.setAttribute(SubjectFactory.PRINCIPALS_SESSION_KEY, principals);
+            session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, principals);
         } else {
             Session session = subject.getSession(false);
             if (session != null) {
-                session.removeAttribute(SubjectFactory.PRINCIPALS_SESSION_KEY);
+                session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
             }
         }
 
         if (subject.isAuthenticated()) {
             Session session = subject.getSession();
-            session.setAttribute(SubjectFactory.AUTHENTICATED_SESSION_KEY, subject.isAuthenticated());
+            session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, subject.isAuthenticated());
         } else {
             Session session = subject.getSession(false);
             if (session != null) {
-                session.removeAttribute(SubjectFactory.AUTHENTICATED_SESSION_KEY);
+                session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
             }
         }
     }
@@ -305,7 +317,7 @@ public class DefaultSecurityManager exte
                 onFailedLogin(token, ae, subject);
             } catch (Exception e) {
                 if (log.isInfoEnabled()) {
-                    log.info("onFailedLogin(AuthenticationToken,AuthenticationException) method threw an " +
+                    log.info("onFailedLogin method threw an " +
                             "exception.  Logging and propagating original AuthenticationException.", e);
                 }
             }
@@ -332,56 +344,59 @@ public class DefaultSecurityManager exte
         rememberMeLogout(subject);
     }
 
+    protected SubjectContext copy(SubjectContext subjectContext) {
+        return new DefaultSubjectContext(subjectContext);
+    }
+
     /**
-     * This implementation attempts to resolve any session ID that may exist in the context argument by
-     * passing it to the {@link #resolveSession(Map)} method.  The
+     * This implementation attempts to resolve any session ID that may exist in the context by
+     * passing it to the {@link #resolveSession(SubjectContext)} method.  The
      * return value from that call is then used to attempt to resolve the subject identity via the
-     * {@link #resolvePrincipals(java.util.Map)} method.  The return value from that call is then used to create
+     * {@link #resolvePrincipals(SubjectContext)} method.  The return value from that call is then used to create
      * the {@code Subject} instance by calling
-     * <code>{@link #getSubjectFactory() getSubjectFactory()}.{@link SubjectFactory#createSubject(java.util.Map) createSubject}(resolvedContext);</code>
+     * <code>{@link #getSubjectFactory() getSubjectFactory()}.{@link SubjectFactory#createSubject createSubject}(resolvedContext);</code>
      *
-     * @param context any data needed to direct how the Subject should be constructed.
+     * @param subjectContext any data needed to direct how the Subject should be constructed.
      * @return the {@code Subject} instance reflecting the specified initialization data.
-     * @see SubjectFactory#createSubject(java.util.Map)
+     * @see SubjectFactory#createSubject
      * @since 1.0
      */
-    public Subject createSubject(Map context) {
-        if (context == null) {
-            context = new HashMap();
-        }
+    public Subject createSubject(SubjectContext subjectContext) {
+        //create a copy so we don't modify the argument's backing map:
+        SubjectContext context = copy(subjectContext);
 
-        //ensure that the context map has a SecurityManager instance, and if not, add one:
-        Map resolved = ensureSecurityManager(context);
+        //ensure that the context has a SecurityManager instance, and if not, add one:
+        context = ensureSecurityManager(context);
 
         //Translate a session id if it exists into a Session object before sending to the SubjectFactory
         //The SubjectFactory should not need to know how to acquire sessions as it is often environment
         //specific - better to shield the SF from these details:
-        resolved = resolveSession(resolved);
+        context = resolveSession(context);
 
         //Similarly, the SubjectFactory should not have any concept of RememberMe - translate that here first
         //if possible before handing off to the SubjectFactory:
-        resolved = resolvePrincipals(resolved);
+        context = resolvePrincipals(context);
 
-        return getSubjectFactory().createSubject(resolved);
+        return getSubjectFactory().createSubject(context);
     }
 
     /**
-     * Determines if there is a {@code SecurityManager} instance in the context map under the
-     * {@link SubjectFactory#SECURITY_MANAGER} key, and if not, adds 'this' to the map under that key.  This ensures
-     * the SubjectFactory instance will have access to a SecurityManager during Subject construction if necessary.
+     * Determines if there is a {@code SecurityManager} instance in the context, and if not, adds 'this' to the
+     * context.  This ensures the SubjectFactory instance will have access to a SecurityManager during Subject
+     * construction if necessary.
      *
      * @param context the subject context data that may contain a SecurityManager instance.
-     * @return The context Map to use to pass to a {@link SubjectFactory} for subject creation.
+     * @return The SubjectContext to use to pass to a {@link SubjectFactory} for subject creation.
      * @since 1.0
      */
     @SuppressWarnings({"unchecked"})
-    protected Map ensureSecurityManager(Map context) {
-        if (context.containsKey(SubjectFactory.SECURITY_MANAGER)) {
-            log.debug("Context already contains a SecurityManager instance.  Returning.");
+    protected SubjectContext ensureSecurityManager(SubjectContext context) {
+        if (context.getSecurityManager() != null) {
+            log.trace("Context already contains a SecurityManager instance.  Returning.");
             return context;
         }
         log.trace("No SecurityManager found in context.  Adding self reference.");
-        context.put(SubjectFactory.SECURITY_MANAGER, this);
+        context.setSecurityManager(this);
         return context;
     }
 
@@ -390,22 +405,17 @@ public class DefaultSecurityManager exte
      * context that represents this resolved {@code Session} to ensure it may be referenced if necessary by the
      * invoked {@link SubjectFactory} that performs actual {@link Subject} construction.
      * <p/>
-     * The session id, if it exists in the context map, should be available as a value under the
-     * <code>{@link SubjectFactory SubjectFactory}.{@link SubjectFactory#SESSION_ID SESSION_ID}</code> key constant.
-     * If a session is resolved, a copy of the original context Map is made to ensure the method argument is not
-     * changed, the resolved session is placed into the copy and the copy is returned.
-     * <p/>
      * If there is a {@code Session} already in the context because that is what the caller wants to be used for
      * {@code Subject} construction, or if no session is resolved, this method effectively does nothing and immediately
      * returns the Map method argument unaltered.
      *
      * @param context the subject context data that may contain a session id that should be converted to a Session instance.
-     * @return The context Map to use to pass to a {@link SubjectFactory} for subject creation.
+     * @return The context to use to pass to a {@link SubjectFactory} for subject creation.
      * @since 1.0
      */
     @SuppressWarnings({"unchecked"})
-    protected Map resolveSession(Map context) {
-        if (context.containsKey(SubjectFactory.SESSION)) {
+    protected SubjectContext resolveSession(SubjectContext context) {
+        if (context.resolveSession() != null) {
             log.debug("Context already contains a session.  Returning.");
             return context;
         }
@@ -415,7 +425,7 @@ public class DefaultSecurityManager exte
         if (sessionId != null) {
             try {
                 Session session = getSession(sessionId);
-                context.put(SubjectFactory.SESSION, session);
+                context.setSession(session);
             } catch (InvalidSessionException e) {
                 onInvalidSessionId(sessionId, e);
                 log.debug("Referenced sessionId {} is invalid.  Ignoring and creating an anonymous " +
@@ -429,65 +439,30 @@ public class DefaultSecurityManager exte
     }
 
     /**
-     * Heuristically determines if the specified subject map can resolve a Subject identity ({@link PrincipalCollection})
-     * either directly or indirectly by value association.  This implementation returns {@code true} in the following
-     * two cases, {@code false} otherwise:
-     * <ol>
-     * <li>If the context {@link Map#containsKey contains} a key {@link SubjectFactory#PRINCIPALS}, it is assumed
-     * the identity has been explicitly provided already.</li>
-     * <li>If the context has a {@link Session} under the {@link SubjectFactory#SESSION} key, it attempts to resolve
-     * any identity associated with that {@code Session} instance.  If one can be found in the {@code Session}, it is
-     * assumed that {@code Session} identity should be used/retained.</li>
-     * </ol>
-     *
-     * @param context the subject context data that may provide (directly or indirectly through one of its values) a
-     *                {@link PrincipalCollection} identity.
-     * @return {@code true} if an identity can be resolved, {@code false} otherwise.
-     * @since 1.0
-     */
-    protected boolean containsIdentity(Map context) {
-        if (context.containsKey(SubjectFactory.PRINCIPALS)) {
-            log.trace("Context already contains an explicit identity.");
-            return true;
-        }
-        if (context.containsKey(SubjectFactory.SESSION)) {
-            Session session = (Session) context.get(SubjectFactory.SESSION);
-            if (session != null) {
-                Object principals = session.getAttribute(SubjectFactory.PRINCIPALS_SESSION_KEY);
-                if (principals != null) {
-                    log.trace("Context already contains an implicit (session-based) identity.");
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
      * Attempts to resolve an identity (a {@link PrincipalCollection}) for the context using heuristics.  The
      * implementation strategy:
      * <ol>
-     * <li>Check the context to see if it already {@link #containsIdentity(java.util.Map) contains an identity}.  If
+     * <li>Check the context to see if it can already {@link SubjectContext#resolvePrincipals resolve an identity}.  If
      * so, this method does nothing and returns the method argument unaltered.</li>
      * <li>Check for a RememberMe identity by calling {@link #getRememberedIdentity}.  If that method returns a
-     * non-null value, create a <em>copy</em> of the method argument, and place the remembered {@link PrincipalCollection}
-     * in the copied context map under the {@link SubjectFactory#PRINCIPALS} key and return that copied context.</li>
+     * non-null value, place the remembered {@link PrincipalCollection} in the context and return the context.</li>
      * </ol>
      *
      * @param context the subject context data that may provide (directly or indirectly through one of its values) a
      *                {@link PrincipalCollection} identity.
-     * @return The context Map to use to pass to a {@link SubjectFactory} for subject creation.
+     * @return The Subject context to use to pass to a {@link SubjectFactory} for subject creation.
      * @since 1.0
      */
     @SuppressWarnings({"unchecked"})
-    protected Map resolvePrincipals(Map context) {
-        if (!containsIdentity(context)) {
+    protected SubjectContext resolvePrincipals(SubjectContext context) {
+        PrincipalCollection principals = context.resolvePrincipals();
+        if (CollectionUtils.isEmpty(principals)) {
             log.trace("No identity (PrincipalCollection) found in the context.  Looking for a remembered identity.");
-            PrincipalCollection principals = getRememberedIdentity(context);
-            if (principals != null) {
+            principals = getRememberedIdentity(context);
+            if (!CollectionUtils.isEmpty(principals)) {
                 log.debug("Found remembered PrincipalCollection.  Adding to the context to be used " +
                         "for subject construction by the SubjectFactory.");
-                context.put(SubjectFactory.PRINCIPALS, principals);
+                context.setPrincipals(principals);
             } else {
                 log.trace("No remembered identity found.  Returning original context.");
             }
@@ -508,19 +483,17 @@ public class DefaultSecurityManager exte
     }
 
     /**
-     * Utility method to retrieve the session id from the given subject context Map which will be used to resolve
-     * to a {@link Session} or {@code null} if there is no session id in the map.  If the session id exists, it is
-     * expected to be available in the map under the
-     * <code>{@link SubjectFactory SubjectFactory}.{@link SubjectFactory#SESSION_ID SESSION_ID}</code> constant.
+     * Utility method to retrieve the session id from the given subject context which will be used to resolve
+     * to a {@link Session}, or {@code null} if there is no session id available.
      *
      * @param subjectContext the context map with data that will be used to construct a {@link Subject} instance via
      *                       a {@link SubjectFactory}
      * @return a session id to resolve to a {@link Session} instance or {@code null} if a session id could not be found.
-     * @see #createSubject(java.util.Map)
-     * @see SubjectFactory#createSubject(java.util.Map)
+     * @see SecurityManager#createSubject(SubjectContext)
+     * @see SubjectFactory#createSubject(SubjectContext)
      */
-    protected Serializable getSessionId(Map subjectContext) {
-        return (Serializable) subjectContext.get(SubjectFactory.SESSION_ID);
+    protected Serializable getSessionId(SubjectContext subjectContext) {
+        return subjectContext.getSessionId();
     }
 
     public void logout(Subject subject) {
@@ -598,12 +571,12 @@ public class DefaultSecurityManager exte
     protected void unbind(Subject subject) {
         Session session = subject.getSession(false);
         if (session != null) {
-            session.removeAttribute(SubjectFactory.PRINCIPALS_SESSION_KEY);
-            session.removeAttribute(SubjectFactory.AUTHENTICATED_SESSION_KEY);
+            session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
+            session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
         }
     }
 
-    protected PrincipalCollection getRememberedIdentity(Map subjectContext) {
+    protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext) {
         RememberMeManager rmm = getRememberMeManager();
         if (rmm != null) {
             try {

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java Thu Apr 22 22:49:21 2010
@@ -18,23 +18,15 @@
  */
 package org.apache.shiro.mgt;
 
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.HostAuthenticationToken;
 import org.apache.shiro.session.Session;
-import org.apache.shiro.subject.DelegatingSubject;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.Subject;
-import org.apache.shiro.util.CollectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Map;
+import org.apache.shiro.subject.SubjectContext;
+import org.apache.shiro.subject.support.DelegatingSubject;
 
 
 /**
- * Default {@link SubjectFactory SubjectFactory} implementation that creates {@link DelegatingSubject DelegatingSubject}
+ * Default {@link SubjectFactory SubjectFactory} implementation that creates {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject}
  * instances.
  *
  * @author Les Hazlewood
@@ -42,132 +34,15 @@ import java.util.Map;
  */
 public class DefaultSubjectFactory implements SubjectFactory {
 
-    private static transient final Logger log = LoggerFactory.getLogger(DefaultSubjectFactory.class);
-
     public DefaultSubjectFactory() {
     }
 
-    @SuppressWarnings({"unchecked"})
-    protected static <E> E getTypedValue(Map context, String key, Class<E> type) {
-        E found = null;
-        Object o = context.get(key);
-        if (o != null) {
-            if (!type.isAssignableFrom(o.getClass())) {
-                String msg = "Invalid object found in context Map under key [" + key + "].  Expected type " +
-                        "was [" + type.getName() + "], but the object under that key is of type " +
-                        "[" + o.getClass().getName() + "].";
-                throw new IllegalArgumentException(msg);
-            }
-            found = (E) o;
-        }
-        return found;
-    }
-
-    protected SecurityManager getSecurityManager(Map context) {
-        SecurityManager securityManager = getTypedValue(context, SubjectFactory.SECURITY_MANAGER, SecurityManager.class);
-        if (securityManager == null) {
-            if (log.isDebugEnabled()) {
-                log.debug("No SecurityManager available in subject context map.  " +
-                        "Falling back to SecurityUtils.getSecurityManager() lookup.");
-            }
-            securityManager = SecurityUtils.getSecurityManager();
-        }
-        if (securityManager == null) {
-            String msg = "No " + SecurityManager.class.getName() + " instance was available in the subject context " +
-                    "via the " + SubjectFactory.SECURITY_MANAGER + " key.  " +
-                    "This is required for this " + SubjectFactory.class.getSimpleName() + " implementation to " +
-                    "function.";
-            throw new IllegalStateException(msg);
-        }
-        return securityManager;
-    }
-
-    protected PrincipalCollection getPrincipals(Map context, Session session) {
-        PrincipalCollection principals = getTypedValue(context, SubjectFactory.PRINCIPALS, PrincipalCollection.class);
-
-        if (CollectionUtils.isEmpty(principals)) {
-            //check to see if they were just authenticated:
-            AuthenticationInfo info = getTypedValue(context, SubjectFactory.AUTHENTICATION_INFO, AuthenticationInfo.class);
-            if (info != null) {
-                principals = info.getPrincipals();
-            }
-        }
-
-        if (CollectionUtils.isEmpty(principals)) {
-            Subject subject = getTypedValue(context, SubjectFactory.SUBJECT, Subject.class);
-            if (subject != null) {
-                principals = subject.getPrincipals();
-            }
-        }
-
-        if (CollectionUtils.isEmpty(principals)) {
-            //try the session:
-            if (session != null) {
-                principals = (PrincipalCollection) session.getAttribute(SubjectFactory.PRINCIPALS_SESSION_KEY);
-            }
-        }
-
-        return principals;
-    }
-
-    protected Session getSession(Map context) {
-        Session session = getTypedValue(context, SubjectFactory.SESSION, Session.class);
-
-        if (session == null) {
-            //try the Subject if it exists:
-            Subject existingSubject = getTypedValue(context, SubjectFactory.SUBJECT, Subject.class);
-            if (existingSubject != null) {
-                session = existingSubject.getSession(false);
-            }
-        }
-
-        return session;
-    }
-
-    protected String getHost(Map context, Session session) {
-        String host = getTypedValue(context, SubjectFactory.HOST, String.class);
-
-        if (host == null) {
-            //check to see if there is an AuthenticationToken from which to retrieve it:
-            AuthenticationToken token = getTypedValue(context, SubjectFactory.AUTHENTICATION_TOKEN, AuthenticationToken.class);
-            if (token instanceof HostAuthenticationToken) {
-                host = ((HostAuthenticationToken) token).getHost();
-            }
-        }
-
-        if (host == null) {
-            if (session != null) {
-                host = session.getHost();
-            }
-        }
-
-        return host;
-    }
-
-    protected boolean isAuthenticated(Map context, Session session) {
-        Boolean authc = getTypedValue(context, SubjectFactory.AUTHENTICATED, Boolean.class);
-        if (authc == null) {
-            //see if there is an AuthenticationInfo object.  If so, the very presence of one indicates a successful
-            //authentication attempt:
-            AuthenticationInfo info = getTypedValue(context, SubjectFactory.AUTHENTICATION_INFO, AuthenticationInfo.class);
-            authc = info != null;
-        }
-        if (!authc) {
-            //fall back to a session check:
-            if (session != null) {
-                Boolean sessionAuthc = (Boolean) session.getAttribute(SubjectFactory.AUTHENTICATED_SESSION_KEY);
-                authc = sessionAuthc != null && sessionAuthc;
-            }
-        }
-        return authc;
-    }
-
-    public Subject createSubject(Map context) {
-        SecurityManager securityManager = getSecurityManager(context);
-        Session session = getSession(context);
-        PrincipalCollection principals = getPrincipals(context, session);
-        boolean authenticated = isAuthenticated(context, session);
-        String host = getHost(context, session);
+    public Subject createSubject(SubjectContext context) {
+        SecurityManager securityManager = context.resolveSecurityManager();
+        Session session = context.resolveSession();
+        PrincipalCollection principals = context.resolvePrincipals();
+        boolean authenticated = context.resolveAuthenticated();
+        String host = context.resolveHost();
         return newSubjectInstance(principals, authenticated, host, session, securityManager);
     }
 

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/RememberMeManager.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/RememberMeManager.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/RememberMeManager.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/RememberMeManager.java Thu Apr 22 22:49:21 2010
@@ -23,8 +23,7 @@ import org.apache.shiro.authc.Authentica
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.Subject;
-
-import java.util.Map;
+import org.apache.shiro.subject.SubjectContext;
 
 /**
  * A RememberMeManager is responsible for remembering a Subject's identity across that Subject's sessions with
@@ -47,7 +46,7 @@ public interface RememberMeManager {
      * @return he remembered principals or {@code null} if none could be acquired.
      * @since 1.0
      */
-    PrincipalCollection getRememberedPrincipals(Map subjectContext);
+    PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext);
 
     /**
      * Reacts to a successful authentication attempt, typically saving the principals to be retrieved ('remembered')

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SecurityManager.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SecurityManager.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SecurityManager.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SecurityManager.java Thu Apr 22 22:49:21 2010
@@ -24,8 +24,7 @@ import org.apache.shiro.authc.Authentica
 import org.apache.shiro.authz.Authorizer;
 import org.apache.shiro.session.mgt.SessionManager;
 import org.apache.shiro.subject.Subject;
-
-import java.util.Map;
+import org.apache.shiro.subject.SubjectContext;
 
 
 /**
@@ -47,13 +46,8 @@ import java.util.Map;
  * <p/>
  * <b>Usage Note</b>: In actuality the large majority of application programmers won't interact with a SecurityManager
  * very often, if at all.  <em>Most</em> application programmers only care about security operations for the currently
- * executing user.
- * <p/>
- * In that case, the application programmer can call the
- * {@link #getSubject() getSubject()} method and then use that returned instance for continued interaction with
- * Shiro.  If your application code does not have a direct handle to the application's
- * {@code SecurityManager}, you can use {@link org.apache.shiro.SecurityUtils SecurityUtils} anywhere in your code
- * to achieve the same result.
+ * executing user, usually attained by calling
+ * {@link org.apache.shiro.SecurityUtils#getSubject() SecurityUtils.getSubject()}.
  * <p/>
  * Framework developers on the other hand might find working with an actual SecurityManager useful.
  *
@@ -62,6 +56,7 @@ import java.util.Map;
  * @since 0.2
  */
 public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
+
     /**
      * Logs in the specified Subject using the given {@code authenticationToken}, returning an updated Subject
      * instance reflecting the authenticated state if successful or throwing {@code AuthenticationException} if it is
@@ -93,7 +88,6 @@ public interface SecurityManager extends
      * Framework developers on the other hand might find calling this method directly useful in certain cases.
      *
      * @param subject the subject to log out.
-     * @see #getSubject()
      * @since 1.0
      */
     void logout(Subject subject);
@@ -103,20 +97,19 @@ public interface SecurityManager extends
      * <p/>
      * The context can be anything needed by this {@code SecurityManager} to construct a {@code Subject} instance.
      * Most Shiro end-users will never call this method - it exists primarily for
-     * framework development and to support any underlying {@link SubjectFactory SubjectFactory} implementations that
-     * may be configured to be used by the {@code SecurityManager}.
+     * framework development and to support any underlying custom {@link SubjectFactory SubjectFactory} implementations
+     * that may be used by the {@code SecurityManager}.
      * <h4>Usage</h4>
-     * The difference between calling this method and {@link #getSubject() getSubject()} is that the {@code Subject}
-     * instance returned from this method is not automatically 'bound' to the application
-     * for further use.  That is, after calling this method, a call to {@code getSubject()} will not necessarily return
-     * the same instance.  Callers are expected to know that {@code Subject} instances have local scope only and any
-     * other further use beyond the calling method must be managed manually.
+     * After calling this method, the returned instance is <em>not</em> bound to the application for further use.
+     * Callers are expected to know that {@code Subject} instances have local scope only and any
+     * other further use beyond the calling method must be managed explicitly.
      *
      * @param context any data needed to direct how the Subject should be constructed.
      * @return the {@code Subject} instance reflecting the specified initialization data.
-     * @see SubjectFactory#createSubject(java.util.Map)
+     * @see SubjectFactory#createSubject(SubjectContext)
+     * @see Subject.Builder
      * @since 1.0
      */
-    Subject createSubject(Map context);
+    Subject createSubject(SubjectContext context);
 
 }

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SubjectFactory.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SubjectFactory.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SubjectFactory.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/SubjectFactory.java Thu Apr 22 22:49:21 2010
@@ -19,6 +19,7 @@
 package org.apache.shiro.mgt;
 
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.SubjectContext;
 
 import java.util.Map;
 
@@ -30,55 +31,16 @@ import java.util.Map;
  */
 public interface SubjectFactory {
 
-    public static final String SECURITY_MANAGER = SubjectFactory.class.getName() + ".SECURITY_MANAGER";
-
-    public static final String SESSION_ID = SubjectFactory.class.getName() + ".SESSION_ID";
-
-    public static final String AUTHENTICATION_TOKEN = SubjectFactory.class.getName() + ".AUTHENTICATION_TOKEN";
-
-    public static final String AUTHENTICATION_INFO = SubjectFactory.class.getName() + ".AUTHENTICATION_INFO";
-
-    public static final String SUBJECT = SubjectFactory.class.getName() + ".SUBJECT";
-
-    public static final String PRINCIPALS = SubjectFactory.class.getName() + ".PRINCIPALS";
-
-    public static final String SESSION = SubjectFactory.class.getName() + ".SESSION";
-
-    public static final String AUTHENTICATED = SubjectFactory.class.getName() + ".AUTHENTICATED";
-
-    public static final String HOST = SubjectFactory.class.getName() + ".HOST";
-    
-    /**
-     * The session key that is used to store subject principals.
-     */
-    public static final String PRINCIPALS_SESSION_KEY = SubjectFactory.class.getName() + "_PRINCIPALS_SESSION_KEY";
-
-    /**
-     * The session key that is used to store whether or not the user is authenticated.
-     */
-    public static final String AUTHENTICATED_SESSION_KEY = SubjectFactory.class.getName() + "_AUTHENTICATED_SESSION_KEY";
-    
-
-    /**
-     * @deprecated use the {@link #HOST HOST} key instead.  This will be removed prior to 1.0.
-     */
-    @Deprecated
-    public static final String INET_ADDRESS = HOST;
-
-    public static final String SERVLET_REQUEST = SubjectFactory.class.getName() + ".SERVLET_REQUEST";
-
-    public static final String SERVLET_RESPONSE = SubjectFactory.class.getName() + ".SERVLET_RESPONSE";
-
     /**
      * Creates a new Subject instance reflecting the state of the specified contextual data.  The data would be
      * anything required to required to construct a {@code Subject} instance and its contents can vary based on
-     * environment.  Any data supported by Shiro core will be keyed by one of the static String constants
-     * in this class.
+     * environment.  Any data supported by Shiro core will be accessible by one of the accessor methods.  All other
+     * data is available as map {@link Map#get attribute}s.
      *
      * @param context the contextual data to be used by the implementation to construct an appropriate {@code Subject}
      *                instance.
      * @return a {@code Subject} instance created based on the specified context.
      */
-    Subject createSubject(Map context);
+    Subject createSubject(SubjectContext context);
 
 }

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java Thu Apr 22 22:49:21 2010
@@ -26,13 +26,13 @@ import org.apache.shiro.authz.Permission
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.mgt.SubjectFactory;
 import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.support.DefaultSubjectContext;
+import org.apache.shiro.util.CollectionUtils;
 import org.apache.shiro.util.StringUtils;
 
 import java.io.Serializable;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.Callable;
 
 /**
@@ -549,7 +549,7 @@ public interface Subject {
          * Hold all contextual data via the Builder instance's method invocations to be sent to the
          * {@code SecurityManager} during the {@link #buildSubject} call.
          */
-        private final Map<String, Object> subjectContext;
+        private final SubjectContext subjectContext;
 
         /**
          * The SecurityManager to invoke during the {@link #buildSubject} call.
@@ -576,7 +576,22 @@ public interface Subject {
                 throw new NullPointerException("SecurityManager method argument cannot be null.");
             }
             this.securityManager = securityManager;
-            this.subjectContext = new HashMap<String, Object>();
+            this.subjectContext = newSubjectContextInstance();
+            if (this.subjectContext == null) {
+                throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
+                        "cannot be null.");
+            }
+            this.subjectContext.setSecurityManager(securityManager);
+        }
+
+        /**
+         * Creates a new {@code SubjectContext} instance to be used to populate with subject contextual data that
+         * will then be sent to the {@code SecurityManager} to create a new {@code Subject} instance.
+         *
+         * @return a new {@code SubjectContext} instance
+         */
+        protected SubjectContext newSubjectContextInstance() {
+            return new DefaultSubjectContext();
         }
 
         /**
@@ -585,7 +600,7 @@ public interface Subject {
          *
          * @return the backing context used to build the {@code Subject} instance, available to subclasses.
          */
-        protected Map<String, Object> getSubjectContext() {
+        protected SubjectContext getSubjectContext() {
             return this.subjectContext;
         }
 
@@ -618,7 +633,7 @@ public interface Subject {
          */
         public Builder sessionId(Serializable sessionId) {
             if (sessionId != null) {
-                this.subjectContext.put(SubjectFactory.SESSION_ID, sessionId);
+                this.subjectContext.setSessionId(sessionId);
             }
             return this;
         }
@@ -632,20 +647,22 @@ public interface Subject {
          */
         public Builder host(String host) {
             if (StringUtils.hasText(host)) {
-                this.subjectContext.put(SubjectFactory.HOST, host);
+                this.subjectContext.setHost(host);
             }
             return this;
         }
 
         /**
-         * Ensures the {@code Subject} being built will use the specified {@link Session} instance.
+         * Ensures the {@code Subject} being built will use the specified {@link Session} instance.  Note that it is
+         * more common to use the {@link #sessionId sessionId} builder method rather than having to construct a
+         * {@code Session} instance for this method.
          *
          * @param session the session to use as the {@code Subject}'s {@link Session}
          * @return this {@code Builder} instance for method chaining.
          */
         public Builder session(Session session) {
             if (session != null) {
-                this.subjectContext.put(SubjectFactory.SESSION, session);
+                this.subjectContext.setSession(session);
             }
             return this;
         }
@@ -675,8 +692,8 @@ public interface Subject {
          * @return this {@code Builder} instance for method chaining.
          */
         public Builder principals(PrincipalCollection principals) {
-            if (principals != null && !principals.isEmpty()) {
-                this.subjectContext.put(SubjectFactory.PRINCIPALS, principals);
+            if (!CollectionUtils.isEmpty(principals)) {
+                this.subjectContext.setPrincipals(principals);
             }
             return this;
         }
@@ -693,7 +710,7 @@ public interface Subject {
          * @see org.apache.shiro.subject.Subject#isAuthenticated()
          */
         public Builder authenticated(boolean authenticated) {
-            this.subjectContext.put(SubjectFactory.AUTHENTICATED, authenticated);
+            this.subjectContext.setAuthenticated(authenticated);
             return this;
         }
 
@@ -709,19 +726,18 @@ public interface Subject {
          * {@code SubjectFactory} implementation can use when building custom Subject instances. As such, this method
          * is only useful when a custom {@code SubjectFactory} implementation has been configured.
          *
-         * @see SubjectFactory#createSubject(java.util.Map)
-         *
-         * @param attributeKey the key under which the corresponding value will be stored in the context {@code Map}.
+         * @param attributeKey   the key under which the corresponding value will be stored in the context {@code Map}.
          * @param attributeValue the value to store in the context map under the specified {@code attributeKey}.
          * @return this {@code Builder} instance for method chaining.
          * @throws IllegalArgumentException if the {@code attributeKey} is {@code null}.
+         * @see SubjectFactory#createSubject(SubjectContext)
          */
         public Builder contextAttribute(String attributeKey, Object attributeValue) {
             if (attributeKey == null) {
                 String msg = "Subject context map key cannot be null.";
                 throw new IllegalArgumentException(msg);
             }
-            if (attributeValue == null ) {
+            if (attributeValue == null) {
                 this.subjectContext.remove(attributeKey);
             } else {
                 this.subjectContext.put(attributeKey, attributeValue);

Added: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/SubjectContext.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/SubjectContext.java?rev=937094&view=auto
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/SubjectContext.java (added)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/SubjectContext.java Thu Apr 22 22:49:21 2010
@@ -0,0 +1,218 @@
+/*
+ * 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.apache.shiro.subject;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.session.Session;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * A {@code SubjectContext} is a 'bucket' of data presented to a {@link SecurityManager} which interprets
+ * this data to construct {@link org.apache.shiro.subject.Subject Subject} instances.  It is essentially a Map of data
+ * with a few additional type-safe methods for easy retrieval of objects commonly used to construct Subject instances.
+ * <p/>
+ * While this interface contains type-safe setters and getters for common data types, the map can contain anything
+ * additional that might be needed by the {@link SecurityManager} or
+ * {@link org.apache.shiro.mgt.SubjectFactory SubjectFactory} implementation to construct {@code Subject} instances.
+ * <h2>Data Resolution</h2>
+ * The {@link SubjectContext} interface also allows for heuristic resolution of data used to construct a subject
+ * instance.  That is, if an attribute has not been explicitly provided via a setter method, the {@code resolve*}
+ * methods can use heuristics to obtain that data in another way from other attributes.
+ * <p/>
+ * For example, if one calls {@link #getPrincipals()} and no principals are returned, perhaps the principals exist
+ * in the {@link #getSession() session} or another attribute in the context.  The {@link #resolvePrincipals()} will know
+ * how to resolve the principals based on heuristics.  If the {@code resolve*} methods return {@code null} then the
+ * data could not be achieved by any heuristics and must be considered as not available in the context.
+ * <p/>
+ * The general idea is that the normal getters can be called to see if the value was explicitly set.  The
+ * {@code resolve*} methods should be used when actually constructing the {@code Subject} instance to ensure the most
+ * specific/accurate data can be used.
+ *
+ * <b>USAGE</b>: Most Shiro end-users will never use a {@code SubjectContext} instance directly and instead will use a
+ * {@link Subject.Builder} (which internally uses a {@code SubjectContext}) and build {@code Subject} instances that
+ * way.
+ *
+ * @author Les Hazlewood
+ * @see org.apache.shiro.mgt.SecurityManager#createSubject SecurityManager.createSubject
+ * @see org.apache.shiro.mgt.SubjectFactory SubjectFactory
+ * @since 1.0
+ */
+public interface SubjectContext extends Map<String, Object> {
+
+    /**
+     * Returns the SecurityManager instance that should be used to back the constructed {@link Subject} instance or
+     * {@code null} if one has not yet been provided to this context.
+     *
+     * @return the SecurityManager instance that should be used to back the constructed {@link Subject} instance or
+     *         {@code null} if one has not yet been provided to this context.
+     */
+    SecurityManager getSecurityManager();
+
+    /**
+     * Sets the SecurityManager instance that should be used to back the constructed {@link Subject} instance
+     * (typically used to support {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject} implementations).
+     *
+     * @param securityManager the SecurityManager instance that should be used to back the constructed {@link Subject}
+     *                        instance.
+     */
+    void setSecurityManager(SecurityManager securityManager);
+
+    /**
+     * Resolves the {@code SecurityManager} instance that should be used to back the constructed {@link Subject}
+     * instance (typically used to support {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject} implementations).
+     *
+     * @return the {@code SecurityManager} instance that should be used to back the constructed {@link Subject}
+     *         instance
+     */
+    SecurityManager resolveSecurityManager();
+
+    /**
+     * Returns the session id of the session that should be associated with the constructed {@link Subject} instance.
+     * <p/>
+     * The construction process is expected to resolve the session with the specified id and then construct the Subject
+     * instance based on the resolved session.
+     *
+     * @return the session id of the session that should be associated with the constructed {@link Subject} instance.
+     */
+    Serializable getSessionId();
+
+    /**
+     * Sets the session id of the session that should be associated with the constructed {@link Subject} instance.
+     * <p/>
+     * The construction process is expected to resolve the session with the specified id and then construct the Subject
+     * instance based on the resolved session.
+     *
+     * @param sessionId the session id of the session that should be associated with the constructed {@link Subject}
+     *                  instance.
+     */
+    void setSessionId(Serializable sessionId);
+
+    /**
+     * Returns any existing {@code Subject} that may be in use at the time the new {@code Subject} instance is
+     * being created.
+     * <p/>
+     * This is typically used in the case where the existing {@code Subject} instance returned by
+     * this method is unauthenticated and a new {@code Subject} instance is being created to reflect a successful
+     * authentication - you want to return most of the state of the previous {@code Subject} instance when creating the
+     * newly authenticated instance.
+     *
+     * @return any existing {@code Subject} that may be in use at the time the new {@code Subject} instance is
+     *         being created.
+     */
+    Subject getSubject();
+
+    /**
+     * Sets the existing {@code Subject} that may be in use at the time the new {@code Subject} instance is
+     * being created.
+     * <p/>
+     * This is typically used in the case where the existing {@code Subject} instance returned by
+     * this method is unauthenticated and a new {@code Subject} instance is being created to reflect a successful
+     * authentication - you want to return most of the state of the previous {@code Subject} instance when creating the
+     * newly authenticated instance.
+     *
+     * @param subject the existing {@code Subject} that may be in use at the time the new {@code Subject} instance is
+     *                being created.
+     */
+    void setSubject(Subject subject);
+
+    /**
+     * Returns the principals (aka identity) that the constructed {@code Subject} should reflect.
+     *
+     * @return the principals (aka identity) that the constructed {@code Subject} should reflect.
+     */
+    PrincipalCollection getPrincipals();
+
+    PrincipalCollection resolvePrincipals();
+
+    /**
+     * Sets the principals (aka identity) that the constructed {@code Subject} should reflect.
+     *
+     * @param principals the principals (aka identity) that the constructed {@code Subject} should reflect.
+     */
+    void setPrincipals(PrincipalCollection principals);
+
+    /**
+     * Returns the {@code Session} to use when building the {@code Subject} instance.  Note that it is more
+     * common to specify a {@link #setSessionId sessionId} to acquire the desired session rather than having to
+     * construct a {@code Session} to be returned by this method.
+     *
+     * @return the {@code Session} to use when building the {@code Subject} instance.
+     */
+    Session getSession();
+
+    /**
+     * Sets the {@code Session} to use when building the {@code Subject} instance.  Note that it is more
+     * common to specify a {@link #setSessionId sessionId} to automatically resolve the desired session rather than
+     * constructing a {@code Session} to call this method.
+     *
+     * @param session the {@code Session} to use when building the {@code Subject} instance.
+     */
+    void setSession(Session session);
+
+    Session resolveSession();
+
+    /**
+     * Returns {@code true} if the constructed {@code Subject} should be considered authenticated, {@code false}
+     * otherwise.  Be careful setting this value to {@code true} - you should know what you are doing and have a good
+     * reason for ignoring Shiro's default authentication state mechanisms.
+     *
+     * @return {@code true} if the constructed {@code Subject} should be considered authenticated, {@code false}
+     *         otherwise.
+     */
+    boolean isAuthenticated();
+
+    /**
+     * Sets whether or not the constructed {@code Subject} instance should be considered as authenticated.  Be careful
+     * when specifying {@code true} - you should know what you are doing and have a good reason for ignoring Shiro's
+     * default authentication state mechanisms.
+     *
+     * @param authc whether or not the constructed {@code Subject} instance should be considered as authenticated.
+     */
+    void setAuthenticated(boolean authc);
+
+    boolean resolveAuthenticated();
+
+    AuthenticationInfo getAuthenticationInfo();
+
+    void setAuthenticationInfo(AuthenticationInfo info);
+
+    AuthenticationToken getAuthenticationToken();
+
+    void setAuthenticationToken(AuthenticationToken token);
+
+    /**
+     * Returns the host name or IP that should reflect the constructed {@code Subject}'s originating location.
+     *
+     * @return the host name or IP that should reflect the constructed {@code Subject}'s originating location.
+     */
+    String getHost();
+
+    /**
+     * Sets the host name or IP that should reflect the constructed {@code Subject}'s originating location.
+     *
+     * @param host the host name or IP that should reflect the constructed {@code Subject}'s originating location.
+     */
+    void setHost(String host);
+
+    String resolveHost();
+}

Added: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java?rev=937094&view=auto
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java (added)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java Thu Apr 22 22:49:21 2010
@@ -0,0 +1,347 @@
+/*
+ * 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.apache.shiro.subject.support;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.UnavailableSecurityManagerException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.HostAuthenticationToken;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.SubjectContext;
+import org.apache.shiro.util.CollectionUtils;
+import org.apache.shiro.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Default implementation of the {@link SubjectContext} interface.  Note that the getters and setters are not
+ * simple pass-through methods to an underlying attribute;  the getters will employ numerous heuristics to acquire
+ * their data attribute as best as possible (for example, if {@link #getPrincipals} is invoked, if the principals aren't
+ * in the backing map, it might check to see if there is a subject or session in the map and attempt to acquire the
+ * principals from those objects).
+ *
+ * @author Les Hazlewood
+ * @since 1.0
+ */
+public class DefaultSubjectContext implements SubjectContext {
+
+    private static final String SECURITY_MANAGER = DefaultSubjectContext.class.getName() + ".SECURITY_MANAGER";
+
+    private static final String SESSION_ID = DefaultSubjectContext.class.getName() + ".SESSION_ID";
+
+    private static final String AUTHENTICATION_TOKEN = DefaultSubjectContext.class.getName() + ".AUTHENTICATION_TOKEN";
+
+    private static final String AUTHENTICATION_INFO = DefaultSubjectContext.class.getName() + ".AUTHENTICATION_INFO";
+
+    private static final String SUBJECT = DefaultSubjectContext.class.getName() + ".SUBJECT";
+
+    private static final String PRINCIPALS = DefaultSubjectContext.class.getName() + ".PRINCIPALS";
+
+    private static final String SESSION = DefaultSubjectContext.class.getName() + ".SESSION";
+
+    private static final String AUTHENTICATED = DefaultSubjectContext.class.getName() + ".AUTHENTICATED";
+
+    private static final String HOST = DefaultSubjectContext.class.getName() + ".HOST";
+
+    /**
+     * The session key that is used to store subject principals.
+     */
+    public static final String PRINCIPALS_SESSION_KEY = DefaultSubjectContext.class.getName() + "_PRINCIPALS_SESSION_KEY";
+
+    /**
+     * The session key that is used to store whether or not the user is authenticated.
+     */
+    public static final String AUTHENTICATED_SESSION_KEY = DefaultSubjectContext.class.getName() + "_AUTHENTICATED_SESSION_KEY";
+
+    private static final transient Logger log = LoggerFactory.getLogger(DefaultSubjectContext.class);
+
+    private final Map<String, Object> backingMap;
+
+    public DefaultSubjectContext() {
+        this.backingMap = new HashMap<String, Object>();
+    }
+
+    public DefaultSubjectContext(SubjectContext ctx) {
+        this();
+        if (!CollectionUtils.isEmpty(ctx)) {
+            this.backingMap.putAll(ctx);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected <E> E getTypedValue(String key, Class<E> type) {
+        E found = null;
+        Object o = backingMap.get(key);
+        if (o != null) {
+            if (!type.isAssignableFrom(o.getClass())) {
+                String msg = "Invalid object found in SubjectContext Map under key [" + key + "].  Expected type " +
+                        "was [" + type.getName() + "], but the object under that key is of type " +
+                        "[" + o.getClass().getName() + "].";
+                throw new IllegalArgumentException(msg);
+            }
+            found = (E) o;
+        }
+        return found;
+    }
+
+    public SecurityManager getSecurityManager() {
+        return getTypedValue(SECURITY_MANAGER, SecurityManager.class);
+    }
+
+    public void setSecurityManager(SecurityManager securityManager) {
+        if (securityManager != null) {
+            put(SECURITY_MANAGER, securityManager);
+        }
+    }
+
+    public SecurityManager resolveSecurityManager() {
+        SecurityManager securityManager = getSecurityManager();
+        if (securityManager == null) {
+            if (log.isDebugEnabled()) {
+                log.debug("No SecurityManager available in subject context map.  " +
+                        "Falling back to SecurityUtils.getSecurityManager() lookup.");
+            }
+            try {
+                securityManager = SecurityUtils.getSecurityManager();
+            } catch (UnavailableSecurityManagerException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("No SecurityManager available via SecurityUtils.  Heuristics exhausted.", e);
+                }
+            }
+        }
+        return securityManager;
+    }
+
+    public Serializable getSessionId() {
+        return getTypedValue(SESSION_ID, Serializable.class);
+    }
+
+    public void setSessionId(Serializable sessionId) {
+        if (sessionId != null) {
+            put(SESSION_ID, sessionId);
+        }
+    }
+
+    public Subject getSubject() {
+        return getTypedValue(SUBJECT, Subject.class);
+    }
+
+    public void setSubject(Subject subject) {
+        if (subject != null) {
+            put(SUBJECT, subject);
+        }
+    }
+
+    public PrincipalCollection getPrincipals() {
+        return getTypedValue(PRINCIPALS, PrincipalCollection.class);
+    }
+
+    public void setPrincipals(PrincipalCollection principals) {
+        if (!CollectionUtils.isEmpty(principals)) {
+            put(PRINCIPALS, principals);
+        }
+    }
+
+    public PrincipalCollection resolvePrincipals() {
+        PrincipalCollection principals = getPrincipals();
+
+        if (CollectionUtils.isEmpty(principals)) {
+            //check to see if they were just authenticated:
+            AuthenticationInfo info = getAuthenticationInfo();
+            if (info != null) {
+                principals = info.getPrincipals();
+            }
+        }
+
+        if (CollectionUtils.isEmpty(principals)) {
+            Subject subject = getSubject();
+            if (subject != null) {
+                principals = subject.getPrincipals();
+            }
+        }
+
+        if (CollectionUtils.isEmpty(principals)) {
+            //try the session:
+            Session session = resolveSession();
+            if (session != null) {
+                principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
+            }
+        }
+
+        return principals;
+    }
+
+
+    public Session getSession() {
+        return getTypedValue(SESSION, Session.class);
+    }
+
+    public void setSession(Session session) {
+        if (session != null) {
+            put(SESSION, session);
+        }
+    }
+
+    public Session resolveSession() {
+        Session session = getSession();
+        if (session == null) {
+            //try the Subject if it exists:
+            Subject existingSubject = getSubject();
+            if (existingSubject != null) {
+                session = existingSubject.getSession(false);
+            }
+        }
+        return session;
+    }
+
+    public boolean isAuthenticated() {
+        Boolean authc = getTypedValue(AUTHENTICATED, Boolean.class);
+        return authc != null && authc;
+    }
+
+    public void setAuthenticated(boolean authc) {
+        put(AUTHENTICATED, authc);
+    }
+
+    public boolean resolveAuthenticated() {
+        Boolean authc = getTypedValue(AUTHENTICATED, Boolean.class);
+        if (authc == null) {
+            //see if there is an AuthenticationInfo object.  If so, the very presence of one indicates a successful
+            //authentication attempt:
+            AuthenticationInfo info = getAuthenticationInfo();
+            authc = info != null;
+        }
+        if (!authc) {
+            //fall back to a session check:
+            Session session = resolveSession();
+            if (session != null) {
+                Boolean sessionAuthc = (Boolean) session.getAttribute(AUTHENTICATED_SESSION_KEY);
+                authc = sessionAuthc != null && sessionAuthc;
+            }
+        }
+
+        return authc;
+    }
+
+    public AuthenticationInfo getAuthenticationInfo() {
+        return getTypedValue(AUTHENTICATION_INFO, AuthenticationInfo.class);
+    }
+
+    public void setAuthenticationInfo(AuthenticationInfo info) {
+        if (info != null) {
+            put(AUTHENTICATION_INFO, info);
+        }
+    }
+
+    public AuthenticationToken getAuthenticationToken() {
+        return getTypedValue(AUTHENTICATION_TOKEN, AuthenticationToken.class);
+    }
+
+    public void setAuthenticationToken(AuthenticationToken token) {
+        if (token != null) {
+            put(AUTHENTICATION_TOKEN, token);
+        }
+    }
+
+    public String getHost() {
+        return getTypedValue(HOST, String.class);
+    }
+
+    public void setHost(String host) {
+        if (StringUtils.hasText(host)) {
+            put(HOST, host);
+        }
+    }
+
+    public String resolveHost() {
+        String host = getHost();
+
+        if (host == null) {
+            //check to see if there is an AuthenticationToken from which to retrieve it:
+            AuthenticationToken token = getAuthenticationToken();
+            if (token instanceof HostAuthenticationToken) {
+                host = ((HostAuthenticationToken) token).getHost();
+            }
+        }
+
+        if (host == null) {
+            Session session = resolveSession();
+            if (session != null) {
+                host = session.getHost();
+            }
+        }
+
+        return host;
+    }
+
+    public int size() {
+        return backingMap.size();
+    }
+
+    public boolean isEmpty() {
+        return backingMap.isEmpty();
+    }
+
+    public boolean containsKey(Object o) {
+        return backingMap.containsKey(o);
+    }
+
+    public boolean containsValue(Object o) {
+        return backingMap.containsValue(o);
+    }
+
+    public Object get(Object o) {
+        return backingMap.get(o);
+    }
+
+    public Object put(String s, Object o) {
+        return backingMap.put(s, o);
+    }
+
+    public Object remove(Object o) {
+        return backingMap.remove(o);
+    }
+
+    public void putAll(Map<? extends String, ?> map) {
+        backingMap.putAll(map);
+    }
+
+    public void clear() {
+        backingMap.clear();
+    }
+
+    public Set<String> keySet() {
+        return Collections.unmodifiableSet(backingMap.keySet());
+    }
+
+    public Collection<Object> values() {
+        return Collections.unmodifiableCollection(backingMap.values());
+    }
+
+    public Set<Entry<String, Object>> entrySet() {
+        return Collections.unmodifiableSet(backingMap.entrySet());
+    }
+}

Copied: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java (from r935228, incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/DelegatingSubject.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java?p2=incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java&p1=incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/DelegatingSubject.java&r1=935228&r2=937094&rev=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/DelegatingSubject.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java Thu Apr 22 22:49:21 2010
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.subject;
+package org.apache.shiro.subject.support;
 
 import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationToken;
@@ -29,8 +29,9 @@ import org.apache.shiro.session.InvalidS
 import org.apache.shiro.session.ProxiedSession;
 import org.apache.shiro.session.Session;
 import org.apache.shiro.session.mgt.DelegatingSession;
-import org.apache.shiro.subject.support.SubjectCallable;
-import org.apache.shiro.subject.support.SubjectRunnable;
+import org.apache.shiro.subject.ExecutionException;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.Subject;
 import org.apache.shiro.util.CollectionUtils;
 import org.apache.shiro.util.ThreadContext;
 import org.slf4j.Logger;

Propchange: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java Thu Apr 22 22:49:21 2010
@@ -19,7 +19,6 @@
 package org.apache.shiro.subject.support;
 
 import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.subject.DelegatingSubject;
 import org.apache.shiro.subject.Subject;
 import org.apache.shiro.util.ThreadContext;
 import org.apache.shiro.util.ThreadState;

Modified: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java (original)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java Thu Apr 22 22:49:21 2010
@@ -20,11 +20,10 @@ package org.apache.shiro.mgt;
 
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.SubjectContext;
+import org.apache.shiro.subject.support.DefaultSubjectContext;
 import org.junit.Test;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import static org.junit.Assert.assertNull;
 
 /**
@@ -33,9 +32,9 @@ import static org.junit.Assert.assertNul
 public class AbstractRememberMeManagerTest {
 
     /**
-     * Tests the {@link AbstractRememberMeManager#getRememberedPrincipals(java.util.Map)} method
+     * Tests the {@link AbstractRememberMeManager#getRememberedPrincipals(SubjectContext)} method
      * implementation when the internal
-     * {@link AbstractRememberMeManager#getRememberedSerializedIdentity(java.util.Map)} method
+     * {@link AbstractRememberMeManager#getRememberedSerializedIdentity(SubjectContext)} method
      * returns null or empty bytes.
      */
     @Test
@@ -43,23 +42,23 @@ public class AbstractRememberMeManagerTe
         AbstractRememberMeManager rmm = new DummyRememberMeManager();
         //Since the dummy's getRememberedSerializedIdentity implementation returns an empty byte
         //array, we should be ok:
-        PrincipalCollection principals = rmm.getRememberedPrincipals(new HashMap());
+        PrincipalCollection principals = rmm.getRememberedPrincipals(new DefaultSubjectContext());
         assertNull(principals);
 
         //try with a null return value too:
         rmm = new DummyRememberMeManager() {
             @Override
-            protected byte[] getRememberedSerializedIdentity(Map subjectContext) {
+            protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {
                 return null;
             }
         };
-        principals = rmm.getRememberedPrincipals(new HashMap());
+        principals = rmm.getRememberedPrincipals(new DefaultSubjectContext());
         assertNull(principals);
     }
 
     private static class DummyRememberMeManager extends AbstractRememberMeManager {
         @Override
-        protected void forgetIdentity(Map subjectContext) {
+        protected void forgetIdentity(SubjectContext subjectContext) {
             //do nothing
         }
 
@@ -72,7 +71,7 @@ public class AbstractRememberMeManagerTe
         }
 
         @Override
-        protected byte[] getRememberedSerializedIdentity(Map subjectContext) {
+        protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {
             return new byte[0];
         }
     }

Modified: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java?rev=937094&r1=937093&r2=937094&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java (original)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java Thu Apr 22 22:49:21 2010
@@ -22,16 +22,18 @@ import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.mgt.DefaultSecurityManager;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.support.DelegatingSubject;
 import org.apache.shiro.util.ThreadContext;
-import static org.easymock.EasyMock.*;
 import org.junit.After;
-import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.io.Serializable;
 import java.util.concurrent.Callable;
 
+import static org.easymock.EasyMock.createNiceMock;
+import static org.junit.Assert.*;
+
 
 /**
  * @author Les Hazlewood