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 2012/05/05 07:31:36 UTC
svn commit: r1334318 - in /shiro/branches/SHIRO-317:
core/src/main/java/org/apache/shiro/mgt/
core/src/main/java/org/apache/shiro/session/mgt/
core/src/main/java/org/apache/shiro/subject/
core/src/main/java/org/apache/shiro/subject/support/ support/spr...
Author: lhazlewood
Date: Sat May 5 05:31:35 2012
New Revision: 1334318
URL: http://svn.apache.org/viewvc?rev=1334318&view=rev
Log:
Initial implementation of SHIRO-317 complete.
Added:
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/Flushable.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/FlushableSessionManager.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/UpdateDeferrable.java
Modified:
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionKey.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/Subject.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/SubjectContext.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java
shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
shiro/branches/SHIRO-317/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java
shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSecurityManager.java
shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSubjectFactory.java
shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/subject/support/WebDelegatingSubject.java
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java Sat May 5 05:31:35 2012
@@ -459,9 +459,13 @@ public class DefaultSecurityManager exte
}
protected SessionKey getSessionKey(SubjectContext context) {
+ DefaultSessionKey sessionKey = null;
Serializable sessionId = context.getSessionId();
if (sessionId != null) {
- return new DefaultSessionKey(sessionId);
+ sessionKey = new DefaultSessionKey(sessionId);
+ }
+ if (sessionKey != null) {
+ sessionKey.setUpdateDeferred(context.isSessionUpdateDeferred());
}
return null;
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java Sat May 5 05:31:35 2012
@@ -54,7 +54,10 @@ public abstract class AbstractNativeSess
public Session start(SessionContext context) {
Session session = createSession(context);
- applyGlobalSessionTimeout(session);
+ session.setTimeout(getGlobalSessionTimeout()); //todo this configuration in Session creation
+ if (isUpdateImmediate(context)) {
+ onChange(session);
+ }
onStart(session, context);
notifyStart(session);
//Don't expose the EIS-tier Session object to the client-tier:
@@ -77,11 +80,6 @@ public abstract class AbstractNativeSess
*/
protected abstract Session createSession(SessionContext context) throws AuthorizationException;
- protected void applyGlobalSessionTimeout(Session session) {
- session.setTimeout(getGlobalSessionTimeout());
- onChange(session);
- }
-
/**
* Template method that allows subclasses to react to a new session being created.
* <p/>
@@ -117,11 +115,45 @@ public abstract class AbstractNativeSess
protected abstract Session doGetSession(SessionKey key) throws InvalidSessionException;
protected Session createExposedSession(Session session, SessionContext context) {
- return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
+ SessionKey sessionKey = createSessionKey(session, context);
+ return new DelegatingSession(this, sessionKey);
+ }
+
+ //since 1.3
+ protected SessionKey createSessionKey(Session session, SessionContext context) {
+ SessionKey key = doCreateSessionKey(session, context);
+ if (key instanceof UpdateDeferrable && context instanceof UpdateDeferrable) {
+ UpdateDeferrable udKey = (UpdateDeferrable)key;
+ UpdateDeferrable udContext = (UpdateDeferrable)context;
+ udKey.setUpdateDeferred(udContext.isUpdateDeferred());
+ }
+ return key;
+ }
+
+ //since 1.3
+ protected SessionKey createSessionKey(Session session, SessionKey oldKey) {
+ SessionKey newKey = doCreateSessionKey(session, oldKey);
+ if (newKey instanceof UpdateDeferrable && oldKey instanceof UpdateDeferrable) {
+ UpdateDeferrable newKeyUdDeferrable = (UpdateDeferrable)newKey;
+ UpdateDeferrable oldKeyUdDeferrable = (UpdateDeferrable)oldKey;
+ newKeyUdDeferrable.setUpdateDeferred(oldKeyUdDeferrable.isUpdateDeferred());
+ }
+ return newKey;
+ }
+
+ //since 1.3
+ protected SessionKey doCreateSessionKey(Session session, SessionContext context) {
+ return new DefaultSessionKey(session.getId());
+ }
+
+ //since 1.3
+ protected SessionKey doCreateSessionKey(Session session, SessionKey sessionKey) {
+ return new DefaultSessionKey(session.getId());
}
protected Session createExposedSession(Session session, SessionKey key) {
- return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
+ SessionKey sessionKey = createSessionKey(session, key);
+ return new DelegatingSession(this, sessionKey);
}
/**
@@ -181,13 +213,17 @@ public abstract class AbstractNativeSess
public void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException {
Session s = lookupRequiredSession(key);
s.setTimeout(maxIdleTimeInMillis);
- onChange(s);
+ if (isUpdateImmediate(key)) {
+ onChange(s);
+ }
}
public void touch(SessionKey key) throws InvalidSessionException {
Session s = lookupRequiredSession(key);
s.touch();
- onChange(s);
+ if (isUpdateImmediate(key)) {
+ onChange(s);
+ }
}
public String getHost(SessionKey key) {
@@ -212,14 +248,26 @@ public abstract class AbstractNativeSess
} else {
Session s = lookupRequiredSession(sessionKey);
s.setAttribute(attributeKey, value);
- onChange(s);
+ if (isUpdateImmediate(sessionKey)) {
+ onChange(s);
+ }
}
}
+ //since 1.3
+ protected final boolean isUpdateDeferred(Object object) {
+ return object instanceof UpdateDeferrable && ((UpdateDeferrable)object).isUpdateDeferred();
+ }
+
+ //since 1.3
+ protected final boolean isUpdateImmediate(Object object) {
+ return !isUpdateDeferred(object);
+ }
+
public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
Session s = lookupRequiredSession(sessionKey);
Object removed = s.removeAttribute(attributeKey);
- if (removed != null) {
+ if (removed != null && isUpdateImmediate(sessionKey)) {
onChange(s);
}
return removed;
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionContext.java Sat May 5 05:31:35 2012
@@ -30,12 +30,13 @@ import java.util.Map;
*
* @since 1.0
*/
-public class DefaultSessionContext extends MapContext implements SessionContext {
+public class DefaultSessionContext extends MapContext implements SessionContext, UpdateDeferrable {
private static final long serialVersionUID = -1424160751361252966L;
private static final String HOST = DefaultSessionContext.class.getName() + ".HOST";
private static final String SESSION_ID = DefaultSessionContext.class.getName() + ".SESSION_ID";
+ private static final String UPDATE_DEFERRED = DefaultSessionContext.class.getName() + ".UPDATE_DEFERRED";
public DefaultSessionContext() {
super();
@@ -62,4 +63,14 @@ public class DefaultSessionContext exten
public void setSessionId(Serializable sessionId) {
nullSafePut(SESSION_ID, sessionId);
}
+
+ public boolean isUpdateDeferred() {
+ Boolean bool = getTypedValue(UPDATE_DEFERRED, Boolean.class);
+ //noinspection UnnecessaryUnboxing
+ return bool != null && bool.booleanValue();
+ }
+
+ public void setUpdateDeferred(boolean deferred) {
+ nullSafePut(UPDATE_DEFERRED, deferred);
+ }
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionKey.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionKey.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionKey.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionKey.java Sat May 5 05:31:35 2012
@@ -24,10 +24,12 @@ import java.io.Serializable;
*
* @since 1.0
*/
-public class DefaultSessionKey implements SessionKey, Serializable {
+public class DefaultSessionKey implements SessionKey, Serializable, UpdateDeferrable {
private Serializable sessionId;
+ private boolean updateDeferred;
+
public DefaultSessionKey() {
}
@@ -42,4 +44,12 @@ public class DefaultSessionKey implement
public Serializable getSessionId() {
return this.sessionId;
}
+
+ public boolean isUpdateDeferred() {
+ return this.updateDeferred;
+ }
+
+ public void setUpdateDeferred(boolean deferred) {
+ this.updateDeferred = deferred;
+ }
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java Sat May 5 05:31:35 2012
@@ -20,10 +20,12 @@ package org.apache.shiro.session.mgt;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.CacheManagerAware;
+import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
+import org.apache.shiro.util.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,12 +40,15 @@ import java.util.Date;
*
* @since 0.1
*/
-public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware {
+public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware, FlushableSessionManager {
//TODO - complete JavaDoc
private static final Logger log = LoggerFactory.getLogger(DefaultSessionManager.class);
+ //since 1.3
+ private static final String SESSION_THREAD_CACHE_KEY = DefaultSessionManager.class.getName() + ".SESSION_THREAD_CACHE_KEY";
+
private SessionFactory sessionFactory;
protected SessionDAO sessionDAO; //todo - move SessionDAO up to AbstractValidatingSessionManager?
@@ -156,6 +161,9 @@ public class DefaultSessionManager exten
log.trace("Creating session for host {}", s.getHost());
}
create(s);
+ if (isUpdateDeferred(context)) {
+ addToFirstLevelCache(s); //since 1.3
+ }
return s;
}
@@ -192,6 +200,7 @@ public class DefaultSessionManager exten
if (isDeleteInvalidSessions()) {
delete(session);
}
+ removeSessionFromFirstLevelCache(); //since 1.3
}
protected void onExpiration(Session session) {
@@ -206,9 +215,11 @@ public class DefaultSessionManager exten
if (isDeleteInvalidSessions()) {
delete(session);
}
+ removeSessionFromFirstLevelCache(); //since 1.3
}
protected void onChange(Session session) {
+ log.trace("Updating session {}", session);
sessionDAO.update(session);
}
@@ -219,7 +230,22 @@ public class DefaultSessionManager exten
"session could not be found.", sessionKey);
return null;
}
- Session s = retrieveSessionFromDataSource(sessionId);
+
+ Session s = null;
+
+ boolean updateDeferred = false;
+ if (sessionKey instanceof UpdateDeferrable && ((UpdateDeferrable)sessionKey).isUpdateDeferred()) {
+ updateDeferred = true;
+ }
+ if (updateDeferred) {
+ s = retrieveSessionFromFirstLevelCache(sessionId);
+ }
+ if (s == null) {
+ s = retrieveSessionFromDataSource(sessionId);
+ if (s != null && updateDeferred) {
+ addToFirstLevelCache(s);
+ }
+ }
if (s == null) {
//session ID was provided, meaning one is expected to be found, but we couldn't find one:
String msg = "Could not find session with ID [" + sessionId + "]";
@@ -232,7 +258,35 @@ public class DefaultSessionManager exten
return sessionKey.getSessionId();
}
+ //since 1.3
+ protected final void addToFirstLevelCache(Session session) {
+ ThreadContext.put(SESSION_THREAD_CACHE_KEY, session);
+ }
+
+ //since 1.3
+ protected final Session retrieveSessionFromFirstLevelCache(Serializable sessionId) {
+ Session session = (Session) ThreadContext.get(SESSION_THREAD_CACHE_KEY);
+ if (session != null && sessionId.equals(session.getId())) {
+ return session;
+ }
+ return null;
+ }
+
+ protected final void removeSessionFromFirstLevelCache() {
+ ThreadContext.remove(SESSION_THREAD_CACHE_KEY);
+ }
+
+ public void flush(SessionKey key) throws InvalidSessionException {
+ Serializable sessionId = getSessionId(key);
+ Session session = retrieveSessionFromFirstLevelCache(sessionId);
+ if (session != null) {
+ log.trace("Flushing session to data store. Session id: {}", session.getId());
+ sessionDAO.update(session);
+ }
+ }
+
protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
+ log.trace("Retrieving session from data source. Session id: {}", sessionId);
return sessionDAO.readSession(sessionId);
}
@@ -244,5 +298,4 @@ public class DefaultSessionManager exten
Collection<Session> active = sessionDAO.getActiveSessions();
return active != null ? active : Collections.<Session>emptySet();
}
-
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java Sat May 5 05:31:35 2012
@@ -31,16 +31,16 @@ import java.util.Date;
* This implementation is basically a proxy to a server-side {@link NativeSessionManager NativeSessionManager},
* which will return the proper results for each method call.
* <p/>
- * <p>A <tt>DelegatingSession</tt> will cache data when appropriate to avoid a remote method invocation,
+ * A <tt>DelegatingSession</tt> will cache data when appropriate to avoid a remote method invocation,
* only communicating with the server when necessary.
* <p/>
- * <p>Of course, if used in-process with a NativeSessionManager business POJO, as might be the case in a
+ * Of course, if used in-process with a NativeSessionManager business POJO, as might be the case in a
* web-based application where the web classes and server-side business pojos exist in the same
* JVM, a remote method call will not be incurred.
*
* @since 0.1
*/
-public class DelegatingSession implements Session, Serializable {
+public class DelegatingSession implements Session, Flushable, Serializable {
//TODO - complete JavaDoc
@@ -158,4 +158,17 @@ public class DelegatingSession implement
public Object removeAttribute(Object attributeKey) throws InvalidSessionException {
return sessionManager.removeAttribute(this.key, attributeKey);
}
+
+ /**
+ * If the backing SessionManager is a {@link FlushableSessionManager}, this method calls
+ * sessionManager.{@link FlushableSessionManager#flush(SessionKey) flush(sessionKey)}, otherwise this implementation
+ * does nothing.
+ *
+ * @since 1.3
+ */
+ public void flush() {
+ if (sessionManager instanceof FlushableSessionManager) {
+ ((FlushableSessionManager) sessionManager).flush(this.key);
+ }
+ }
}
Added: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/Flushable.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/Flushable.java?rev=1334318&view=auto
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/Flushable.java (added)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/Flushable.java Sat May 5 05:31:35 2012
@@ -0,0 +1,33 @@
+/*
+ * 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.session.mgt;
+
+/**
+ * This is a Shiro implementation-specific interface and should probably not be called by Shiro end-users directly.
+ * This interface and its implementations are subject to change without notice.
+ * <p/>
+ * The Flushable interface allows a session to 'flush' its changes with an underlying SessionManager. It is used
+ * in caching strategies so the underlying session data store is not accessed too frequently during a request.
+ *
+ * @since 1.3
+ */
+public interface Flushable {
+
+ void flush();
+}
Added: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/FlushableSessionManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/FlushableSessionManager.java?rev=1334318&view=auto
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/FlushableSessionManager.java (added)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/FlushableSessionManager.java Sat May 5 05:31:35 2012
@@ -0,0 +1,36 @@
+/*
+ * 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.session.mgt;
+
+import org.apache.shiro.session.InvalidSessionException;
+
+/**
+ * @since 1.3
+ */
+public interface FlushableSessionManager extends NativeSessionManager {
+
+ /**
+ * Persists any unsaved Session state to the underlying Session data store.
+ *
+ * @param key the session key to use to look up the target session.
+ * @throws InvalidSessionException if the session id is invalid (it does not exist or it is stopped or expired).
+ */
+ void flush(SessionKey key) throws InvalidSessionException;
+
+}
Added: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/UpdateDeferrable.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/UpdateDeferrable.java?rev=1334318&view=auto
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/UpdateDeferrable.java (added)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/session/mgt/UpdateDeferrable.java Sat May 5 05:31:35 2012
@@ -0,0 +1,53 @@
+/*
+ * 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.session.mgt;
+
+/**
+ * @since 1.3
+ */
+public interface UpdateDeferrable {
+
+ /**
+ * Returns {@code true} if modifications that require persistence (updates) can be deferred until a future time,
+ * for example, at the end of a request or method invocation. Returns {@code false} if update operations must
+ * be relayed immediately to the backing data store as they occur.
+ * <p/>
+ * This interface corresponds only to updates; create and delete operations will always be immediately relayed
+ * to the underlying data store.
+ * <p/>
+ * A {@code true} value reduces the number of times the backing data store (such as a cache or database) must be
+ * accessed during particular scope (e.g. request or thread). A {@code false} value indicates that the backing
+ * data store will handle updates immediately, probably because it has its own caching mechanism in place to limit
+ * data store round trips. For example, if the data store had a built-in first level cache with a pluggable
+ * second-level cache).
+ * <p/>
+ * For Shiro 1.3 and later, the default value is {@code true}. Setting this to {@code false} reverts to Shiro
+ * 1.2 and earlier behavior, but expects the backing datastore to handle many reads/writes during a single
+ * request or invocation.
+ *
+ * @return {@code true} if state modifications during the course of a request or invocation can be persisted at the
+ * end of a request/invocation, or {@code false} if session modifications must be immediately relayed to
+ * backing data store as they occur.
+ * @see <a href="https://issues.apache.org/jira/browse/SHIRO-317">SHIRO-317</a>
+ * @since 1.3
+ */
+ boolean isUpdateDeferred();
+
+ void setUpdateDeferred(boolean deferred);
+}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/Subject.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/Subject.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/Subject.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/Subject.java Sat May 5 05:31:35 2012
@@ -52,7 +52,7 @@ import java.util.concurrent.Callable;
* simply convert these String values to {@link Permission Permission} instances and then just call the corresponding
* type-safe method. (Shiro's default implementations do String-to-Permission conversion for these methods using
* {@link org.apache.shiro.authz.permission.PermissionResolver PermissionResolver}s.)
- * <p/>
+ *
* These overloaded *Permission methods forgo type-saftey for the benefit of convenience and simplicity,
* so you should choose which ones to use based on your preferences and needs.
*
@@ -309,7 +309,7 @@ public interface Subject {
*
* @param roleIdentifiers roleIdentifiers the application-specific role identifiers to check (usually role ids or role names).
* @throws AuthorizationException org.apache.shiro.authz.AuthorizationException
- * if this Subject does not have all of the specified roles.
+ * if this Subject does not have all of the specified roles.
* @since 1.1.0
*/
void checkRoles(String... roleIdentifiers) throws AuthorizationException;
@@ -356,13 +356,13 @@ public interface Subject {
* this method act as the logical equivalent to this code:
* <pre>
* {@link #getPrincipal() getPrincipal()} != null && !{@link #isAuthenticated() isAuthenticated()}</pre>
- * <p/>
+ *
* Note as indicated by the above code example, if a {@code Subject} is remembered, they are
* <em>NOT</em> considered authenticated. A check against {@link #isAuthenticated() isAuthenticated()} is a more
* strict check than that reflected by this method. For example, a check to see if a subject can access financial
* information should almost always depend on {@link #isAuthenticated() isAuthenticated()} to <em>guarantee</em> a
* verified identity, and not this method.
- * <p/>
+ *
* Once the subject is authenticated, they are no longer considered only remembered because their identity would
* have been verified during the current session.
* <h4>Remembered vs Authenticated</h4>
@@ -370,23 +370,23 @@ public interface Subject {
* the remembered identity gives the system an idea who that user probably is, but in reality, has no way of
* absolutely <em>guaranteeing</em> if the remembered {@code Subject} represents the user currently
* using the application.
- * <p/>
+ *
* So although many parts of the application can still perform user-specific logic based on the remembered
* {@link #getPrincipals() principals}, such as customized views, it should never perform highly-sensitive
* operations until the user has legitimately verified their identity by executing a successful authentication
* attempt.
- * <p/>
+ *
* We see this paradigm all over the web, and we will use <a href="http://www.amazon.com">Amazon.com</a> as an
* example:
- * <p/>
+ *
* When you visit Amazon.com and perform a login and ask it to 'remember me', it will set a cookie with your
* identity. If you don't log out and your session expires, and you come back, say the next day, Amazon still knows
* who you <em>probably</em> are: you still see all of your book and movie recommendations and similar user-specific
* features since these are based on your (remembered) user id.
- * <p/>
+ *
* BUT, if you try to do something sensitive, such as access your account's billing data, Amazon forces you
* to do an actual log-in, requiring your username and password.
- * <p/>
+ *
* This is because although amazon.com assumed your identity from 'remember me', it recognized that you were not
* actually authenticated. The only way to really guarantee you are who you say you are, and therefore allow you
* access to sensitive account data, is to force you to perform an actual successful authentication. You can
@@ -585,18 +585,18 @@ public interface Subject {
* specify the exact {@code SecurityManager} instance to be used by the additional
* <code>Subject.{@link #Builder(org.apache.shiro.mgt.SecurityManager) Builder(securityManager)}</code>
* constructor if desired.
- * <p/>
+ *
* All other methods may be called before the {@link #buildSubject() buildSubject()} method to
* provide context on how to construct the {@code Subject} instance. For example, if you have a session id and
* want to acquire the {@code Subject} that 'owns' that session (assuming the session exists and is not expired):
* <pre>
* Subject subject = new Subject.Builder().sessionId(sessionId).buildSubject();</pre>
- * <p/>
+ *
* Similarly, if you want a {@code Subject} instance reflecting a certain identity:
* <pre>
* PrincipalCollection principals = new SimplePrincipalCollection("username", <em>yourRealmName</em>);
* Subject subject = new Subject.Builder().principals(principals).build();</pre>
- * <p/>
+ *
* <b>Note*</b> that the returned {@code Subject} instance is <b>not</b> automatically bound to the application (thread)
* for further use. That is,
* {@link org.apache.shiro.SecurityUtils SecurityUtils}.{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()}
@@ -675,15 +675,15 @@ public interface Subject {
* session alone. In other words, this is almost always sufficient:
* <pre>
* new Subject.Builder().sessionId(sessionId).buildSubject();</pre>
- * <p/>
+ *
* <b>Although simple in concept, this method provides very powerful functionality previously absent in almost
* all Java environments:</b>
- * <p/>
+ *
* The ability to reference a {@code Subject} and their server-side session
* <em>across clients of different mediums</em> such as web applications, Java applets,
* standalone C# clients over XML-RPC and/or SOAP, and many others. This is a <em>huge</em>
* benefit in heterogeneous enterprise applications.
- * <p/>
+ *
* To maintain session integrity across client mediums, the {@code sessionId} <b>must</b> be transmitted
* to all client mediums securely (e.g. over SSL) to prevent man-in-the-middle attacks. This
* is nothing new - all web applications are susceptible to the same problem when transmitting
@@ -740,7 +740,7 @@ public interface Subject {
* <pre>
* PrincipalCollection identity = new {@link org.apache.shiro.subject.SimplePrincipalCollection#SimplePrincipalCollection(Object, String) SimplePrincipalCollection}("jsmith", "myRealm");
* Subject jsmith = new Subject.Builder().principals(identity).buildSubject();</pre>
- * <p/>
+ *
* Similarly, if your application's unique identifier for users is a {@code long} value (such as might be used
* as a primary key in a relational database) and you were using a {@code JDBC}
* {@code Realm} named, (unimaginatively) "jdbcRealm", you might create the Subject
@@ -794,6 +794,43 @@ public interface Subject {
}
/**
+ * Sets whether or not any session associated with the built subject will have state changes deferred until
+ * a caller can explicitly {@code flush()} the session.
+ * <p/>
+ * Setting this to be true is a performance enhancement, typically used during web requests and/or invocation
+ * chains but <b>REQUIRES THE FOLLOWING</b>:
+ * <p/>
+ * The caller building the subject <em>MUST</em> {@link org.apache.shiro.session.mgt.Flushable#flush() flush} any
+ * session modifications at the end of the request/invocation chain. For example:
+ * <pre>
+ * final Subject subject = new Subject.Builder(...).sessionUpdateDeferred(true).... .buildSubject();
+ *
+ * try {
+ * subject.execute(...);
+ * } finally {
+ * Session session = subject.getSession(false);
+ * if (session instanceof Flushable) {
+ * ((Flushable)session).flush();
+ * }
+ * }
+ * </pre>
+ * <p/>
+ * The flush() call MUST be called to persist any session state changes during the execution or the session
+ * will never be updated.
+ *
+ * @param deferred {@code true} to defer session updates until flush may be called, {@code false} to ensure
+ * updates are persisted as they occur.
+ * @return whether or not any session associated with the built subject will have state changes deferred until
+ * a caller can explicitly {@code flush()} the session.
+ * @see org.apache.shiro.session.mgt.UpdateDeferrable#isUpdateDeferred()
+ * @since 1.3
+ */
+ public Builder sessionUpdateDeferred(boolean deferred) {
+ this.subjectContext.setSessionUpdateDeferred(deferred);
+ return this;
+ }
+
+ /**
* Allows custom attributes to be added to the underlying context {@code Map} used to construct the
* {@link Subject} instance.
* <p/>
@@ -846,5 +883,4 @@ public interface Subject {
return this.securityManager.createSubject(this.subjectContext);
}
}
-
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/SubjectContext.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/SubjectContext.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/SubjectContext.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/SubjectContext.java Sat May 5 05:31:35 2012
@@ -234,4 +234,16 @@ public interface SubjectContext extends
void setHost(String host);
String resolveHost();
+
+ /**
+ * @since 1.3
+ * @see org.apache.shiro.session.mgt.UpdateDeferrable UpdateDeferrable
+ */
+ boolean isSessionUpdateDeferred();
+
+ /**
+ * @since 1.3
+ * @see org.apache.shiro.session.mgt.UpdateDeferrable UpdateDeferrable
+ */
+ void setSessionUpdateDeferred(boolean deferred);
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java Sat May 5 05:31:35 2012
@@ -77,6 +77,8 @@ public class DefaultSubjectContext exten
*/
public static final String AUTHENTICATED_SESSION_KEY = DefaultSubjectContext.class.getName() + "_AUTHENTICATED_SESSION_KEY";
+ public static final String SESSION_UPDATE_DEFERRED = DefaultSubjectContext.class.getName() + ".SESSION_UPDATE_DEFERRED";
+
private static final transient Logger log = LoggerFactory.getLogger(DefaultSubjectContext.class);
public DefaultSubjectContext() {
@@ -273,4 +275,13 @@ public class DefaultSubjectContext exten
return host;
}
+
+ public boolean isSessionUpdateDeferred() {
+ Boolean bool = getTypedValue(SESSION_UPDATE_DEFERRED, Boolean.class);
+ return bool != null && bool;
+ }
+
+ public void setSessionUpdateDeferred(boolean deferred) {
+ nullSafePut(SESSION_UPDATE_DEFERRED, deferred);
+ }
}
Modified: shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java (original)
+++ shiro/branches/SHIRO-317/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java Sat May 5 05:31:35 2012
@@ -30,6 +30,7 @@ import org.apache.shiro.session.ProxiedS
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionContext;
+import org.apache.shiro.session.mgt.Flushable;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.PrincipalCollection;
@@ -79,10 +80,9 @@ public class DelegatingSubject implement
protected boolean authenticated;
protected String host;
protected Session session;
- /**
- * @since 1.2
- */
- protected boolean sessionCreationEnabled;
+ protected boolean sessionCreationEnabled; //since 1.2
+ protected boolean sessionUpdateDeferred; //since 1.3
+
private List<PrincipalCollection> runAsPrincipals; //supports assumed identities (aka 'run as')
protected transient SecurityManager securityManager;
@@ -99,6 +99,13 @@ public class DelegatingSubject implement
//since 1.2
public DelegatingSubject(PrincipalCollection principals, boolean authenticated, String host,
Session session, boolean sessionCreationEnabled, SecurityManager securityManager) {
+ this(principals, authenticated, host, session, sessionCreationEnabled, false, securityManager);
+ }
+
+ //since 1.3
+ public DelegatingSubject(PrincipalCollection principals, boolean authenticated, String host,
+ Session session, boolean sessionCreationEnabled, boolean sessionUpdateDeferred,
+ SecurityManager securityManager) {
if (securityManager == null) {
throw new IllegalArgumentException("SecurityManager argument cannot be null.");
}
@@ -111,13 +118,18 @@ public class DelegatingSubject implement
this.runAsPrincipals = getRunAsPrincipals(this.session);
}
this.sessionCreationEnabled = sessionCreationEnabled;
+ this.sessionUpdateDeferred = sessionUpdateDeferred;
}
protected Session decorate(Session session) {
if (session == null) {
throw new IllegalArgumentException("session cannot be null");
}
- return new StoppingAwareProxiedSession(session, this);
+ if (session instanceof Flushable) {
+ return new FlushableStoppingAwareProxiedSession(session, this);
+ } else {
+ return new StoppingAwareProxiedSession(session, this);
+ }
}
public SecurityManager getSecurityManager() {
@@ -310,6 +322,10 @@ public class DelegatingSubject implement
return this.sessionCreationEnabled;
}
+ protected boolean isSessionUpdateDeferred() {
+ return this.sessionUpdateDeferred;
+ }
+
public Session getSession() {
return getSession(true);
}
@@ -340,10 +356,14 @@ public class DelegatingSubject implement
}
protected SessionContext createSessionContext() {
- SessionContext sessionContext = new DefaultSessionContext();
+ DefaultSessionContext sessionContext = new DefaultSessionContext();
if (StringUtils.hasText(host)) {
sessionContext.setHost(host);
}
+ //added for 1.3 (see SHIRO-317):
+ if (isSessionUpdateDeferred()) {
+ sessionContext.setUpdateDeferred(isSessionUpdateDeferred());
+ }
return sessionContext;
}
@@ -418,6 +438,27 @@ public class DelegatingSubject implement
}
}
+ private class FlushableStoppingAwareProxiedSession extends StoppingAwareProxiedSession implements Flushable {
+
+ private final DelegatingSubject owner;
+
+ private FlushableStoppingAwareProxiedSession(Session target, DelegatingSubject owningSubject) {
+ super(target, owningSubject);
+ if (!(target instanceof Flushable)) {
+ throw new IllegalStateException("Target session must be Flushable");
+ }
+ owner = owningSubject;
+ }
+
+ public void stop() throws InvalidSessionException {
+ super.stop();
+ owner.sessionStopped();
+ }
+
+ public void flush() {
+ ((Flushable)delegate).flush();
+ }
+ }
// ======================================
// 'Run As' support implementations
Modified: shiro/branches/SHIRO-317/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java (original)
+++ shiro/branches/SHIRO-317/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java Sat May 5 05:31:35 2012
@@ -20,8 +20,11 @@ package org.apache.shiro.spring.remoting
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.Flushable;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.remoting.support.DefaultRemoteInvocationExecutor;
@@ -59,6 +62,8 @@ public class SecureRemoteInvocationExecu
*/
private SecurityManager securityManager;
+ private boolean isSessionUpdateDeferred = true; //since 1.3
+
/*--------------------------------------------
| C O N S T R U C T O R S |
============================================*/
@@ -71,6 +76,18 @@ public class SecureRemoteInvocationExecu
this.securityManager = securityManager;
}
+ public boolean isSessionUpdateDeferred() {
+ return isSessionUpdateDeferred;
+ }
+
+ public void setSessionUpdateDeferred(boolean sessionUpdateDeferred) {
+ isSessionUpdateDeferred = sessionUpdateDeferred;
+ }
+
+ protected boolean isSessionUpdateDeferred(RemoteInvocation ri, Object targetObject) {
+ return isSessionUpdateDeferred();
+ }
+
/*--------------------------------------------
| M E T H O D S |
============================================*/
@@ -100,12 +117,29 @@ public class SecureRemoteInvocationExecu
}
}
+ boolean sessionUpdateDeferred = isSessionUpdateDeferred(invocation, targetObject);
+ builder.sessionUpdateDeferred(sessionUpdateDeferred);
+
Subject subject = builder.buildSubject();
- return subject.execute(new Callable() {
- public Object call() throws Exception {
- return SecureRemoteInvocationExecutor.super.invoke(invocation, targetObject);
+
+ try {
+ return subject.execute(new Callable() {
+ public Object call() throws Exception {
+ return SecureRemoteInvocationExecutor.super.invoke(invocation, targetObject);
+ }
+ });
+ } finally {
+ try {
+ if (sessionUpdateDeferred) {
+ Session session = subject.getSession(false);
+ if (session instanceof Flushable) {
+ ((Flushable)session).flush();
+ }
+ }
+ } finally {
+ ThreadContext.remove();
}
- });
+ }
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof NoSuchMethodException) {
Modified: shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSecurityManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSecurityManager.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSecurityManager.java (original)
+++ shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSecurityManager.java Sat May 5 05:31:35 2012
@@ -201,10 +201,11 @@ public class DefaultWebSecurityManager e
Serializable sessionId = context.getSessionId();
ServletRequest request = WebUtils.getRequest(context);
ServletResponse response = WebUtils.getResponse(context);
- return new WebSessionKey(sessionId, request, response);
+ WebSessionKey wsk = new WebSessionKey(sessionId, request, response);
+ wsk.setUpdateDeferred(context.isSessionUpdateDeferred());
+ return wsk;
} else {
return super.getSessionKey(context);
-
}
}
Modified: shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSubjectFactory.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSubjectFactory.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSubjectFactory.java (original)
+++ shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/mgt/DefaultWebSubjectFactory.java Sat May 5 05:31:35 2012
@@ -53,13 +53,14 @@ public class DefaultWebSubjectFactory ex
SecurityManager securityManager = wsc.resolveSecurityManager();
Session session = wsc.resolveSession();
boolean sessionEnabled = wsc.isSessionCreationEnabled();
+ boolean sessionUpdateDeferred = wsc.isSessionUpdateDeferred();
PrincipalCollection principals = wsc.resolvePrincipals();
boolean authenticated = wsc.resolveAuthenticated();
String host = wsc.resolveHost();
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
- return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
+ return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled, sessionUpdateDeferred,
request, response, securityManager);
}
@@ -72,7 +73,7 @@ public class DefaultWebSubjectFactory ex
String host, Session session,
ServletRequest request, ServletResponse response,
SecurityManager securityManager) {
- return new WebDelegatingSubject(principals, authenticated, host, session, true,
+ return new WebDelegatingSubject(principals, authenticated, host, session, true, false,
request, response, securityManager);
}
}
Modified: shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java (original)
+++ shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java Sat May 5 05:31:35 2012
@@ -20,8 +20,10 @@ package org.apache.shiro.web.servlet;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.Flushable;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
@@ -289,7 +291,9 @@ public abstract class AbstractShiroFilte
* @since 1.0
*/
protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
- return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
+ WebSubject.Builder builder = new WebSubject.Builder(getSecurityManager(), request, response);
+ builder.sessionUpdateDeferred(true); //added in 1.3 - MUST be accompanied by finally 'flush'.
+ return builder.buildWebSubject();
}
/**
@@ -358,14 +362,26 @@ public abstract class AbstractShiroFilte
final Subject subject = createSubject(request, response);
- //noinspection unchecked
- subject.execute(new Callable() {
- public Object call() throws Exception {
- updateSessionLastAccessTime(request, response);
- executeChain(request, response, chain);
- return null;
- }
- });
+ try {
+ //noinspection unchecked
+ subject.execute(new Callable() {
+ public Object call() throws Exception {
+ try {
+ updateSessionLastAccessTime(request, response);
+ executeChain(request, response, chain);
+ return null;
+ } finally {
+ Session session = subject.getSession(false);
+ if (session instanceof Flushable) {
+ ((Flushable) session).flush();
+ }
+ }
+ }
+ });
+ } finally {
+ ThreadContext.remove(); //silence innocuous Tomcat ThreadLocal warnings
+ }
+
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
Modified: shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java (original)
+++ shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java Sat May 5 05:31:35 2012
@@ -21,14 +21,12 @@ package org.apache.shiro.web.session.mgt
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
-import org.apache.shiro.session.mgt.DefaultSessionManager;
-import org.apache.shiro.session.mgt.DelegatingSession;
-import org.apache.shiro.session.mgt.SessionContext;
-import org.apache.shiro.session.mgt.SessionKey;
+import org.apache.shiro.session.mgt.*;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
+import org.apache.shiro.web.util.RequestPairSource;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -134,25 +132,26 @@ public class DefaultWebSessionManager ex
return id;
}
- protected Session createExposedSession(Session session, SessionContext context) {
+ @Override
+ protected SessionKey doCreateSessionKey(Session session, SessionContext context) {
if (!WebUtils.isWeb(context)) {
- return super.createExposedSession(session, context);
+ return super.doCreateSessionKey(session, context);
}
- ServletRequest request = WebUtils.getRequest(context);
- ServletResponse response = WebUtils.getResponse(context);
- SessionKey key = new WebSessionKey(session.getId(), request, response);
- return new DelegatingSession(this, key);
+ return createSessionKey(session, (RequestPairSource)context);
}
- protected Session createExposedSession(Session session, SessionKey key) {
+ @Override
+ protected SessionKey doCreateSessionKey(Session session, SessionKey key) {
if (!WebUtils.isWeb(key)) {
- return super.createExposedSession(session, key);
+ return super.doCreateSessionKey(session, key);
}
+ return createSessionKey(session, (RequestPairSource)key);
+ }
- ServletRequest request = WebUtils.getRequest(key);
- ServletResponse response = WebUtils.getResponse(key);
- SessionKey sessionKey = new WebSessionKey(session.getId(), request, response);
- return new DelegatingSession(this, sessionKey);
+ protected SessionKey createSessionKey(Session session, RequestPairSource rpSource) {
+ ServletRequest request = WebUtils.getRequest(rpSource);
+ ServletResponse response = WebUtils.getResponse(rpSource);
+ return new WebSessionKey(session.getId(), request, response);
}
/**
Modified: shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/subject/support/WebDelegatingSubject.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/subject/support/WebDelegatingSubject.java?rev=1334318&r1=1334317&r2=1334318&view=diff
==============================================================================
--- shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/subject/support/WebDelegatingSubject.java (original)
+++ shiro/branches/SHIRO-317/web/src/main/java/org/apache/shiro/web/subject/support/WebDelegatingSubject.java Sat May 5 05:31:35 2012
@@ -40,24 +40,32 @@ import javax.servlet.ServletResponse;
*/
public class WebDelegatingSubject extends DelegatingSubject implements WebSubject {
- private static final long serialVersionUID = -1655724323350159250L;
-
- private final ServletRequest servletRequest;
- private final ServletResponse servletResponse;
+ private final transient ServletRequest servletRequest;
+ private final transient ServletResponse servletResponse;
+ @SuppressWarnings("UnusedDeclaration") //should keep for backwards compatibility
public WebDelegatingSubject(PrincipalCollection principals, boolean authenticated,
String host, Session session,
ServletRequest request, ServletResponse response,
SecurityManager securityManager) {
- this(principals, authenticated, host, session, true, request, response, securityManager);
+ this(principals, authenticated, host, session, true, false, request, response, securityManager);
}
//since 1.2
+ @SuppressWarnings("UnusedDeclaration") //should keep for backwards compatibility
public WebDelegatingSubject(PrincipalCollection principals, boolean authenticated,
String host, Session session, boolean sessionEnabled,
ServletRequest request, ServletResponse response,
SecurityManager securityManager) {
- super(principals, authenticated, host, session, sessionEnabled, securityManager);
+ this(principals, authenticated, host, session, sessionEnabled, false, request, response, securityManager);
+ }
+
+ //since 1.3
+ public WebDelegatingSubject(PrincipalCollection principals, boolean authenticated,
+ String host, Session session, boolean sessionEnabled, boolean sessionUpdateDeferred,
+ ServletRequest request, ServletResponse response,
+ SecurityManager securityManager) {
+ super(principals, authenticated, host, session, sessionEnabled, sessionUpdateDeferred, securityManager);
this.servletRequest = request;
this.servletResponse = response;
}
@@ -91,13 +99,18 @@ public class WebDelegatingSubject extend
@Override
protected SessionContext createSessionContext() {
- WebSessionContext wsc = new DefaultWebSessionContext();
+ DefaultWebSessionContext wsc = new DefaultWebSessionContext();
String host = getHost();
if (StringUtils.hasText(host)) {
wsc.setHost(host);
}
+ //added for 1.3 (see SHIRO-317):
+ if (isSessionUpdateDeferred()) {
+ wsc.setUpdateDeferred(isSessionUpdateDeferred());
+ }
wsc.setServletRequest(this.servletRequest);
wsc.setServletResponse(this.servletResponse);
+
return wsc;
}
}