You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by jb...@apache.org on 2012/06/20 06:03:29 UTC
svn commit: r1351930 - in /shiro/trunk:
core/src/main/java/org/apache/shiro/config/
core/src/main/java/org/apache/shiro/config/event/
core/src/test/groovy/org/apache/shiro/config/
core/src/test/java/org/apache/shiro/config/ samples/web/src/main/webapp/...
Author: jbunting
Date: Wed Jun 20 04:03:28 2012
New Revision: 1351930
URL: http://svn.apache.org/viewvc?rev=1351930&view=rev
Log:
SHIRO-364: Adding initial implementation of bean listeners
Added:
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
Modified:
shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
shiro/trunk/samples/web/src/main/webapp/WEB-INF/shiro.ini
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java?rev=1351930&r1=1351929&r2=1351930&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java Wed Jun 20 04:03:28 2012
@@ -68,6 +68,12 @@ public class IniSecurityManagerFactory e
return this.builder != null ? Collections.unmodifiableMap(builder.getObjects()) : null;
}
+ public void destroy() {
+ if(this.builder != null) {
+ builder.destroy();
+ }
+ }
+
private SecurityManager getSecurityManagerBean() {
return builder.getBean(SECURITY_MANAGER_NAME, SecurityManager.class);
}
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java?rev=1351930&r1=1351929&r2=1351930&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java Wed Jun 20 04:03:28 2012
@@ -22,6 +22,7 @@ import org.apache.commons.beanutils.Bean
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
+import org.apache.shiro.config.event.*;
import org.apache.shiro.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,22 +58,41 @@ public class ReflectionBuilder {
private static final char MAP_PROPERTY_BEGIN_TOKEN = '[';
private static final char MAP_PROPERTY_END_TOKEN = ']';
- private Map<String, ?> objects;
+ private final Map<String, Object> objects;
+ private final List<BeanListener> listeners = new ArrayList<BeanListener>();
+ private final BeanListener compositeListener = new BeanListener() {
+ public void onBeanEvent(BeanEvent beanEvent) {
+ for(BeanListener listener: listeners) {
+ listener.onBeanEvent(beanEvent);
+ }
+ }
+ };
public ReflectionBuilder() {
this.objects = new LinkedHashMap<String, Object>();
}
public ReflectionBuilder(Map<String, ?> defaults) {
- this.objects = CollectionUtils.isEmpty(defaults) ? new LinkedHashMap<String, Object>() : defaults;
+ this.objects = new LinkedHashMap<String, Object>();
+ if(!CollectionUtils.isEmpty(defaults)) {
+ this.objects.putAll(defaults);
+ }
}
public Map<String, ?> getObjects() {
return objects;
}
+ /**
+ * @deprecated Use of this method will break the event contract. We recommend not using it.
+ * @param objects
+ */
+ @Deprecated
public void setObjects(Map<String, ?> objects) {
- this.objects = CollectionUtils.isEmpty(objects) ? new LinkedHashMap<String, Object>() : objects;
+ this.objects.clear();
+ if(!CollectionUtils.isEmpty(objects)) {
+ this.objects.putAll(objects);
+ }
}
public Object getBean(String id) {
@@ -119,15 +139,68 @@ public class ReflectionBuilder {
createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());
}
+ // Set properties on listeners
+ Iterator<Map.Entry<String, String>> entryIterator = propertyMap.entrySet().iterator();
+ while(entryIterator.hasNext()) {
+ Map.Entry<String, String> entry = entryIterator.next();
+ if(isListenerProperty(entry.getKey())) {
+ applyProperty(entry.getKey(), entry.getValue(), objects);
+ entryIterator.remove();
+ }
+ }
+
+ Map<String, Object> immutableObjects = Collections.unmodifiableMap(objects);
+
+ // Add listeners to listener set, notifying events on them as we go - order is important here
+ for(Map.Entry<String, ?> entry: objects.entrySet()) {
+ if(entry.getValue() instanceof BeanListener) {
+ compositeListener.onBeanEvent(new ConfiguredBeanEvent(entry.getKey(), entry.getValue(), immutableObjects));
+ listeners.add((BeanListener) entry.getValue());
+ }
+ }
+
+ // notify instantiated event on non-listeners
+ for(Map.Entry<String, ?> entry: objects.entrySet()) {
+ if(!(entry.getValue() instanceof BeanListener)) {
+ compositeListener.onBeanEvent(new InstantiatedBeanEvent(entry.getKey(), entry.getValue(), immutableObjects));
+ }
+ }
+
// Set all properties
for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
applyProperty(entry.getKey(), entry.getValue(), objects);
}
+
+ for(Map.Entry<String, ?> entry: objects.entrySet()) {
+ if(!(entry.getValue() instanceof BeanListener)) {
+ compositeListener.onBeanEvent(new ConfiguredBeanEvent(entry.getKey(), entry.getValue(), immutableObjects));
+ }
+ }
}
return objects;
}
+ public void destroy() {
+ final Map<String, Object> immutableObjects = Collections.unmodifiableMap(objects);
+ for(Map.Entry<String, ?> entry: objects.entrySet()) {
+ compositeListener.onBeanEvent(new DestroyedBeanEvent(entry.getKey(), entry.getValue(), immutableObjects));
+ }
+ }
+
+ private boolean isListenerProperty(String key) {
+ int index = key.indexOf('.');
+
+ if (index >= 0) {
+ String name = key.substring(0, index);
+
+ return objects.containsKey(name) && objects.get(name) instanceof BeanListener;
+ } else {
+ throw new IllegalArgumentException("All property keys must contain a '.' character. " +
+ "(e.g. myBean.property = value) These should already be separated out by buildObjects().");
+ }
+ }
+
protected void createNewInstance(Map<String, Object> objects, String name, String value) {
Object currentInstance = objects.get(name);
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,29 @@
+package org.apache.shiro.config.event;
+
+import java.util.EventObject;
+import java.util.Map;
+
+public class BeanEvent extends EventObject {
+ private String beanName;
+ private Object bean;
+ private final Map<String, Object> beanContext;
+
+ public BeanEvent(final String beanName, final Object bean, final Map<String, Object> beanContext) {
+ super(bean);
+ this.beanName = beanName;
+ this.bean = bean;
+ this.beanContext = beanContext;
+ }
+
+ public String getBeanName() {
+ return beanName;
+ }
+
+ public Object getBean() {
+ return bean;
+ }
+
+ public Map<String, Object> getBeanContext() {
+ return beanContext;
+ }
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListener.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListener.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListener.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,14 @@
+package org.apache.shiro.config.event;
+
+/**
+ * Extension point that provides for notification of lifecycle events in the bean configuration process. This is an
+ * extension point of the (typically) ini-based bean instantiation strategy used by default by shiro. It is intended
+ * as a bare-bones corollary to the more advanced lifecycle facilities offered in full-fledged dependency injection
+ * frameworks.
+ *
+ * The type of event is determined by the type of the beanEvent object. Use of {@see BeanListenerSupport} is
+ * recommended.
+ */
+public interface BeanListener {
+ void onBeanEvent(BeanEvent beanEvent);
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,31 @@
+package org.apache.shiro.config.event;
+
+public abstract class BeanListenerSupport implements BeanListener {
+ public void onBeanEvent(final BeanEvent beanEvent) {
+ if (beanEvent instanceof InstantiatedBeanEvent) {
+ this.onInstantiatedBeanEvent((InstantiatedBeanEvent) beanEvent);
+ } else if (beanEvent instanceof ConfiguredBeanEvent) {
+ this.onConfiguredBeanEvent((ConfiguredBeanEvent) beanEvent);
+ } else if (beanEvent instanceof DestroyedBeanEvent) {
+ this.onDestroyedBeanEvent((DestroyedBeanEvent) beanEvent);
+ } else {
+ this.onUnhandledBeanEvent(beanEvent);
+ }
+ }
+
+ protected void onUnhandledBeanEvent(final BeanEvent beanEvent) {
+ // do nothing
+ }
+
+ protected void onInstantiatedBeanEvent(final InstantiatedBeanEvent beanEvent) {
+ // do nothing
+ }
+
+ protected void onConfiguredBeanEvent(final ConfiguredBeanEvent beanEvent) {
+ // do nothing
+ }
+
+ protected void onDestroyedBeanEvent(final DestroyedBeanEvent beanEvent) {
+ // do nothing
+ }
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,9 @@
+package org.apache.shiro.config.event;
+
+import java.util.Map;
+
+public class ConfiguredBeanEvent extends BeanEvent {
+ public ConfiguredBeanEvent(final String beanName, final Object bean, final Map<String, Object> beanContext) {
+ super(beanName, bean, beanContext);
+ }
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,9 @@
+package org.apache.shiro.config.event;
+
+import java.util.Map;
+
+public class DestroyedBeanEvent extends BeanEvent {
+ public DestroyedBeanEvent(final String beanName, final Object bean, final Map<String, Object> beanContext) {
+ super(beanName, bean, beanContext);
+ }
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,9 @@
+package org.apache.shiro.config.event;
+
+import java.util.Map;
+
+public class InstantiatedBeanEvent extends BeanEvent {
+ public InstantiatedBeanEvent(final String beanName, final Object bean, final Map<String, Object> beanContext) {
+ super(beanName, bean, beanContext);
+ }
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,32 @@
+package org.apache.shiro.config.event;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A stock bean listener implementation that logs all events via the standard logging mechanism.
+ */
+public class LoggingBeanListener extends BeanListenerSupport {
+
+ private static final Logger logger = LoggerFactory.getLogger(LoggingBeanListener.class);
+
+ @Override
+ protected void onUnhandledBeanEvent(BeanEvent beanEvent) {
+ logger.warn("UNHANDLED EVENT :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
+ }
+
+ @Override
+ protected void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
+ logger.info("INSTANTIATED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
+ }
+
+ @Override
+ protected void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
+ logger.info("CONFIGURED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
+ }
+
+ @Override
+ protected void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
+ logger.info("DESTROYED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
+ }
+}
Modified: shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy?rev=1351930&r1=1351929&r2=1351930&view=diff
==============================================================================
--- shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy (original)
+++ shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy Wed Jun 20 04:03:28 2012
@@ -23,6 +23,7 @@ import org.apache.shiro.codec.CodecSuppo
import org.apache.shiro.codec.Hex
import org.apache.shiro.realm.ldap.JndiLdapRealm
import org.apache.shiro.util.CollectionUtils
+import org.apache.shiro.config.event.BeanEvent
/**
* Unit tests for the {@link ReflectionBuilder} implementation.
@@ -393,4 +394,82 @@ class ReflectionBuilderTest extends Groo
assertEquals(5, bean.getIntProp());
assertEquals("someString", bean.getStringProp());
}
+
+ void testBeanListeners() {
+ Map<String, String> defs = new LinkedHashMap<String, String>();
+ defs.put("listenerOne", RecordingBeanListener.class.getName());
+ defs.put("listenerTwo", RecordingBeanListener.class.getName());
+
+ defs.put("simpleBeanFactory", "org.apache.shiro.config.SimpleBeanFactory");
+ defs.put("simpleBeanFactory.factoryInt", "5");
+ defs.put("simpleBeanFactory.factoryString", "someString");
+ defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+ defs.put("compositeBean.simpleBean", '$simpleBeanFactory');
+
+ ReflectionBuilder builder = new ReflectionBuilder();
+ Map<String, ?> objects = builder.buildObjects(defs);
+ assertFalse(CollectionUtils.isEmpty(objects));
+
+ assertInstantiatedEvents("listenerOne", objects);
+ assertConfiguredEvents("listenerOne", objects, true);
+ assertInstantiatedEvents("listenerTwo", objects);
+ assertConfiguredEvents("listenerTwo", objects, false);
+
+ builder.destroy();
+
+ assertDestroyedEvents("listenerOne", objects);
+ assertDestroyedEvents("listenerTwo", objects);
+ }
+
+ void assertInstantiatedEvents(String name, Map<String, ?> objects) {
+ Object bean = objects.get(name);
+ assertTrue("Bean " + name + " is not a " + RecordingBeanListener.class.getSimpleName(),
+ bean instanceof RecordingBeanListener);
+ List<BeanEvent> instantiatedEvents = bean.getInstantiateEvents();
+ assertEquals(2, instantiatedEvents.size())
+
+ checkType(name, instantiatedEvents, "simpleBeanFactory", SimpleBeanFactory.class);
+ checkType(name, instantiatedEvents, "compositeBean", CompositeBean.class);
+
+ // instantiate notifications do not occur for listeners
+ }
+
+ void assertConfiguredEvents(String name, Map<String, ?> objects, boolean includeListener) {
+ Object bean = objects.get(name);
+ assertTrue("Bean " + name + " is not a " + RecordingBeanListener.class.getSimpleName(),
+ bean instanceof RecordingBeanListener);
+ List<BeanEvent> configuredEvents = bean.getConfiguredEvents();
+ assertEquals(includeListener ? 3 : 2, configuredEvents.size())
+
+ checkType(name, configuredEvents, "simpleBeanFactory", SimpleBeanFactory.class);
+ checkType(name, configuredEvents, "compositeBean", CompositeBean.class);
+ if(includeListener) {
+ checkType(name, configuredEvents, "listenerTwo", RecordingBeanListener.class);
+ }
+ }
+
+ void assertDestroyedEvents(String name, Map<String, ?> objects) {
+ Object bean = objects.get(name);
+ assertTrue("Bean " + name + " is not a " + RecordingBeanListener.class.getSimpleName(),
+ bean instanceof RecordingBeanListener);
+ List<BeanEvent> configuredEvents = bean.getDestroyedEvents();
+ assertEquals(4, configuredEvents.size())
+
+ checkType(name, configuredEvents, "simpleBeanFactory", SimpleBeanFactory.class);
+ checkType(name, configuredEvents, "compositeBean", CompositeBean.class);
+ checkType(name, configuredEvents, "listenerOne", RecordingBeanListener.class);
+ checkType(name, configuredEvents, "listenerTwo", RecordingBeanListener.class);
+ }
+
+ void checkType(String instanceName, List<BeanEvent> events, String name, Class<?> expectedType) {
+ for(BeanEvent event: events) {
+ if(event.getBeanName().equals(name)) {
+ assertTrue("Notification for bean " + name + " did not provide an instance of " + expectedType
+ + " to listener " + instanceName,
+ expectedType.isInstance(event.getBean()))
+ return;
+ }
+ }
+ fail("No bean named " + name + " was ever notified to listener " + instanceName + ".");
+ }
}
Added: shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java?rev=1351930&view=auto
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java (added)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java Wed Jun 20 04:03:28 2012
@@ -0,0 +1,51 @@
+package org.apache.shiro.config;
+
+import org.apache.shiro.config.event.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RecordingBeanListener extends BeanListenerSupport {
+
+ private List<InstantiatedBeanEvent> instantiateEvents = new ArrayList<InstantiatedBeanEvent>();
+ private List<ConfiguredBeanEvent> configuredEvents = new ArrayList<ConfiguredBeanEvent>();
+ private List<DestroyedBeanEvent> destroyedEvents = new ArrayList<DestroyedBeanEvent>();
+
+ private List<BeanEvent> unhandledEvents = new ArrayList<BeanEvent>();
+
+ @Override
+ protected void onUnhandledBeanEvent(BeanEvent beanEvent) {
+ this.unhandledEvents.add(beanEvent);
+ }
+
+ @Override
+ protected void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
+ this.instantiateEvents.add(beanEvent);
+ }
+
+ @Override
+ protected void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
+ this.configuredEvents.add(beanEvent);
+ }
+
+ @Override
+ protected void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
+ this.destroyedEvents.add(beanEvent);
+ }
+
+ public List<InstantiatedBeanEvent> getInstantiateEvents() {
+ return instantiateEvents;
+ }
+
+ public List<ConfiguredBeanEvent> getConfiguredEvents() {
+ return configuredEvents;
+ }
+
+ public List<DestroyedBeanEvent> getDestroyedEvents() {
+ return destroyedEvents;
+ }
+
+ public List<BeanEvent> getUnhandledEvents() {
+ return unhandledEvents;
+ }
+}
Modified: shiro/trunk/samples/web/src/main/webapp/WEB-INF/shiro.ini
URL: http://svn.apache.org/viewvc/shiro/trunk/samples/web/src/main/webapp/WEB-INF/shiro.ini?rev=1351930&r1=1351929&r2=1351930&view=diff
==============================================================================
--- shiro/trunk/samples/web/src/main/webapp/WEB-INF/shiro.ini (original)
+++ shiro/trunk/samples/web/src/main/webapp/WEB-INF/shiro.ini Wed Jun 20 04:03:28 2012
@@ -21,6 +21,8 @@
# http://shiro.apache.org/web.html for more.
[main]
+listener = org.apache.shiro.config.event.LoggingBeanListener
+
shiro.loginUrl = /login.jsp
[users]