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/06/18 04:01:12 UTC
svn commit: r1351193 [1/2] - in /shiro/branches/SHIRO-317b:
core/src/main/java/org/apache/shiro/event/
core/src/main/java/org/apache/shiro/session/event/
core/src/main/java/org/apache/shiro/session/mgt/
core/src/main/java/org/apache/shiro/session/mgt/e...
Author: lhazlewood
Date: Mon Jun 18 02:01:11 2012
New Revision: 1351193
URL: http://svn.apache.org/viewvc?rev=1351193&view=rev
Log:
- Added new first-level-caching Web SessionManager implementation
- Condensed core SessionManager hierarchy to a single more easily understood StandardSessionManager implementation.
- Added event infrastructure mechanism for loose coupling.
- Updated ID generation to use a Java Pattern object for slightly better performance, additionally using all uppercase characters for easier visual log file scanning.
- Created new RequestIdGenerator for request-specific globally unique IDs.
- Introduced new session AccessTimestampEvaluator to allow end-users to configure which requests update a session's lastAccessTimestamp.
Added:
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Publisher.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/ShiroEvent.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubjectEvent.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Subscriber.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubscriberRegistry.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SynchronousEventBus.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/InvalidSessionEvent.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/SessionEvent.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StartedSessionEvent.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StoppedSessionEvent.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/StandardSessionManager.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/util/Assert.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/BeginServletRequestEvent.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/EndServletRequestEvent.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/ServletRequestEvent.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/AccessTimestampEvaluator.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/RequestIdGenerator.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/SpecCompliantAccessTimestampEvaluator.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/StandardWebSessionManager.java
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/UuidRequestIdGenerator.java
Modified:
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/JavaUuidSessionIdGenerator.java
shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java
shiro/branches/SHIRO-317b/samples/guice/ (props changed)
shiro/branches/SHIRO-317b/samples/quickstart-guice/ (props changed)
shiro/branches/SHIRO-317b/samples/web/pom.xml
shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Publisher.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Publisher.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Publisher.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Publisher.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,9 @@
+package org.apache.shiro.event;
+
+/**
+ * @since 1.3
+ */
+public interface Publisher {
+
+ void publish(Object event);
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/ShiroEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/ShiroEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/ShiroEvent.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/ShiroEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,21 @@
+package org.apache.shiro.event;
+
+import java.util.Date;
+import java.util.EventObject;
+
+/**
+ * @since 1.3
+ */
+public class ShiroEvent extends EventObject {
+
+ private final long timestamp; //millis since Epoch (UTC time zone).
+
+ public ShiroEvent(Object source) {
+ super(source);
+ this.timestamp = new Date().getTime();
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubjectEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubjectEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubjectEvent.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubjectEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,20 @@
+package org.apache.shiro.event;
+
+import org.apache.shiro.subject.Subject;
+
+/**
+ * @since 1.3
+ */
+public class SubjectEvent extends ShiroEvent {
+
+ private final Subject subject;
+
+ public SubjectEvent(Subject subject) {
+ super(subject);
+ this.subject = subject;
+ }
+
+ public Subject getSubject() {
+ return subject;
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Subscriber.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Subscriber.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Subscriber.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/Subscriber.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,9 @@
+package org.apache.shiro.event;
+
+/**
+ * @since 1.3
+ */
+public interface Subscriber {
+
+ void onEvent(Object event);
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubscriberRegistry.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubscriberRegistry.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubscriberRegistry.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SubscriberRegistry.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,15 @@
+package org.apache.shiro.event;
+
+/**
+ * @since 1.3
+ */
+public interface SubscriberRegistry {
+
+ void subscribe(Subscriber subscriber);
+
+ void subscribe(Subscriber subscriber, Class... messageTypes);
+
+ void unsubscribe(Subscriber subscriber);
+
+ void unsubscribe(Subscriber subscriber, Class... messageTypes);
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SynchronousEventBus.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SynchronousEventBus.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SynchronousEventBus.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/event/SynchronousEventBus.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,255 @@
+package org.apache.shiro.event;
+
+import org.apache.shiro.util.Assert;
+
+import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * @since 1.3
+ */
+public class SynchronousEventBus implements Publisher, SubscriberRegistry {
+
+ private final InternalRegistry registry;
+
+ public SynchronousEventBus() {
+ this.registry = new InternalRegistry();
+ }
+
+ public void publish(Object event) {
+ if (event == null) {
+ return;
+ }
+ Class eventClass = event.getClass();
+ Set<Class> keys = registry.keySet();
+ for( Class clazz : keys) {
+ if (clazz.isAssignableFrom(eventClass)) {
+ List<Subscriber> subscribers = registry.get(clazz);
+ if (subscribers != null) {
+ for (Subscriber subscriber : subscribers) {
+ subscriber.onEvent(event);
+ }
+ }
+ }
+ }
+ }
+
+ public void subscribe(Subscriber subscriber) {
+ subscribe(subscriber, Object.class);
+ }
+
+ public void subscribe(Subscriber subscriber, Class... types) {
+ Assert.notNull(subscriber, "Subscriber argument cannot be null.");
+ types = (types != null && types.length > 0) ? types : new Class[]{Object.class};
+
+ for(Class clazz : types) {
+ this.registry.subscribe(clazz, subscriber);
+ }
+ }
+
+ public void unsubscribe(Subscriber subscriber) {
+ unsubscribe(subscriber, (Class[])null);
+ }
+
+ public void unsubscribe(Subscriber subscriber, Class... types) {
+ if (subscriber == null) {
+ return;
+ }
+ if (types == null) {
+ this.registry.unsubscribe(subscriber);
+ } else {
+ for (Class clazz : types) {
+ this.registry.unsubscribe(clazz, subscriber);
+ }
+ }
+ }
+
+ private static class SubscribedClassComparator implements Comparator<Class> {
+
+ public int compare(Class a, Class b) {
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (b == null) {
+ return 1;
+ } else if (a == b || a.equals(b)) {
+ return 0;
+ } else {
+ if (a.isAssignableFrom(b)) {
+ return 1;
+ } else if (b.isAssignableFrom(a)) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+
+ private static class InternalRegistry implements Map<Class, List<Subscriber>> {
+
+ private final Lock readLock;
+ private final Lock writeLock;
+ private final Map<Class,List<Subscriber>> map;
+
+ private InternalRegistry() {
+ this.map = new TreeMap<Class, List<Subscriber>>(new SubscribedClassComparator());
+ ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ readLock = rwl.readLock();
+ writeLock = rwl.writeLock();
+ }
+
+ public int size() {
+ readLock.lock();
+ try {
+ return map.size();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public boolean isEmpty() {
+ readLock.lock();
+ try {
+ return map.isEmpty();
+
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public boolean containsKey(Object o) {
+ readLock.lock();
+ try {
+ return map.containsKey(o);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public boolean containsValue(Object o) {
+ readLock.lock();
+ try {
+ return map.containsValue(o);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public List<Subscriber> get(Object o) {
+ readLock.lock();
+ try {
+ return map.get(o);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public List<Subscriber> put(Class c, List<Subscriber> subscribers) {
+ writeLock.lock();
+ try {
+ return this.map.put(c, subscribers);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public List<Subscriber> remove(Object o) {
+ writeLock.lock();
+ try {
+ return this.map.remove(o);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public void putAll(Map<? extends Class, ? extends List<Subscriber>> map) {
+ writeLock.lock();
+ try {
+ this.map.putAll(map);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public void subscribe(Class c, Subscriber s) {
+ writeLock.lock();
+ try {
+ List<Subscriber> subscribers = this.map.get(c);
+ if (subscribers == null) {
+ subscribers = new ArrayList<Subscriber>();
+ this.map.put(c, subscribers);
+ }
+ if (!subscribers.contains(s)) {
+ subscribers.add(s);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public void unsubscribe(Class c, Subscriber s) {
+ writeLock.lock();
+ try {
+ List<Subscriber> subscribers = this.map.get(c);
+ if (subscribers != null) {
+ subscribers.remove(s);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public void unsubscribe(Subscriber s) {
+ writeLock.lock();
+ try {
+ for(Map.Entry<Class,List<Subscriber>> entry : this.map.entrySet()) {
+ List<Subscriber> subscribers = entry.getValue();
+ subscribers.remove(s);
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public void clear() {
+ writeLock.lock();
+ try {
+ map.clear();
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public Set<Class> keySet() {
+ readLock.lock();
+ try {
+ return Collections.unmodifiableSet(map.keySet());
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public Collection<List<Subscriber>> values() {
+ readLock.lock();
+ try {
+ return Collections.unmodifiableCollection(map.values());
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ public Set<Entry<Class, List<Subscriber>>> entrySet() {
+ readLock.lock();
+ try {
+ return Collections.unmodifiableSet(map.entrySet());
+ } finally {
+ readLock.unlock();
+ }
+ }
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/InvalidSessionEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/InvalidSessionEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/InvalidSessionEvent.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/InvalidSessionEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,33 @@
+package org.apache.shiro.session.event;
+
+import org.apache.shiro.session.ExpiredSessionException;
+import org.apache.shiro.session.InvalidSessionException;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.SessionKey;
+
+/**
+ * @since 1.3
+ */
+public class InvalidSessionEvent extends SessionEvent {
+
+ private final SessionKey sessionKey;
+ private final InvalidSessionException exception;
+
+ public InvalidSessionEvent(Session session, SessionKey sessionKey, InvalidSessionException exception) {
+ super(session);
+ this.sessionKey = sessionKey;
+ this.exception = exception;
+ }
+
+ public SessionKey getSessionKey() {
+ return sessionKey;
+ }
+
+ public InvalidSessionException getException() {
+ return exception;
+ }
+
+ public boolean isSessionExpired() {
+ return exception instanceof ExpiredSessionException;
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/SessionEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/SessionEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/SessionEvent.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/SessionEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,21 @@
+package org.apache.shiro.session.event;
+
+import org.apache.shiro.event.ShiroEvent;
+import org.apache.shiro.session.Session;
+
+/**
+ * @since 1.3
+ */
+public abstract class SessionEvent extends ShiroEvent {
+
+ private final Session session;
+
+ public SessionEvent(Session session) {
+ super(session);
+ this.session = session;
+ }
+
+ public Session getSession() {
+ return session;
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StartedSessionEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StartedSessionEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StartedSessionEvent.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StartedSessionEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,21 @@
+package org.apache.shiro.session.event;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.SessionContext;
+
+/**
+ * @since 1.3
+ */
+public class StartedSessionEvent extends SessionEvent {
+
+ private final SessionContext sessionContext;
+
+ public StartedSessionEvent(Session session, SessionContext sessionContext) {
+ super(session);
+ this.sessionContext = sessionContext;
+ }
+
+ public SessionContext getSessionContext() {
+ return sessionContext;
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StoppedSessionEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StoppedSessionEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StoppedSessionEvent.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/event/StoppedSessionEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,21 @@
+package org.apache.shiro.session.event;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.SessionKey;
+
+/**
+ * @since 1.3
+ */
+public class StoppedSessionEvent extends SessionEvent {
+
+ private final SessionKey sessionKey;
+
+ public StoppedSessionEvent(Session session, SessionKey sessionKey) {
+ super(session);
+ this.sessionKey = sessionKey;
+ }
+
+ public SessionKey getSessionKey() {
+ return sessionKey;
+ }
+}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/StandardSessionManager.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/StandardSessionManager.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/StandardSessionManager.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/StandardSessionManager.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,456 @@
+package org.apache.shiro.session.mgt;
+
+import org.apache.shiro.cache.CacheManager;
+import org.apache.shiro.cache.CacheManagerAware;
+import org.apache.shiro.event.Publisher;
+import org.apache.shiro.session.*;
+import org.apache.shiro.session.event.InvalidSessionEvent;
+import org.apache.shiro.session.event.SessionEvent;
+import org.apache.shiro.session.event.StartedSessionEvent;
+import org.apache.shiro.session.event.StoppedSessionEvent;
+import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
+import org.apache.shiro.session.mgt.eis.SessionDAO;
+import org.apache.shiro.util.Assert;
+import org.apache.shiro.util.CollectionUtils;
+import org.apache.shiro.util.Destroyable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+
+/**
+ * @since 1.3
+ */
+public class StandardSessionManager implements NativeSessionManager, ValidatingSessionManager, CacheManagerAware, Destroyable {
+
+ private static final Logger log = LoggerFactory.getLogger(StandardSessionManager.class);
+
+ protected static final long MILLIS_PER_SECOND = 1000;
+ protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
+ public static final long DEFAULT_SESSION_TIMEOUT = 30 * MILLIS_PER_MINUTE; //30 minutes
+
+ private long defaultSessionTimeout = DEFAULT_SESSION_TIMEOUT;
+ private boolean deleteInvalidSessions;
+ private SessionValidationScheduler sessionValidationScheduler;
+ protected SessionFactory sessionFactory;
+ protected SessionDAO sessionDAO;
+ protected CacheManager cacheManager;
+ protected Publisher publisher;
+
+ public StandardSessionManager() {
+ this.sessionValidationScheduler = new ExecutorServiceSessionValidationScheduler(this);
+ this.deleteInvalidSessions = true;
+ this.sessionFactory = new SimpleSessionFactory();
+ this.sessionDAO = new MemorySessionDAO();
+ }
+
+ public long getDefaultSessionTimeout() {
+ return this.defaultSessionTimeout;
+ }
+
+ public void setDefaultSessionTimeout(long defaultSessionTimeout) {
+ this.defaultSessionTimeout = defaultSessionTimeout;
+ }
+
+ /**
+ * Returns {@code true} if sessions should be automatically deleted after they are discovered to be invalid,
+ * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. The
+ * default is {@code true} to ensure no orphans exist in the underlying data store.
+ * <h4>Usage</h4>
+ * It is ok to set this to {@code false} <b><em>ONLY</em></b> if you have some other process that you manage yourself
+ * that periodically deletes invalid sessions from the backing data store over time, such as via a Quartz or Cron
+ * job. If you do not do this, the invalid sessions will become 'orphans' and fill up the data store over time.
+ * <p/>
+ * This property is provided because some systems need the ability to perform querying/reporting against sessions in
+ * the data store, even after they have stopped or expired. Setting this attribute to {@code false} will allow
+ * such querying, but with the caveat that the application developer/configurer deletes the sessions themselves by
+ * some other means (cron, quartz, etc).
+ *
+ * @return {@code true} if sessions should be automatically deleted after they are discovered to be invalid,
+ * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control.
+ */
+ public boolean isDeleteInvalidSessions() {
+ return deleteInvalidSessions;
+ }
+
+ /**
+ * Sets whether or not sessions should be automatically deleted after they are discovered to be invalid. Default
+ * value is {@code true} to ensure no orphans will exist in the underlying data store.
+ * <h4>WARNING</h4>
+ * Only set this value to {@code false} if you are manually going to delete sessions yourself by some process
+ * (quartz, cron, etc) external to Shiro's control. See the
+ * {@link #isDeleteInvalidSessions() isDeleteInvalidSessions()} JavaDoc for more.
+ *
+ * @param deleteInvalidSessions whether or not sessions should be automatically deleted after they are discovered
+ * to be invalid.
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public void setDeleteInvalidSessions(boolean deleteInvalidSessions) {
+ this.deleteInvalidSessions = deleteInvalidSessions;
+ }
+
+ public SessionValidationScheduler getSessionValidationScheduler() {
+ return sessionValidationScheduler;
+ }
+
+ public void setSessionValidationScheduler(SessionValidationScheduler sessionValidationScheduler) {
+ this.sessionValidationScheduler = sessionValidationScheduler;
+ }
+
+ public SessionDAO getSessionDAO() {
+ return this.sessionDAO;
+ }
+
+ public void setSessionDAO(SessionDAO sessionDAO) {
+ this.sessionDAO = sessionDAO;
+ applyCacheManagerToSessionDAO();
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public CacheManager getCacheManager() {
+ return this.cacheManager;
+ }
+
+ public void setCacheManager(CacheManager cacheManager) {
+ this.cacheManager = cacheManager;
+ applyCacheManagerToSessionDAO();
+ }
+
+ /**
+ * Sets the internal {@code CacheManager} on the {@code SessionDAO} if it implements the
+ * {@link org.apache.shiro.cache.CacheManagerAware CacheManagerAware} interface.
+ * <p/>
+ * This method is called after setting a cacheManager via the
+ * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) setCacheManager} method <em>em</em> when
+ * setting a {@code SessionDAO} via the {@link #setSessionDAO} method to allow it to be propagated
+ * in either case.
+ */
+ private void applyCacheManagerToSessionDAO() {
+ if (this.cacheManager != null && this.sessionDAO != null && this.sessionDAO instanceof CacheManagerAware) {
+ ((CacheManagerAware) this.sessionDAO).setCacheManager(this.cacheManager);
+ }
+ }
+
+ /**
+ * Returns the {@code SessionFactory} used to generate new {@link Session} instances. The default instance
+ * is a {@link SimpleSessionFactory}.
+ *
+ * @return the {@code SessionFactory} used to generate new {@link Session} instances.
+ */
+ public SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+ /**
+ * Sets the {@code SessionFactory} used to generate new {@link Session} instances. The default instance
+ * is a {@link SimpleSessionFactory}.
+ *
+ * @param sessionFactory the {@code SessionFactory} used to generate new {@link Session} instances.
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public void setSessionFactory(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public Publisher getPublisher() {
+ return publisher;
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public void setPublisher(Publisher publisher) {
+ this.publisher = publisher;
+ }
+
+ /* =====================================================================
+ Destroyable implementation
+ ===================================================================== */
+
+ public void destroy() throws Exception {
+ if (this.sessionValidationScheduler != null) {
+ this.sessionValidationScheduler.disableSessionValidation();
+ }
+ }
+
+ /* =====================================================================
+ SessionManager implementation
+ ===================================================================== */
+
+ public Session start(SessionContext context) {
+ enableSessionValidationIfNecessary();
+ Session internal = createInternalSession(context);
+ //Don't expose the EIS-tier Session object to the client-tier:
+ return createExposedSession(internal, context);
+ }
+
+ protected Session createInternalSession(SessionContext context) {
+
+ Session session = getSessionFactory().createSession(context);
+ if (log.isTraceEnabled()) {
+ log.trace("Creating session for host {}", session.getHost());
+ }
+
+ session.setTimeout(getDefaultSessionTimeout());
+
+ if (log.isDebugEnabled()) {
+ log.debug("Creating new EIS record for new session instance [" + session + "]");
+ }
+
+ createInternalSession(session, context);
+
+ StartedSessionEvent event = new StartedSessionEvent(session, context);
+ notify(event);
+
+ return session;
+ }
+
+ protected void createInternalSession(Session session, SessionContext context) {
+ getSessionDAO().create(session);
+ }
+
+ public Session getSession(SessionKey key) throws SessionException {
+ enableSessionValidationIfNecessary();
+ Session session = getInternalSession(key);
+ return session != null ? createExposedSession(session, key) : null;
+ }
+
+ protected Session getInternalSession(SessionKey key) {
+
+ log.trace("Attempting to retrieve session with key {}", key);
+
+ Serializable sessionId = getSessionId(key);
+ if (sessionId == null) {
+ log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " +
+ "session could not be found.", key);
+ return null;
+ }
+ Session session = getInternalSession(key, sessionId);
+ if (session == 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 + "]";
+ throw new UnknownSessionException(msg);
+ }
+
+ validate(session, key);
+
+ return session;
+ }
+
+ protected Session getInternalSession(SessionKey sessionKey, Serializable resolvedSessionId) {
+ return getSessionDAO().readSession(resolvedSessionId);
+ }
+
+ protected final void enableSessionValidationIfNecessary() {
+ if (this.sessionValidationScheduler != null && !this.sessionValidationScheduler.isEnabled()) {
+ this.sessionValidationScheduler.enableSessionValidation();
+ }
+ }
+
+ protected Serializable getSessionId(SessionKey sessionKey) {
+ return sessionKey.getSessionId();
+ }
+
+ /* =====================================================================
+ ValidatingSessionManager methods
+ ===================================================================== */
+
+ public void validateSessions() {
+ log.debug("Validating active sessions...");
+
+ int invalidCount = 0;
+
+ Collection<Session> activeSessions = getSessionDAO().getActiveSessions();
+
+ if (activeSessions != null) {
+ for (Session s : activeSessions) {
+ try {
+ //simulate a lookup key to satisfy the method signature.
+ //this could probably stand to be cleaned up in future versions:
+ SessionKey key = new DefaultSessionKey(s.getId());
+ validate(s, key);
+ } catch (InvalidSessionException e) {
+ if (log.isTraceEnabled()) {
+ boolean expired = (e instanceof ExpiredSessionException);
+ String msg = "Invalidated session with id [" + s.getId() + "]" +
+ (expired ? " (expired)" : " (stopped)");
+ log.trace(msg);
+ }
+ invalidCount++;
+ }
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ String msg = "Finished session validation.";
+ if (invalidCount > 0) {
+ msg += " [" + invalidCount + "] sessions were stopped.";
+ } else {
+ msg += " No sessions were stopped.";
+ }
+ log.debug(msg);
+ }
+ }
+
+ protected void validate(Session session, SessionKey key) throws InvalidSessionException {
+ Assert.isInstanceOf(ValidatingSession.class, session, StandardSessionManager.class.getName() +
+ " implementations require native sessions to implement " + ValidatingSession.class.getName());
+
+ try {
+ ((ValidatingSession) session).validate();
+ } catch (InvalidSessionException ise) {
+ onStop(session, key, ise);
+ throw ise;
+ }
+ }
+
+ protected void onStop(Session session, SessionKey key, InvalidSessionException ise) {
+
+ boolean expired = ise instanceof ExpiredSessionException;
+
+ if (session instanceof SimpleSession) {
+ SimpleSession ss = (SimpleSession) session;
+ if (expired) {
+ ss.setExpired(expired);
+ } else {
+ Date stopTs = ss.getStopTimestamp();
+ ss.setLastAccessTime(stopTs);
+ }
+ }
+
+ Session immutable = beforeStopNotification(session);
+ SessionEvent event = (ise != null) ?
+ new InvalidSessionEvent(immutable, key, ise) :
+ new StoppedSessionEvent(immutable, key);
+ notify(event);
+
+ if (isDeleteInvalidSessions()) {
+ delete(session, key);
+ } else {
+ update(session, key);
+ }
+ }
+
+ protected void delete(Session session, SessionKey key) {
+ log.debug("Deleting DAO session {}", session.getId());
+ getSessionDAO().delete(session);
+ }
+
+ protected void update(Session session, SessionKey key) {
+ log.debug("Updating DAO session {}", session.getId());
+ getSessionDAO().update(session);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void notify(Object event) {
+ if (event != null && this.publisher != null) {
+ this.publisher.publish(event);
+ }
+ }
+
+ protected Session createExposedSession(Session session, Object context) {
+ return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
+ }
+
+ /**
+ * Returns the session instance to use to pass to registered {@code SessionListener}s for notification
+ * that the session has been stopped (stopped or expired).
+ * <p/>
+ * The default implementation returns an {@link ImmutableProxiedSession ImmutableProxiedSession} instance to ensure
+ * that the specified {@code session} argument is not modified by any listeners.
+ *
+ * @param session the stopped {@code Session}.
+ * @return the {@code Session} instance to use for {@link #notify(Object) notification}.
+ */
+ protected Session beforeStopNotification(Session session) {
+ return new ImmutableProxiedSession(session);
+ }
+
+ /* =====================================================================
+ NativeSessionManager implementation
+ ===================================================================== */
+
+ public Date getStartTimestamp(SessionKey key) {
+ return getInternalSession(key).getStartTimestamp();
+ }
+
+ public Date getLastAccessTime(SessionKey key) {
+ return getInternalSession(key).getLastAccessTime();
+ }
+
+ public long getTimeout(SessionKey key) throws InvalidSessionException {
+ return getInternalSession(key).getTimeout();
+ }
+
+ public void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException {
+ Session session = getInternalSession(key);
+ session.setTimeout(maxIdleTimeInMillis);
+ update(session, key);
+ }
+
+ public void touch(SessionKey key) throws InvalidSessionException {
+ Session session = getInternalSession(key);
+ session.touch();
+ update(session, key);
+ }
+
+ public String getHost(SessionKey key) {
+ return getInternalSession(key).getHost();
+ }
+
+ public Collection<Object> getAttributeKeys(SessionKey key) {
+ Collection<Object> c = getInternalSession(key).getAttributeKeys();
+ if (!CollectionUtils.isEmpty(c)) {
+ return Collections.unmodifiableCollection(c);
+ }
+ return Collections.emptySet();
+ }
+
+ public Object getAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
+ return getInternalSession(sessionKey).getAttribute(attributeKey);
+ }
+
+ public void setAttribute(SessionKey key, Object attributeKey, Object value) throws InvalidSessionException {
+ if (value == null) {
+ removeAttribute(key, attributeKey);
+ } else {
+ Session s = getInternalSession(key);
+ s.setAttribute(attributeKey, value);
+ update(s, key);
+ }
+ }
+
+ public Object removeAttribute(SessionKey key, Object attributeKey) throws InvalidSessionException {
+ Session session = getInternalSession(key);
+ Object removed = session.removeAttribute(attributeKey);
+ if (removed != null) {
+ update(session, key);
+ }
+ return removed;
+ }
+
+ public boolean isValid(SessionKey key) {
+ try {
+ checkValid(key);
+ return true;
+ } catch (InvalidSessionException e) {
+ return false;
+ }
+ }
+
+ public void stop(SessionKey key) throws InvalidSessionException {
+ Session session = getInternalSession(key);
+ if (log.isDebugEnabled()) {
+ log.debug("Stopping session with id [" + session.getId() + "]");
+ }
+ session.stop();
+ onStop(session, key, null);
+ }
+
+ public void checkValid(SessionKey key) throws InvalidSessionException {
+ //just try to acquire it. If there is a problem, an exception will be thrown:
+ getInternalSession(key);
+ }
+}
Modified: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/JavaUuidSessionIdGenerator.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/JavaUuidSessionIdGenerator.java?rev=1351193&r1=1351192&r2=1351193&view=diff
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/JavaUuidSessionIdGenerator.java (original)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/JavaUuidSessionIdGenerator.java Mon Jun 18 02:01:11 2012
@@ -22,6 +22,7 @@ import org.apache.shiro.session.Session;
import java.io.Serializable;
import java.util.UUID;
+import java.util.regex.Pattern;
/**
* {@link SessionIdGenerator} that generates String values of JDK {@link java.util.UUID}'s as the session IDs.
@@ -30,6 +31,8 @@ import java.util.UUID;
*/
public class JavaUuidSessionIdGenerator implements SessionIdGenerator {
+ private final Pattern PATTERN = Pattern.compile("-");
+
/**
* Ignores the method argument and simply returns
* {@code UUID}.{@link java.util.UUID#randomUUID() randomUUID()}.{@code toString()}.
@@ -38,6 +41,7 @@ public class JavaUuidSessionIdGenerator
* @return the String value of the JDK's next {@link UUID#randomUUID() randomUUID()}.
*/
public Serializable generateId(Session session) {
- return UUID.randomUUID().toString();
+ String id = UUID.randomUUID().toString();
+ return PATTERN.matcher(id).replaceAll("").toUpperCase();
}
}
Modified: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java?rev=1351193&r1=1351192&r2=1351193&view=diff
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java (original)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java Mon Jun 18 02:01:11 2012
@@ -74,10 +74,12 @@ public class MemorySessionDAO extends Ab
if (id == null) {
throw new NullPointerException("id argument cannot be null.");
}
+ log.trace("Storing session {}.", id);
return sessions.putIfAbsent(id, session);
}
protected Session doReadSession(Serializable sessionId) {
+ log.trace("Retrieving session {}", sessionId);
return sessions.get(sessionId);
}
@@ -91,6 +93,7 @@ public class MemorySessionDAO extends Ab
}
Serializable id = session.getId();
if (id != null) {
+ log.trace("Removing session {}", id);
sessions.remove(id);
}
}
Added: shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/util/Assert.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/util/Assert.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/util/Assert.java (added)
+++ shiro/branches/SHIRO-317b/core/src/main/java/org/apache/shiro/util/Assert.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,387 @@
+package org.apache.shiro.util;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Assertion utility class that assists in validating arguments.
+ * Useful for identifying programmer errors early and clearly at runtime.
+ *
+ * <p>For example, if the contract of a public method states it does not
+ * allow <code>null</code> arguments, Assert can be used to validate that
+ * contract. Doing this clearly indicates a contract violation when it
+ * occurs and protects the class's invariants.
+ *
+ * <p>Typically used to validate method arguments rather than configuration
+ * properties, to check for cases that are usually programmer errors rather than
+ * configuration errors. In contrast to config initialization code, there is
+ * usally no point in falling back to defaults in such methods.
+ *
+ * <p>This class is similar to JUnit's assertion library. If an argument value is
+ * deemed invalid, an {@link IllegalArgumentException} is thrown (typically).
+ * For example:
+ *
+ * <pre class="code">
+ * Assert.notNull(clazz, "The class must not be null");
+ * Assert.isTrue(i > 0, "The value must be greater than zero");</pre>
+ *
+ * Mainly for internal use within the framework; consider Jakarta's Commons Lang
+ * >= 2.0 for a more comprehensive suite of assertion utilities.
+ * <p/>
+ * <em>Gratefully borrowed from the Spring Framework, also Apache 2.0 licensed</em>
+ *
+ * @author Keith Donald
+ * @author Juergen Hoeller
+ * @author Colin Sampaleanu
+ * @author Rob Harrop
+ * @since 1.3
+ */
+public abstract class Assert {
+
+ /**
+ * Assert a boolean expression, throwing <code>IllegalArgumentException</code>
+ * if the test result is <code>false</code>.
+ * <pre class="code">Assert.isTrue(i > 0, "The value must be greater than zero");</pre>
+ * @param expression a boolean expression
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if expression is <code>false</code>
+ */
+ public static void isTrue(boolean expression, String message) {
+ if (!expression) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert a boolean expression, throwing <code>IllegalArgumentException</code>
+ * if the test result is <code>false</code>.
+ * <pre class="code">Assert.isTrue(i > 0);</pre>
+ * @param expression a boolean expression
+ * @throws IllegalArgumentException if expression is <code>false</code>
+ */
+ public static void isTrue(boolean expression) {
+ isTrue(expression, "[Assertion failed] - this expression must be true");
+ }
+
+ /**
+ * Assert that an object is <code>null</code> .
+ * <pre class="code">Assert.isNull(value, "The value must be null");</pre>
+ * @param object the object to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object is not <code>null</code>
+ */
+ public static void isNull(Object object, String message) {
+ if (object != null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that an object is <code>null</code> .
+ * <pre class="code">Assert.isNull(value);</pre>
+ * @param object the object to check
+ * @throws IllegalArgumentException if the object is not <code>null</code>
+ */
+ public static void isNull(Object object) {
+ isNull(object, "[Assertion failed] - the object argument must be null");
+ }
+
+ /**
+ * Assert that an object is not <code>null</code> .
+ * <pre class="code">Assert.notNull(clazz, "The class must not be null");</pre>
+ * @param object the object to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object is <code>null</code>
+ */
+ public static void notNull(Object object, String message) {
+ if (object == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that an object is not <code>null</code> .
+ * <pre class="code">Assert.notNull(clazz);</pre>
+ * @param object the object to check
+ * @throws IllegalArgumentException if the object is <code>null</code>
+ */
+ public static void notNull(Object object) {
+ notNull(object, "[Assertion failed] - this argument is required; it must not be null");
+ }
+
+ /**
+ * Assert that the given String is not empty; that is,
+ * it must not be <code>null</code> and not the empty String.
+ * <pre class="code">Assert.hasLength(name, "Name must not be empty");</pre>
+ * @param text the String to check
+ * @param message the exception message to use if the assertion fails
+ * @see StringUtils#hasLength
+ */
+ public static void hasLength(String text, String message) {
+ if (!StringUtils.hasLength(text)) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that the given String is not empty; that is,
+ * it must not be <code>null</code> and not the empty String.
+ * <pre class="code">Assert.hasLength(name);</pre>
+ * @param text the String to check
+ * @see StringUtils#hasLength
+ */
+ public static void hasLength(String text) {
+ hasLength(text,
+ "[Assertion failed] - this String argument must have length; it must not be null or empty");
+ }
+
+ /**
+ * Assert that the given String has valid text content; that is, it must not
+ * be <code>null</code> and must contain at least one non-whitespace character.
+ * <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
+ * @param text the String to check
+ * @param message the exception message to use if the assertion fails
+ * @see StringUtils#hasText
+ */
+ public static void hasText(String text, String message) {
+ if (!StringUtils.hasText(text)) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that the given String has valid text content; that is, it must not
+ * be <code>null</code> and must contain at least one non-whitespace character.
+ * <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
+ * @param text the String to check
+ * @see StringUtils#hasText
+ */
+ public static void hasText(String text) {
+ hasText(text,
+ "[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
+ }
+
+ /**
+ * Assert that the given text does not contain the given substring.
+ * <pre class="code">Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");</pre>
+ * @param textToSearch the text to search
+ * @param substring the substring to find within the text
+ * @param message the exception message to use if the assertion fails
+ */
+ public static void doesNotContain(String textToSearch, String substring, String message) {
+ if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) &&
+ textToSearch.indexOf(substring) != -1) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that the given text does not contain the given substring.
+ * <pre class="code">Assert.doesNotContain(name, "rod");</pre>
+ * @param textToSearch the text to search
+ * @param substring the substring to find within the text
+ */
+ public static void doesNotContain(String textToSearch, String substring) {
+ doesNotContain(textToSearch, substring,
+ "[Assertion failed] - this String argument must not contain the substring [" + substring + "]");
+ }
+
+
+ /**
+ * Assert that an array has elements; that is, it must not be
+ * <code>null</code> and must have at least one element.
+ * <pre class="code">Assert.notEmpty(array, "The array must have elements");</pre>
+ * @param array the array to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object array is <code>null</code> or has no elements
+ */
+ public static void notEmpty(Object[] array, String message) {
+ if (array == null || array.length == 0) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that an array has elements; that is, it must not be
+ * <code>null</code> and must have at least one element.
+ * <pre class="code">Assert.notEmpty(array);</pre>
+ * @param array the array to check
+ * @throws IllegalArgumentException if the object array is <code>null</code> or has no elements
+ */
+ public static void notEmpty(Object[] array) {
+ notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
+ }
+
+ /**
+ * Assert that an array has no null elements.
+ * Note: Does not complain if the array is empty!
+ * <pre class="code">Assert.noNullElements(array, "The array must have non-null elements");</pre>
+ * @param array the array to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object array contains a <code>null</code> element
+ */
+ public static void noNullElements(Object[] array, String message) {
+ if (array != null) {
+ for (int i = 0; i < array.length; i++) {
+ if (array[i] == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+ }
+ }
+
+ /**
+ * Assert that an array has no null elements.
+ * Note: Does not complain if the array is empty!
+ * <pre class="code">Assert.noNullElements(array);</pre>
+ * @param array the array to check
+ * @throws IllegalArgumentException if the object array contains a <code>null</code> element
+ */
+ public static void noNullElements(Object[] array) {
+ noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
+ }
+
+ /**
+ * Assert that a collection has elements; that is, it must not be
+ * <code>null</code> and must have at least one element.
+ * <pre class="code">Assert.notEmpty(collection, "Collection must have elements");</pre>
+ * @param collection the collection to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the collection is <code>null</code> or has no elements
+ */
+ public static void notEmpty(Collection collection, String message) {
+ if (CollectionUtils.isEmpty(collection)) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that a collection has elements; that is, it must not be
+ * <code>null</code> and must have at least one element.
+ * <pre class="code">Assert.notEmpty(collection, "Collection must have elements");</pre>
+ * @param collection the collection to check
+ * @throws IllegalArgumentException if the collection is <code>null</code> or has no elements
+ */
+ public static void notEmpty(Collection collection) {
+ notEmpty(collection,
+ "[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
+ }
+
+ /**
+ * Assert that a Map has entries; that is, it must not be <code>null</code>
+ * and must have at least one entry.
+ * <pre class="code">Assert.notEmpty(map, "Map must have entries");</pre>
+ * @param map the map to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the map is <code>null</code> or has no entries
+ */
+ public static void notEmpty(Map map, String message) {
+ if (CollectionUtils.isEmpty(map)) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that a Map has entries; that is, it must not be <code>null</code>
+ * and must have at least one entry.
+ * <pre class="code">Assert.notEmpty(map);</pre>
+ * @param map the map to check
+ * @throws IllegalArgumentException if the map is <code>null</code> or has no entries
+ */
+ public static void notEmpty(Map map) {
+ notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
+ }
+
+
+ /**
+ * Assert that the provided object is an instance of the provided class.
+ * <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
+ * @param clazz the required class
+ * @param obj the object to check
+ * @throws IllegalArgumentException if the object is not an instance of clazz
+ * @see Class#isInstance
+ */
+ public static void isInstanceOf(Class clazz, Object obj) {
+ isInstanceOf(clazz, obj, "");
+ }
+
+ /**
+ * Assert that the provided object is an instance of the provided class.
+ * <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
+ * @param type the type to check against
+ * @param obj the object to check
+ * @param message a message which will be prepended to the message produced by
+ * the function itself, and which may be used to provide context. It should
+ * normally end in a ": " or ". " so that the function generate message looks
+ * ok when prepended to it.
+ * @throws IllegalArgumentException if the object is not an instance of clazz
+ * @see Class#isInstance
+ */
+ public static void isInstanceOf(Class type, Object obj, String message) {
+ notNull(type, "Type to check against must not be null");
+ if (!type.isInstance(obj)) {
+ throw new IllegalArgumentException(message +
+ "Object of class [" + (obj != null ? obj.getClass().getName() : "null") +
+ "] must be an instance of " + type);
+ }
+ }
+
+ /**
+ * Assert that <code>superType.isAssignableFrom(subType)</code> is <code>true</code>.
+ * <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
+ * @param superType the super type to check
+ * @param subType the sub type to check
+ * @throws IllegalArgumentException if the classes are not assignable
+ */
+ public static void isAssignable(Class superType, Class subType) {
+ isAssignable(superType, subType, "");
+ }
+
+ /**
+ * Assert that <code>superType.isAssignableFrom(subType)</code> is <code>true</code>.
+ * <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
+ * @param superType the super type to check against
+ * @param subType the sub type to check
+ * @param message a message which will be prepended to the message produced by
+ * the function itself, and which may be used to provide context. It should
+ * normally end in a ": " or ". " so that the function generate message looks
+ * ok when prepended to it.
+ * @throws IllegalArgumentException if the classes are not assignable
+ */
+ public static void isAssignable(Class superType, Class subType, String message) {
+ notNull(superType, "Type to check against must not be null");
+ if (subType == null || !superType.isAssignableFrom(subType)) {
+ throw new IllegalArgumentException(message + subType + " is not assignable to " + superType);
+ }
+ }
+
+
+ /**
+ * Assert a boolean expression, throwing <code>IllegalStateException</code>
+ * if the test result is <code>false</code>. Call isTrue if you wish to
+ * throw IllegalArgumentException on an assertion failure.
+ * <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
+ * @param expression a boolean expression
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalStateException if expression is <code>false</code>
+ */
+ public static void state(boolean expression, String message) {
+ if (!expression) {
+ throw new IllegalStateException(message);
+ }
+ }
+
+ /**
+ * Assert a boolean expression, throwing {@link IllegalStateException}
+ * if the test result is <code>false</code>.
+ * <p>Call {@link #isTrue(boolean)} if you wish to
+ * throw {@link IllegalArgumentException} on an assertion failure.
+ * <pre class="code">Assert.state(id == null);</pre>
+ * @param expression a boolean expression
+ * @throws IllegalStateException if the supplied expression is <code>false</code>
+ */
+ public static void state(boolean expression) {
+ state(expression, "[Assertion failed] - this state invariant must be true");
+ }
+
+}
Propchange: shiro/branches/SHIRO-317b/samples/guice/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Jun 18 02:01:11 2012
@@ -0,0 +1 @@
+*.iml
Propchange: shiro/branches/SHIRO-317b/samples/quickstart-guice/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Jun 18 02:01:11 2012
@@ -0,0 +1 @@
+*.iml
Modified: shiro/branches/SHIRO-317b/samples/web/pom.xml
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/samples/web/pom.xml?rev=1351193&r1=1351192&r2=1351193&view=diff
==============================================================================
--- shiro/branches/SHIRO-317b/samples/web/pom.xml (original)
+++ shiro/branches/SHIRO-317b/samples/web/pom.xml Mon Jun 18 02:01:11 2012
@@ -113,6 +113,7 @@
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
+ <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Added: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/BeginServletRequestEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/BeginServletRequestEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/BeginServletRequestEvent.java (added)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/BeginServletRequestEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,18 @@
+package org.apache.shiro.web.event;
+
+import org.apache.shiro.subject.Subject;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * Event triggered at the beginning of a Shiro-filtered servlet request, before the filter chain has been invoked.
+ *
+ * @since 1.3
+ */
+public class BeginServletRequestEvent extends ServletRequestEvent {
+
+ public BeginServletRequestEvent(Subject subject, ServletRequest servletRequest, ServletResponse servletResponse) {
+ super(subject, servletRequest, servletResponse);
+ }
+}
Added: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/EndServletRequestEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/EndServletRequestEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/EndServletRequestEvent.java (added)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/EndServletRequestEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,32 @@
+package org.apache.shiro.web.event;
+
+import org.apache.shiro.subject.Subject;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * Event triggered at the end of a Shiro-filtered servlet request.
+ *
+ * @since 1.3
+ */
+public class EndServletRequestEvent extends ServletRequestEvent {
+
+ private final Throwable throwable;
+
+ public EndServletRequestEvent(Subject subject, ServletRequest servletRequest, ServletResponse servletResponse, Throwable t) {
+ super(subject, servletRequest, servletResponse);
+ this.throwable = t;
+ }
+
+ /**
+ * Returns any Throwable that might have resulted during request execution or {@code null} if no Throwable was
+ * triggered.
+ *
+ * @return any Throwable that might have resulted during request execution or {@code null} if no Throwable was
+ * triggered.
+ */
+ public Throwable getThrowable() {
+ return throwable;
+ }
+}
Added: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/ServletRequestEvent.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/ServletRequestEvent.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/ServletRequestEvent.java (added)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/event/ServletRequestEvent.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,32 @@
+package org.apache.shiro.web.event;
+
+import org.apache.shiro.event.SubjectEvent;
+import org.apache.shiro.subject.Subject;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * An event triggered during the lifecycle of a Shiro-filtered ServletRequest.
+ *
+ * @since 1.3
+ */
+public class ServletRequestEvent extends SubjectEvent {
+
+ private final ServletRequest servletRequest;
+ private final ServletResponse servletResponse;
+
+ public ServletRequestEvent(Subject subject, ServletRequest servletRequest, ServletResponse servletResponse) {
+ super(subject);
+ this.servletRequest = servletRequest;
+ this.servletResponse = servletResponse;
+ }
+
+ public ServletRequest getServletRequest() {
+ return servletRequest;
+ }
+
+ public ServletResponse getServletResponse() {
+ return servletResponse;
+ }
+}
Modified: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java?rev=1351193&r1=1351192&r2=1351193&view=diff
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java (original)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java Mon Jun 18 02:01:11 2012
@@ -19,9 +19,18 @@
package org.apache.shiro.web.servlet;
import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.event.Publisher;
+import org.apache.shiro.event.Subscriber;
+import org.apache.shiro.event.SynchronousEventBus;
+import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.ThreadContext;
+import org.apache.shiro.web.event.BeginServletRequestEvent;
+import org.apache.shiro.web.event.EndServletRequestEvent;
+import org.apache.shiro.web.event.ServletRequestEvent;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
@@ -36,6 +45,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.util.UUID;
import java.util.concurrent.Callable;
/**
@@ -68,13 +78,15 @@ import java.util.concurrent.Callable;
* See the Shiro <a href="http://shiro.apache.org/subject.html">Subject documentation</a> for more information as to
* if you would do this, particularly the sections on the {@code Subject.Builder} and Thread Association.
*
- * @since 1.0
* @see <a href="http://shiro.apache.org/subject.html">Subject documentation</a>
+ * @since 1.0
*/
public abstract class AbstractShiroFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(AbstractShiroFilter.class);
+ public static final String REQUEST_ID_ATTR_NAME = "ApacheShiroRequestId";
+
private static final String STATIC_INIT_PARAM_NAME = "staticSecurityManagerEnabled";
// Reference to the security manager used by this filter
@@ -83,9 +95,14 @@ public abstract class AbstractShiroFilte
// Used to determine which chain should handle an incoming request/response
private FilterChainResolver filterChainResolver;
+ // Used to publish various events of interest
+ // since 1.3
+ private Publisher publisher;
+
/**
* Whether or not to bind the constructed SecurityManager instance to static memory (via
* SecurityUtils.setSecurityManager). This was added to support https://issues.apache.org/jira/browse/SHIRO-287
+ *
* @since 1.2
*/
private boolean staticSecurityManagerEnabled;
@@ -110,6 +127,16 @@ public abstract class AbstractShiroFilte
this.filterChainResolver = filterChainResolver;
}
+ //since 1.3
+ public Publisher getPublisher() {
+ return publisher;
+ }
+
+ //since 1.3
+ public void setPublisher(Publisher publisher) {
+ this.publisher = publisher;
+ }
+
/**
* Returns {@code true} if the constructed {@link #getSecurityManager() securityManager} reference should be bound
* to static memory (via
@@ -119,12 +146,11 @@ public abstract class AbstractShiroFilte
* The default value is {@code false}.
* <p/>
*
- *
* @return {@code true} if the constructed {@link #getSecurityManager() securityManager} reference should be bound
* to static memory (via {@code SecurityUtils.}{@link SecurityUtils#setSecurityManager(org.apache.shiro.mgt.SecurityManager) setSecurityManager}),
* {@code false} otherwise.
- * @since 1.2
* @see <a href="https://issues.apache.org/jira/browse/SHIRO-287">SHIRO-287</a>
+ * @since 1.2
*/
public boolean isStaticSecurityManagerEnabled() {
return staticSecurityManagerEnabled;
@@ -137,10 +163,10 @@ public abstract class AbstractShiroFilte
* The default value is {@code false}.
*
* @param staticSecurityManagerEnabled if the constructed {@link #getSecurityManager() securityManager} reference
- * should be bound to static memory (via
- * {@code SecurityUtils.}{@link SecurityUtils#setSecurityManager(org.apache.shiro.mgt.SecurityManager) setSecurityManager}).
- * @since 1.2
+ * should be bound to static memory (via
+ * {@code SecurityUtils.}{@link SecurityUtils#setSecurityManager(org.apache.shiro.mgt.SecurityManager) setSecurityManager}).
* @see <a href="https://issues.apache.org/jira/browse/SHIRO-287">SHIRO-287</a>
+ * @since 1.2
*/
public void setStaticSecurityManagerEnabled(boolean staticSecurityManagerEnabled) {
this.staticSecurityManagerEnabled = staticSecurityManagerEnabled;
@@ -155,14 +181,18 @@ public abstract class AbstractShiroFilte
if (isStaticSecurityManagerEnabled()) {
SecurityUtils.setSecurityManager(getSecurityManager());
}
+ //since 1.3: TODO: this should be done in configuration, not here. WORK IN PROGRESS.
+ ensurePublisher();
+ //since 1.3 TODO: this should be done in configuration, not here. WORK IN PROGRESS.
+ subscribeSessionManager();
}
/**
* Checks if the init-param that configures the filter to use static memory has been configured, and if so,
* sets the {@link #setStaticSecurityManagerEnabled(boolean)} attribute with the configured value.
*
- * @since 1.2
* @see <a href="https://issues.apache.org/jira/browse/SHIRO-287">SHIRO-287</a>
+ * @since 1.2
*/
private void applyStaticSecurityManagerEnabledConfig() {
String value = getInitParam(STATIC_INIT_PARAM_NAME);
@@ -191,6 +221,25 @@ public abstract class AbstractShiroFilte
}
}
+ //since 1.3: TODO: this should be done in configuration, not here. WORK IN PROGRESS.
+ private void ensurePublisher() {
+ if (this.publisher == null) {
+ this.publisher = new SynchronousEventBus();
+ }
+ }
+
+ //since 1.3 TODO: this should be done in configuration, not here. WORK IN PROGRESS.
+ private void subscribeSessionManager() {
+ WebSecurityManager securityManager = getSecurityManager();
+ if (securityManager instanceof SessionsSecurityManager) {
+ SessionManager sessionManager = ((SessionsSecurityManager) securityManager).getSessionManager();
+ if (sessionManager instanceof Subscriber && this.publisher instanceof SynchronousEventBus) {
+ Subscriber subscriber = (Subscriber) sessionManager;
+ ((SynchronousEventBus) this.publisher).subscribe(subscriber, ServletRequestEvent.class);
+ }
+ }
+ }
+
protected WebSecurityManager createDefaultSecurityManager() {
return new DefaultWebSecurityManager();
}
@@ -347,31 +396,50 @@ public abstract class AbstractShiroFilte
* @throws IOException if an IO error occurs
* @throws javax.servlet.ServletException if an Throwable other than an IOException
*/
+ @SuppressWarnings("unchecked")
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
+ ServletRequest request = null;
+ ServletResponse response = null;
+ Subject subject = null;
Throwable t = null;
try {
- final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
- final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
-
- 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;
- }
- });
+ final ServletRequest finalRequest = request = prepareServletRequest(servletRequest, servletResponse, chain);
+ final ServletResponse finalResponse = response = prepareServletResponse(request, servletResponse, chain);
+ subject = createSubject(request, response);
+
+ getPublisher().publish(new BeginServletRequestEvent(subject, request, response));
+
+ try {
+ subject.execute(new Callable() {
+ public Object call() throws Exception {
+ updateSessionLastAccessTimeIfNecessary(finalRequest, finalResponse);
+ executeChain(finalRequest, finalResponse, chain);
+ return null;
+ }
+ });
+ } finally {
+ ThreadContext.remove(); //silence innocuous Tomcat ThreadLocal warnings
+ }
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}
+ try {
+ getPublisher().publish(new EndServletRequestEvent(subject, request, response, t));
+ } catch (Throwable t2) {
+ if (t == null) {
+ t = t2;
+ } else {
+ log.warn("afterCompletion resulted in an unexpected Throwable. This will be ignored and logged " +
+ "here the original request exception will be propagated instead.", t2);
+ }
+ }
+
if (t != null) {
if (t instanceof ServletException) {
throw (ServletException) t;
@@ -385,6 +453,26 @@ public abstract class AbstractShiroFilte
}
}
+ //since 1.3, added for backwards compatibility only!
+ protected final void updateSessionLastAccessTimeIfNecessary(ServletRequest request, ServletResponse response) {
+ WebSecurityManager securityManager = getSecurityManager();
+ if (securityManager.isHttpSessionMode()) {
+ return;
+ }
+
+ if (securityManager instanceof SessionsSecurityManager) {
+ SessionManager manager = ((SessionsSecurityManager) securityManager).getSessionManager();
+ if (manager instanceof Subscriber) {
+ //session accessTimestamp updates should be handled by BeginServletRequestEvents, not in the
+ //ShiroFilter:
+ return;
+ }
+ }
+
+ //new 1.3 components were not found, revert to < 1.3 behavior for backwards compatibility:
+ updateSessionLastAccessTime(request, response);
+ }
+
/**
* Returns the {@code FilterChain} to execute for the given request.
* <p/>
Added: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/AccessTimestampEvaluator.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/AccessTimestampEvaluator.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/AccessTimestampEvaluator.java (added)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/AccessTimestampEvaluator.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,39 @@
+package org.apache.shiro.web.session.mgt;
+
+import org.apache.shiro.web.event.BeginServletRequestEvent;
+
+/**
+ * Component that allows customization of when a valid session's lastAccessTimestamp will be updated.
+ * <p/>
+ * The Servlet Specification specifies that a session lastAccessTimestamp will be updated on each request that is
+ * associated with a valid session. Default implementations of this interface enforce this (expected) behavior.
+ * <p/>
+ * <b>WARNING:</b> Be careful about changing the default behavior of these implementations or writing your own.
+ * If sessions' last access timestamps are not updated properly, they will not time out as expected, which could
+ * introduce security attack vectors.
+ * <p/>
+ * However, scrutinized implementations of this interface can be useful in certain scenarios, depending on business
+ * and/or security requirements.
+ *
+ * @see <a href="http://www.scribd.com/doc/23278127/58/SRV-7-6-Last-Accessed-Times">Servlet Specification, Section 7.6</a>
+ * @since 1.3
+ */
+public interface AccessTimestampEvaluator {
+
+ /**
+ * Returns {@code true} if a request with an associated valid session should result in updating the session's
+ * {@code lastAccessTimestamp} to the current time this method is invoked, {@code false} otherwise.
+ * <p/>
+ * <b>Servlet Specification-compliant implementations always return {@code true}</b> to guarantee default
+ * session behavior. However, returning false may be useful in certain scenarios depending on business and/or
+ * security requirements. If you implement this interface, return {@code false} judiciously.
+ * <p/>
+ * See the class-level JavaDoc for more.
+ *
+ * @param event the event that indicates a request is starting (but not yet propagated down the servlet filter
+ * chain).
+ * @return {@code true} if a request with an associated valid session should result in updating the session's
+ * {@code lastAccessTimestamp} to the current time this method is invoked, {@code false} otherwise.
+ */
+ boolean isUpdateAccessTimestamp(BeginServletRequestEvent event);
+}
Added: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/RequestIdGenerator.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/RequestIdGenerator.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/RequestIdGenerator.java (added)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/RequestIdGenerator.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,21 @@
+package org.apache.shiro.web.session.mgt;
+
+import org.apache.shiro.web.event.BeginServletRequestEvent;
+
+/**
+ * Generates globally unique IDs to assign to ServletRequests (for example, as a request attribute). This is
+ * useful for debugging in multi-threaded/multi-request environments.
+ *
+ * @since 1.3
+ */
+public interface RequestIdGenerator {
+
+ /**
+ * Returns a globally unique request ID to be associated with an incoming ServletRequest, or {@code null} if no
+ * ID should be associated. Useful for debugging in multi-threaded/multi-request environments.
+ *
+ * @return a globally unique request ID to be associated with an incoming ServletRequest, or {@code null} if no
+ * ID should be associated.
+ */
+ String generateId(BeginServletRequestEvent event);
+}
Added: shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/SpecCompliantAccessTimestampEvaluator.java
URL: http://svn.apache.org/viewvc/shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/SpecCompliantAccessTimestampEvaluator.java?rev=1351193&view=auto
==============================================================================
--- shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/SpecCompliantAccessTimestampEvaluator.java (added)
+++ shiro/branches/SHIRO-317b/web/src/main/java/org/apache/shiro/web/session/mgt/SpecCompliantAccessTimestampEvaluator.java Mon Jun 18 02:01:11 2012
@@ -0,0 +1,18 @@
+package org.apache.shiro.web.session.mgt;
+
+import org.apache.shiro.web.event.BeginServletRequestEvent;
+
+/**
+ * Servlet Specification-compliant implementation that always returns {@code true}.
+ *
+ * @since 1.3
+ */
+public class SpecCompliantAccessTimestampEvaluator implements AccessTimestampEvaluator {
+
+ /**
+ * Servlet Specification-compliant implementation that always returns {@code true}.
+ */
+ public boolean isUpdateAccessTimestamp(BeginServletRequestEvent event) {
+ return true;
+ }
+}