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]