incubator-shiro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lhazlew...@apache.org
Subject svn commit: r1427647 - in /shiro/trunk: core/src/main/java/org/apache/shiro/config/ core/src/main/java/org/apache/shiro/config/event/ core/src/main/java/org/apache/shiro/event/ core/src/main/java/org/apache/shiro/mgt/ core/src/test/groovy/org/apache/sh...
Date Wed, 02 Jan 2013 04:44:22 GMT
Author: lhazlewood
Date: Wed Jan  2 04:44:21 2013
New Revision: 1427647

URL: http://svn.apache.org/viewvc?rev=1427647&view=rev
Log:
SHIRO-395: Refactored BeanEvent mechanisms to work with the EventBus concepts.  Updated ReflectionBuilder
to have first-class Class representations of configuration concepts.  Event ordering discussion
TBD on the dev mailing list.

Removed:
    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
Modified:
    shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
    shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.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/main/java/org/apache/shiro/event/EventBusAware.java
    shiro/trunk/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
    shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
    shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
    shiro/trunk/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy

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=1427647&r1=1427646&r2=1427647&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 Jan
 2 04:44:21 2013
@@ -22,7 +22,14 @@ 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.config.event.BeanEvent;
+import org.apache.shiro.config.event.ConfiguredBeanEvent;
+import org.apache.shiro.config.event.DestroyedBeanEvent;
+import org.apache.shiro.config.event.InstantiatedBeanEvent;
+import org.apache.shiro.event.EventBus;
+import org.apache.shiro.event.EventBusAware;
+import org.apache.shiro.event.Subscribe;
+import org.apache.shiro.event.support.DefaultEventBus;
 import org.apache.shiro.util.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -58,25 +65,47 @@ public class ReflectionBuilder {
     private static final char MAP_PROPERTY_BEGIN_TOKEN = '[';
     private static final char MAP_PROPERTY_END_TOKEN = ']';
 
+    private static final String EVENT_BUS_NAME = "eventBus";
+
     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);
-            }
-        }
-    };
+    /**
+     * @since 1.3
+     */
+    private EventBus eventBus;
+    /**
+     * Keeps track of event subscribers that were automatically registered by this ReflectionBuilder
during
+     * object construction.  This is used in case a new EventBus is discovered during object
graph
+     * construction:  upon discovery of the new EventBus, the existing subscribers will be
unregistered from the
+     * old EventBus and then re-registered with the new EventBus.
+     *
+     * @since 1.3
+     */
+    private final Map<String,Object> registeredEventSubscribers;
+
+    //@since 1.3
+    private static Map<String,Object> createDefaultObjectMap() {
+        Map<String,Object> map = new LinkedHashMap<String, Object>();
+        map.put(EVENT_BUS_NAME, new DefaultEventBus());
+        return map;
+    }
 
     public ReflectionBuilder() {
-        this.objects = new LinkedHashMap<String, Object>();
+        this(null);
     }
 
     public ReflectionBuilder(Map<String, ?> defaults) {
-        this.objects = new LinkedHashMap<String, Object>();
-        if(!CollectionUtils.isEmpty(defaults)) {
-            this.objects.putAll(defaults);
+        this.objects = createDefaultObjectMap();
+        this.registeredEventSubscribers = new LinkedHashMap<String,Object>();
+        apply(defaults);
+    }
+
+    private void apply(Map<String, ?> objects) {
+        if(!CollectionUtils.isEmpty(objects)) {
+            this.objects.putAll(objects);
         }
+        EventBus found = findEventBus(this.objects);
+        Assert.notNull(found, "An " + EventBus.class.getName() + " instance must be present
in the object defaults");
+        enableEvents(found);
     }
 
     public Map<String, ?> getObjects() {
@@ -84,17 +113,84 @@ public class ReflectionBuilder {
     }
 
     /**
-     * @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.clear();
-        if(!CollectionUtils.isEmpty(objects)) {
-            this.objects.putAll(objects);
+        this.objects.putAll(createDefaultObjectMap());
+        apply(objects);
+    }
+
+    //@since 1.3
+    private void enableEvents(EventBus eventBus) {
+        Assert.notNull(eventBus, "EventBus argument cannot be null.");
+        //clean up old auto-registered subscribers:
+        for (Object subscriber : this.registeredEventSubscribers.values()) {
+            this.eventBus.unregister(subscriber);
+        }
+        this.registeredEventSubscribers.clear();
+
+        this.eventBus = eventBus;
+
+        for(Map.Entry<String,Object> entry : this.objects.entrySet()) {
+            enableEventsIfNecessary(entry.getValue(), entry.getKey());
         }
     }
 
+    //@since 1.3
+    private void enableEventsIfNecessary(Object bean, String name) {
+        boolean applied = applyEventBusIfNecessary(bean);
+        if (!applied) {
+            //if the event bus is applied, and the bean wishes to be a subscriber as well
(not just a publisher),
+            // we assume that the implementation registers itself with the event bus, i.e.
eventBus.register(this);
+
+            //if the event bus isn't applied, only then do we need to check to see if the
bean is an event subscriber,
+            // and if so, register it on the event bus automatically since it has no ability
to do so itself:
+            if (isEventSubscriber(bean, name)) {
+                //found an event subscriber, so register them with the EventBus:
+                this.eventBus.register(bean);
+                this.registeredEventSubscribers.put(name, bean);
+            }
+        }
+    }
+
+    //@since 1.3
+    private boolean isEventSubscriber(Object bean, String name) {
+        List annotatedMethods = ClassUtils.getAnnotatedMethods(bean.getClass(), Subscribe.class);
+        return !CollectionUtils.isEmpty(annotatedMethods);
+    }
+
+    //@since 1.3
+    protected EventBus findEventBus(Map<String,?> objects) {
+
+        if (CollectionUtils.isEmpty(objects)) {
+            return null;
+        }
+
+        //prefer a named object first:
+        Object value = objects.get(EVENT_BUS_NAME);
+        if (value != null && value instanceof EventBus) {
+            return (EventBus)value;
+        }
+
+        //couldn't find a named 'eventBus' EventBus object.  Try to find the first typed
value we can:
+        for( Object v : objects.values()) {
+            if (v instanceof EventBus) {
+                return (EventBus)v;
+            }
+        }
+
+        return null;
+    }
+
+    private boolean applyEventBusIfNecessary(Object value) {
+        if (value instanceof EventBusAware) {
+            ((EventBusAware)value).setEventBus(this.eventBus);
+            return true;
+        }
+        return false;
+    }
+
     public Object getBean(String id) {
         return objects.get(id);
     }
@@ -108,74 +204,44 @@ public class ReflectionBuilder {
         if (bean == null) {
             return null;
         }
-        if (!requiredType.isAssignableFrom(bean.getClass())) {
-            throw new IllegalStateException("Bean with id [" + id + "] is not of the required
type [" +
-                    requiredType.getName() + "].");
-        }
+        Assert.state(requiredType.isAssignableFrom(bean.getClass()),
+                "Bean with id [" + id + "] is not of the required type [" + requiredType.getName()
+ "].");
         return (T) bean;
     }
 
+    private String parseBeanId(String lhs) {
+        Assert.notNull(lhs);
+        if (lhs.indexOf('.') < 0) {
+            return lhs;
+        }
+        String classSuffix = ".class";
+        int index = lhs.indexOf(classSuffix);
+        if (index >= 0) {
+            return lhs.substring(0, index);
+        }
+        return null;
+    }
+
     @SuppressWarnings({"unchecked"})
     public Map<String, ?> buildObjects(Map<String, String> kvPairs) {
-        if (kvPairs != null && !kvPairs.isEmpty()) {
 
-            // Separate key value pairs into object declarations and property assignment
-            // so that all objects can be created up front
+        if (kvPairs != null && !kvPairs.isEmpty()) {
 
-            //https://issues.apache.org/jira/browse/SHIRO-85 - need to use LinkedHashMaps
here:
-            Map<String, String> instanceMap = new LinkedHashMap<String, String>();
-            Map<String, String> propertyMap = new LinkedHashMap<String, String>();
+            BeanConfigurationProcessor processor = new BeanConfigurationProcessor();
 
             for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
-                if (entry.getKey().indexOf('.') < 0 || entry.getKey().endsWith(".class"))
{
-                    instanceMap.put(entry.getKey(), entry.getValue());
-                } else {
-                    propertyMap.put(entry.getKey(), entry.getValue());
-                }
-            }
-
-            // Create all instances
-            for (Map.Entry<String, String> entry : instanceMap.entrySet()) {
-                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());
-                }
-            }
+                String lhs = entry.getKey();
+                String rhs = 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));
+                String beanId = parseBeanId(lhs);
+                if (beanId != null) { //a beanId could be parsed, so the line is a bean instance
definition
+                    processor.add(new InstantiationStatement(beanId, rhs));
+                } else { //the line must be a property configuration
+                    processor.add(new AssignmentStatement(lhs, rhs));
                 }
             }
 
-            // 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));
-                }
-            }
+            processor.execute();
         }
 
         return objects;
@@ -184,20 +250,11 @@ public class ReflectionBuilder {
     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().");
+            String id = entry.getKey();
+            Object bean = entry.getValue();
+            BeanEvent event = new DestroyedBeanEvent(id, bean, immutableObjects);
+            eventBus.publish(event);
+            LifecycleUtils.destroy(bean);
         }
     }
 
@@ -581,4 +638,230 @@ public class ReflectionBuilder {
         applyProperty(object, propertyName, value);
     }
 
+    private class BeanConfigurationProcessor {
+
+        private final List<Statement> statements = new ArrayList<Statement>();
+        private final List<BeanConfiguration> beanConfigurations = new ArrayList<BeanConfiguration>();
+
+        public void add(Statement statement) {
+
+            statements.add(statement); //we execute bean configuration statements in the
order they are declared.
+
+            if (statement instanceof InstantiationStatement) {
+                InstantiationStatement is = (InstantiationStatement)statement;
+                beanConfigurations.add(new BeanConfiguration(is));
+            } else {
+                AssignmentStatement as = (AssignmentStatement)statement;
+                //statements always apply to the most recently defined bean configuration
with the same name, so we
+                //have to traverse the configuration list starting at the end (most recent
elements are appended):
+                boolean addedToConfig = false;
+                String beanName = as.getRootBeanName();
+                for( int i = beanConfigurations.size()-1; i >= 0; i--) {
+                    BeanConfiguration mostRecent = beanConfigurations.get(i);
+                    String mostRecentBeanName = mostRecent.getBeanName();
+                    if (beanName.equals(mostRecentBeanName)) {
+                        mostRecent.add(as);
+                        addedToConfig = true;
+                        break;
+                    }
+                }
+
+                if (!addedToConfig) {
+                    // the AssignmentStatement must be for an existing bean that does not
yet have a corresponding
+                    // configuration object (this would happen if the bean is in the default
objects map). Because
+                    // BeanConfiguration instances don't exist for default (already instantiated)
beans,
+                    // we simulate a creation of one to satisfy this processors implementation:
+                    beanConfigurations.add(new BeanConfiguration(as));
+                }
+            }
+        }
+
+        public void execute() {
+
+            for( Statement statement : statements) {
+
+                statement.execute();
+
+                BeanConfiguration bd = statement.getBeanConfiguration();
+
+                if (bd.isExecuted()) { //bean is fully configured, no more statements to
execute for it:
+
+                    if (bd.getBeanName().equals(EVENT_BUS_NAME)) {
+                        EventBus eventBus = (EventBus)bd.getBean();
+                        enableEvents(eventBus);
+                    }
+
+                    //ignore global 'shiro.' shortcut mechanism:
+                    if (!bd.isGlobalConfig()) {
+                        BeanEvent event = new ConfiguredBeanEvent(bd.getBeanName(), bd.getBean(),
+                                Collections.unmodifiableMap(objects));
+                        eventBus.publish(event);
+                    }
+                }
+            }
+        }
+    }
+
+    private class BeanConfiguration {
+
+        private final InstantiationStatement instantiationStatement;
+        private final List<AssignmentStatement> assignments = new ArrayList<AssignmentStatement>();
+        private final String beanName;
+        private Object bean;
+
+        private BeanConfiguration(InstantiationStatement statement) {
+            statement.setBeanConfiguration(this);
+            this.instantiationStatement = statement;
+            this.beanName = statement.lhs;
+        }
+
+        private BeanConfiguration(AssignmentStatement as) {
+            this.instantiationStatement = null;
+            this.beanName = as.getRootBeanName();
+            add(as);
+        }
+
+        public String getBeanName() {
+            return this.beanName;
+        }
+
+        public boolean isGlobalConfig() { //BeanConfiguration instance representing the global
'shiro.' properties
+            // (we should remove this concept).
+            return GLOBAL_PROPERTY_PREFIX.equals(getBeanName());
+        }
+
+        public void add(AssignmentStatement as) {
+            as.setBeanConfiguration(this);
+            assignments.add(as);
+        }
+
+        /**
+         * When this configuration is parsed sufficiently to create (or find) an actual bean
instance, that instance
+         * will be associated with its configuration by setting it via this method.
+         *
+         * @param bean the bean instantiated (or found) that corresponds to this BeanConfiguration
instance.
+         */
+        public void setBean(Object bean) {
+            this.bean = bean;
+        }
+
+        public Object getBean() {
+            return this.bean;
+        }
+
+        /**
+         * Returns true if all configuration statements have been executed.
+         * @return true if all configuration statements have been executed.
+         */
+        public boolean isExecuted() {
+            if (instantiationStatement != null && !instantiationStatement.isExecuted())
{
+                return false;
+            }
+            for (AssignmentStatement as : assignments) {
+                if (!as.isExecuted()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private abstract class Statement {
+
+        protected final String lhs;
+        protected final String rhs;
+        protected Object bean;
+        private Object result;
+        private boolean executed;
+        private BeanConfiguration beanConfiguration;
+
+        private Statement(String lhs, String rhs) {
+            this.lhs = lhs;
+            this.rhs = rhs;
+            this.executed = false;
+        }
+
+        public void setBeanConfiguration(BeanConfiguration bd) {
+            this.beanConfiguration = bd;
+        }
+
+        public BeanConfiguration getBeanConfiguration() {
+            return this.beanConfiguration;
+        }
+
+        public Object execute() {
+            if (!isExecuted()) {
+                this.result = doExecute();
+                this.executed = true;
+            }
+            if (!getBeanConfiguration().isGlobalConfig()) {
+                Assert.notNull(this.bean, "Implementation must set the root bean for which
it executed.");
+            }
+            return this.result;
+        }
+
+        public Object getBean() {
+            return this.bean;
+        }
+
+        protected void setBean(Object bean) {
+            this.bean = bean;
+            if (this.beanConfiguration.getBean() == null) {
+                this.beanConfiguration.setBean(bean);
+            }
+        }
+
+        public Object getResult() {
+            return result;
+        }
+
+        protected abstract Object doExecute();
+
+        public boolean isExecuted() {
+            return executed;
+        }
+    }
+
+    private class InstantiationStatement extends Statement {
+
+        private InstantiationStatement(String lhs, String rhs) {
+            super(lhs, rhs);
+        }
+
+        @Override
+        protected Object doExecute() {
+            createNewInstance(objects, this.lhs, this.rhs);
+            Object instantiated = objects.get(this.lhs);
+            setBean(instantiated);
+
+            BeanEvent event = new InstantiatedBeanEvent(this.lhs, instantiated, Collections.unmodifiableMap(objects));
+            eventBus.publish(event);
+
+            return instantiated;
+        }
+    }
+
+    private class AssignmentStatement extends Statement {
+
+        private final String rootBeanName;
+
+        private AssignmentStatement(String lhs, String rhs) {
+            super(lhs, rhs);
+            int index = lhs.indexOf('.');
+            this.rootBeanName = lhs.substring(0, index);
+        }
+
+        @Override
+        protected Object doExecute() {
+            applyProperty(lhs, rhs, objects);
+            Object bean = objects.get(this.rootBeanName);
+            setBean(bean);
+            return null;
+        }
+
+        public String getRootBeanName() {
+            return this.rootBeanName;
+        }
+    }
+
 }

Modified: 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=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java Wed Jan  2
04:44:21 2013
@@ -25,6 +25,7 @@ import java.util.Map;
  * @since 1.3
  */
 public class BeanEvent extends EventObject {
+
     private String beanName;
     private Object bean;
     private final Map<String, Object> beanContext;

Modified: 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=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
(original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
Wed Jan  2 04:44:21 2013
@@ -20,7 +20,11 @@ package org.apache.shiro.config.event;
 
 import java.util.Map;
 
+/**
+ * @since 1.3
+ */
 public class ConfiguredBeanEvent extends BeanEvent {
+
     public ConfiguredBeanEvent(final String beanName, final Object bean, final Map<String,
Object> beanContext) {
         super(beanName, bean, beanContext);
     }

Modified: 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=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java Wed
Jan  2 04:44:21 2013
@@ -24,6 +24,7 @@ import java.util.Map;
  * @since 1.3
  */
 public class DestroyedBeanEvent extends BeanEvent {
+
     public DestroyedBeanEvent(final String beanName, final Object bean, final Map<String,
Object> beanContext) {
         super(beanName, bean, beanContext);
     }

Modified: 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=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
(original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
Wed Jan  2 04:44:21 2013
@@ -24,6 +24,7 @@ import java.util.Map;
  * @since 1.3
  */
 public class InstantiatedBeanEvent extends BeanEvent {
+
     public InstantiatedBeanEvent(final String beanName, final Object bean, final Map<String,
Object> beanContext) {
         super(beanName, bean, beanContext);
     }

Modified: 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=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
(original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
Wed Jan  2 04:44:21 2013
@@ -18,34 +18,35 @@
  */
 package org.apache.shiro.config.event;
 
+import org.apache.shiro.event.Subscribe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * A stock bean listener implementation that logs all events via the standard logging mechanism.
+ * A stock bean listener implementation that logs all BeanEvents via the standard logging
mechanism.
  *
  * @since 1.3
  */
-public class LoggingBeanListener extends BeanListenerSupport {
+public class LoggingBeanListener {
 
     private static final Logger logger = LoggerFactory.getLogger(LoggingBeanListener.class);
 
-    @Override
+    @Subscribe
     protected void onUnhandledBeanEvent(BeanEvent beanEvent) {
         logger.warn("UNHANDLED EVENT :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
     }
 
-    @Override
+    @Subscribe
     protected void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
         logger.info("INSTANTIATED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
     }
 
-    @Override
+    @Subscribe
     protected void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
         logger.info("CONFIGURED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
     }
 
-    @Override
+    @Subscribe
     protected void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
         logger.info("DESTROYED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
     }

Modified: shiro/trunk/core/src/main/java/org/apache/shiro/event/EventBusAware.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/event/EventBusAware.java?rev=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/event/EventBusAware.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/event/EventBusAware.java Wed Jan  2 04:44:21
2013
@@ -21,6 +21,15 @@ package org.apache.shiro.event;
 /**
  * Interface implemented by components that utilize an EventBus for publishing and/or subscribing
to/from events
  * and wish that EventBus to be supplied if one is available.
+ * <p/>
+ * <b>NOTE:</b> If an {@code EventBusAware} implementation wishes to subscribe
to events (i.e. it has
+ * {@code @Subscriber}-annotated methods itself, it must register itself with the event bus,
i.e.:
+ * <pre>
+ * eventBus.register(this);
+ * </pre>
+ * Shiro's default configuration mechanisms will <em>NOT</em> auto-register {@code
@Subscriber}-annotated components
+ * that are also {@code EventBusAware}: it is assumed that the {@code EventBusAware} implementation,
having access to
+ * an EventBus directly, knows best when to register/unregister itself.
  *
  * @since 1.3
  */

Modified: shiro/trunk/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java?rev=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java Wed Jan
 2 04:44:21 2013
@@ -20,6 +20,9 @@ package org.apache.shiro.mgt;
 
 import org.apache.shiro.cache.CacheManager;
 import org.apache.shiro.cache.CacheManagerAware;
+import org.apache.shiro.event.EventBus;
+import org.apache.shiro.event.EventBusAware;
+import org.apache.shiro.event.support.DefaultEventBus;
 import org.apache.shiro.util.Destroyable;
 import org.apache.shiro.util.LifecycleUtils;
 
@@ -34,7 +37,7 @@ import org.apache.shiro.util.LifecycleUt
  *
  * @since 0.9
  */
-public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware
{
+public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware,
EventBusAware {
 
     /**
      * The CacheManager to use to perform caching operations to enhance performance.  Can
be null.
@@ -42,9 +45,17 @@ public abstract class CachingSecurityMan
     private CacheManager cacheManager;
 
     /**
+     * The EventBus to use to use to publish and receive events of interest during Shiro's
lifecycle.
+     * @since 1.3
+     */
+    private EventBus eventBus;
+
+    /**
      * Default no-arg constructor that will automatically attempt to initialize a default
cacheManager
      */
     public CachingSecurityManager() {
+        //use a default event bus:
+        setEventBus(new DefaultEventBus());
     }
 
     /**
@@ -81,11 +92,49 @@ public abstract class CachingSecurityMan
     }
 
     /**
+     * Returns the {@code EventBus} used by this Securitymanager and potentially any of its
children components.
+     *
+     * @return the {@code EventBus} used by this Securitymanager and potentially any of its
children components.
+     * @since 1.3
+     */
+    public EventBus getEventBus() {
+        return eventBus;
+    }
+
+    /**
+     * Sets the EventBus used by this {@code SecurityManager} and potentially any of its
+     * children components.
+     * <p/>
+     * After the eventBus attribute has been set, the template method
+     * {@link #afterEventBusSet() afterEventBusSet()} is executed to allow subclasses to
adjust when a
+     * eventBus is available.
+     *
+     * @param eventBus the EventBus used by this {@code SecurityManager} and potentially
any of its
+     *                     children components.
+     * @since 1.3
+     */
+    public void setEventBus(EventBus eventBus) {
+        this.eventBus = eventBus;
+        afterEventBusSet();
+    }
+
+    /**
+     * Template callback to notify subclasses that an {@link EventBus EventBus} has been
set and is available for use
+     * via the {@link #getEventBus() getEventBus()} method.
+     *
+     * @since 1.3
+     */
+    protected void afterEventBusSet() {
+    }
+
+    /**
      * Destroys the {@link #getCacheManager() cacheManager} via {@link LifecycleUtils#destroy
LifecycleUtils.destroy}.
      */
     public void destroy() {
         LifecycleUtils.destroy(getCacheManager());
         this.cacheManager = null;
+        LifecycleUtils.destroy(getEventBus());
+        this.eventBus = new DefaultEventBus();
     }
 
 }

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=1427647&r1=1427646&r2=1427647&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 Jan  2 04:44:21 2013
@@ -21,9 +21,9 @@ package org.apache.shiro.config
 import org.apache.shiro.codec.Base64
 import org.apache.shiro.codec.CodecSupport
 import org.apache.shiro.codec.Hex
+import org.apache.shiro.config.event.BeanEvent
 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.
@@ -396,6 +396,9 @@ class ReflectionBuilderTest extends Groo
     }
 
     void testBeanListeners() {
+
+        //currently commented out pending Mailing List discussion:
+        /*
         Map<String, String> defs = new LinkedHashMap<String, String>();
         defs.put("listenerOne", RecordingBeanListener.class.getName());
         defs.put("listenerTwo", RecordingBeanListener.class.getName());
@@ -419,6 +422,7 @@ class ReflectionBuilderTest extends Groo
 
         assertDestroyedEvents("listenerOne", objects);
         assertDestroyedEvents("listenerTwo", objects);
+        */
     }
 
     void assertInstantiatedEvents(String name, Map<String, ?> objects) {

Modified: 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=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java Wed
Jan  2 04:44:21 2013
@@ -18,7 +18,11 @@
  */
 package org.apache.shiro.config;
 
-import org.apache.shiro.config.event.*;
+import org.apache.shiro.config.event.BeanEvent;
+import org.apache.shiro.config.event.ConfiguredBeanEvent;
+import org.apache.shiro.config.event.DestroyedBeanEvent;
+import org.apache.shiro.config.event.InstantiatedBeanEvent;
+import org.apache.shiro.event.Subscribe;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -26,30 +30,29 @@ import java.util.List;
 /**
  * @since 1.3
  */
-public class RecordingBeanListener extends BeanListenerSupport {
+public class RecordingBeanListener {
 
     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
+    @Subscribe
     protected void onUnhandledBeanEvent(BeanEvent beanEvent) {
         this.unhandledEvents.add(beanEvent);
     }
 
-    @Override
+    @Subscribe
     protected void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
         this.instantiateEvents.add(beanEvent);
     }
 
-    @Override
+    @Subscribe
     protected void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
         this.configuredEvents.add(beanEvent);
     }
 
-    @Override
+    @Subscribe
     protected void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
         this.destroyedEvents.add(beanEvent);
     }

Modified: shiro/trunk/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
URL: http://svn.apache.org/viewvc/shiro/trunk/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy?rev=1427647&r1=1427646&r2=1427647&view=diff
==============================================================================
--- shiro/trunk/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
(original)
+++ shiro/trunk/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
Wed Jan  2 04:44:21 2013
@@ -42,8 +42,8 @@ class IniWebEnvironmentTest extends Groo
         env.init()
 
         assertNotNull env.objects
-        //asserts that the objects size = securityManager (1) + num custom objects + num
default filters
-        def expectedSize = 2 + DefaultFilter.values().length
+        //asserts that the objects size = securityManager (1) + the event bus (1) + num custom
objects + num default filters
+        def expectedSize = 3 + DefaultFilter.values().length
         assertEquals expectedSize, env.objects.size()
         assertNotNull env.objects['securityManager']
         assertNotNull env.objects['compositeBean']



Mime
View raw message