You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by bd...@apache.org on 2016/07/11 21:04:28 UTC
[01/13] shiro git commit: SHIRO-364: Adding initial implementation of
bean listeners
Repository: shiro
Updated Branches:
refs/heads/1.3.x dbfacf1f5 -> bd8c31415
SHIRO-364: Adding initial implementation of bean listeners
git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1351930 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/0a886db6
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/0a886db6
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/0a886db6
Branch: refs/heads/1.3.x
Commit: 0a886db69ae665c0f29c3513e032f72c1b65095e
Parents: dbfacf1
Author: Jared Bunting <jb...@apache.org>
Authored: Wed Jun 20 04:03:28 2012 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:49:21 2016 -0400
----------------------------------------------------------------------
.../shiro/config/IniSecurityManagerFactory.java | 6 +
.../apache/shiro/config/ReflectionBuilder.java | 79 ++++++-
.../apache/shiro/config/event/BeanEvent.java | 29 +++
.../apache/shiro/config/event/BeanListener.java | 14 ++
.../shiro/config/event/BeanListenerSupport.java | 31 +++
.../shiro/config/event/ConfiguredBeanEvent.java | 9 +
.../shiro/config/event/DestroyedBeanEvent.java | 9 +
.../config/event/InstantiatedBeanEvent.java | 9 +
.../shiro/config/event/LoggingBeanListener.java | 32 +++
.../shiro/config/ReflectionBuilderTest.groovy | 213 +++++++------------
.../shiro/config/RecordingBeanListener.java | 51 +++++
samples/web/src/main/webapp/WEB-INF/shiro.ini | 2 +
12 files changed, 349 insertions(+), 135 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java b/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
index 5101b35..6a30f0a 100644
--- a/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
+++ b/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
@@ -73,6 +73,12 @@ public class IniSecurityManagerFactory extends IniFactorySupport<SecurityManager
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);
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
index 6ca49c0..3d15f9a 100644
--- a/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
+++ b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
@@ -22,6 +22,7 @@ import org.apache.commons.beanutils.BeanUtils;
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,10 +139,43 @@ 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));
+ }
+ }
}
//SHIRO-413: init method must be called for constructed objects that are Initializable
@@ -131,6 +184,26 @@ public class ReflectionBuilder {
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);
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
new file mode 100644
index 0000000..108c8eb
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
@@ -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;
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanListener.java b/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
new file mode 100644
index 0000000..1f0a448
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
@@ -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);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java b/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
new file mode 100644
index 0000000..60fc3d5
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
@@ -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
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
new file mode 100644
index 0000000..f183a6b
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
@@ -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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
new file mode 100644
index 0000000..a92cc2f
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
@@ -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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
new file mode 100644
index 0000000..75d2ccf
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
@@ -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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
new file mode 100644
index 0000000..5a230e6
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
@@ -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());
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
index e5431bd..39957a9 100644
--- a/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
+++ b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
@@ -23,15 +23,13 @@ import org.apache.shiro.codec.CodecSupport
import org.apache.shiro.codec.Hex
import org.apache.shiro.realm.ldap.JndiLdapRealm
import org.apache.shiro.util.CollectionUtils
-import org.junit.Test
-import static org.junit.Assert.*
+import org.apache.shiro.config.event.BeanEvent
/**
* Unit tests for the {@link ReflectionBuilder} implementation.
*/
-class ReflectionBuilderTest {
+class ReflectionBuilderTest extends GroovyTestCase {
- @Test
void testStandardPropertyAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -46,8 +44,7 @@ class ReflectionBuilderTest {
assertTrue cBean.intProp == 42
assertTrue cBean.simpleBean instanceof SimpleBean
}
-
- @Test
+
void testMapEntryAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -59,7 +56,6 @@ class ReflectionBuilderTest {
assertTrue cBean.simpleBeanMap['simpleBean2'] instanceof SimpleBean
}
- @Test
void testArrayEntryAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -71,7 +67,6 @@ class ReflectionBuilderTest {
assertTrue cBean.compositeBeanArray[0] instanceof CompositeBean
}
- @Test
void testNestedPathAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -86,7 +81,6 @@ class ReflectionBuilderTest {
assertTrue cbean1.compositeBeanMap['cbean2'].compositeBeanArray[0].simpleBean.name == 'sbean1'
}
- @Test
//asserts SHIRO-305: https://issues.apache.org/jira/browse/SHIRO-305
void testNestedMapAssignmentWithPeriodDelimitedKeys() {
def ini = new Ini()
@@ -106,7 +100,6 @@ class ReflectionBuilderTest {
assertEquals 'plain ssl', ldapRealm.contextFactory.environment['com.sun.jndi.ldap.connect.pool.protocol']
}
- @Test
void testSimpleConfig() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -124,7 +117,6 @@ class ReflectionBuilderTest {
assertEquals(compositeBean.getIntProp(), 42);
}
- @Test
void testWithConfiguredNullValue() {
Map<String,Object> defaults = new LinkedHashMap<String,Object>();
CompositeBean cBean = new CompositeBean();
@@ -148,7 +140,6 @@ class ReflectionBuilderTest {
assertNull(compositeBean.getSimpleBean());
}
- @Test
void testWithConfiguredNullLiteralValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -166,7 +157,6 @@ class ReflectionBuilderTest {
assertEquals("null", compositeBean.getStringProp());
}
- @Test
void testWithConfiguredEmptyStringValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -184,7 +174,6 @@ class ReflectionBuilderTest {
assertEquals("", compositeBean.getStringProp());
}
- @Test
void testWithConfiguredEmptyStringLiteralValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -202,7 +191,6 @@ class ReflectionBuilderTest {
assertEquals("\"\"", compositeBean.getStringProp());
}
- @Test
void testSimpleConfigWithDollarSignStringValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -215,7 +203,6 @@ class ReflectionBuilderTest {
assertEquals(compositeBean.getStringProp(), '$500');
}
- @Test
void testObjectReferenceConfig() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
@@ -237,7 +224,6 @@ class ReflectionBuilderTest {
assertEquals(simpleBean.getIntProp(), 101);
}
- @Test
void testObjectReferenceConfigWithTypeMismatch() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
@@ -251,7 +237,6 @@ class ReflectionBuilderTest {
}
}
- @Test
void testObjectReferenceConfigWithInvalidReference() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
@@ -265,7 +250,6 @@ class ReflectionBuilderTest {
}
}
- @Test
void testSetProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -282,31 +266,6 @@ class ReflectionBuilderTest {
assertEquals(2, simpleBeans.size());
}
- @Test
- //SHIRO-423
- void testSetPropertyWithReferencedSet() {
- def set = [new SimpleBean('foo'), new SimpleBean('bar')] as Set
-
- def defs = [
- compositeBean: 'org.apache.shiro.config.CompositeBean',
- 'compositeBean.simpleBeanSet': '$set'
- ]
-
- ReflectionBuilder builder = new ReflectionBuilder(['set': set]);
- Map objects = builder.buildObjects(defs);
- assertFalse(CollectionUtils.isEmpty(objects));
- CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
- assertNotNull(cBean);
- Set<SimpleBean> simpleBeans = cBean.getSimpleBeanSet();
- assertNotNull(simpleBeans);
- assertSame set, simpleBeans
- assertEquals(2, simpleBeans.size());
- def i = simpleBeans.iterator()
- assertEquals 'foo', i.next().name
- assertEquals 'bar', i.next().name
- }
-
- @Test
void testListProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -323,30 +282,6 @@ class ReflectionBuilderTest {
assertEquals(3, simpleBeans.size());
}
- @Test
- //SHIRO-423
- void testListPropertyWithReferencedList() {
- List list = [new SimpleBean('foo'), new SimpleBean('bar')] as List
-
- def defs = [
- compositeBean: 'org.apache.shiro.config.CompositeBean',
- 'compositeBean.simpleBeanList': '$list'
- ]
-
- ReflectionBuilder builder = new ReflectionBuilder(['list': list]);
- Map objects = builder.buildObjects(defs);
- assertFalse(CollectionUtils.isEmpty(objects));
- CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
- assertNotNull(cBean);
- def simpleBeans = cBean.getSimpleBeanList();
- assertNotNull(simpleBeans);
- assertSame list, simpleBeans
- assertEquals(2, simpleBeans.size());
- assertEquals 'foo', simpleBeans[0].name
- assertEquals 'bar', simpleBeans[1].name
- }
-
- @Test
void testCollectionProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -364,31 +299,6 @@ class ReflectionBuilderTest {
assertEquals(3, simpleBeans.size());
}
- @Test
- //SHIRO-423
- void testCollectionPropertyWithReferencedCollection() {
- def c = [new SimpleBean('foo'), new SimpleBean('bar')]
-
- def defs = [
- compositeBean: 'org.apache.shiro.config.CompositeBean',
- 'compositeBean.simpleBeanCollection': '$collection'
- ]
-
- ReflectionBuilder builder = new ReflectionBuilder(['collection': c]);
- Map objects = builder.buildObjects(defs);
- assertFalse(CollectionUtils.isEmpty(objects));
- CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
- assertNotNull(cBean);
- def simpleBeans = cBean.getSimpleBeanCollection();
- assertNotNull(simpleBeans);
- assertSame c, simpleBeans
- assertEquals(2, simpleBeans.size());
- def i = simpleBeans.iterator()
- assertEquals 'foo', i.next().name
- assertEquals 'bar', i.next().name
- }
-
- @Test
void testByteArrayHexProperty() {
String source = "Hello, world.";
byte[] bytes = CodecSupport.toBytes(source);
@@ -409,7 +319,6 @@ class ReflectionBuilderTest {
assertEquals(source, reconstituted);
}
- @Test
void testByteArrayBase64Property() {
String source = "Hello, world.";
byte[] bytes = CodecSupport.toBytes(source);
@@ -428,7 +337,6 @@ class ReflectionBuilderTest {
assertEquals(reconstituted, source);
}
- @Test
void testMapProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -449,30 +357,6 @@ class ReflectionBuilderTest {
assertTrue(value instanceof SimpleBean);
}
- @Test
- //SHIRO-423
- void testMapPropertyWithReferencedMap() {
- def map = ['foo': new SimpleBean('foo'), 'bar': new SimpleBean('bar')]
-
- def defs = [
- compositeBean: 'org.apache.shiro.config.CompositeBean',
- 'compositeBean.simpleBeanMap': '$map'
- ]
-
- ReflectionBuilder builder = new ReflectionBuilder(['map': map]);
- Map objects = builder.buildObjects(defs);
- assertFalse(CollectionUtils.isEmpty(objects));
- CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
- assertNotNull(cBean);
- def simpleBeansMap = cBean.getSimpleBeanMap();
- assertNotNull(simpleBeansMap);
- assertSame map, simpleBeansMap
- assertEquals(2, simpleBeansMap.size());
- assertEquals 'foo', simpleBeansMap['foo'].name
- assertEquals 'bar', simpleBeansMap['bar'].name
- }
-
- @Test
void testNestedListProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -493,19 +377,6 @@ class ReflectionBuilderTest {
assertEquals(2, children.size());
}
- @Test
- //asserts SHIRO-413
- void testInitializable() {
- def defs = [
- initializableBean: 'org.apache.shiro.config.InitializableBean'
- ]
- def builder = new ReflectionBuilder()
- def objects = builder.buildObjects(defs)
- def bean = objects.get('initializableBean') as InitializableBean
- assertTrue bean.isInitialized()
- }
-
- @Test
void testFactoryInstantiation() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBeanFactory", "org.apache.shiro.config.SimpleBeanFactory");
@@ -523,4 +394,82 @@ class ReflectionBuilderTest {
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 + ".");
+ }
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
new file mode 100644
index 0000000..82a2f13
--- /dev/null
+++ b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
@@ -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;
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/0a886db6/samples/web/src/main/webapp/WEB-INF/shiro.ini
----------------------------------------------------------------------
diff --git a/samples/web/src/main/webapp/WEB-INF/shiro.ini b/samples/web/src/main/webapp/WEB-INF/shiro.ini
index 60ff990..46d1e1b 100644
--- a/samples/web/src/main/webapp/WEB-INF/shiro.ini
+++ b/samples/web/src/main/webapp/WEB-INF/shiro.ini
@@ -21,6 +21,8 @@
# http://shiro.apache.org/web.html for more.
[main]
+listener = org.apache.shiro.config.event.LoggingBeanListener
+
shiro.loginUrl = /login.jsp
# We need to set the cipherKey, if you want the rememberMe cookie to work after restarting or on multiple nodes.
[05/13] shiro git commit: SHIRO-395: Consolidated Publisher and
SubscriberRegistry interfaces into a single EventBus interface. The separate
interfaces were causing a lot of unnecessary complexity if you had a
component that need to both publish and sub
Posted by bd...@apache.org.
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy
deleted file mode 100644
index 1615e46..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import static org.easymock.EasyMock.createStrictMock
-
-/**
- * @since 1.3
- */
-class EventListenerComparatorTest extends GroovyTestCase {
-
- EventListenerComparator comparator
-
- @Override
- protected void setUp() {
- comparator = new EventListenerComparator()
- }
-
- void testANull() {
- def result = comparator.compare(null, createStrictMock(EventListener))
- assertEquals(-1, result)
- }
-
- void testBNull() {
- def result = comparator.compare(createStrictMock(EventListener), null)
- assertEquals 1, result
- }
-
- void testBothNull() {
- assertEquals 0, comparator.compare(null, null)
- }
-
- void testBothSame() {
- def mock = createStrictMock(EventListener)
- assertEquals 0, comparator.compare(mock, mock)
- }
-
- void testBothEventListener() {
- def a = createStrictMock(EventListener)
- def b = createStrictMock(EventListener)
- assertEquals 0, comparator.compare(a, b)
- }
-
- void testATypedListenerBNormalListener() {
- def a = createStrictMock(TypedEventListener)
- def b = createStrictMock(EventListener)
- assertEquals(-1, comparator.compare(a, b))
- }
-
- void testANormalBTypedListener() {
- def a = createStrictMock(EventListener)
- def b = createStrictMock(TypedEventListener)
- assertEquals 1, comparator.compare(a, b)
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy
deleted file mode 100644
index b0b63c1..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import org.apache.shiro.event.Subscribe
-
-/**
- * @since 1.3
- */
-class ExceptionThrowingSubscriber extends TestSubscriber {
-
- @Subscribe
- void onEvent(ErrorCausingEvent event) {
- throw new UnsupportedOperationException("This throws!")
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy
deleted file mode 100644
index b594c92..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class FooEvent extends EventObject {
-
- FooEvent(Object o) {
- super(o)
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy
deleted file mode 100644
index 8489185..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class NotAnnotatedSubscriber {
- //not a subscriber - no methods have been annotated on purpose.
- void hello() {}
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
deleted file mode 100644
index 9a8b1e4..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class SimpleEvent {
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy
deleted file mode 100644
index 0fa44ea..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import org.apache.shiro.event.Subscribe
-
-/**
- * @since 1.3
- */
-class SimpleSubscriber {
-
- int count
-
- SimpleSubscriber() {
- count = 0
- }
-
- @Subscribe
- void onEvent(SimpleEvent event) {
- count++
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy
deleted file mode 100644
index bf3c060..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import java.lang.reflect.Method
-
-/**
- * @since 1.3
- */
-class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
-
- void testInvalidConstruction() {
-
- def target = new Object()
-
- def method = Object.class.getMethods()[0] //any old method will do
-
- try {
- //noinspection GroovyResultOfObjectAllocationIgnored
- new SingleArgumentMethodEventListener(target, method)
- fail("exception expected")
- } catch (IllegalArgumentException iae) {
- assertEquals iae.message, "Event handler methods must accept a single argument."
- }
- }
-
- void testValidConstruction() {
-
- def target = new TestSubscriber()
- def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
-
- def listener = new SingleArgumentMethodEventListener(target, method)
-
- assertSame target, listener.getTarget()
- assertSame method, listener.getMethod()
- }
-
- void testMethodException() {
-
- def target = new TestSubscriber()
- def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
-
- def listener = new SingleArgumentMethodEventListener(target, method) {
- @Override
- Method getMethod() {
- //sneakily swap out the valid method with an erroneous one. This wouldn't ever happen normally, we're
- //just doing this as a test harness:
- return Object.class.getMethods()[0] //any method will do
- }
- }
-
- //now invoke the erroneous method and ensure we get an exception:
- try {
- listener.onEvent(new FooEvent(this))
- fail("exception expected")
- } catch (IllegalStateException ise) {
- assertTrue ise.message.startsWith("Unable to invoke event handler method")
- }
- }
-
- void testAccepts() {
- def target = new TestSubscriber()
- def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
-
- def listener = new SingleArgumentMethodEventListener(target, method)
-
- assertTrue listener.accepts(new FooEvent(this))
- }
-
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy
deleted file mode 100644
index 8dc717c..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import org.apache.shiro.event.Subscribe
-
-/**
- * @since 1.3
- */
-class SubclassTestSubscriber extends TestSubscriber {
-
- int bazCount
-
- SubclassTestSubscriber() {
- bazCount = 0
- }
-
- @Subscribe
- void onEvent(BazEvent event) {
- bazCount++
- lastEvent = event;
- }
-
- @Subscribe
- void onEvent(ErrorCausingEvent event) {
- throw new UnsupportedOperationException("This throws!")
- }
-
- @Override
- int getCount() {
- return super.getCount() + bazCount
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy
deleted file mode 100644
index 3de5488..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import org.apache.shiro.event.Subscribe
-
-/**
- * @since 1.3
- */
-class TestSubscriber {
-
- int fooCount, barCount
- Object lastEvent;
-
- TestSubscriber() {
- fooCount = barCount = 0
- }
-
- @Subscribe
- void onFooEvent(FooEvent event) {
- fooCount++
- lastEvent = event;
- }
-
- @Subscribe
- void onBarEvent(BarEvent event) {
- barCount++
- lastEvent = event;
- }
-
- int getCount() {
- return fooCount + barCount
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/AnnotationEventListenerResolverTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/AnnotationEventListenerResolverTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/AnnotationEventListenerResolverTest.groovy
new file mode 100644
index 0000000..fb5b1b4
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/AnnotationEventListenerResolverTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class AnnotationEventListenerResolverTest extends GroovyTestCase {
+
+ void testSetGetAnnotationClass() {
+ def resolver = new AnnotationEventListenerResolver();
+ resolver.setAnnotationClass(Override.class) //any old annotation will do for the test
+ assertSame Override.class, resolver.getAnnotationClass()
+ }
+
+ void testNullInstanceArgument() {
+ def resolver = new AnnotationEventListenerResolver()
+ def collection = resolver.getEventListeners(null)
+ assertNotNull collection
+ assertTrue collection.isEmpty()
+ }
+
+ void testNoAnnotationsArgument() {
+ def resolver = new AnnotationEventListenerResolver()
+ def collection = resolver.getEventListeners(new NotAnnotatedSubscriber())
+ assertNotNull collection
+ assertTrue collection.isEmpty()
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/BarEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/BarEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/support/BarEvent.groovy
new file mode 100644
index 0000000..20d93e0
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/BarEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class BarEvent extends FooEvent {
+
+ BarEvent(Object o) {
+ super(o)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/BazEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/BazEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/support/BazEvent.groovy
new file mode 100644
index 0000000..891e89e
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/BazEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class BazEvent extends BarEvent {
+
+ BazEvent(Object o) {
+ super(o)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy
new file mode 100644
index 0000000..1adb4f5
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class ClassComparatorTest extends GroovyTestCase {
+
+ ClassComparator comparator
+
+ @Override
+ protected void setUp() {
+ comparator = new ClassComparator()
+ }
+
+ void testANull() {
+ def result = comparator.compare(null, Object)
+ assertEquals(-1, result)
+ }
+
+ void testBNull() {
+ def result = comparator.compare(Object, null)
+ assertEquals 1, result
+ }
+
+ void testBothNull() {
+ assertEquals 0, comparator.compare(null, null)
+ }
+
+ void testBothSame() {
+ assertEquals 0, comparator.compare(Object, Object)
+ }
+
+ void testAParentOfB() {
+ assertEquals 1, comparator.compare(Number, Integer)
+ }
+
+ void testBParentOfA() {
+ assertEquals(-1, comparator.compare(Integer, Number))
+ }
+
+ void testUnrelated() {
+ assertEquals(0, comparator.compare(Integer, Boolean))
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
new file mode 100644
index 0000000..1f8d688
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import static org.easymock.EasyMock.*
+
+/**
+ * @since 1.3
+ */
+class DefaultEventBusTest extends GroovyTestCase {
+
+ DefaultEventBus bus;
+
+ @Override
+ protected void setUp() {
+ bus = new DefaultEventBus()
+ }
+
+ void testSetEventListenerResolver() {
+ def resolver = new EventListenerResolver() {
+ List<EventListener> getEventListeners(Object instance) {
+ return null //dummy implementation
+ }
+ }
+ bus.setEventListenerResolver(resolver)
+ assertSame resolver, bus.getEventListenerResolver()
+ }
+
+ void testSimpleSubscribe() {
+ def subscriber = new TestSubscriber();
+
+ bus.register(subscriber);
+
+ def event = new FooEvent(this)
+
+ bus.publish(event)
+
+ assertEquals 1, subscriber.fooCount
+ assertEquals 1, subscriber.count
+ assertSame event, subscriber.lastEvent
+ }
+
+ void testPublishNullEvent() {
+ def subscriber = new TestSubscriber();
+ bus.register(subscriber)
+
+ bus.publish(null)
+
+ assertEquals 0, subscriber.count
+ }
+
+ void testSubscribeNullInstance() {
+ def resolver = createStrictMock(EventListenerResolver) //assert no methods are called on this
+ bus.eventListenerResolver = resolver
+
+ replay(resolver)
+
+ bus.register(null)
+
+ verify(resolver)
+ }
+
+ void testSubscribeWithoutAnnotations() {
+ def subscriber = new NotAnnotatedSubscriber()
+ bus.register(subscriber)
+
+ bus.publish(new FooEvent(this))
+
+ assertEquals 0, bus.registry.size()
+ }
+
+ void testUnsubscribeNullInstance() {
+ bus.unregister(null)
+ }
+
+ void testUnsubscribe() {
+ def subscriber = new TestSubscriber()
+ bus.register(subscriber)
+ assertEquals 1, bus.registry.size()
+
+ def event = new FooEvent(this)
+
+ bus.publish(event)
+
+ assertSame event, subscriber.lastEvent
+ assertEquals 1, subscriber.fooCount
+ assertEquals 1, subscriber.count
+
+ bus.unregister(subscriber)
+
+ assertEquals 0, bus.registry.size()
+ }
+
+ void testPolymorphicSubscribeMethodsOnlyOneInvoked() {
+ def subscriber = new TestSubscriber()
+ bus.register(subscriber)
+
+ def event = new BarEvent(this)
+
+ bus.publish(event)
+
+ assertSame event, subscriber.lastEvent
+ assertEquals 0, subscriber.fooCount
+ assertEquals 1, subscriber.barCount
+ assertEquals 1, subscriber.count
+ }
+
+ void testPolymorphicSubscribeMethodsOnlyOneInvokedWithListenerSubclass() {
+ def subscriber = new SubclassTestSubscriber()
+ bus.register(subscriber)
+
+ def event = new BazEvent(this)
+
+ bus.publish(event)
+
+ assertSame event, subscriber.lastEvent
+ assertEquals 1, subscriber.count
+ assertEquals 1, subscriber.bazCount
+ assertEquals 0, subscriber.fooCount
+ assertEquals 0, subscriber.barCount
+ }
+
+ void testSubscribeWithErroneousAnnotation() {
+ def subscriber = new ErroneouslyAnnotatedSubscriber()
+ //noinspection GroovyUnusedCatchParameter
+ try {
+ bus.register(subscriber)
+ fail("exception expected")
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ void testContinueThroughListenerExceptions() {
+ def ok = new SimpleSubscriber()
+ def error = new ExceptionThrowingSubscriber()
+
+ bus.register(ok)
+ bus.register(error)
+
+ bus.publish(new ErrorCausingEvent())
+ bus.publish(new SimpleEvent())
+
+ assertEquals 1, ok.count
+ assertEquals 0, error.count
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/ErroneouslyAnnotatedSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/ErroneouslyAnnotatedSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/ErroneouslyAnnotatedSubscriber.groovy
new file mode 100644
index 0000000..66724a9
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/ErroneouslyAnnotatedSubscriber.groovy
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class ErroneouslyAnnotatedSubscriber {
+
+ @Subscribe
+ void onEvent(FooEvent event, Object someOtherArg) {
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/ErrorCausingEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/ErrorCausingEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/support/ErrorCausingEvent.groovy
new file mode 100644
index 0000000..177aa2a
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/ErrorCausingEvent.groovy
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class ErrorCausingEvent {
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/EventListenerComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/EventListenerComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/EventListenerComparatorTest.groovy
new file mode 100644
index 0000000..8cae13f
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/EventListenerComparatorTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import static org.easymock.EasyMock.createStrictMock
+
+/**
+ * @since 1.3
+ */
+class EventListenerComparatorTest extends GroovyTestCase {
+
+ EventListenerComparator comparator
+
+ @Override
+ protected void setUp() {
+ comparator = new EventListenerComparator()
+ }
+
+ void testANull() {
+ def result = comparator.compare(null, createStrictMock(EventListener))
+ assertEquals(-1, result)
+ }
+
+ void testBNull() {
+ def result = comparator.compare(createStrictMock(EventListener), null)
+ assertEquals 1, result
+ }
+
+ void testBothNull() {
+ assertEquals 0, comparator.compare(null, null)
+ }
+
+ void testBothSame() {
+ def mock = createStrictMock(EventListener)
+ assertEquals 0, comparator.compare(mock, mock)
+ }
+
+ void testBothEventListener() {
+ def a = createStrictMock(EventListener)
+ def b = createStrictMock(EventListener)
+ assertEquals 0, comparator.compare(a, b)
+ }
+
+ void testATypedListenerBNormalListener() {
+ def a = createStrictMock(TypedEventListener)
+ def b = createStrictMock(EventListener)
+ assertEquals(-1, comparator.compare(a, b))
+ }
+
+ void testANormalBTypedListener() {
+ def a = createStrictMock(EventListener)
+ def b = createStrictMock(TypedEventListener)
+ assertEquals 1, comparator.compare(a, b)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/ExceptionThrowingSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/ExceptionThrowingSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/ExceptionThrowingSubscriber.groovy
new file mode 100644
index 0000000..4972cf0
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/ExceptionThrowingSubscriber.groovy
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class ExceptionThrowingSubscriber extends TestSubscriber {
+
+ @Subscribe
+ void onEvent(ErrorCausingEvent event) {
+ throw new UnsupportedOperationException("This throws!")
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/FooEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/FooEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/support/FooEvent.groovy
new file mode 100644
index 0000000..e7e2cf8
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/FooEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class FooEvent extends EventObject {
+
+ FooEvent(Object o) {
+ super(o)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/NotAnnotatedSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/NotAnnotatedSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/NotAnnotatedSubscriber.groovy
new file mode 100644
index 0000000..591903b
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/NotAnnotatedSubscriber.groovy
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class NotAnnotatedSubscriber {
+ //not a subscriber - no methods have been annotated on purpose.
+ void hello() {}
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/SimpleEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/SimpleEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/support/SimpleEvent.groovy
new file mode 100644
index 0000000..9f91c5a
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/SimpleEvent.groovy
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * @since 1.3
+ */
+class SimpleEvent {
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/SimpleSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/SimpleSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/SimpleSubscriber.groovy
new file mode 100644
index 0000000..601aa8b
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/SimpleSubscriber.groovy
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class SimpleSubscriber {
+
+ int count
+
+ SimpleSubscriber() {
+ count = 0
+ }
+
+ @Subscribe
+ void onEvent(SimpleEvent event) {
+ count++
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
new file mode 100644
index 0000000..56ef608
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import java.lang.reflect.Method
+
+/**
+ * @since 1.3
+ */
+class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
+
+ void testInvalidConstruction() {
+
+ def target = new Object()
+
+ def method = Object.class.getMethods()[0] //any old method will do
+
+ try {
+ //noinspection GroovyResultOfObjectAllocationIgnored
+ new SingleArgumentMethodEventListener(target, method)
+ fail("exception expected")
+ } catch (IllegalArgumentException iae) {
+ assertEquals iae.message, "Event handler methods must accept a single argument."
+ }
+ }
+
+ void testValidConstruction() {
+
+ def target = new TestSubscriber()
+ def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
+
+ def listener = new SingleArgumentMethodEventListener(target, method)
+
+ assertSame target, listener.getTarget()
+ assertSame method, listener.getMethod()
+ }
+
+ void testMethodException() {
+
+ def target = new TestSubscriber()
+ def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
+
+ def listener = new SingleArgumentMethodEventListener(target, method) {
+ @Override
+ Method getMethod() {
+ //sneakily swap out the valid method with an erroneous one. This wouldn't ever happen normally, we're
+ //just doing this as a test harness:
+ return Object.class.getMethods()[0] //any method will do
+ }
+ }
+
+ //now invoke the erroneous method and ensure we get an exception:
+ try {
+ listener.onEvent(new FooEvent(this))
+ fail("exception expected")
+ } catch (IllegalStateException ise) {
+ assertTrue ise.message.startsWith("Unable to invoke event handler method")
+ }
+ }
+
+ void testAccepts() {
+ def target = new TestSubscriber()
+ def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
+
+ def listener = new SingleArgumentMethodEventListener(target, method)
+
+ assertTrue listener.accepts(new FooEvent(this))
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/SubclassTestSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/SubclassTestSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/SubclassTestSubscriber.groovy
new file mode 100644
index 0000000..629c024
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/SubclassTestSubscriber.groovy
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class SubclassTestSubscriber extends TestSubscriber {
+
+ int bazCount
+
+ SubclassTestSubscriber() {
+ bazCount = 0
+ }
+
+ @Subscribe
+ void onEvent(BazEvent event) {
+ bazCount++
+ lastEvent = event;
+ }
+
+ @Subscribe
+ void onEvent(ErrorCausingEvent event) {
+ throw new UnsupportedOperationException("This throws!")
+ }
+
+ @Override
+ int getCount() {
+ return super.getCount() + bazCount
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/support/TestSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/TestSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/TestSubscriber.groovy
new file mode 100644
index 0000000..de75c31
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/TestSubscriber.groovy
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class TestSubscriber {
+
+ int fooCount, barCount
+ Object lastEvent;
+
+ TestSubscriber() {
+ fooCount = barCount = 0
+ }
+
+ @Subscribe
+ void onFooEvent(FooEvent event) {
+ fooCount++
+ lastEvent = event;
+ }
+
+ @Subscribe
+ void onBarEvent(BarEvent event) {
+ barCount++
+ lastEvent = event;
+ }
+
+ int getCount() {
+ return fooCount + barCount
+ }
+}
[09/13] shiro git commit: SHIRO-395: event bus + event +
infrastructure changes. 100% class/line coverage for all new events + event
bus and supporting components.
Posted by bd...@apache.org.
SHIRO-395: event bus + event + infrastructure changes. 100% class/line coverage for all new events + event bus and supporting components.
git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1454782 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/4929b4a4
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/4929b4a4
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/4929b4a4
Branch: refs/heads/1.3.x
Commit: 4929b4a415c48da576568efd41659cbb9d547d96
Parents: 41c3183
Author: Les Hazlewood <lh...@apache.org>
Authored: Sun Mar 10 04:12:51 2013 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:55:44 2016 -0400
----------------------------------------------------------------------
.../main/java/org/apache/shiro/config/Ini.java | 15 +-
.../apache/shiro/config/ReflectionBuilder.java | 65 ++++++--
.../apache/shiro/config/event/BeanEvent.java | 26 +++-
.../shiro/config/event/ConfiguredBeanEvent.java | 7 +
.../shiro/config/event/DestroyedBeanEvent.java | 27 ++++
.../config/event/InitializedBeanEvent.java | 37 +++++
.../config/event/InstantiatedBeanEvent.java | 27 ++++
.../config/event/LoggingBeanEventListener.java | 42 ++++++
.../shiro/config/event/LoggingBeanListener.java | 53 -------
.../main/java/org/apache/shiro/event/Event.java | 27 ++++
.../shiro/event/support/ClassComparator.java | 73 ---------
.../shiro/event/support/DefaultEventBus.java | 122 +++++++++++----
.../event/support/EventClassComparator.java | 74 +++++++++
.../event/support/EventListenerComparator.java | 7 +-
.../SingleArgumentMethodEventListener.java | 12 +-
.../shiro/mgt/CachingSecurityManager.java | 15 +-
.../apache/shiro/mgt/RealmSecurityManager.java | 36 +++++
.../shiro/mgt/SessionsSecurityManager.java | 31 ++++
.../mgt/AbstractNativeSessionManager.java | 44 +++++-
.../shiro/config/ReflectionBuilderTest.groovy | 151 +++++++++++--------
.../shiro/config/event/BeanEventTest.groovy | 48 ++++++
.../org/apache/shiro/event/EventTest.groovy | 30 ++++
.../event/support/ClassComparatorTest.groovy | 62 --------
.../event/support/DefaultEventBusTest.groovy | 12 ++
.../support/EventClassComparatorTest.groovy | 64 ++++++++
.../InvalidMethodModiferSubscriber.groovy | 32 ++++
...SingleArgumentMethodEventListenerTest.groovy | 20 ++-
.../shiro/config/RecordingBeanListener.java | 26 +++-
samples/web/src/main/webapp/WEB-INF/shiro.ini | 2 +-
29 files changed, 875 insertions(+), 312 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/Ini.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/Ini.java b/core/src/main/java/org/apache/shiro/config/Ini.java
index 8dbaef0..a71bc20 100644
--- a/core/src/main/java/org/apache/shiro/config/Ini.java
+++ b/core/src/main/java/org/apache/shiro/config/Ini.java
@@ -24,8 +24,17 @@ import org.apache.shiro.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
/**
* A class representing the <a href="http://en.wikipedia.org/wiki/INI_file">INI</a> text configuration format.
@@ -542,7 +551,7 @@ public class Ini implements Map<String, Ini.Section> {
throw new IllegalArgumentException(msg);
}
- log.trace("Discovered key/value pair: {}={}", key, value);
+ log.trace("Discovered key/value pair: {} = {}", key, value);
return new String[]{key, value};
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
index ac4d2a1..d7f0d1a 100644
--- a/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
+++ b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
@@ -25,17 +25,33 @@ import org.apache.shiro.codec.Hex;
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.InitializedBeanEvent;
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.apache.shiro.util.Assert;
+import org.apache.shiro.util.ByteSource;
+import org.apache.shiro.util.ClassUtils;
+import org.apache.shiro.util.CollectionUtils;
+import org.apache.shiro.util.Factory;
+import org.apache.shiro.util.LifecycleUtils;
+import org.apache.shiro.util.Nameable;
+import org.apache.shiro.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.PropertyDescriptor;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
@@ -83,7 +99,7 @@ public class ReflectionBuilder {
private final Map<String,Object> registeredEventSubscribers;
//@since 1.3
- private static Map<String,Object> createDefaultObjectMap() {
+ private Map<String,Object> createDefaultObjectMap() {
Map<String,Object> map = new LinkedHashMap<String, Object>();
map.put(EVENT_BUS_NAME, new DefaultEventBus());
return map;
@@ -252,13 +268,25 @@ public class ReflectionBuilder {
public void destroy() {
final Map<String, Object> immutableObjects = Collections.unmodifiableMap(objects);
- for(Map.Entry<String, ?> entry: objects.entrySet()) {
+
+ //destroy objects in the opposite order they were initialized:
+ List<Map.Entry<String,?>> entries = new ArrayList<Map.Entry<String,?>>(objects.entrySet());
+ Collections.reverse(entries);
+
+ for(Map.Entry<String, ?> entry: entries) {
String id = entry.getKey();
Object bean = entry.getValue();
- BeanEvent event = new DestroyedBeanEvent(id, bean, immutableObjects);
- eventBus.publish(event);
- LifecycleUtils.destroy(bean);
+
+ //don't destroy the eventbus until the end - we need it to still be 'alive' while publishing destroy events:
+ if (bean != this.eventBus) { //memory equality check (not .equals) on purpose
+ LifecycleUtils.destroy(bean);
+ BeanEvent event = new DestroyedBeanEvent(id, bean, immutableObjects);
+ eventBus.publish(event);
+ this.eventBus.unregister(bean); //bean is now destroyed - it should not receive any other events
+ }
}
+ //only now destroy the event bus:
+ LifecycleUtils.destroy(this.eventBus);
}
protected void createNewInstance(Map<String, Object> objects, String name, String value) {
@@ -740,6 +768,7 @@ public class ReflectionBuilder {
if (bd.isExecuted()) { //bean is fully configured, no more statements to execute for it:
+ //bean configured overrides the 'eventBus' bean - replace the existing eventBus with the one configured:
if (bd.getBeanName().equals(EVENT_BUS_NAME)) {
EventBus eventBus = (EventBus)bd.getBean();
enableEvents(eventBus);
@@ -751,6 +780,16 @@ public class ReflectionBuilder {
Collections.unmodifiableMap(objects));
eventBus.publish(event);
}
+
+ //initialize the bean if necessary:
+ LifecycleUtils.init(bd.getBean());
+
+ //ignore global 'shiro.' shortcut mechanism:
+ if (!bd.isGlobalConfig()) {
+ BeanEvent event = new InitializedBeanEvent(bd.getBeanName(), bd.getBean(),
+ Collections.unmodifiableMap(objects));
+ eventBus.publish(event);
+ }
}
}
}
@@ -884,11 +923,17 @@ public class ReflectionBuilder {
@Override
protected Object doExecute() {
- createNewInstance(objects, this.lhs, this.rhs);
- Object instantiated = objects.get(this.lhs);
+ String beanName = this.lhs;
+ createNewInstance(objects, beanName, this.rhs);
+ Object instantiated = objects.get(beanName);
setBean(instantiated);
- BeanEvent event = new InstantiatedBeanEvent(this.lhs, instantiated, Collections.unmodifiableMap(objects));
+ //also ensure the instantiated bean has access to the event bus or is subscribed to events if necessary:
+ //Note: because events are being enabled on this bean here (before the instantiated event below is
+ //triggered), beans can react to their own instantiation events.
+ enableEventsIfNecessary(instantiated, beanName);
+
+ BeanEvent event = new InstantiatedBeanEvent(beanName, instantiated, Collections.unmodifiableMap(objects));
eventBus.publish(event);
return instantiated;
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
index 09e83fc..7c3c967 100644
--- a/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
@@ -1,9 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.config.event;
-import java.util.EventObject;
+import org.apache.shiro.event.Event;
+
import java.util.Map;
-public class BeanEvent extends EventObject {
+/**
+ * @since 1.3
+ */
+public abstract class BeanEvent extends Event {
private String beanName;
private Object bean;
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
index 8d210a2..2636330 100644
--- a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
@@ -3,7 +3,14 @@ package org.apache.shiro.config.event;
import java.util.Map;
/**
+ * Event triggered when a configured bean has been instantiated and fully configured but right before the bean has been
+ * initialized.
+ *
* @since 1.3
+ * @see InstantiatedBeanEvent
+ * @see org.apache.shiro.util.Initializable Initializable
+ * @see InitializedBeanEvent
+ * @see DestroyedBeanEvent
*/
public class ConfiguredBeanEvent extends BeanEvent {
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
index f05ee7e..d040e6b 100644
--- a/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
@@ -1,7 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.config.event;
import java.util.Map;
+/**
+ * Event triggered when a configured bean has been destroyed.
+ *
+ * @since 1.3
+ * @see org.apache.shiro.util.Destroyable Destroyable
+ * @see InstantiatedBeanEvent
+ * @see ConfiguredBeanEvent
+ * @see InitializedBeanEvent
+ */
public class DestroyedBeanEvent extends BeanEvent {
public DestroyedBeanEvent(final String beanName, final Object bean, final Map<String, Object> beanContext) {
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/InitializedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/InitializedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/InitializedBeanEvent.java
new file mode 100644
index 0000000..070d548
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/InitializedBeanEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.config.event;
+
+import java.util.Map;
+
+/**
+ * Event triggered when a configured bean has been instantiated, fully configured and initialized.
+ *
+ * @since 1.3
+ * @see org.apache.shiro.util.Initializable Initializable
+ * @see InstantiatedBeanEvent
+ * @see ConfiguredBeanEvent
+ * @see DestroyedBeanEvent
+ */
+public class InitializedBeanEvent extends BeanEvent {
+
+ public InitializedBeanEvent(String beanName, Object bean, Map<String, Object> beanContext) {
+ super(beanName, bean, beanContext);
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
index 4ba9d6e..991217f 100644
--- a/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
@@ -1,7 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.config.event;
import java.util.Map;
+/**
+ * Event triggered when a configured bean has been instantiated but before it is configured or initialized.
+ *
+ * @since 1.3
+ * @see ConfiguredBeanEvent
+ * @see InitializedBeanEvent
+ * @see DestroyedBeanEvent
+ * @see org.apache.shiro.util.Initializable Initializable
+ */
public class InstantiatedBeanEvent extends BeanEvent {
public InstantiatedBeanEvent(final String beanName, final Object bean, final Map<String, Object> beanContext) {
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
new file mode 100644
index 0000000..eb1e8a1
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.config.event;
+
+import org.apache.shiro.event.Subscribe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A stock bean listener implementation that logs all BeanEvents as TRACE log statements.
+ *
+ * @since 1.3
+ */
+public class LoggingBeanEventListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(LoggingBeanEventListener.class);
+ private static final String SUFFIX = BeanEvent.class.getSimpleName();
+
+ @Subscribe
+ public void onEvent(BeanEvent e) {
+ String className = e.getClass().getSimpleName();
+ int i = className.lastIndexOf(SUFFIX);
+ String subclassPrefix = className.substring(0, i);
+ logger.trace("{} bean '{}' [{}]", new Object[]{subclassPrefix, e.getBeanName(), e.getBean()});
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
deleted file mode 100644
index a6b9bbe..0000000
--- a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.config.event;
-
-import org.apache.shiro.event.Subscribe;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A stock bean listener implementation that logs all BeanEvents via the standard logging mechanism.
- *
- * @since 1.3
- */
-public class LoggingBeanListener {
-
- private static final Logger logger = LoggerFactory.getLogger(LoggingBeanListener.class);
-
- @Subscribe
- protected void onUnhandledBeanEvent(BeanEvent beanEvent) {
- logger.warn("UNHANDLED EVENT :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
- }
-
- @Subscribe
- protected void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
- logger.info("INSTANTIATED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
- }
-
- @Subscribe
- protected void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
- logger.info("CONFIGURED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
- }
-
- @Subscribe
- protected void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
- logger.info("DESTROYED :: {} :: {}", beanEvent.getBeanName(), beanEvent.getBean());
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/event/Event.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/Event.java b/core/src/main/java/org/apache/shiro/event/Event.java
new file mode 100644
index 0000000..8989796
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/Event.java
@@ -0,0 +1,27 @@
+package org.apache.shiro.event;
+
+import java.util.EventObject;
+
+/**
+ * Root class for all of Shiro's event classes. Provides access to the timestamp when the event occurred.
+ *
+ * @since 1.3
+ */
+public abstract class Event extends EventObject {
+
+ private final long timestamp; //millis since Epoch (UTC time zone).
+
+ public Event(Object source) {
+ super(source);
+ this.timestamp = System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the timestamp when this event occurred as the number of milliseconds since Epoch (UTC time zone).
+ *
+ * @return the timestamp when this event occurred as the number of milliseconds since Epoch (UTC time zone).
+ */
+ public long getTimestamp() {
+ return this.timestamp;
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java b/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java
deleted file mode 100644
index 21db4ba..0000000
--- a/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.support;
-
-import java.util.Comparator;
-
-/**
- * Compares two classes based on their position in a hierarchy. Classes higher up in a hierarchy are 'greater than'
- * (ordered later) than classes lower in a hierarchy (ordered earlier). Classes in unrelated hierarchies have the same
- * order priority.
- * <p/>
- * Event bus implementations use this comparator to determine which event listener method to invoke when polymorphic
- * listener methods are defined:
- * <p/>
- * If two event classes exist A and B, where A is the parent class of B (and B is a subclass of A) and an event
- * subscriber listens to both events:
- * <pre>
- * @Subscribe
- * public void onEvent(A a) { ... }
- *
- * @Subscribe
- * public void onEvent(B b) { ... }
- * </pre>
- *
- * The {@code onEvent(B b)} method will be invoked on the subscriber and the
- * {@code onEvent(A a)} method will <em>not</em> be invoked. This is to prevent multiple dispatching of a single event
- * to the same consumer.
- * <p/>
- * The ClassComparator is used to order listener method priority based on their event argument class - methods handling
- * event subclasses have higher precedence than superclasses.
- *
- * @since 1.3
- */
-public class ClassComparator 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;
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java b/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
index e8520db..d4d5e8c 100644
--- a/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
+++ b/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
@@ -22,8 +22,15 @@ import org.apache.shiro.event.EventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A default event bus implementation that synchronously publishes events to registered listeners. Listeners can be
@@ -44,35 +51,62 @@ import java.util.concurrent.ConcurrentHashMap;
* <h2>Receiving Events</h2>
* A component can receive events of interest by doing the following.
* <ol>
- * <li>For each type of event you wish to consume, create a public method that accepts a single event argument.
- * The method argument type indicates the type of event to receive.</li>
- * <li>Annotate each of these public methods with the {@link org.apache.shiro.event.Subscribe Subscribe} annotation.</li>
- * <li>Register the component with the event bus:
- * <pre>
+ * <li>For each type of event you wish to consume, create a public method that accepts a single event argument.
+ * The method argument type indicates the type of event to receive.</li>
+ * <li>Annotate each of these public methods with the {@link org.apache.shiro.event.Subscribe Subscribe} annotation.</li>
+ * <li>Register the component with the event bus:
+ * <pre>
* eventBus.register(myComponent);
* </pre>
- * </li>
+ * </li>
* </ol>
* After registering the component, when when an event of a respective type is published, the component's
* {@code Subscribe}-annotated method(s) will be invoked as expected.
- * <p/>
+ *
* This design (and its constituent helper components) was largely influenced by
* Guava's <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/eventbus/EventBus.html">EventBus</a>
* concept, although no code was shared/imported (even though Guava is Apache 2.0 licensed and could have
* been used).
*
+ * This implementation is thread-safe and may be used concurrently.
+ *
* @since 1.3
*/
public class DefaultEventBus implements EventBus {
private static final Logger log = LoggerFactory.getLogger(DefaultEventBus.class);
+ private static final String EVENT_LISTENER_ERROR_MSG = "Event listener processing failed. Listeners should " +
+ "generally handle exceptions directly and not propagate to the event bus.";
+
+ //this is stateless, we can retain a static final reference:
+ private static final EventListenerComparator EVENT_LISTENER_COMPARATOR = new EventListenerComparator();
+
private EventListenerResolver eventListenerResolver;
- private final Map<Object,Subscriber> registry;
+ //We want to preserve registration order to deliver events to objects in the order that they are registered
+ //with the event bus. This has the nice effect that any Shiro system-level components that are registered first
+ //(likely to happen upon startup) have precedence over those registered by end-user components later.
+ //
+ //One might think that this could have been done by just using a ConcurrentSkipListMap (which is available only on
+ //JDK 6 or later). However, this approach requires the implementation of a Comparator to sort elements, and this
+ //surfaces a problem: for any given random event listener, there isn't any guaranteed property to exist that can be
+ //inspected to determine order of registration, since registration order is an artifact of this EventBus
+ //implementation, not the listeners themselves.
+ //
+ //Therefore, we use a simple concurrent lock to wrap a LinkedHashMap - the LinkedHashMap retains insertion order
+ //and the lock provides thread-safety in probably a much simpler mechanism than attempting to write a
+ //EventBus-specific Comparator. This technique is also likely to be faster than a ConcurrentSkipListMap, which
+ //is about 3-5 times slower than a standard ConcurrentMap.
+ private final Map<Object, Subscription> registry;
+ private final Lock registryReadLock;
+ private final Lock registryWriteLock;
public DefaultEventBus() {
- this.registry = new ConcurrentHashMap<Object, Subscriber>();
+ this.registry = new LinkedHashMap<Object, Subscription>(); //not thread safe, so we need locks:
+ ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ this.registryReadLock = rwl.readLock();
+ this.registryWriteLock = rwl.writeLock();
this.eventListenerResolver = new AnnotationEventListenerResolver();
}
@@ -90,14 +124,32 @@ public class DefaultEventBus implements EventBus {
return;
}
- for( Subscriber subscriber : registry.values() ) {
- subscriber.onEvent(event);
+ registryReadLock.lock();
+ try {
+ //performing the entire iteration within the lock will be a slow operation if the registry has a lot of
+ //contention. However, it is expected that the very large majority of cases the registry will be
+ //read-mostly with very little writes (registrations or removals) occurring during a typical application
+ //lifetime.
+ //
+ //The alternative would be to copy the registry.values() collection to a new LinkedHashSet within the lock
+ //only and the iteration on this new collection could be outside the lock. This has the performance penalty
+ //however of always creating a new collection every time an event is published, which could be more
+ //costly for the majority of applications, especially if the number of listeners is large.
+ //
+ //Finally, the read lock is re-entrant, so multiple publish calls will be
+ //concurrent without penalty since publishing is a read-only operation on the registry.
+
+ for (Subscription subscription : this.registry.values()) {
+ subscription.onEvent(event);
+ }
+ } finally {
+ registryReadLock.unlock();
}
}
public void register(Object instance) {
if (instance == null) {
- log.info("Received null instance for registration. Ignoring registration request.");
+ log.info("Received null instance for event listener registration. Ignoring registration request.");
return;
}
@@ -106,54 +158,60 @@ public class DefaultEventBus implements EventBus {
List<EventListener> listeners = getEventListenerResolver().getEventListeners(instance);
if (listeners == null || listeners.isEmpty()) {
- log.warn("Unable to resolve any event listeners for the subscriber instance [" + instance +
- "]. Ignoring registration request.");
+ log.warn("Unable to resolve event listeners for subscriber instance [{}]. Ignoring registration request.",
+ instance);
return;
}
- Subscriber subscriber = new Subscriber(instance, listeners);
+ Subscription subscription = new Subscription(listeners);
- this.registry.put(instance, subscriber);
+ this.registryWriteLock.lock();
+ try {
+ this.registry.put(instance, subscription);
+ } finally {
+ this.registryWriteLock.unlock();
+ }
}
public void unregister(Object instance) {
if (instance == null) {
return;
}
- this.registry.remove(instance);
+ this.registryWriteLock.lock();
+ try {
+ this.registry.remove(instance);
+ } finally {
+ this.registryWriteLock.unlock();
+ }
}
- private class Subscriber {
+ private class Subscription {
- private final Object instance;
- private final List<EventListener> registeredListeners;
+ private final List<EventListener> listeners;
- public Subscriber(Object instance, List<EventListener> listeners) {
- this.instance = instance;
+ public Subscription(List<EventListener> listeners) {
List<EventListener> toSort = new ArrayList<EventListener>(listeners);
- Collections.sort(toSort, new EventListenerComparator());
- this.registeredListeners = toSort;
+ Collections.sort(toSort, EVENT_LISTENER_COMPARATOR);
+ this.listeners = toSort;
}
public void onEvent(Object event) {
Set<Object> delivered = new HashSet<Object>();
- for(EventListener listener : this.registeredListeners) {
+ for (EventListener listener : this.listeners) {
Object target = listener;
if (listener instanceof SingleArgumentMethodEventListener) {
- SingleArgumentMethodEventListener singleArgListener = (SingleArgumentMethodEventListener)listener;
+ SingleArgumentMethodEventListener singleArgListener = (SingleArgumentMethodEventListener) listener;
target = singleArgListener.getTarget();
}
if (listener.accepts(event) && !delivered.contains(target)) {
try {
listener.onEvent(event);
} catch (Throwable t) {
- log.warn("Event listener processing failed. Listeners should generally " +
- "handle exceptions directly and not propagate to the event bus.", t);
- } finally {
- delivered.add(target);
+ log.warn(EVENT_LISTENER_ERROR_MSG, t);
}
+ delivered.add(target);
}
}
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/event/support/EventClassComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/EventClassComparator.java b/core/src/main/java/org/apache/shiro/event/support/EventClassComparator.java
new file mode 100644
index 0000000..4a6ba71
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/EventClassComparator.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import java.util.Comparator;
+
+/**
+ * Compares two event classes based on their position in a class hierarchy. Classes higher up in a hierarchy are
+ * 'greater than' (ordered later) than classes lower in a hierarchy (ordered earlier). Classes in unrelated
+ * hierarchies have the same order priority.
+ * <p/>
+ * Event bus implementations use this comparator to determine which event listener method to invoke when polymorphic
+ * listener methods are defined:
+ * <p/>
+ * If two event classes exist A and B, where A is the parent class of B (and B is a subclass of A) and an event
+ * subscriber listens to both events:
+ * <pre>
+ * @Subscribe
+ * public void onEvent(A a) { ... }
+ *
+ * @Subscribe
+ * public void onEvent(B b) { ... }
+ * </pre>
+ *
+ * The {@code onEvent(B b)} method will be invoked on the subscriber and the
+ * {@code onEvent(A a)} method will <em>not</em> be invoked. This is to prevent multiple dispatching of a single event
+ * to the same consumer.
+ * <p/>
+ * The EventClassComparator is used to order listener method priority based on their event argument class - methods
+ * handling event subclasses have higher precedence than superclasses.
+ *
+ * @since 1.3
+ */
+public class EventClassComparator implements Comparator<Class> {
+
+ @SuppressWarnings("unchecked")
+ 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;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java b/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
index 817c805..92bb595 100644
--- a/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
+++ b/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
@@ -29,13 +29,16 @@ import java.util.Comparator;
* EventListener instances have the same order priority.
* <p/>
* When both objects being compared are TypedEventListeners, they are ordered according to the rules of the
- * {@link ClassComparator}, using the TypedEventListeners'
+ * {@link EventClassComparator}, using the TypedEventListeners'
* {@link TypedEventListener#getEventType() eventType}.
*
* @since 1.3
*/
public class EventListenerComparator implements Comparator<EventListener> {
+ //event class comparator is stateless, so we can retain an instance:
+ private static final EventClassComparator EVENT_CLASS_COMPARATOR = new EventClassComparator();
+
public int compare(EventListener a, EventListener b) {
if (a == null) {
if (b == null) {
@@ -52,7 +55,7 @@ public class EventListenerComparator implements Comparator<EventListener> {
TypedEventListener ta = (TypedEventListener)a;
if (b instanceof TypedEventListener) {
TypedEventListener tb = (TypedEventListener)b;
- return new ClassComparator().compare(ta.getEventType(), tb.getEventType());
+ return EVENT_CLASS_COMPARATOR.compare(ta.getEventType(), tb.getEventType());
} else {
return -1; //TypedEventListeners are 'less than' (higher priority) than non typed
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java b/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
index f30c11a..9475a7e 100644
--- a/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
+++ b/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
@@ -19,6 +19,7 @@
package org.apache.shiro.event.support;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
/**
* A event listener that invokes a target object's method that accepts a single event argument.
@@ -35,6 +36,8 @@ public class SingleArgumentMethodEventListener implements TypedEventListener {
this.method = method;
//assert that the method is defined as expected:
getMethodArgumentType(method);
+
+ assertPublicMethod(method);
}
public Object getTarget() {
@@ -45,6 +48,13 @@ public class SingleArgumentMethodEventListener implements TypedEventListener {
return this.method;
}
+ private void assertPublicMethod(Method method) {
+ int modifiers = method.getModifiers();
+ if (!Modifier.isPublic(modifiers)) {
+ throw new IllegalArgumentException("Event handler method [" + method + "] must be public.");
+ }
+ }
+
public boolean accepts(Object event) {
return event != null && getEventType().isInstance(event);
}
@@ -58,7 +68,7 @@ public class SingleArgumentMethodEventListener implements TypedEventListener {
try {
method.invoke(getTarget(), event);
} catch (Exception e) {
- throw new IllegalStateException("Unable to invoke event handler method [" + method + "]", e);
+ throw new IllegalStateException("Unable to invoke event handler method [" + method + "].", e);
}
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
index 9cfaae9..1001dd8 100644
--- a/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
+++ b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
@@ -89,12 +89,13 @@ public abstract class CachingSecurityManager implements SecurityManager, Destroy
* {@link #getCacheManager getCacheManager()} method.
*/
protected void afterCacheManagerSet() {
+ applyEventBusToCacheManager();
}
/**
- * Returns the {@code EventBus} used by this Securitymanager and potentially any of its children components.
+ * 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.
+ * @return the {@code EventBus} used by this SecurityManager and potentially any of its children components.
* @since 1.3
*/
public EventBus getEventBus() {
@@ -119,12 +120,22 @@ public abstract class CachingSecurityManager implements SecurityManager, Destroy
}
/**
+ * @since 1.3
+ */
+ protected void applyEventBusToCacheManager() {
+ if (this.eventBus != null && this.cacheManager != null && this.cacheManager instanceof EventBusAware) {
+ ((EventBusAware)this.cacheManager).setEventBus(this.eventBus);
+ }
+ }
+
+ /**
* 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() {
+ applyEventBusToCacheManager();
}
/**
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java
index b63672b..f5392e2 100644
--- a/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java
+++ b/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java
@@ -20,6 +20,8 @@ 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.realm.Realm;
import org.apache.shiro.util.LifecycleUtils;
@@ -83,6 +85,7 @@ public abstract class RealmSecurityManager extends CachingSecurityManager {
protected void afterRealmsSet() {
applyCacheManagerToRealms();
+ applyEventBusToRealms();
}
/**
@@ -119,14 +122,47 @@ public abstract class RealmSecurityManager extends CachingSecurityManager {
}
/**
+ * Sets the internal {@link #getEventBus EventBus} on any internal configured
+ * {@link #getRealms Realms} that implement the {@link EventBusAware} interface.
+ * <p/>
+ * This method is called after setting an eventBus on this securityManager via the
+ * {@link #setEventBus(org.apache.shiro.event.EventBus) setEventBus} method to allow it to be propagated
+ * down to all the internal Realms that would need to use it.
+ * <p/>
+ * It is also called after setting one or more realms via the {@link #setRealm setRealm} or
+ * {@link #setRealms setRealms} methods to allow these newly available realms to be given the EventBus
+ * already in use.
+ *
+ * @since 1.3
+ */
+ protected void applyEventBusToRealms() {
+ EventBus eventBus = getEventBus();
+ Collection<Realm> realms = getRealms();
+ if (eventBus != null && realms != null && !realms.isEmpty()) {
+ for(Realm realm : realms) {
+ if (realm instanceof EventBusAware) {
+ ((EventBusAware)realm).setEventBus(eventBus);
+ }
+ }
+ }
+ }
+
+ /**
* Simply calls {@link #applyCacheManagerToRealms() applyCacheManagerToRealms()} to allow the
* newly set {@link org.apache.shiro.cache.CacheManager CacheManager} to be propagated to the internal collection of <code>Realm</code>
* that would need to use it.
*/
protected void afterCacheManagerSet() {
+ super.afterCacheManagerSet();
applyCacheManagerToRealms();
}
+ @Override
+ protected void afterEventBusSet() {
+ super.afterEventBusSet();
+ applyEventBusToRealms();
+ }
+
public void destroy() {
LifecycleUtils.destroy(getRealms());
this.realms = null;
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java
index 05a7dec..eb00b56 100644
--- a/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java
+++ b/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java
@@ -20,6 +20,8 @@ package org.apache.shiro.mgt;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.cache.CacheManagerAware;
+import org.apache.shiro.event.EventBus;
+import org.apache.shiro.event.EventBusAware;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionManager;
@@ -82,6 +84,7 @@ public abstract class SessionsSecurityManager extends AuthorizingSecurityManager
protected void afterSessionManagerSet() {
applyCacheManagerToSessionManager();
+ applyEventBusToSessionManager();
}
/**
@@ -99,12 +102,24 @@ public abstract class SessionsSecurityManager extends AuthorizingSecurityManager
* {@link #applyCacheManagerToSessionManager() applyCacheManagerToSessionManager()} to ensure the
* <code>CacheManager</code> is applied to the SessionManager as necessary.
*/
+ @Override
protected void afterCacheManagerSet() {
super.afterCacheManagerSet();
applyCacheManagerToSessionManager();
}
/**
+ * Sets any configured EventBus on the SessionManager if necessary.
+ *
+ * @since 1.3
+ */
+ @Override
+ protected void afterEventBusSet() {
+ super.afterEventBusSet();
+ applyEventBusToSessionManager();
+ }
+
+ /**
* Ensures the internal delegate <code>SessionManager</code> is injected with the newly set
* {@link #setCacheManager CacheManager} so it may use it for its internal caching needs.
* <p/>
@@ -117,6 +132,22 @@ public abstract class SessionsSecurityManager extends AuthorizingSecurityManager
}
}
+ /**
+ * Ensures the internal delegate <code>SessionManager</code> is injected with the newly set
+ * {@link #setEventBus EventBus} so it may use it for its internal event needs.
+ * <p/>
+ * Note: This implementation only injects the EventBus into the SessionManager if the SessionManager
+ * instance implements the {@link EventBusAware EventBusAware} interface.
+ *
+ * @since 1.3
+ */
+ protected void applyEventBusToSessionManager() {
+ EventBus eventBus = getEventBus();
+ if (eventBus != null && this.sessionManager instanceof EventBusAware) {
+ ((EventBusAware)this.sessionManager).setEventBus(eventBus);
+ }
+ }
+
public Session start(SessionContext context) throws AuthorizationException {
return this.sessionManager.start(context);
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java
index e3826d0..86353bd 100644
--- a/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java
+++ b/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java
@@ -19,7 +19,13 @@
package org.apache.shiro.session.mgt;
import org.apache.shiro.authz.AuthorizationException;
-import org.apache.shiro.session.*;
+import org.apache.shiro.event.EventBus;
+import org.apache.shiro.event.EventBusAware;
+import org.apache.shiro.session.InvalidSessionException;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.SessionException;
+import org.apache.shiro.session.SessionListener;
+import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,10 +42,12 @@ import java.util.Date;
*
* @since 1.0
*/
-public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager {
+public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware {
private static final Logger log = LoggerFactory.getLogger(AbstractSessionManager.class);
+ private EventBus eventBus;
+
private Collection<SessionListener> listeners;
public AbstractNativeSessionManager() {
@@ -55,6 +63,38 @@ public abstract class AbstractNativeSessionManager extends AbstractSessionManage
return this.listeners;
}
+ /**
+ * Returns the EventBus used to publish SessionEvents.
+ *
+ * @return the EventBus used to publish SessionEvents.
+ * @since 1.3
+ */
+ protected EventBus getEventBus() {
+ return eventBus;
+ }
+
+ /**
+ * Sets the EventBus to use to publish SessionEvents.
+ *
+ * @param eventBus the EventBus to use to publish SessionEvents.
+ * @since 1.3
+ */
+ public void setEventBus(EventBus eventBus) {
+ this.eventBus = eventBus;
+ }
+
+ /**
+ * Publishes events on the event bus if the event bus is non-null, otherwise does nothing.
+ *
+ * @param event the event to publish on the event bus if the event bus exists.
+ * @since 1.3
+ */
+ protected void publishEvent(Object event) {
+ if (this.eventBus != null) {
+ this.eventBus.publish(event);
+ }
+ }
+
public Session start(SessionContext context) {
Session session = createSession(context);
applyGlobalSessionTimeout(session);
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
index e2ed22e..278efa2 100644
--- a/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
+++ b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
@@ -24,12 +24,17 @@ 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.junit.Test
+
+import static org.junit.Assert.*
/**
* Unit tests for the {@link ReflectionBuilder} implementation.
*/
-class ReflectionBuilderTest extends GroovyTestCase {
+@SuppressWarnings("GrMethodMayBeStatic")
+class ReflectionBuilderTest {
+ @Test
void testStandardPropertyAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -44,7 +49,8 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertTrue cBean.intProp == 42
assertTrue cBean.simpleBean instanceof SimpleBean
}
-
+
+ @Test
void testMapEntryAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -56,6 +62,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertTrue cBean.simpleBeanMap['simpleBean2'] instanceof SimpleBean
}
+ @Test
void testArrayEntryAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -67,6 +74,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertTrue cBean.compositeBeanArray[0] instanceof CompositeBean
}
+ @Test
void testNestedPathAssignment() {
ReflectionBuilder builder = new ReflectionBuilder();
@@ -82,6 +90,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
}
//asserts SHIRO-305: https://issues.apache.org/jira/browse/SHIRO-305
+ @Test
void testNestedMapAssignmentWithPeriodDelimitedKeys() {
def ini = new Ini()
ini.load('''
@@ -100,6 +109,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals 'plain ssl', ldapRealm.contextFactory.environment['com.sun.jndi.ldap.connect.pool.protocol']
}
+ @Test
void testSimpleConfig() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -117,6 +127,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(compositeBean.getIntProp(), 42);
}
+ @Test
void testWithConfiguredNullValue() {
Map<String,Object> defaults = new LinkedHashMap<String,Object>();
CompositeBean cBean = new CompositeBean();
@@ -140,6 +151,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertNull(compositeBean.getSimpleBean());
}
+ @Test
void testWithConfiguredNullLiteralValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -157,6 +169,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals("null", compositeBean.getStringProp());
}
+ @Test
void testWithConfiguredEmptyStringValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -174,6 +187,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals("", compositeBean.getStringProp());
}
+ @Test
void testWithConfiguredEmptyStringLiteralValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -191,6 +205,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals("\"\"", compositeBean.getStringProp());
}
+ @Test
void testSimpleConfigWithDollarSignStringValue() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
@@ -203,6 +218,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(compositeBean.getStringProp(), '$500');
}
+ @Test
void testObjectReferenceConfig() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
@@ -224,32 +240,27 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(simpleBean.getIntProp(), 101);
}
+ @Test(expected=ConfigurationException)
void testObjectReferenceConfigWithTypeMismatch() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
defs.put("compositeBean.simpleBean", "simpleBean");
ReflectionBuilder builder = new ReflectionBuilder();
- try {
- builder.buildObjects(defs);
- "Should have encountered an " + ConfigurationException.class.name
- } catch (ConfigurationException expected) {
- }
+ builder.buildObjects(defs);
}
+ @Test(expected=UnresolveableReferenceException)
void testObjectReferenceConfigWithInvalidReference() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
defs.put("compositeBean.simpleBean", '$foo');
ReflectionBuilder builder = new ReflectionBuilder();
- try {
- builder.buildObjects(defs);
- fail "should have encountered an " + UnresolveableReferenceException.class.name
- } catch (UnresolveableReferenceException expected) {
- }
+ builder.buildObjects(defs);
}
+ @Test
void testSetProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -266,6 +277,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(2, simpleBeans.size());
}
+ @Test
void testListProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -282,6 +294,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(3, simpleBeans.size());
}
+ @Test
void testCollectionProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -299,6 +312,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(3, simpleBeans.size());
}
+ @Test
void testByteArrayHexProperty() {
String source = "Hello, world.";
byte[] bytes = CodecSupport.toBytes(source);
@@ -319,6 +333,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(source, reconstituted);
}
+ @Test
void testByteArrayBase64Property() {
String source = "Hello, world.";
byte[] bytes = CodecSupport.toBytes(source);
@@ -337,6 +352,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(reconstituted, source);
}
+ @Test
void testMapProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -357,6 +373,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertTrue(value instanceof SimpleBean);
}
+ @Test
void testNestedListProperty() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
@@ -377,6 +394,7 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals(2, children.size());
}
+ @Test
void testFactoryInstantiation() {
Map<String, String> defs = new LinkedHashMap<String, String>();
defs.put("simpleBeanFactory", "org.apache.shiro.config.SimpleBeanFactory");
@@ -395,77 +413,84 @@ class ReflectionBuilderTest extends GroovyTestCase {
assertEquals("someString", bean.getStringProp());
}
+ @Test
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());
+ def ini = new Ini();
+ ini.load '''
+ loggingListener = org.apache.shiro.config.event.LoggingBeanEventListener
+ listenerOne = org.apache.shiro.config.RecordingBeanListener
+ listenerTwo = org.apache.shiro.config.RecordingBeanListener
- 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');
+ simpleBeanFactory = org.apache.shiro.config.SimpleBeanFactory
+ simpleBeanFactory.factoryInt = 5
+ simpleBeanFactory.factoryString = someString
+
+ compositeBean = org.apache.shiro.config.CompositeBean
+ compositeBean.simpleBean = $simpleBeanFactory
+ '''
ReflectionBuilder builder = new ReflectionBuilder();
- Map<String, ?> objects = builder.buildObjects(defs);
+ Map<String, ?> objects = builder.buildObjects(ini.getSections().iterator().next());
assertFalse(CollectionUtils.isEmpty(objects));
- assertInstantiatedEvents("listenerOne", objects);
- assertConfiguredEvents("listenerOne", objects, true);
- assertInstantiatedEvents("listenerTwo", objects);
- assertConfiguredEvents("listenerTwo", objects, false);
+ assertInstantiatedEvents("listenerOne", objects, 4) //3 beans following + its own instantiated event
+ assertConfiguredEvents("listenerOne", objects, 4) //3 beans following + its own configured event
+ assertInitializedEvents("listenerOne", objects, 4) //3 beans following + its own initialized event
+
+ assertInstantiatedEvents("listenerTwo", objects, 3) //2 beans following + its own instantiated event
+ assertConfiguredEvents("listenerTwo", objects, 3); //2 beans following + its own configured event
+ assertInitializedEvents("listenerTwo", objects, 3); //2 beans following + its own initialized event
builder.destroy();
- assertDestroyedEvents("listenerOne", objects);
- assertDestroyedEvents("listenerTwo", objects);
- */
+ assertDestroyedEvents("listenerOne", objects, 4); //3 beans defined after it + its own destroyed event
+ assertDestroyedEvents("listenerTwo", objects, 3); //2 beans defined after it + its own destroyed event
}
- 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())
+ void assertInstantiatedEvents(String name, Map<String, ?> objects, int expected) {
+ def bean = objects.get(name) as RecordingBeanListener
+ def events = bean.getInstantiatedEvents()
+ assertEquals(expected, events.size())
+
+ checkType(name, events, "simpleBeanFactory", SimpleBeanFactory);
+ checkType(name, events, "compositeBean", CompositeBean);
+ }
- checkType(name, instantiatedEvents, "simpleBeanFactory", SimpleBeanFactory.class);
- checkType(name, instantiatedEvents, "compositeBean", CompositeBean.class);
+ void assertConfiguredEvents(String name, Map<String, ?> objects, int expected) {
+ def bean = objects.get(name) as RecordingBeanListener
+ def events = bean.getConfiguredEvents();
+ assertEquals(expected, events.size())
- // instantiate notifications do not occur for listeners
+ checkType(name, events, "listenerTwo", RecordingBeanListener);
+ checkType(name, events, "simpleBeanFactory", SimpleBeanFactory);
+ checkType(name, events, "compositeBean", CompositeBean);
}
- 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 assertInitializedEvents(String name, Map<String, ?> objects, int expected) {
+ def bean = objects.get(name) as RecordingBeanListener
+ def events = bean.getInitializedEvents();
+ assertEquals(expected, events.size())
+
+ checkType(name, events, "listenerTwo", RecordingBeanListener);
+ checkType(name, events, "simpleBeanFactory", SimpleBeanFactory);
+ checkType(name, events, "compositeBean", CompositeBean);
}
- 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 assertDestroyedEvents(String name, Map<String, ?> objects, int expected) {
+ def bean = objects.get(name) as RecordingBeanListener
+ def events = bean.getDestroyedEvents();
+ assertEquals(expected, events.size())
+
+ if (expected > 3) {
+ checkType(name, events, "listenerOne", RecordingBeanListener);
+ }
+ checkType(name, events, "listenerTwo", RecordingBeanListener);
+ checkType(name, events, "simpleBeanFactory", SimpleBeanFactory);
+ checkType(name, events, "compositeBean", CompositeBean);
}
- void checkType(String instanceName, List<BeanEvent> events, String name, Class<?> expectedType) {
+ void checkType(String instanceName, List<? extends 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
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/config/event/BeanEventTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/config/event/BeanEventTest.groovy b/core/src/test/groovy/org/apache/shiro/config/event/BeanEventTest.groovy
new file mode 100644
index 0000000..f0ca32c
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/config/event/BeanEventTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.config.event
+
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertSame
+
+/**
+ * @since 1.3
+ */
+class BeanEventTest {
+
+ @Test
+ void testDefault() {
+
+ def m = [foo: 'bar'] as Map<String,Object>
+ Object o = new Object()
+ BeanEvent evt = new MyBeanEvent('baz', o, m)
+
+ assertEquals 'baz', evt.beanName
+ assertSame o, evt.bean
+ assertSame m, evt.beanContext
+ }
+
+ private class MyBeanEvent extends BeanEvent {
+ MyBeanEvent(String beanName, Object bean, Map<String, Object> beanContext) {
+ super(beanName, bean, beanContext)
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy b/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
new file mode 100644
index 0000000..5f2282b
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
@@ -0,0 +1,30 @@
+package org.apache.shiro.event
+
+import org.junit.Test
+
+import static org.junit.Assert.assertSame
+import static org.junit.Assert.assertTrue
+
+/**
+ * @since 1.3
+ */
+class EventTest {
+
+ @Test
+ void testDefault() {
+ Object source = new Object()
+ long start = System.currentTimeMillis()
+ Event e = new DummyEvent(source)
+ long stop = System.currentTimeMillis()
+
+ assertSame source, e.source
+ assertTrue start <= e.timestamp
+ assertTrue stop >= e.timestamp
+ }
+
+ private class DummyEvent extends Event {
+ DummyEvent(Object source) {
+ super(source)
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy
deleted file mode 100644
index 1adb4f5..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/support/ClassComparatorTest.groovy
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.support
-
-/**
- * @since 1.3
- */
-class ClassComparatorTest extends GroovyTestCase {
-
- ClassComparator comparator
-
- @Override
- protected void setUp() {
- comparator = new ClassComparator()
- }
-
- void testANull() {
- def result = comparator.compare(null, Object)
- assertEquals(-1, result)
- }
-
- void testBNull() {
- def result = comparator.compare(Object, null)
- assertEquals 1, result
- }
-
- void testBothNull() {
- assertEquals 0, comparator.compare(null, null)
- }
-
- void testBothSame() {
- assertEquals 0, comparator.compare(Object, Object)
- }
-
- void testAParentOfB() {
- assertEquals 1, comparator.compare(Number, Integer)
- }
-
- void testBParentOfA() {
- assertEquals(-1, comparator.compare(Integer, Number))
- }
-
- void testUnrelated() {
- assertEquals(0, comparator.compare(Integer, Boolean))
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
index 1f8d688..9418b21 100644
--- a/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
+++ b/core/src/test/groovy/org/apache/shiro/event/support/DefaultEventBusTest.groovy
@@ -76,6 +76,18 @@ class DefaultEventBusTest extends GroovyTestCase {
verify(resolver)
}
+ void testSubscribeWithNullResolvedListeners() {
+ def resolver = new EventListenerResolver() {
+ List<EventListener> getEventListeners(Object instance) {
+ return null //dummy implementation
+ }
+ }
+ bus.setEventListenerResolver(resolver)
+ def subscriber = new NotAnnotatedSubscriber()
+ bus.register(subscriber);
+ assertEquals 0, bus.registry.size()
+ }
+
void testSubscribeWithoutAnnotations() {
def subscriber = new NotAnnotatedSubscriber()
bus.register(subscriber)
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/event/support/EventClassComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/EventClassComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/EventClassComparatorTest.groovy
new file mode 100644
index 0000000..d39e3c1
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/EventClassComparatorTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+/**
+ * Tests for the {@link EventClassComparator} implementation.
+ *
+ * @since 1.3
+ */
+class EventClassComparatorTest extends GroovyTestCase {
+
+ EventClassComparator comparator
+
+ @Override
+ protected void setUp() {
+ comparator = new EventClassComparator()
+ }
+
+ void testANull() {
+ def result = comparator.compare(null, Object)
+ assertEquals(-1, result)
+ }
+
+ void testBNull() {
+ def result = comparator.compare(Object, null)
+ assertEquals 1, result
+ }
+
+ void testBothNull() {
+ assertEquals 0, comparator.compare(null, null)
+ }
+
+ void testBothSame() {
+ assertEquals 0, comparator.compare(Object, Object)
+ }
+
+ void testAParentOfB() {
+ assertEquals 1, comparator.compare(Number, Integer)
+ }
+
+ void testBParentOfA() {
+ assertEquals(-1, comparator.compare(Integer, Number))
+ }
+
+ void testUnrelated() {
+ assertEquals(0, comparator.compare(Integer, Boolean))
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/event/support/InvalidMethodModiferSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/InvalidMethodModiferSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/support/InvalidMethodModiferSubscriber.groovy
new file mode 100644
index 0000000..3751669
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/support/InvalidMethodModiferSubscriber.groovy
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class InvalidMethodModiferSubscriber {
+
+ @Subscribe
+ protected void onEvent(Object event) {
+ throw new IllegalStateException("Should never be called.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy b/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
index 56ef608..aa8798a 100644
--- a/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
+++ b/core/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy
@@ -18,13 +18,18 @@
*/
package org.apache.shiro.event.support
+import org.junit.Test
+
import java.lang.reflect.Method
+import static org.junit.Assert.*
+
/**
* @since 1.3
*/
-class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
+class SingleArgumentMethodEventListenerTest {
+ @Test
void testInvalidConstruction() {
def target = new Object()
@@ -40,6 +45,7 @@ class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
}
}
+ @Test
void testValidConstruction() {
def target = new TestSubscriber()
@@ -51,6 +57,7 @@ class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
assertSame method, listener.getMethod()
}
+ @Test
void testMethodException() {
def target = new TestSubscriber()
@@ -74,6 +81,7 @@ class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
}
}
+ @Test
void testAccepts() {
def target = new TestSubscriber()
def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
@@ -83,4 +91,14 @@ class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
assertTrue listener.accepts(new FooEvent(this))
}
+ @Test(expected=IllegalArgumentException)
+ void testNonPublicMethodSubscriber() {
+ def target = new InvalidMethodModiferSubscriber()
+ def method = InvalidMethodModiferSubscriber.class.getDeclaredMethods().find { it.name == "onEvent" }
+
+ new SingleArgumentMethodEventListener(target, method)
+ }
+
+
+
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
index b09d124..54eb3f7 100644
--- a/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
+++ b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
@@ -21,6 +21,7 @@ package org.apache.shiro.config;
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.InitializedBeanEvent;
import org.apache.shiro.config.event.InstantiatedBeanEvent;
import org.apache.shiro.event.Subscribe;
@@ -34,30 +35,41 @@ public class RecordingBeanListener {
private List<InstantiatedBeanEvent> instantiateEvents = new ArrayList<InstantiatedBeanEvent>();
private List<ConfiguredBeanEvent> configuredEvents = new ArrayList<ConfiguredBeanEvent>();
+ private List<InitializedBeanEvent> initializedEvents = new ArrayList<InitializedBeanEvent>();
private List<DestroyedBeanEvent> destroyedEvents = new ArrayList<DestroyedBeanEvent>();
private List<BeanEvent> unhandledEvents = new ArrayList<BeanEvent>();
+ @SuppressWarnings("UnusedDeclaration") //used via reflection
@Subscribe
- protected void onUnhandledBeanEvent(BeanEvent beanEvent) {
+ public void onUnhandledBeanEvent(BeanEvent beanEvent) {
this.unhandledEvents.add(beanEvent);
}
+ @SuppressWarnings("UnusedDeclaration") //used via reflection
@Subscribe
- protected void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
+ public void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
this.instantiateEvents.add(beanEvent);
}
+ @SuppressWarnings("UnusedDeclaration") //used via reflection
@Subscribe
- protected void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
+ public void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
this.configuredEvents.add(beanEvent);
}
+ @SuppressWarnings("UnusedDeclaration") //used via reflection
@Subscribe
- protected void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
+ public void onInitializedBeanEvent(InitializedBeanEvent beanEvent) {
+ this.initializedEvents.add(beanEvent);
+ }
+
+ @SuppressWarnings("UnusedDeclaration") //used via reflection
+ @Subscribe
+ public void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
this.destroyedEvents.add(beanEvent);
}
- public List<InstantiatedBeanEvent> getInstantiateEvents() {
+ public List<InstantiatedBeanEvent> getInstantiatedEvents() {
return instantiateEvents;
}
@@ -65,6 +77,10 @@ public class RecordingBeanListener {
return configuredEvents;
}
+ public List<InitializedBeanEvent> getInitializedEvents() {
+ return initializedEvents;
+ }
+
public List<DestroyedBeanEvent> getDestroyedEvents() {
return destroyedEvents;
}
[02/13] shiro git commit: SHIRO-395: added initial implementation -
all org.apache.shiro.event.** additions are at 100% test coverage.
Posted by bd...@apache.org.
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy
new file mode 100644
index 0000000..3de5488
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/TestSubscriber.groovy
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class TestSubscriber {
+
+ int fooCount, barCount
+ Object lastEvent;
+
+ TestSubscriber() {
+ fooCount = barCount = 0
+ }
+
+ @Subscribe
+ void onFooEvent(FooEvent event) {
+ fooCount++
+ lastEvent = event;
+ }
+
+ @Subscribe
+ void onBarEvent(BarEvent event) {
+ barCount++
+ lastEvent = event;
+ }
+
+ int getCount() {
+ return fooCount + barCount
+ }
+}
[08/13] shiro git commit: SHIRO-395: event bus + event +
infrastructure changes. 100% class/line coverage for all new events + event
bus and supporting components.
Posted by bd...@apache.org.
http://git-wip-us.apache.org/repos/asf/shiro/blob/4929b4a4/samples/web/src/main/webapp/WEB-INF/shiro.ini
----------------------------------------------------------------------
diff --git a/samples/web/src/main/webapp/WEB-INF/shiro.ini b/samples/web/src/main/webapp/WEB-INF/shiro.ini
index 46d1e1b..33c7586 100644
--- a/samples/web/src/main/webapp/WEB-INF/shiro.ini
+++ b/samples/web/src/main/webapp/WEB-INF/shiro.ini
@@ -21,7 +21,7 @@
# http://shiro.apache.org/web.html for more.
[main]
-listener = org.apache.shiro.config.event.LoggingBeanListener
+listener = org.apache.shiro.config.event.LoggingBeanEventListener
shiro.loginUrl = /login.jsp
[11/13] shiro git commit: Small bug fix and test for handling
classnames in LoggingBeanEventListener
Posted by bd...@apache.org.
Small bug fix and test for handling classnames in LoggingBeanEventListener
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/67e7a36d
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/67e7a36d
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/67e7a36d
Branch: refs/heads/1.3.x
Commit: 67e7a36d406201860836f07916470f6dc0815d13
Parents: 1e6a89c
Author: Brian Demers <bd...@apache.org>
Authored: Mon Jul 11 09:19:31 2016 -0400
Committer: Brian Demers <bd...@apache.org>
Committed: Mon Jul 11 09:19:31 2016 -0400
----------------------------------------------------------------------
.../config/event/LoggingBeanEventListener.java | 2 +-
.../event/LoggingBeanEventListenerTest.groovy | 48 ++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/67e7a36d/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
index eb1e8a1..f6837e2 100644
--- a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
+++ b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanEventListener.java
@@ -36,7 +36,7 @@ public class LoggingBeanEventListener {
public void onEvent(BeanEvent e) {
String className = e.getClass().getSimpleName();
int i = className.lastIndexOf(SUFFIX);
- String subclassPrefix = className.substring(0, i);
+ String subclassPrefix = i > 0 ? className.substring(0, i) : className;
logger.trace("{} bean '{}' [{}]", new Object[]{subclassPrefix, e.getBeanName(), e.getBean()});
}
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/67e7a36d/core/src/test/groovy/org/apache/shiro/config/event/LoggingBeanEventListenerTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/config/event/LoggingBeanEventListenerTest.groovy b/core/src/test/groovy/org/apache/shiro/config/event/LoggingBeanEventListenerTest.groovy
new file mode 100644
index 0000000..2d37f99
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/config/event/LoggingBeanEventListenerTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.config.event
+
+import org.junit.Test
+
+/**
+ * @since 1.3
+ */
+class LoggingBeanEventListenerTest {
+
+ /**
+ * Test that LoggingBeanEventListener attempts to substring class names of BeanEvents that contain the
+ * string 'BeanEvent'.
+ */
+ @Test
+ void testMisnamedBeanEventClass() {
+
+ def m = [foo: 'bar'] as Map<String,Object>
+ Object o = new Object()
+ BeanEvent evt = new MisnamedBean('baz', o, m)
+
+ // This was previously throwing a StringIndexOutOfBoundsException
+ new LoggingBeanEventListener().onEvent(evt)
+ }
+
+ private class MisnamedBean extends BeanEvent {
+ MisnamedBean(String beanName, Object bean, Map<String, Object> beanContext) {
+ super(beanName, bean, beanContext)
+ }
+ }
+}
[06/13] shiro git commit: SHIRO-395: Consolidated Publisher and
SubscriberRegistry interfaces into a single EventBus interface. The separate
interfaces were causing a lot of unnecessary complexity if you had a
component that need to both publish and sub
Posted by bd...@apache.org.
SHIRO-395: Consolidated Publisher and SubscriberRegistry interfaces into a single EventBus interface. The separate interfaces were causing a lot of unnecessary complexity if you had a component that need to both publish and subscribe. The new EventBus interface more easily/cleanly satisfies 3 usage scenarios in a single component (publish, subscribe and publish+subscribe). All related test cases have 100% class, method and line coverage.
git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1426977 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/bdecc0ba
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/bdecc0ba
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/bdecc0ba
Branch: refs/heads/1.3.x
Commit: bdecc0ba3265bfab031f0d10d9b9d3961670c0e3
Parents: 0c7a8b5
Author: Les Hazlewood <lh...@apache.org>
Authored: Sun Dec 30 19:11:17 2012 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:49:55 2016 -0400
----------------------------------------------------------------------
.../java/org/apache/shiro/event/EventBus.java | 85 ++++++++++
.../org/apache/shiro/event/EventBusAware.java | 35 ++++
.../java/org/apache/shiro/event/Publisher.java | 34 ----
.../bus/AnnotationEventListenerResolver.java | 96 -----------
.../apache/shiro/event/bus/ClassComparator.java | 73 ---------
.../apache/shiro/event/bus/DefaultEventBus.java | 161 ------------------
.../apache/shiro/event/bus/EventListener.java | 56 -------
.../event/bus/EventListenerComparator.java | 68 --------
.../shiro/event/bus/EventListenerResolver.java | 50 ------
.../bus/SingleArgumentMethodEventListener.java | 74 ---------
.../shiro/event/bus/SubscriberRegistry.java | 45 -----
.../shiro/event/bus/TypedEventListener.java | 27 ---
.../AnnotationEventListenerResolver.java | 96 +++++++++++
.../shiro/event/support/ClassComparator.java | 73 +++++++++
.../shiro/event/support/DefaultEventBus.java | 161 ++++++++++++++++++
.../shiro/event/support/EventListener.java | 56 +++++++
.../event/support/EventListenerComparator.java | 68 ++++++++
.../event/support/EventListenerResolver.java | 50 ++++++
.../SingleArgumentMethodEventListener.java | 74 +++++++++
.../shiro/event/support/TypedEventListener.java | 27 +++
.../AnnotationEventListenerResolverTest.groovy | 45 -----
.../org/apache/shiro/event/bus/BarEvent.groovy | 29 ----
.../org/apache/shiro/event/bus/BazEvent.groovy | 29 ----
.../shiro/event/bus/ClassComparatorTest.groovy | 62 -------
.../shiro/event/bus/DefaultEventBusTest.groovy | 163 -------------------
.../bus/ErroneouslyAnnotatedSubscriber.groovy | 31 ----
.../shiro/event/bus/ErrorCausingEvent.groovy | 25 ---
.../bus/EventListenerComparatorTest.groovy | 71 --------
.../bus/ExceptionThrowingSubscriber.groovy | 32 ----
.../org/apache/shiro/event/bus/FooEvent.groovy | 29 ----
.../event/bus/NotAnnotatedSubscriber.groovy | 27 ---
.../apache/shiro/event/bus/SimpleEvent.groovy | 25 ---
.../shiro/event/bus/SimpleSubscriber.groovy | 38 -----
...SingleArgumentMethodEventListenerTest.groovy | 86 ----------
.../event/bus/SubclassTestSubscriber.groovy | 49 ------
.../shiro/event/bus/TestSubscriber.groovy | 50 ------
.../AnnotationEventListenerResolverTest.groovy | 45 +++++
.../apache/shiro/event/support/BarEvent.groovy | 29 ++++
.../apache/shiro/event/support/BazEvent.groovy | 29 ++++
.../event/support/ClassComparatorTest.groovy | 62 +++++++
.../event/support/DefaultEventBusTest.groovy | 163 +++++++++++++++++++
.../ErroneouslyAnnotatedSubscriber.groovy | 31 ++++
.../event/support/ErrorCausingEvent.groovy | 25 +++
.../support/EventListenerComparatorTest.groovy | 71 ++++++++
.../support/ExceptionThrowingSubscriber.groovy | 32 ++++
.../apache/shiro/event/support/FooEvent.groovy | 29 ++++
.../event/support/NotAnnotatedSubscriber.groovy | 27 +++
.../shiro/event/support/SimpleEvent.groovy | 25 +++
.../shiro/event/support/SimpleSubscriber.groovy | 38 +++++
...SingleArgumentMethodEventListenerTest.groovy | 86 ++++++++++
.../event/support/SubclassTestSubscriber.groovy | 49 ++++++
.../shiro/event/support/TestSubscriber.groovy | 50 ++++++
52 files changed, 1516 insertions(+), 1475 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/EventBus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/EventBus.java b/core/src/main/java/org/apache/shiro/event/EventBus.java
new file mode 100644
index 0000000..525240f
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/EventBus.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event;
+
+/**
+ * An event bus can publish events to event subscribers as well as provide a mechanism for registering and unregistering
+ * event subscribers.
+ * <p/>
+ * An event bus enables a publish/subscribe paradigm within Shiro - components can publish or consume events they
+ * find relevant without needing to be tightly coupled to other components. This affords great
+ * flexibility within Shiro by promoting loose coupling and high cohesion between components and a much safer pluggable
+ * architecture.
+ * <h2>Sending Events</h2>
+ * If a component wishes to publish events to other components:
+ * <pre>
+ * MyEvent myEvent = createMyEvent();
+ * eventBus.publish(myEvent);
+ * </pre>
+ * The event bus will determine the type of event and then dispatch the event to components that wish to receive
+ * events of that type.
+ * <h2>Receiving Events</h2>
+ * A component can receive events of interest by doing the following.
+ * <ol>
+ * <li>For each type of event you wish to consume, create a public method that accepts a single event argument.
+ * The method argument type indicates the type of event to receive.</li>
+ * <li>Annotate each of these public methods with the {@link org.apache.shiro.event.Subscribe Subscribe} annotation.</li>
+ * <li>Register the component with the event bus:
+ * <pre>
+ * eventBus.register(myComponent);
+ * </pre>
+ * </li>
+ * </ol>
+ * After registering the component, when when an event of a respective type is published, the component's
+ * {@code Subscribe}-annotated method(s) will be invoked as expected.
+ * <p/>
+ * This design (and its constituent helper components) was largely influenced by
+ * Guava's <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/eventbus/EventBus.html">EventBus</a>
+ * concept, although no code was viewed/copied/imported (even though Guava code is Apache 2.0 licensed and could have
+ * been used).
+ *
+ * @since 1.3
+ */
+public interface EventBus {
+
+ /**
+ * Publishes the specified event to an event subsystem that will deliver events to relevant {@link Subscribe}rs.
+ *
+ * @param event The event object to distribute to relevant subscribers.
+ */
+ void publish(Object event);
+
+ /**
+ * Registers all event handler methods on the specified instance to receive relevant events. The handler methods
+ * are determined by the {@code EventBus} implementation, typically by using an
+ * {@link org.apache.shiro.event.support.EventListenerResolver EventListenerResolver}
+ * (e.g. {@link org.apache.shiro.event.support.AnnotationEventListenerResolver AnnotationEventListenerResolver}).
+ *
+ * @param subscriber the object whose event handler methods should be registered to receive events.
+ */
+ void register(Object subscriber);
+
+ /**
+ * Unregisters all previously-registered event handler methods on the specified instance. If the specified object
+ * was not previously registered, calling this method has no effect.
+ *
+ * @param subscriber the previously
+ */
+ void unregister(Object subscriber);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/EventBusAware.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/EventBusAware.java b/core/src/main/java/org/apache/shiro/event/EventBusAware.java
new file mode 100644
index 0000000..7d26613
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/EventBusAware.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.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.
+ *
+ * @since 1.3
+ */
+public interface EventBusAware {
+
+ /**
+ * Sets the available {@code EventBus} that may be used for publishing and subscribing to/from events.
+ *
+ * @param bus the available {@code EventBus}.
+ */
+ void setEventBus(EventBus bus);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/Publisher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/Publisher.java b/core/src/main/java/org/apache/shiro/event/Publisher.java
deleted file mode 100644
index 50c1c8d..0000000
--- a/core/src/main/java/org/apache/shiro/event/Publisher.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event;
-
-/**
- * Publishes events to an event subsystem that will deliver events to registered {@link Subscribe}rs.
- *
- * @since 1.3
- */
-public interface Publisher {
-
- /**
- * Publishes the specified event to an event subsystem that will deliver events to relevant {@link Subscribe}rs.
- *
- * @param event The event object to distribute to relevant subscribers.
- */
- void publish(Object event);
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java b/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java
deleted file mode 100644
index 883dbcf..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-import org.apache.shiro.event.Subscribe;
-import org.apache.shiro.util.ClassUtils;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Inspects an object for annotated methods of interest and creates an {@link EventListener} for each method discovered.
- * An event bus will call the resulting listeners as relevant events arrive.
- * <p/>
- * The default {@link #setAnnotationClass(Class) annotationClass} is {@link Subscribe}, indicating any
- * {@link Subscribe}-annotated method will be represented as an EventListener.
- *
- * @see SingleArgumentMethodEventListener
- * @since 1.3
- */
-public class AnnotationEventListenerResolver implements EventListenerResolver {
-
- private Class<? extends Annotation> annotationClass;
-
- public AnnotationEventListenerResolver() {
- this.annotationClass = Subscribe.class;
- }
-
- /**
- * Returns a new collection of {@link EventListener} instances, each instance corresponding to an annotated
- * method discovered on the specified {@code instance} argument.
- *
- * @param instance the instance to inspect for annotated event handler methods.
- * @return a new collection of {@link EventListener} instances, each instance corresponding to an annotated
- * method discovered on the specified {@code instance} argument.
- */
- public List<EventListener> getEventListeners(Object instance) {
- if (instance == null) {
- return Collections.emptyList();
- }
-
- List<Method> methods = ClassUtils.getAnnotatedMethods(instance.getClass(), getAnnotationClass());
- if (methods == null || methods.isEmpty()) {
- return Collections.emptyList();
- }
-
- List<EventListener> listeners = new ArrayList<EventListener>(methods.size());
-
- for (Method m : methods) {
- listeners.add(new SingleArgumentMethodEventListener(instance, m));
- }
-
- return listeners;
- }
-
- /**
- * Returns the type of annotation that indicates a method that should be represented as an {@link EventListener},
- * defaults to {@link Subscribe}.
- *
- * @return the type of annotation that indicates a method that should be represented as an {@link EventListener},
- * defaults to {@link Subscribe}.
- */
- public Class<? extends Annotation> getAnnotationClass() {
- return annotationClass;
- }
-
- /**
- * Sets the type of annotation that indicates a method that should be represented as an {@link EventListener}.
- * The default value is {@link Subscribe}.
- *
- * @param annotationClass the type of annotation that indicates a method that should be represented as an
- * {@link EventListener}. The default value is {@link Subscribe}.
- */
- public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
- this.annotationClass = annotationClass;
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java b/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java
deleted file mode 100644
index 4d1928f..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-import java.util.Comparator;
-
-/**
- * Compares two classes based on their position in a hierarchy. Classes higher up in a hierarchy are 'greater than'
- * (ordered later) than classes lower in a hierarchy (ordered earlier). Classes in unrelated hierarchies have the same
- * order priority.
- * <p/>
- * Event bus implementations use this comparator to determine which event listener method to invoke when polymorphic
- * listener methods are defined:
- * <p/>
- * If two event classes exist A and B, where A is the parent class of B (and B is a subclass of A) and an event
- * subscriber listens to both events:
- * <pre>
- * @Subscribe
- * public void onEvent(A a) { ... }
- *
- * @Subscribe
- * public void onEvent(B b) { ... }
- * </pre>
- *
- * The {@code onEvent(B b)} method will be invoked on the subscriber and the
- * {@code onEvent(A a)} method will <em>not</em> be invoked. This is to prevent multiple dispatching of a single event
- * to the same consumer.
- * <p/>
- * The ClassComparator is used to order listener method priority based on their event argument class - methods handling
- * event subclasses have higher precedence than superclasses.
- *
- * @since 1.3
- */
-public class ClassComparator 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;
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java b/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java
deleted file mode 100644
index 4b74522..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-
-import org.apache.shiro.event.Publisher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A default event bus implementation that synchronously publishes events to registered listeners. Listeners can be
- * registered or unregistered for events as necessary.
- * <p/>
- * An event bus enables a publish/subscribe paradigm within Shiro - components can publish or consume events they
- * find relevant without needing to be tightly coupled to other components. This affords great
- * flexibility within Shiro by promoting loose coupling and high cohesion between components and a much safer pluggable
- * architecture.
- * <h2>Sending Events</h2>
- * If a component wishes to publish events to other components:
- * <pre>
- * MyEvent myEvent = createMyEvent();
- * eventBus.publish(myEvent);
- * </pre>
- * The event bus will determine the type of event and then dispatch the event to components that wish to receive
- * events of that type.
- * <h2>Receiving Events</h2>
- * A component can receive events of interest by doing the following.
- * <ol>
- * <li>For each type of event you wish to consume, create a public method that accepts a single event argument.
- * The method argument type indicates the type of event to receive.</li>
- * <li>Annotate each of these public methods with the {@link org.apache.shiro.event.Subscribe Subscribe} annotation.</li>
- * <li>Register the component with the event bus:
- * <pre>
- * eventBus.register(myComponent);
- * </pre>
- * </li>
- * </ol>
- * After registering the component, when when an event of a respective type is published, the component's
- * {@code Subscribe}-annotated method(s) will be invoked as expected.
- * <p/>
- * This design (and its constituent helper components) was largely influenced by
- * Guava's <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/eventbus/EventBus.html">EventBus</a>
- * concept, although no code was shared/imported (even though Guava is Apache 2.0 licensed and could have
- * been used).
- *
- * @since 1.3
- */
-public class DefaultEventBus implements Publisher, SubscriberRegistry {
-
- private static final Logger log = LoggerFactory.getLogger(DefaultEventBus.class);
-
- private EventListenerResolver eventListenerResolver;
-
- private final Map<Object,Subscriber> registry;
-
- public DefaultEventBus() {
- this.registry = new ConcurrentHashMap<Object, Subscriber>();
- this.eventListenerResolver = new AnnotationEventListenerResolver();
- }
-
- public EventListenerResolver getEventListenerResolver() {
- return eventListenerResolver;
- }
-
- public void setEventListenerResolver(EventListenerResolver eventListenerResolver) {
- this.eventListenerResolver = eventListenerResolver;
- }
-
- public void publish(Object event) {
- if (event == null) {
- log.info("Received null event for publishing. Ignoring and returning.");
- return;
- }
-
- for( Subscriber subscriber : registry.values() ) {
- subscriber.onEvent(event);
- }
- }
-
- public void register(Object instance) {
- if (instance == null) {
- log.info("Received null instance for registration. Ignoring registration request.");
- return;
- }
-
- unregister(instance);
-
- List<EventListener> listeners = getEventListenerResolver().getEventListeners(instance);
-
- if (listeners == null || listeners.isEmpty()) {
- log.warn("Unable to resolve any event listeners for the subscriber instance [" + instance +
- "]. Ignoring registration request.");
- return;
- }
-
- Subscriber subscriber = new Subscriber(instance, listeners);
-
- this.registry.put(instance, subscriber);
- }
-
- public void unregister(Object instance) {
- if (instance == null) {
- return;
- }
- this.registry.remove(instance);
- }
-
- private class Subscriber {
-
- private final Object instance;
- private final List<EventListener> registeredListeners;
-
- public Subscriber(Object instance, List<EventListener> listeners) {
- this.instance = instance;
- List<EventListener> toSort = new ArrayList<EventListener>(listeners);
- Collections.sort(toSort, new EventListenerComparator());
- this.registeredListeners = toSort;
- }
-
- public void onEvent(Object event) {
-
- Set<Object> delivered = new HashSet<Object>();
-
- for(EventListener listener : this.registeredListeners) {
- Object target = listener;
- if (listener instanceof SingleArgumentMethodEventListener) {
- SingleArgumentMethodEventListener singleArgListener = (SingleArgumentMethodEventListener)listener;
- target = singleArgListener.getTarget();
- }
- if (listener.accepts(event) && !delivered.contains(target)) {
- try {
- listener.onEvent(event);
- delivered.add(target);
- } catch (Throwable t) {
- log.warn("Event listener processing failed. Listeners should generally " +
- "handle exceptions directly and not propagate to the event bus.", t);
- }
- }
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/EventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/EventListener.java b/core/src/main/java/org/apache/shiro/event/bus/EventListener.java
deleted file mode 100644
index a653674..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/EventListener.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-/**
- * An event listener knows how to accept and process events of a particular type (or types).
- * <p/>
- * Note that this interface is in the event bus package (and not the event package directly) because it is a supporting
- * concept for event bus implementations and not something that application
- * developers using Shiro should implement directly. App developers should instead use the
- * {@link org.apache.shiro.event.Subscribe Subscribe} annotation on methods they wish to receive events.
- * <p/>
- * This interface therefore mainly represents a 'middle man' between the event bus and the actual subscribing
- * component. As such, event bus implementors (or framework/infrastructural implementors) or those that wish to
- * customize listener/dispatch functionality might find this concept useful.
- * <p/>
- * It is a concept almost always used in conjunction with a {@link EventListenerResolver} implementation.
- *
- * @see SingleArgumentMethodEventListener
- * @see AnnotationEventListenerResolver
- *
- * @since 1.3
- */
-public interface EventListener {
-
- /**
- * Returns {@code true} if the listener instance can process the specified event object, {@code false} otherwise.
- * @param event the event object to test
- * @return {@code true} if the listener instance can process the specified event object, {@code false} otherwise.
- */
- boolean accepts(Object event);
-
- /**
- * Handles the specified event. Again, as this interface is an implementation concept, implementations of this
- * method will likely dispatch the event to a 'real' processor (e.g. method).
- *
- * @param event the event to handle.
- */
- void onEvent(Object event);
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java b/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java
deleted file mode 100644
index d096591..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-import java.util.Comparator;
-
-/**
- * Compares two event listeners to determine the order in which they should be invoked when an event is dispatched.
- * The lower the order, the sooner it will be invoked (the higher its precedence). The higher the order, the later
- * it will be invoked (the lower its precedence).
- * <p/>
- * TypedEventListeners have a higher precedence (i.e. a lower order) than standard EventListener instances. Standard
- * EventListener instances have the same order priority.
- * <p/>
- * When both objects being compared are TypedEventListeners, they are ordered according to the rules of the
- * {@link ClassComparator}, using the TypedEventListeners'
- * {@link org.apache.shiro.event.bus.TypedEventListener#getEventType() eventType}.
- *
- * @since 1.3
- */
-public class EventListenerComparator implements Comparator<EventListener> {
-
- public int compare(EventListener a, EventListener 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 instanceof TypedEventListener) {
- TypedEventListener ta = (TypedEventListener)a;
- if (b instanceof TypedEventListener) {
- TypedEventListener tb = (TypedEventListener)b;
- return new ClassComparator().compare(ta.getEventType(), tb.getEventType());
- } else {
- return -1; //TypedEventListeners are 'less than' (higher priority) than non typed
- }
- } else {
- if (b instanceof TypedEventListener) {
- return 1;
- } else {
- return 0;
- }
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java b/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java
deleted file mode 100644
index b865d89..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-import java.util.List;
-
-/**
- * An {@code EventListenerResolver} knows how to resolve (either create or lookup) {@link EventListener} instances
- * as a result of inspecting a subscriber object, mostly likely a
- * {@link org.apache.shiro.event.Subscribe Subscribe}-annotated object instance.
- * <p/>
- * This interface exists primarily as a support concept for the {@link DefaultEventBus} implementation. Custom
- * implementations of this interface can be configured on a {@link DefaultEventBus} instance to determine exactly
- * how a subscriber receives events.
- * <p/>
- * For example, the {@link AnnotationEventListenerResolver AnnotationEventListenerResolver} will inspect a runtime
- * object for {@link org.apache.shiro.event.Subscribe Subscribe}-annotated methods, and for each method found, return
- * an {@link EventListener} instance representing the method to invoke.
- *
- * @see AnnotationEventListenerResolver
- * @see SingleArgumentMethodEventListener
- * @since 1.3
- */
-public interface EventListenerResolver {
-
- /**
- * Returns {@link EventListener} instances as a result of inspecting a subscriber object, mostly likely with
- * {@link org.apache.shiro.event.Subscribe Subscribe}-annotated methods.
- *
- * @param instance the subscriber instance for which EventListener instances should be acquired.
- * @return {@link EventListener} instances as a result of inspecting a subscriber object.
- */
- List<EventListener> getEventListeners(Object instance);
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java b/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java
deleted file mode 100644
index f5ea9f9..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-import java.lang.reflect.Method;
-
-/**
- * A event listener that invokes a target object's method that accepts a single event argument.
- *
- * @since 1.3
- */
-public class SingleArgumentMethodEventListener implements TypedEventListener {
-
- private final Object target;
- private final Method method;
-
- public SingleArgumentMethodEventListener(Object target, Method method) {
- this.target = target;
- this.method = method;
- //assert that the method is defined as expected:
- getMethodArgumentType(method);
- }
-
- public Object getTarget() {
- return this.target;
- }
-
- public Method getMethod() {
- return this.method;
- }
-
- public boolean accepts(Object event) {
- return event != null && getEventType().isInstance(event);
- }
-
- public Class getEventType() {
- return getMethodArgumentType(getMethod());
- }
-
- public void onEvent(Object event) {
- Method method = getMethod();
- try {
- method.invoke(getTarget(), event);
- } catch (Exception e) {
- throw new IllegalStateException("Unable to invoke event handler method [" + method + "]", e);
- }
- }
-
- protected Class getMethodArgumentType(Method method) {
- Class[] paramTypes = method.getParameterTypes();
- if (paramTypes.length != 1) {
- //the default implementation expects a single typed argument and nothing more:
- String msg = "Event handler methods must accept a single argument.";
- throw new IllegalArgumentException(msg);
- }
- return paramTypes[0];
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java b/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java
deleted file mode 100644
index af57d6e..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-/**
- * Allows event subscribers to register or unregister with an event subsystem to receive (or not receive) published
- * events.
- *
- * @since 1.3
- */
-public interface SubscriberRegistry {
-
- /**
- * Registers all event handler methods on the specified instance to receive relevant events. The handler methods
- * are determined by {@link SubscriberRegistry} implementations, typically by using an
- * {@link EventListenerResolver} (e.g. {@link AnnotationEventListenerResolver}).
- *
- * @param subscriber the object whose event handler methods should be registered to receive events.
- */
- void register(Object subscriber);
-
- /**
- * Unregisters all previously-registered event handler methods on the specified instance. If the specified object
- * was not previously registered, calling this method has no effect.
- *
- * @param subscriber the previously
- */
- void unregister(Object subscriber);
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java b/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java
deleted file mode 100644
index 9cb51e0..0000000
--- a/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus;
-
-/**
- * @since 1.3
- */
-public interface TypedEventListener extends EventListener {
-
- Class getEventType();
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java b/core/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java
new file mode 100644
index 0000000..a9a94ce
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import org.apache.shiro.event.Subscribe;
+import org.apache.shiro.util.ClassUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Inspects an object for annotated methods of interest and creates an {@link EventListener} instance for each method
+ * discovered. An event bus will call the resulting listeners as relevant events arrive.
+ * <p/>
+ * The default {@link #setAnnotationClass(Class) annotationClass} is {@link Subscribe}, indicating each
+ * {@link Subscribe}-annotated method will be represented as an EventListener.
+ *
+ * @see SingleArgumentMethodEventListener
+ * @since 1.3
+ */
+public class AnnotationEventListenerResolver implements EventListenerResolver {
+
+ private Class<? extends Annotation> annotationClass;
+
+ public AnnotationEventListenerResolver() {
+ this.annotationClass = Subscribe.class;
+ }
+
+ /**
+ * Returns a new collection of {@link EventListener} instances, each instance corresponding to an annotated
+ * method discovered on the specified {@code instance} argument.
+ *
+ * @param instance the instance to inspect for annotated event handler methods.
+ * @return a new collection of {@link EventListener} instances, each instance corresponding to an annotated
+ * method discovered on the specified {@code instance} argument.
+ */
+ public List<EventListener> getEventListeners(Object instance) {
+ if (instance == null) {
+ return Collections.emptyList();
+ }
+
+ List<Method> methods = ClassUtils.getAnnotatedMethods(instance.getClass(), getAnnotationClass());
+ if (methods == null || methods.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<EventListener> listeners = new ArrayList<EventListener>(methods.size());
+
+ for (Method m : methods) {
+ listeners.add(new SingleArgumentMethodEventListener(instance, m));
+ }
+
+ return listeners;
+ }
+
+ /**
+ * Returns the type of annotation that indicates a method that should be represented as an {@link EventListener},
+ * defaults to {@link Subscribe}.
+ *
+ * @return the type of annotation that indicates a method that should be represented as an {@link EventListener},
+ * defaults to {@link Subscribe}.
+ */
+ public Class<? extends Annotation> getAnnotationClass() {
+ return annotationClass;
+ }
+
+ /**
+ * Sets the type of annotation that indicates a method that should be represented as an {@link EventListener}.
+ * The default value is {@link Subscribe}.
+ *
+ * @param annotationClass the type of annotation that indicates a method that should be represented as an
+ * {@link EventListener}. The default value is {@link Subscribe}.
+ */
+ public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
+ this.annotationClass = annotationClass;
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java b/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java
new file mode 100644
index 0000000..21db4ba
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/ClassComparator.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import java.util.Comparator;
+
+/**
+ * Compares two classes based on their position in a hierarchy. Classes higher up in a hierarchy are 'greater than'
+ * (ordered later) than classes lower in a hierarchy (ordered earlier). Classes in unrelated hierarchies have the same
+ * order priority.
+ * <p/>
+ * Event bus implementations use this comparator to determine which event listener method to invoke when polymorphic
+ * listener methods are defined:
+ * <p/>
+ * If two event classes exist A and B, where A is the parent class of B (and B is a subclass of A) and an event
+ * subscriber listens to both events:
+ * <pre>
+ * @Subscribe
+ * public void onEvent(A a) { ... }
+ *
+ * @Subscribe
+ * public void onEvent(B b) { ... }
+ * </pre>
+ *
+ * The {@code onEvent(B b)} method will be invoked on the subscriber and the
+ * {@code onEvent(A a)} method will <em>not</em> be invoked. This is to prevent multiple dispatching of a single event
+ * to the same consumer.
+ * <p/>
+ * The ClassComparator is used to order listener method priority based on their event argument class - methods handling
+ * event subclasses have higher precedence than superclasses.
+ *
+ * @since 1.3
+ */
+public class ClassComparator 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;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java b/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
new file mode 100644
index 0000000..e8520db
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import org.apache.shiro.event.EventBus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A default event bus implementation that synchronously publishes events to registered listeners. Listeners can be
+ * registered or unregistered for events as necessary.
+ * <p/>
+ * An event bus enables a publish/subscribe paradigm within Shiro - components can publish or consume events they
+ * find relevant without needing to be tightly coupled to other components. This affords great
+ * flexibility within Shiro by promoting loose coupling and high cohesion between components and a much safer
+ * pluggable architecture that is more resilient to change over time.
+ * <h2>Sending Events</h2>
+ * If a component wishes to publish events to other components:
+ * <pre>
+ * MyEvent myEvent = createMyEvent();
+ * eventBus.publish(myEvent);
+ * </pre>
+ * The event bus will determine the type of event and then dispatch the event to components that wish to receive
+ * events of that type.
+ * <h2>Receiving Events</h2>
+ * A component can receive events of interest by doing the following.
+ * <ol>
+ * <li>For each type of event you wish to consume, create a public method that accepts a single event argument.
+ * The method argument type indicates the type of event to receive.</li>
+ * <li>Annotate each of these public methods with the {@link org.apache.shiro.event.Subscribe Subscribe} annotation.</li>
+ * <li>Register the component with the event bus:
+ * <pre>
+ * eventBus.register(myComponent);
+ * </pre>
+ * </li>
+ * </ol>
+ * After registering the component, when when an event of a respective type is published, the component's
+ * {@code Subscribe}-annotated method(s) will be invoked as expected.
+ * <p/>
+ * This design (and its constituent helper components) was largely influenced by
+ * Guava's <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/eventbus/EventBus.html">EventBus</a>
+ * concept, although no code was shared/imported (even though Guava is Apache 2.0 licensed and could have
+ * been used).
+ *
+ * @since 1.3
+ */
+public class DefaultEventBus implements EventBus {
+
+ private static final Logger log = LoggerFactory.getLogger(DefaultEventBus.class);
+
+ private EventListenerResolver eventListenerResolver;
+
+ private final Map<Object,Subscriber> registry;
+
+ public DefaultEventBus() {
+ this.registry = new ConcurrentHashMap<Object, Subscriber>();
+ this.eventListenerResolver = new AnnotationEventListenerResolver();
+ }
+
+ public EventListenerResolver getEventListenerResolver() {
+ return eventListenerResolver;
+ }
+
+ public void setEventListenerResolver(EventListenerResolver eventListenerResolver) {
+ this.eventListenerResolver = eventListenerResolver;
+ }
+
+ public void publish(Object event) {
+ if (event == null) {
+ log.info("Received null event for publishing. Ignoring and returning.");
+ return;
+ }
+
+ for( Subscriber subscriber : registry.values() ) {
+ subscriber.onEvent(event);
+ }
+ }
+
+ public void register(Object instance) {
+ if (instance == null) {
+ log.info("Received null instance for registration. Ignoring registration request.");
+ return;
+ }
+
+ unregister(instance);
+
+ List<EventListener> listeners = getEventListenerResolver().getEventListeners(instance);
+
+ if (listeners == null || listeners.isEmpty()) {
+ log.warn("Unable to resolve any event listeners for the subscriber instance [" + instance +
+ "]. Ignoring registration request.");
+ return;
+ }
+
+ Subscriber subscriber = new Subscriber(instance, listeners);
+
+ this.registry.put(instance, subscriber);
+ }
+
+ public void unregister(Object instance) {
+ if (instance == null) {
+ return;
+ }
+ this.registry.remove(instance);
+ }
+
+ private class Subscriber {
+
+ private final Object instance;
+ private final List<EventListener> registeredListeners;
+
+ public Subscriber(Object instance, List<EventListener> listeners) {
+ this.instance = instance;
+ List<EventListener> toSort = new ArrayList<EventListener>(listeners);
+ Collections.sort(toSort, new EventListenerComparator());
+ this.registeredListeners = toSort;
+ }
+
+ public void onEvent(Object event) {
+
+ Set<Object> delivered = new HashSet<Object>();
+
+ for(EventListener listener : this.registeredListeners) {
+ Object target = listener;
+ if (listener instanceof SingleArgumentMethodEventListener) {
+ SingleArgumentMethodEventListener singleArgListener = (SingleArgumentMethodEventListener)listener;
+ target = singleArgListener.getTarget();
+ }
+ if (listener.accepts(event) && !delivered.contains(target)) {
+ try {
+ listener.onEvent(event);
+ } catch (Throwable t) {
+ log.warn("Event listener processing failed. Listeners should generally " +
+ "handle exceptions directly and not propagate to the event bus.", t);
+ } finally {
+ delivered.add(target);
+ }
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/EventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/EventListener.java b/core/src/main/java/org/apache/shiro/event/support/EventListener.java
new file mode 100644
index 0000000..adf386a
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/EventListener.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+/**
+ * An event listener knows how to accept and process events of a particular type (or types).
+ * <p/>
+ * Note that this interface is in the event implementation support package (and not the event package directly)
+ * because it is a supporting concept for event bus implementations and not something that most application
+ * developers using Shiro should implement directly. App developers should instead use the
+ * {@link org.apache.shiro.event.Subscribe Subscribe} annotation on methods they wish to receive events.
+ * <p/>
+ * This interface therefore mainly represents a 'middle man' between the event bus and the actual subscribing
+ * component. As such, event bus implementors (or framework/infrastructural implementors) or those that wish to
+ * customize listener/dispatch functionality might find this concept useful.
+ * <p/>
+ * It is a concept almost always used in conjunction with a {@link EventListenerResolver} implementation.
+ *
+ * @see SingleArgumentMethodEventListener
+ * @see AnnotationEventListenerResolver
+ *
+ * @since 1.3
+ */
+public interface EventListener {
+
+ /**
+ * Returns {@code true} if the listener instance can process the specified event object, {@code false} otherwise.
+ * @param event the event object to test
+ * @return {@code true} if the listener instance can process the specified event object, {@code false} otherwise.
+ */
+ boolean accepts(Object event);
+
+ /**
+ * Handles the specified event. Again, as this interface is an implementation concept, implementations of this
+ * method will likely dispatch the event to a 'real' processor (e.g. method).
+ *
+ * @param event the event to handle.
+ */
+ void onEvent(Object event);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java b/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
new file mode 100644
index 0000000..817c805
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import java.util.Comparator;
+
+/**
+ * Compares two event listeners to determine the order in which they should be invoked when an event is dispatched.
+ * The lower the order, the sooner it will be invoked (the higher its precedence). The higher the order, the later
+ * it will be invoked (the lower its precedence).
+ * <p/>
+ * TypedEventListeners have a higher precedence (i.e. a lower order) than standard EventListener instances. Standard
+ * EventListener instances have the same order priority.
+ * <p/>
+ * When both objects being compared are TypedEventListeners, they are ordered according to the rules of the
+ * {@link ClassComparator}, using the TypedEventListeners'
+ * {@link TypedEventListener#getEventType() eventType}.
+ *
+ * @since 1.3
+ */
+public class EventListenerComparator implements Comparator<EventListener> {
+
+ public int compare(EventListener a, EventListener 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 instanceof TypedEventListener) {
+ TypedEventListener ta = (TypedEventListener)a;
+ if (b instanceof TypedEventListener) {
+ TypedEventListener tb = (TypedEventListener)b;
+ return new ClassComparator().compare(ta.getEventType(), tb.getEventType());
+ } else {
+ return -1; //TypedEventListeners are 'less than' (higher priority) than non typed
+ }
+ } else {
+ if (b instanceof TypedEventListener) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/EventListenerResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/EventListenerResolver.java b/core/src/main/java/org/apache/shiro/event/support/EventListenerResolver.java
new file mode 100644
index 0000000..7a8773e
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/EventListenerResolver.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import java.util.List;
+
+/**
+ * An {@code EventListenerResolver} knows how to resolve (either create or lookup) {@link EventListener} instances
+ * as a result of inspecting a subscriber object, mostly likely a
+ * {@link org.apache.shiro.event.Subscribe Subscribe}-annotated object instance.
+ * <p/>
+ * This interface exists primarily as a support concept for the {@link DefaultEventBus} implementation. Custom
+ * implementations of this interface can be configured on a {@link DefaultEventBus} instance to determine exactly
+ * how a subscriber receives events.
+ * <p/>
+ * For example, the {@link AnnotationEventListenerResolver AnnotationEventListenerResolver} will inspect a runtime
+ * object for {@link org.apache.shiro.event.Subscribe Subscribe}-annotated methods, and for each method found, return
+ * an {@link EventListener} instance representing the method to invoke.
+ *
+ * @see AnnotationEventListenerResolver
+ * @see SingleArgumentMethodEventListener
+ * @since 1.3
+ */
+public interface EventListenerResolver {
+
+ /**
+ * Returns {@link EventListener} instances as a result of inspecting a subscriber object, mostly likely with
+ * {@link org.apache.shiro.event.Subscribe Subscribe}-annotated methods.
+ *
+ * @param instance the subscriber instance for which EventListener instances should be acquired.
+ * @return {@link EventListener} instances as a result of inspecting a subscriber object.
+ */
+ List<EventListener> getEventListeners(Object instance);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java b/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
new file mode 100644
index 0000000..f30c11a
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/SingleArgumentMethodEventListener.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+import java.lang.reflect.Method;
+
+/**
+ * A event listener that invokes a target object's method that accepts a single event argument.
+ *
+ * @since 1.3
+ */
+public class SingleArgumentMethodEventListener implements TypedEventListener {
+
+ private final Object target;
+ private final Method method;
+
+ public SingleArgumentMethodEventListener(Object target, Method method) {
+ this.target = target;
+ this.method = method;
+ //assert that the method is defined as expected:
+ getMethodArgumentType(method);
+ }
+
+ public Object getTarget() {
+ return this.target;
+ }
+
+ public Method getMethod() {
+ return this.method;
+ }
+
+ public boolean accepts(Object event) {
+ return event != null && getEventType().isInstance(event);
+ }
+
+ public Class getEventType() {
+ return getMethodArgumentType(getMethod());
+ }
+
+ public void onEvent(Object event) {
+ Method method = getMethod();
+ try {
+ method.invoke(getTarget(), event);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to invoke event handler method [" + method + "]", e);
+ }
+ }
+
+ protected Class getMethodArgumentType(Method method) {
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) {
+ //the default implementation expects a single typed argument and nothing more:
+ String msg = "Event handler methods must accept a single argument.";
+ throw new IllegalArgumentException(msg);
+ }
+ return paramTypes[0];
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/main/java/org/apache/shiro/event/support/TypedEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/support/TypedEventListener.java b/core/src/main/java/org/apache/shiro/event/support/TypedEventListener.java
new file mode 100644
index 0000000..f266bf3
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/support/TypedEventListener.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.support;
+
+/**
+ * @since 1.3
+ */
+public interface TypedEventListener extends EventListener {
+
+ Class getEventType();
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy
deleted file mode 100644
index de32bca..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class AnnotationEventListenerResolverTest extends GroovyTestCase {
-
- void testSetGetAnnotationClass() {
- def resolver = new AnnotationEventListenerResolver();
- resolver.setAnnotationClass(Override.class) //any old annotation will do for the test
- assertSame Override.class, resolver.getAnnotationClass()
- }
-
- void testNullInstanceArgument() {
- def resolver = new AnnotationEventListenerResolver()
- def collection = resolver.getEventListeners(null)
- assertNotNull collection
- assertTrue collection.isEmpty()
- }
-
- void testNoAnnotationsArgument() {
- def resolver = new AnnotationEventListenerResolver()
- def collection = resolver.getEventListeners(new NotAnnotatedSubscriber())
- assertNotNull collection
- assertTrue collection.isEmpty()
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy
deleted file mode 100644
index 7123bec..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class BarEvent extends FooEvent {
-
- BarEvent(Object o) {
- super(o)
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy
deleted file mode 100644
index 60a90ca..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class BazEvent extends BarEvent {
-
- BazEvent(Object o) {
- super(o)
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy
deleted file mode 100644
index 6302d98..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class ClassComparatorTest extends GroovyTestCase {
-
- ClassComparator comparator
-
- @Override
- protected void setUp() {
- comparator = new ClassComparator()
- }
-
- void testANull() {
- def result = comparator.compare(null, Object)
- assertEquals(-1, result)
- }
-
- void testBNull() {
- def result = comparator.compare(Object, null)
- assertEquals 1, result
- }
-
- void testBothNull() {
- assertEquals 0, comparator.compare(null, null)
- }
-
- void testBothSame() {
- assertEquals 0, comparator.compare(Object, Object)
- }
-
- void testAParentOfB() {
- assertEquals 1, comparator.compare(Number, Integer)
- }
-
- void testBParentOfA() {
- assertEquals(-1, comparator.compare(Integer, Number))
- }
-
- void testUnrelated() {
- assertEquals(0, comparator.compare(Integer, Boolean))
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy
deleted file mode 100644
index c29b69d..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import static org.easymock.EasyMock.*
-
-/**
- * @since 1.3
- */
-class DefaultEventBusTest extends GroovyTestCase {
-
- DefaultEventBus bus;
-
- @Override
- protected void setUp() {
- bus = new DefaultEventBus()
- }
-
- void testSetEventListenerResolver() {
- def resolver = new EventListenerResolver() {
- List<EventListener> getEventListeners(Object instance) {
- return null //dummy implementation
- }
- }
- bus.setEventListenerResolver(resolver)
- assertSame resolver, bus.getEventListenerResolver()
- }
-
- void testSimpleSubscribe() {
- def subscriber = new TestSubscriber();
-
- bus.register(subscriber);
-
- def event = new FooEvent(this)
-
- bus.publish(event)
-
- assertEquals 1, subscriber.fooCount
- assertEquals 1, subscriber.count
- assertSame event, subscriber.lastEvent
- }
-
- void testPublishNullEvent() {
- def subscriber = new TestSubscriber();
- bus.register(subscriber)
-
- bus.publish(null)
-
- assertEquals 0, subscriber.count
- }
-
- void testSubscribeNullInstance() {
- def resolver = createStrictMock(EventListenerResolver) //assert no methods are called on this
- bus.eventListenerResolver = resolver
-
- replay(resolver)
-
- bus.register(null)
-
- verify(resolver)
- }
-
- void testSubscribeWithoutAnnotations() {
- def subscriber = new NotAnnotatedSubscriber()
- bus.register(subscriber)
-
- bus.publish(new FooEvent(this))
-
- assertEquals 0, bus.registry.size()
- }
-
- void testUnsubscribeNullInstance() {
- bus.unregister(null)
- }
-
- void testUnsubscribe() {
- def subscriber = new TestSubscriber()
- bus.register(subscriber)
- assertEquals 1, bus.registry.size()
-
- def event = new FooEvent(this)
-
- bus.publish(event)
-
- assertSame event, subscriber.lastEvent
- assertEquals 1, subscriber.fooCount
- assertEquals 1, subscriber.count
-
- bus.unregister(subscriber)
-
- assertEquals 0, bus.registry.size()
- }
-
- void testPolymorphicSubscribeMethodsOnlyOneInvoked() {
- def subscriber = new TestSubscriber()
- bus.register(subscriber)
-
- def event = new BarEvent(this)
-
- bus.publish(event)
-
- assertSame event, subscriber.lastEvent
- assertEquals 0, subscriber.fooCount
- assertEquals 1, subscriber.barCount
- assertEquals 1, subscriber.count
- }
-
- void testPolymorphicSubscribeMethodsOnlyOneInvokedWithListenerSubclass() {
- def subscriber = new SubclassTestSubscriber()
- bus.register(subscriber)
-
- def event = new BazEvent(this)
-
- bus.publish(event)
-
- assertSame event, subscriber.lastEvent
- assertEquals 1, subscriber.count
- assertEquals 1, subscriber.bazCount
- assertEquals 0, subscriber.fooCount
- assertEquals 0, subscriber.barCount
- }
-
- void testSubscribeWithErroneousAnnotation() {
- def subscriber = new ErroneouslyAnnotatedSubscriber()
- //noinspection GroovyUnusedCatchParameter
- try {
- bus.register(subscriber)
- fail("exception expected")
- } catch (IllegalArgumentException expected) {
- }
- }
-
- void testContinueThroughListenerExceptions() {
- def ok = new SimpleSubscriber()
- def error = new ExceptionThrowingSubscriber()
-
- bus.register(ok)
- bus.register(error)
-
- bus.publish(new ErrorCausingEvent())
- bus.publish(new SimpleEvent())
-
- assertEquals 1, ok.count
- assertEquals 0, error.count
- }
-
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy
deleted file mode 100644
index 82546a6..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-import org.apache.shiro.event.Subscribe
-
-/**
- * @since 1.3
- */
-class ErroneouslyAnnotatedSubscriber {
-
- @Subscribe
- void onEvent(FooEvent event, Object someOtherArg) {
- }
-}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bdecc0ba/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy
deleted file mode 100644
index 897399f..0000000
--- a/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.shiro.event.bus
-
-/**
- * @since 1.3
- */
-class ErrorCausingEvent {
-}
[07/13] shiro git commit: 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 lis
Posted by bd...@apache.org.
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.
git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1427647 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/41c3183b
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/41c3183b
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/41c3183b
Branch: refs/heads/1.3.x
Commit: 41c3183b1fbecd2d91eeb827478143206cc3b79a
Parents: bdecc0b
Author: Les Hazlewood <lh...@apache.org>
Authored: Wed Jan 2 04:44:21 2013 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:53:11 2016 -0400
----------------------------------------------------------------------
.../apache/shiro/config/ReflectionBuilder.java | 457 +++++++++++++++----
.../apache/shiro/config/event/BeanEvent.java | 1 +
.../shiro/config/event/ConfiguredBeanEvent.java | 4 +
.../shiro/config/event/DestroyedBeanEvent.java | 1 +
.../config/event/InstantiatedBeanEvent.java | 1 +
.../shiro/config/event/LoggingBeanListener.java | 33 +-
.../org/apache/shiro/event/EventBusAware.java | 9 +
.../shiro/mgt/CachingSecurityManager.java | 51 ++-
.../shiro/config/ReflectionBuilderTest.groovy | 6 +-
.../shiro/config/RecordingBeanListener.java | 38 +-
.../shiro/web/env/IniWebEnvironmentTest.groovy | 4 +-
11 files changed, 501 insertions(+), 104 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
index 3d15f9a..ac4d2a1 100644
--- a/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
+++ b/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
@@ -22,7 +22,14 @@ import org.apache.commons.beanutils.BeanUtils;
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,15 +113,82 @@ 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) {
@@ -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());
+ String lhs = entry.getKey();
+ String rhs = entry.getValue();
+
+ 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));
}
}
- // 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());
- }
- }
-
- // 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));
- }
- }
+ processor.execute();
}
//SHIRO-413: init method must be called for constructed objects that are Initializable
@@ -187,20 +253,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);
}
}
@@ -635,4 +692,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;
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
index 108c8eb..09e83fc 100644
--- a/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanEvent.java
@@ -4,6 +4,7 @@ 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;
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
index f183a6b..8d210a2 100644
--- a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
@@ -2,7 +2,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);
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
index a92cc2f..f05ee7e 100644
--- a/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/DestroyedBeanEvent.java
@@ -3,6 +3,7 @@ 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);
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
index 75d2ccf..4ba9d6e 100644
--- a/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/InstantiatedBeanEvent.java
@@ -3,6 +3,7 @@ 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);
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
index 5a230e6..a6b9bbe 100644
--- a/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
+++ b/core/src/main/java/org/apache/shiro/config/event/LoggingBeanListener.java
@@ -1,31 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.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());
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/event/EventBusAware.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/EventBusAware.java b/core/src/main/java/org/apache/shiro/event/EventBusAware.java
index 7d26613..48a2ce4 100644
--- a/core/src/main/java/org/apache/shiro/event/EventBusAware.java
+++ b/core/src/main/java/org/apache/shiro/event/EventBusAware.java
@@ -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
*/
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
index c2c38ad..9cfaae9 100644
--- a/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
+++ b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java
@@ -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.LifecycleUtils;
*
* @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 CachingSecurityManager implements SecurityManager, Destroy
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 CachingSecurityManager implements SecurityManager, Destroy
}
/**
+ * 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();
}
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
index 39957a9..e2ed22e 100644
--- a/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
+++ b/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
@@ -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 GroovyTestCase {
}
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 GroovyTestCase {
assertDestroyedEvents("listenerOne", objects);
assertDestroyedEvents("listenerTwo", objects);
+ */
}
void assertInstantiatedEvents(String name, Map<String, ?> objects) {
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
index 82a2f13..b09d124 100644
--- a/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
+++ b/core/src/test/java/org/apache/shiro/config/RecordingBeanListener.java
@@ -1,34 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.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;
-public class RecordingBeanListener extends BeanListenerSupport {
+/**
+ * @since 1.3
+ */
+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);
}
http://git-wip-us.apache.org/repos/asf/shiro/blob/41c3183b/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
----------------------------------------------------------------------
diff --git a/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy b/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
index 4fdf54e..08b2bc0 100644
--- a/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
+++ b/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
@@ -42,8 +42,8 @@ class IniWebEnvironmentTest extends GroovyTestCase {
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']
[04/13] shiro git commit: SHIRO-395: minor JavaDoc update to indicate
version introduced
Posted by bd...@apache.org.
SHIRO-395: minor JavaDoc update to indicate version introduced
git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1410708 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/0c7a8b50
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/0c7a8b50
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/0c7a8b50
Branch: refs/heads/1.3.x
Commit: 0c7a8b503324fd0a759789ecf256ed738036947b
Parents: bbc8efa
Author: Les Hazlewood <lh...@apache.org>
Authored: Sat Nov 17 08:15:03 2012 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:49:48 2016 -0400
----------------------------------------------------------------------
.../test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/0c7a8b50/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
index 5639f5d..9a8b1e4 100644
--- a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
@@ -19,11 +19,7 @@
package org.apache.shiro.event.bus
/**
- * Created with IntelliJ IDEA.
- * User: lhazlewood
- * Date: 11/16/12
- * Time: 11:09 PM
- * To change this template use File | Settings | File Templates.
+ * @since 1.3
*/
class SimpleEvent {
}
[03/13] shiro git commit: SHIRO-395: added initial implementation -
all org.apache.shiro.event.** additions are at 100% test coverage.
Posted by bd...@apache.org.
SHIRO-395: added initial implementation - all org.apache.shiro.event.** additions are at 100% test coverage.
git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1410707 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/bbc8efac
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/bbc8efac
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/bbc8efac
Branch: refs/heads/1.3.x
Commit: bbc8efaccef60939c8786e317966c878a34203ce
Parents: 0a886db
Author: Les Hazlewood <lh...@apache.org>
Authored: Sat Nov 17 08:12:48 2012 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:49:37 2016 -0400
----------------------------------------------------------------------
.../java/org/apache/shiro/event/Publisher.java | 34 ++
.../java/org/apache/shiro/event/Subscribe.java | 45 ++
.../bus/AnnotationEventListenerResolver.java | 96 +++++
.../apache/shiro/event/bus/ClassComparator.java | 73 ++++
.../apache/shiro/event/bus/DefaultEventBus.java | 161 ++++++++
.../apache/shiro/event/bus/EventListener.java | 56 +++
.../event/bus/EventListenerComparator.java | 68 ++++
.../shiro/event/bus/EventListenerResolver.java | 50 +++
.../bus/SingleArgumentMethodEventListener.java | 74 ++++
.../shiro/event/bus/SubscriberRegistry.java | 45 ++
.../shiro/event/bus/TypedEventListener.java | 27 ++
.../main/java/org/apache/shiro/util/Assert.java | 407 +++++++++++++++++++
.../java/org/apache/shiro/util/ClassUtils.java | 27 ++
.../AnnotationEventListenerResolverTest.groovy | 45 ++
.../org/apache/shiro/event/bus/BarEvent.groovy | 29 ++
.../org/apache/shiro/event/bus/BazEvent.groovy | 29 ++
.../shiro/event/bus/ClassComparatorTest.groovy | 62 +++
.../shiro/event/bus/DefaultEventBusTest.groovy | 163 ++++++++
.../bus/ErroneouslyAnnotatedSubscriber.groovy | 31 ++
.../shiro/event/bus/ErrorCausingEvent.groovy | 25 ++
.../bus/EventListenerComparatorTest.groovy | 71 ++++
.../bus/ExceptionThrowingSubscriber.groovy | 32 ++
.../org/apache/shiro/event/bus/FooEvent.groovy | 29 ++
.../event/bus/NotAnnotatedSubscriber.groovy | 27 ++
.../apache/shiro/event/bus/SimpleEvent.groovy | 29 ++
.../shiro/event/bus/SimpleSubscriber.groovy | 38 ++
...SingleArgumentMethodEventListenerTest.groovy | 86 ++++
.../event/bus/SubclassTestSubscriber.groovy | 49 +++
.../shiro/event/bus/TestSubscriber.groovy | 50 +++
29 files changed, 1958 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/Publisher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/Publisher.java b/core/src/main/java/org/apache/shiro/event/Publisher.java
new file mode 100644
index 0000000..50c1c8d
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/Publisher.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event;
+
+/**
+ * Publishes events to an event subsystem that will deliver events to registered {@link Subscribe}rs.
+ *
+ * @since 1.3
+ */
+public interface Publisher {
+
+ /**
+ * Publishes the specified event to an event subsystem that will deliver events to relevant {@link Subscribe}rs.
+ *
+ * @param event The event object to distribute to relevant subscribers.
+ */
+ void publish(Object event);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/Subscribe.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/Subscribe.java b/core/src/main/java/org/apache/shiro/event/Subscribe.java
new file mode 100644
index 0000000..03bc599
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/Subscribe.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates a method is an event consumer. The method must have a single argument and the argument's type determines
+ * what type of events should be delivered to the method for consumption.
+ * <p/>
+ * For example:
+ * <pre>
+ * @Subscribe
+ * public void onSomeEvent(SomeEvent event) { ... }
+ * </pre>
+ * <p/>
+ * Because the method argument is declared as a {@code SomeEvent} type, the method will be called by the event
+ * dispatcher whenever a {@code SomeEvent} instance (or one of its subclass instances that is not already registered)
+ * is published.
+ *
+ * @since 1.3
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = ElementType.METHOD)
+public @interface Subscribe {
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java b/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java
new file mode 100644
index 0000000..883dbcf
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/AnnotationEventListenerResolver.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+import org.apache.shiro.event.Subscribe;
+import org.apache.shiro.util.ClassUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Inspects an object for annotated methods of interest and creates an {@link EventListener} for each method discovered.
+ * An event bus will call the resulting listeners as relevant events arrive.
+ * <p/>
+ * The default {@link #setAnnotationClass(Class) annotationClass} is {@link Subscribe}, indicating any
+ * {@link Subscribe}-annotated method will be represented as an EventListener.
+ *
+ * @see SingleArgumentMethodEventListener
+ * @since 1.3
+ */
+public class AnnotationEventListenerResolver implements EventListenerResolver {
+
+ private Class<? extends Annotation> annotationClass;
+
+ public AnnotationEventListenerResolver() {
+ this.annotationClass = Subscribe.class;
+ }
+
+ /**
+ * Returns a new collection of {@link EventListener} instances, each instance corresponding to an annotated
+ * method discovered on the specified {@code instance} argument.
+ *
+ * @param instance the instance to inspect for annotated event handler methods.
+ * @return a new collection of {@link EventListener} instances, each instance corresponding to an annotated
+ * method discovered on the specified {@code instance} argument.
+ */
+ public List<EventListener> getEventListeners(Object instance) {
+ if (instance == null) {
+ return Collections.emptyList();
+ }
+
+ List<Method> methods = ClassUtils.getAnnotatedMethods(instance.getClass(), getAnnotationClass());
+ if (methods == null || methods.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<EventListener> listeners = new ArrayList<EventListener>(methods.size());
+
+ for (Method m : methods) {
+ listeners.add(new SingleArgumentMethodEventListener(instance, m));
+ }
+
+ return listeners;
+ }
+
+ /**
+ * Returns the type of annotation that indicates a method that should be represented as an {@link EventListener},
+ * defaults to {@link Subscribe}.
+ *
+ * @return the type of annotation that indicates a method that should be represented as an {@link EventListener},
+ * defaults to {@link Subscribe}.
+ */
+ public Class<? extends Annotation> getAnnotationClass() {
+ return annotationClass;
+ }
+
+ /**
+ * Sets the type of annotation that indicates a method that should be represented as an {@link EventListener}.
+ * The default value is {@link Subscribe}.
+ *
+ * @param annotationClass the type of annotation that indicates a method that should be represented as an
+ * {@link EventListener}. The default value is {@link Subscribe}.
+ */
+ public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
+ this.annotationClass = annotationClass;
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java b/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java
new file mode 100644
index 0000000..4d1928f
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/ClassComparator.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+import java.util.Comparator;
+
+/**
+ * Compares two classes based on their position in a hierarchy. Classes higher up in a hierarchy are 'greater than'
+ * (ordered later) than classes lower in a hierarchy (ordered earlier). Classes in unrelated hierarchies have the same
+ * order priority.
+ * <p/>
+ * Event bus implementations use this comparator to determine which event listener method to invoke when polymorphic
+ * listener methods are defined:
+ * <p/>
+ * If two event classes exist A and B, where A is the parent class of B (and B is a subclass of A) and an event
+ * subscriber listens to both events:
+ * <pre>
+ * @Subscribe
+ * public void onEvent(A a) { ... }
+ *
+ * @Subscribe
+ * public void onEvent(B b) { ... }
+ * </pre>
+ *
+ * The {@code onEvent(B b)} method will be invoked on the subscriber and the
+ * {@code onEvent(A a)} method will <em>not</em> be invoked. This is to prevent multiple dispatching of a single event
+ * to the same consumer.
+ * <p/>
+ * The ClassComparator is used to order listener method priority based on their event argument class - methods handling
+ * event subclasses have higher precedence than superclasses.
+ *
+ * @since 1.3
+ */
+public class ClassComparator 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;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java b/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java
new file mode 100644
index 0000000..4b74522
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/DefaultEventBus.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+
+import org.apache.shiro.event.Publisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A default event bus implementation that synchronously publishes events to registered listeners. Listeners can be
+ * registered or unregistered for events as necessary.
+ * <p/>
+ * An event bus enables a publish/subscribe paradigm within Shiro - components can publish or consume events they
+ * find relevant without needing to be tightly coupled to other components. This affords great
+ * flexibility within Shiro by promoting loose coupling and high cohesion between components and a much safer pluggable
+ * architecture.
+ * <h2>Sending Events</h2>
+ * If a component wishes to publish events to other components:
+ * <pre>
+ * MyEvent myEvent = createMyEvent();
+ * eventBus.publish(myEvent);
+ * </pre>
+ * The event bus will determine the type of event and then dispatch the event to components that wish to receive
+ * events of that type.
+ * <h2>Receiving Events</h2>
+ * A component can receive events of interest by doing the following.
+ * <ol>
+ * <li>For each type of event you wish to consume, create a public method that accepts a single event argument.
+ * The method argument type indicates the type of event to receive.</li>
+ * <li>Annotate each of these public methods with the {@link org.apache.shiro.event.Subscribe Subscribe} annotation.</li>
+ * <li>Register the component with the event bus:
+ * <pre>
+ * eventBus.register(myComponent);
+ * </pre>
+ * </li>
+ * </ol>
+ * After registering the component, when when an event of a respective type is published, the component's
+ * {@code Subscribe}-annotated method(s) will be invoked as expected.
+ * <p/>
+ * This design (and its constituent helper components) was largely influenced by
+ * Guava's <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/eventbus/EventBus.html">EventBus</a>
+ * concept, although no code was shared/imported (even though Guava is Apache 2.0 licensed and could have
+ * been used).
+ *
+ * @since 1.3
+ */
+public class DefaultEventBus implements Publisher, SubscriberRegistry {
+
+ private static final Logger log = LoggerFactory.getLogger(DefaultEventBus.class);
+
+ private EventListenerResolver eventListenerResolver;
+
+ private final Map<Object,Subscriber> registry;
+
+ public DefaultEventBus() {
+ this.registry = new ConcurrentHashMap<Object, Subscriber>();
+ this.eventListenerResolver = new AnnotationEventListenerResolver();
+ }
+
+ public EventListenerResolver getEventListenerResolver() {
+ return eventListenerResolver;
+ }
+
+ public void setEventListenerResolver(EventListenerResolver eventListenerResolver) {
+ this.eventListenerResolver = eventListenerResolver;
+ }
+
+ public void publish(Object event) {
+ if (event == null) {
+ log.info("Received null event for publishing. Ignoring and returning.");
+ return;
+ }
+
+ for( Subscriber subscriber : registry.values() ) {
+ subscriber.onEvent(event);
+ }
+ }
+
+ public void register(Object instance) {
+ if (instance == null) {
+ log.info("Received null instance for registration. Ignoring registration request.");
+ return;
+ }
+
+ unregister(instance);
+
+ List<EventListener> listeners = getEventListenerResolver().getEventListeners(instance);
+
+ if (listeners == null || listeners.isEmpty()) {
+ log.warn("Unable to resolve any event listeners for the subscriber instance [" + instance +
+ "]. Ignoring registration request.");
+ return;
+ }
+
+ Subscriber subscriber = new Subscriber(instance, listeners);
+
+ this.registry.put(instance, subscriber);
+ }
+
+ public void unregister(Object instance) {
+ if (instance == null) {
+ return;
+ }
+ this.registry.remove(instance);
+ }
+
+ private class Subscriber {
+
+ private final Object instance;
+ private final List<EventListener> registeredListeners;
+
+ public Subscriber(Object instance, List<EventListener> listeners) {
+ this.instance = instance;
+ List<EventListener> toSort = new ArrayList<EventListener>(listeners);
+ Collections.sort(toSort, new EventListenerComparator());
+ this.registeredListeners = toSort;
+ }
+
+ public void onEvent(Object event) {
+
+ Set<Object> delivered = new HashSet<Object>();
+
+ for(EventListener listener : this.registeredListeners) {
+ Object target = listener;
+ if (listener instanceof SingleArgumentMethodEventListener) {
+ SingleArgumentMethodEventListener singleArgListener = (SingleArgumentMethodEventListener)listener;
+ target = singleArgListener.getTarget();
+ }
+ if (listener.accepts(event) && !delivered.contains(target)) {
+ try {
+ listener.onEvent(event);
+ delivered.add(target);
+ } catch (Throwable t) {
+ log.warn("Event listener processing failed. Listeners should generally " +
+ "handle exceptions directly and not propagate to the event bus.", t);
+ }
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/EventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/EventListener.java b/core/src/main/java/org/apache/shiro/event/bus/EventListener.java
new file mode 100644
index 0000000..a653674
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/EventListener.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+/**
+ * An event listener knows how to accept and process events of a particular type (or types).
+ * <p/>
+ * Note that this interface is in the event bus package (and not the event package directly) because it is a supporting
+ * concept for event bus implementations and not something that application
+ * developers using Shiro should implement directly. App developers should instead use the
+ * {@link org.apache.shiro.event.Subscribe Subscribe} annotation on methods they wish to receive events.
+ * <p/>
+ * This interface therefore mainly represents a 'middle man' between the event bus and the actual subscribing
+ * component. As such, event bus implementors (or framework/infrastructural implementors) or those that wish to
+ * customize listener/dispatch functionality might find this concept useful.
+ * <p/>
+ * It is a concept almost always used in conjunction with a {@link EventListenerResolver} implementation.
+ *
+ * @see SingleArgumentMethodEventListener
+ * @see AnnotationEventListenerResolver
+ *
+ * @since 1.3
+ */
+public interface EventListener {
+
+ /**
+ * Returns {@code true} if the listener instance can process the specified event object, {@code false} otherwise.
+ * @param event the event object to test
+ * @return {@code true} if the listener instance can process the specified event object, {@code false} otherwise.
+ */
+ boolean accepts(Object event);
+
+ /**
+ * Handles the specified event. Again, as this interface is an implementation concept, implementations of this
+ * method will likely dispatch the event to a 'real' processor (e.g. method).
+ *
+ * @param event the event to handle.
+ */
+ void onEvent(Object event);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java b/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java
new file mode 100644
index 0000000..d096591
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/EventListenerComparator.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+import java.util.Comparator;
+
+/**
+ * Compares two event listeners to determine the order in which they should be invoked when an event is dispatched.
+ * The lower the order, the sooner it will be invoked (the higher its precedence). The higher the order, the later
+ * it will be invoked (the lower its precedence).
+ * <p/>
+ * TypedEventListeners have a higher precedence (i.e. a lower order) than standard EventListener instances. Standard
+ * EventListener instances have the same order priority.
+ * <p/>
+ * When both objects being compared are TypedEventListeners, they are ordered according to the rules of the
+ * {@link ClassComparator}, using the TypedEventListeners'
+ * {@link org.apache.shiro.event.bus.TypedEventListener#getEventType() eventType}.
+ *
+ * @since 1.3
+ */
+public class EventListenerComparator implements Comparator<EventListener> {
+
+ public int compare(EventListener a, EventListener 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 instanceof TypedEventListener) {
+ TypedEventListener ta = (TypedEventListener)a;
+ if (b instanceof TypedEventListener) {
+ TypedEventListener tb = (TypedEventListener)b;
+ return new ClassComparator().compare(ta.getEventType(), tb.getEventType());
+ } else {
+ return -1; //TypedEventListeners are 'less than' (higher priority) than non typed
+ }
+ } else {
+ if (b instanceof TypedEventListener) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java b/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java
new file mode 100644
index 0000000..b865d89
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/EventListenerResolver.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+import java.util.List;
+
+/**
+ * An {@code EventListenerResolver} knows how to resolve (either create or lookup) {@link EventListener} instances
+ * as a result of inspecting a subscriber object, mostly likely a
+ * {@link org.apache.shiro.event.Subscribe Subscribe}-annotated object instance.
+ * <p/>
+ * This interface exists primarily as a support concept for the {@link DefaultEventBus} implementation. Custom
+ * implementations of this interface can be configured on a {@link DefaultEventBus} instance to determine exactly
+ * how a subscriber receives events.
+ * <p/>
+ * For example, the {@link AnnotationEventListenerResolver AnnotationEventListenerResolver} will inspect a runtime
+ * object for {@link org.apache.shiro.event.Subscribe Subscribe}-annotated methods, and for each method found, return
+ * an {@link EventListener} instance representing the method to invoke.
+ *
+ * @see AnnotationEventListenerResolver
+ * @see SingleArgumentMethodEventListener
+ * @since 1.3
+ */
+public interface EventListenerResolver {
+
+ /**
+ * Returns {@link EventListener} instances as a result of inspecting a subscriber object, mostly likely with
+ * {@link org.apache.shiro.event.Subscribe Subscribe}-annotated methods.
+ *
+ * @param instance the subscriber instance for which EventListener instances should be acquired.
+ * @return {@link EventListener} instances as a result of inspecting a subscriber object.
+ */
+ List<EventListener> getEventListeners(Object instance);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java b/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java
new file mode 100644
index 0000000..f5ea9f9
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/SingleArgumentMethodEventListener.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+import java.lang.reflect.Method;
+
+/**
+ * A event listener that invokes a target object's method that accepts a single event argument.
+ *
+ * @since 1.3
+ */
+public class SingleArgumentMethodEventListener implements TypedEventListener {
+
+ private final Object target;
+ private final Method method;
+
+ public SingleArgumentMethodEventListener(Object target, Method method) {
+ this.target = target;
+ this.method = method;
+ //assert that the method is defined as expected:
+ getMethodArgumentType(method);
+ }
+
+ public Object getTarget() {
+ return this.target;
+ }
+
+ public Method getMethod() {
+ return this.method;
+ }
+
+ public boolean accepts(Object event) {
+ return event != null && getEventType().isInstance(event);
+ }
+
+ public Class getEventType() {
+ return getMethodArgumentType(getMethod());
+ }
+
+ public void onEvent(Object event) {
+ Method method = getMethod();
+ try {
+ method.invoke(getTarget(), event);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to invoke event handler method [" + method + "]", e);
+ }
+ }
+
+ protected Class getMethodArgumentType(Method method) {
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) {
+ //the default implementation expects a single typed argument and nothing more:
+ String msg = "Event handler methods must accept a single argument.";
+ throw new IllegalArgumentException(msg);
+ }
+ return paramTypes[0];
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java b/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java
new file mode 100644
index 0000000..af57d6e
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/SubscriberRegistry.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+/**
+ * Allows event subscribers to register or unregister with an event subsystem to receive (or not receive) published
+ * events.
+ *
+ * @since 1.3
+ */
+public interface SubscriberRegistry {
+
+ /**
+ * Registers all event handler methods on the specified instance to receive relevant events. The handler methods
+ * are determined by {@link SubscriberRegistry} implementations, typically by using an
+ * {@link EventListenerResolver} (e.g. {@link AnnotationEventListenerResolver}).
+ *
+ * @param subscriber the object whose event handler methods should be registered to receive events.
+ */
+ void register(Object subscriber);
+
+ /**
+ * Unregisters all previously-registered event handler methods on the specified instance. If the specified object
+ * was not previously registered, calling this method has no effect.
+ *
+ * @param subscriber the previously
+ */
+ void unregister(Object subscriber);
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java b/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java
new file mode 100644
index 0000000..9cb51e0
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/event/bus/TypedEventListener.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus;
+
+/**
+ * @since 1.3
+ */
+public interface TypedEventListener extends EventListener {
+
+ Class getEventType();
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/util/Assert.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/util/Assert.java b/core/src/main/java/org/apache/shiro/util/Assert.java
new file mode 100644
index 0000000..d580247
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/util/Assert.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.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.
+ * Usage also reduces a program's
+ * <a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity">cyclomatic complexity</a>.
+ *
+ * <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");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/main/java/org/apache/shiro/util/ClassUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/util/ClassUtils.java b/core/src/main/java/org/apache/shiro/util/ClassUtils.java
index dd46b06..aa7334b 100644
--- a/core/src/main/java/org/apache/shiro/util/ClassUtils.java
+++ b/core/src/main/java/org/apache/shiro/util/ClassUtils.java
@@ -22,7 +22,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
/**
@@ -208,6 +212,29 @@ public class ClassUtils {
}
/**
+ *
+ * @param type
+ * @param annotation
+ * @return
+ * @since 1.3
+ */
+ public static List<Method> getAnnotatedMethods(final Class<?> type, final Class<? extends Annotation> annotation) {
+ final List<Method> methods = new ArrayList<Method>();
+ Class<?> clazz = type;
+ while (!Object.class.equals(clazz)) {
+ Method[] currentClassMethods = clazz.getDeclaredMethods();
+ for (final Method method : currentClassMethods) {
+ if (annotation == null || method.isAnnotationPresent(annotation)) {
+ methods.add(method);
+ }
+ }
+ // move to the upper class in the hierarchy in search for more methods
+ clazz = clazz.getSuperclass();
+ }
+ return methods;
+ }
+
+ /**
* @since 1.0
*/
private static interface ClassLoaderAccessor {
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy
new file mode 100644
index 0000000..de32bca
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/AnnotationEventListenerResolverTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class AnnotationEventListenerResolverTest extends GroovyTestCase {
+
+ void testSetGetAnnotationClass() {
+ def resolver = new AnnotationEventListenerResolver();
+ resolver.setAnnotationClass(Override.class) //any old annotation will do for the test
+ assertSame Override.class, resolver.getAnnotationClass()
+ }
+
+ void testNullInstanceArgument() {
+ def resolver = new AnnotationEventListenerResolver()
+ def collection = resolver.getEventListeners(null)
+ assertNotNull collection
+ assertTrue collection.isEmpty()
+ }
+
+ void testNoAnnotationsArgument() {
+ def resolver = new AnnotationEventListenerResolver()
+ def collection = resolver.getEventListeners(new NotAnnotatedSubscriber())
+ assertNotNull collection
+ assertTrue collection.isEmpty()
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy
new file mode 100644
index 0000000..7123bec
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/BarEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class BarEvent extends FooEvent {
+
+ BarEvent(Object o) {
+ super(o)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy
new file mode 100644
index 0000000..60a90ca
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/BazEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class BazEvent extends BarEvent {
+
+ BazEvent(Object o) {
+ super(o)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy
new file mode 100644
index 0000000..6302d98
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/ClassComparatorTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class ClassComparatorTest extends GroovyTestCase {
+
+ ClassComparator comparator
+
+ @Override
+ protected void setUp() {
+ comparator = new ClassComparator()
+ }
+
+ void testANull() {
+ def result = comparator.compare(null, Object)
+ assertEquals(-1, result)
+ }
+
+ void testBNull() {
+ def result = comparator.compare(Object, null)
+ assertEquals 1, result
+ }
+
+ void testBothNull() {
+ assertEquals 0, comparator.compare(null, null)
+ }
+
+ void testBothSame() {
+ assertEquals 0, comparator.compare(Object, Object)
+ }
+
+ void testAParentOfB() {
+ assertEquals 1, comparator.compare(Number, Integer)
+ }
+
+ void testBParentOfA() {
+ assertEquals(-1, comparator.compare(Integer, Number))
+ }
+
+ void testUnrelated() {
+ assertEquals(0, comparator.compare(Integer, Boolean))
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy
new file mode 100644
index 0000000..c29b69d
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/DefaultEventBusTest.groovy
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import static org.easymock.EasyMock.*
+
+/**
+ * @since 1.3
+ */
+class DefaultEventBusTest extends GroovyTestCase {
+
+ DefaultEventBus bus;
+
+ @Override
+ protected void setUp() {
+ bus = new DefaultEventBus()
+ }
+
+ void testSetEventListenerResolver() {
+ def resolver = new EventListenerResolver() {
+ List<EventListener> getEventListeners(Object instance) {
+ return null //dummy implementation
+ }
+ }
+ bus.setEventListenerResolver(resolver)
+ assertSame resolver, bus.getEventListenerResolver()
+ }
+
+ void testSimpleSubscribe() {
+ def subscriber = new TestSubscriber();
+
+ bus.register(subscriber);
+
+ def event = new FooEvent(this)
+
+ bus.publish(event)
+
+ assertEquals 1, subscriber.fooCount
+ assertEquals 1, subscriber.count
+ assertSame event, subscriber.lastEvent
+ }
+
+ void testPublishNullEvent() {
+ def subscriber = new TestSubscriber();
+ bus.register(subscriber)
+
+ bus.publish(null)
+
+ assertEquals 0, subscriber.count
+ }
+
+ void testSubscribeNullInstance() {
+ def resolver = createStrictMock(EventListenerResolver) //assert no methods are called on this
+ bus.eventListenerResolver = resolver
+
+ replay(resolver)
+
+ bus.register(null)
+
+ verify(resolver)
+ }
+
+ void testSubscribeWithoutAnnotations() {
+ def subscriber = new NotAnnotatedSubscriber()
+ bus.register(subscriber)
+
+ bus.publish(new FooEvent(this))
+
+ assertEquals 0, bus.registry.size()
+ }
+
+ void testUnsubscribeNullInstance() {
+ bus.unregister(null)
+ }
+
+ void testUnsubscribe() {
+ def subscriber = new TestSubscriber()
+ bus.register(subscriber)
+ assertEquals 1, bus.registry.size()
+
+ def event = new FooEvent(this)
+
+ bus.publish(event)
+
+ assertSame event, subscriber.lastEvent
+ assertEquals 1, subscriber.fooCount
+ assertEquals 1, subscriber.count
+
+ bus.unregister(subscriber)
+
+ assertEquals 0, bus.registry.size()
+ }
+
+ void testPolymorphicSubscribeMethodsOnlyOneInvoked() {
+ def subscriber = new TestSubscriber()
+ bus.register(subscriber)
+
+ def event = new BarEvent(this)
+
+ bus.publish(event)
+
+ assertSame event, subscriber.lastEvent
+ assertEquals 0, subscriber.fooCount
+ assertEquals 1, subscriber.barCount
+ assertEquals 1, subscriber.count
+ }
+
+ void testPolymorphicSubscribeMethodsOnlyOneInvokedWithListenerSubclass() {
+ def subscriber = new SubclassTestSubscriber()
+ bus.register(subscriber)
+
+ def event = new BazEvent(this)
+
+ bus.publish(event)
+
+ assertSame event, subscriber.lastEvent
+ assertEquals 1, subscriber.count
+ assertEquals 1, subscriber.bazCount
+ assertEquals 0, subscriber.fooCount
+ assertEquals 0, subscriber.barCount
+ }
+
+ void testSubscribeWithErroneousAnnotation() {
+ def subscriber = new ErroneouslyAnnotatedSubscriber()
+ //noinspection GroovyUnusedCatchParameter
+ try {
+ bus.register(subscriber)
+ fail("exception expected")
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ void testContinueThroughListenerExceptions() {
+ def ok = new SimpleSubscriber()
+ def error = new ExceptionThrowingSubscriber()
+
+ bus.register(ok)
+ bus.register(error)
+
+ bus.publish(new ErrorCausingEvent())
+ bus.publish(new SimpleEvent())
+
+ assertEquals 1, ok.count
+ assertEquals 0, error.count
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy
new file mode 100644
index 0000000..82546a6
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/ErroneouslyAnnotatedSubscriber.groovy
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class ErroneouslyAnnotatedSubscriber {
+
+ @Subscribe
+ void onEvent(FooEvent event, Object someOtherArg) {
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy
new file mode 100644
index 0000000..897399f
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/ErrorCausingEvent.groovy
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class ErrorCausingEvent {
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy
new file mode 100644
index 0000000..1615e46
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/EventListenerComparatorTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import static org.easymock.EasyMock.createStrictMock
+
+/**
+ * @since 1.3
+ */
+class EventListenerComparatorTest extends GroovyTestCase {
+
+ EventListenerComparator comparator
+
+ @Override
+ protected void setUp() {
+ comparator = new EventListenerComparator()
+ }
+
+ void testANull() {
+ def result = comparator.compare(null, createStrictMock(EventListener))
+ assertEquals(-1, result)
+ }
+
+ void testBNull() {
+ def result = comparator.compare(createStrictMock(EventListener), null)
+ assertEquals 1, result
+ }
+
+ void testBothNull() {
+ assertEquals 0, comparator.compare(null, null)
+ }
+
+ void testBothSame() {
+ def mock = createStrictMock(EventListener)
+ assertEquals 0, comparator.compare(mock, mock)
+ }
+
+ void testBothEventListener() {
+ def a = createStrictMock(EventListener)
+ def b = createStrictMock(EventListener)
+ assertEquals 0, comparator.compare(a, b)
+ }
+
+ void testATypedListenerBNormalListener() {
+ def a = createStrictMock(TypedEventListener)
+ def b = createStrictMock(EventListener)
+ assertEquals(-1, comparator.compare(a, b))
+ }
+
+ void testANormalBTypedListener() {
+ def a = createStrictMock(EventListener)
+ def b = createStrictMock(TypedEventListener)
+ assertEquals 1, comparator.compare(a, b)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy
new file mode 100644
index 0000000..b0b63c1
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/ExceptionThrowingSubscriber.groovy
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class ExceptionThrowingSubscriber extends TestSubscriber {
+
+ @Subscribe
+ void onEvent(ErrorCausingEvent event) {
+ throw new UnsupportedOperationException("This throws!")
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy
new file mode 100644
index 0000000..b594c92
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/FooEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class FooEvent extends EventObject {
+
+ FooEvent(Object o) {
+ super(o)
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy
new file mode 100644
index 0000000..8489185
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/NotAnnotatedSubscriber.groovy
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * @since 1.3
+ */
+class NotAnnotatedSubscriber {
+ //not a subscriber - no methods have been annotated on purpose.
+ void hello() {}
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
new file mode 100644
index 0000000..5639f5d
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleEvent.groovy
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: lhazlewood
+ * Date: 11/16/12
+ * Time: 11:09 PM
+ * To change this template use File | Settings | File Templates.
+ */
+class SimpleEvent {
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy
new file mode 100644
index 0000000..0fa44ea
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/SimpleSubscriber.groovy
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class SimpleSubscriber {
+
+ int count
+
+ SimpleSubscriber() {
+ count = 0
+ }
+
+ @Subscribe
+ void onEvent(SimpleEvent event) {
+ count++
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy
new file mode 100644
index 0000000..bf3c060
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/SingleArgumentMethodEventListenerTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import java.lang.reflect.Method
+
+/**
+ * @since 1.3
+ */
+class SingleArgumentMethodEventListenerTest extends GroovyTestCase {
+
+ void testInvalidConstruction() {
+
+ def target = new Object()
+
+ def method = Object.class.getMethods()[0] //any old method will do
+
+ try {
+ //noinspection GroovyResultOfObjectAllocationIgnored
+ new SingleArgumentMethodEventListener(target, method)
+ fail("exception expected")
+ } catch (IllegalArgumentException iae) {
+ assertEquals iae.message, "Event handler methods must accept a single argument."
+ }
+ }
+
+ void testValidConstruction() {
+
+ def target = new TestSubscriber()
+ def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
+
+ def listener = new SingleArgumentMethodEventListener(target, method)
+
+ assertSame target, listener.getTarget()
+ assertSame method, listener.getMethod()
+ }
+
+ void testMethodException() {
+
+ def target = new TestSubscriber()
+ def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
+
+ def listener = new SingleArgumentMethodEventListener(target, method) {
+ @Override
+ Method getMethod() {
+ //sneakily swap out the valid method with an erroneous one. This wouldn't ever happen normally, we're
+ //just doing this as a test harness:
+ return Object.class.getMethods()[0] //any method will do
+ }
+ }
+
+ //now invoke the erroneous method and ensure we get an exception:
+ try {
+ listener.onEvent(new FooEvent(this))
+ fail("exception expected")
+ } catch (IllegalStateException ise) {
+ assertTrue ise.message.startsWith("Unable to invoke event handler method")
+ }
+ }
+
+ void testAccepts() {
+ def target = new TestSubscriber()
+ def method = TestSubscriber.class.getMethods().find { it.name == "onFooEvent" }
+
+ def listener = new SingleArgumentMethodEventListener(target, method)
+
+ assertTrue listener.accepts(new FooEvent(this))
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/bbc8efac/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy b/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy
new file mode 100644
index 0000000..8dc717c
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/event/bus/SubclassTestSubscriber.groovy
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.event.bus
+
+import org.apache.shiro.event.Subscribe
+
+/**
+ * @since 1.3
+ */
+class SubclassTestSubscriber extends TestSubscriber {
+
+ int bazCount
+
+ SubclassTestSubscriber() {
+ bazCount = 0
+ }
+
+ @Subscribe
+ void onEvent(BazEvent event) {
+ bazCount++
+ lastEvent = event;
+ }
+
+ @Subscribe
+ void onEvent(ErrorCausingEvent event) {
+ throw new UnsupportedOperationException("This throws!")
+ }
+
+ @Override
+ int getCount() {
+ return super.getCount() + bazCount
+ }
+}
[13/13] shiro git commit: Added missing license headers
Posted by bd...@apache.org.
Added missing license headers
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/bd8c3141
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/bd8c3141
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/bd8c3141
Branch: refs/heads/1.3.x
Commit: bd8c314151e3b9ec5f9810a886325c9367d77e74
Parents: bf8febe
Author: Brian Demers <bd...@apache.org>
Authored: Mon Jul 11 16:59:59 2016 -0400
Committer: Brian Demers <bd...@apache.org>
Committed: Mon Jul 11 16:59:59 2016 -0400
----------------------------------------------------------------------
.../apache/shiro/config/event/BeanListener.java | 18 ++++++++++++++++++
.../shiro/config/event/BeanListenerSupport.java | 18 ++++++++++++++++++
.../shiro/config/event/ConfiguredBeanEvent.java | 18 ++++++++++++++++++
.../main/java/org/apache/shiro/event/Event.java | 18 ++++++++++++++++++
.../org/apache/shiro/event/EventTest.groovy | 18 ++++++++++++++++++
5 files changed, 90 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/bd8c3141/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanListener.java b/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
index 1f0a448..2980fc0 100644
--- a/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanListener.java
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.config.event;
/**
http://git-wip-us.apache.org/repos/asf/shiro/blob/bd8c3141/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java b/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
index 60fc3d5..d74b77d 100644
--- a/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
+++ b/core/src/main/java/org/apache/shiro/config/event/BeanListenerSupport.java
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.config.event;
public abstract class BeanListenerSupport implements BeanListener {
http://git-wip-us.apache.org/repos/asf/shiro/blob/bd8c3141/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
index 2636330..24dccee 100644
--- a/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
+++ b/core/src/main/java/org/apache/shiro/config/event/ConfiguredBeanEvent.java
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.config.event;
import java.util.Map;
http://git-wip-us.apache.org/repos/asf/shiro/blob/bd8c3141/core/src/main/java/org/apache/shiro/event/Event.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/event/Event.java b/core/src/main/java/org/apache/shiro/event/Event.java
index 8989796..9fecf8a 100644
--- a/core/src/main/java/org/apache/shiro/event/Event.java
+++ b/core/src/main/java/org/apache/shiro/event/Event.java
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.event;
import java.util.EventObject;
http://git-wip-us.apache.org/repos/asf/shiro/blob/bd8c3141/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy b/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
index 5f2282b..4ee7b69 100644
--- a/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
+++ b/core/src/test/groovy/org/apache/shiro/event/EventTest.groovy
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
package org.apache.shiro.event
import org.junit.Test
[12/13] shiro git commit: fix up logging dependencies in quickstart
samples
Posted by bd...@apache.org.
fix up logging dependencies in quickstart samples
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/bf8febee
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/bf8febee
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/bf8febee
Branch: refs/heads/1.3.x
Commit: bf8febeeff6f3f2a90872f61e7251e9e23a97425
Parents: 67e7a36
Author: Brian Demers <bd...@apache.org>
Authored: Mon Jul 11 09:20:30 2016 -0400
Committer: Brian Demers <bd...@apache.org>
Committed: Mon Jul 11 09:20:30 2016 -0400
----------------------------------------------------------------------
samples/quickstart-guice/pom.xml | 11 +++++++++--
samples/quickstart/pom.xml | 16 ++++++++++++++--
2 files changed, 23 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/bf8febee/samples/quickstart-guice/pom.xml
----------------------------------------------------------------------
diff --git a/samples/quickstart-guice/pom.xml b/samples/quickstart-guice/pom.xml
index 66eae14..b6f2678 100644
--- a/samples/quickstart-guice/pom.xml
+++ b/samples/quickstart-guice/pom.xml
@@ -63,6 +63,8 @@
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-guice</artifactId>
</dependency>
+
+ <!-- configure logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
@@ -70,8 +72,13 @@
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <scope>test</scope>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>runtime</scope>
</dependency>
</dependencies>
http://git-wip-us.apache.org/repos/asf/shiro/blob/bf8febee/samples/quickstart/pom.xml
----------------------------------------------------------------------
diff --git a/samples/quickstart/pom.xml b/samples/quickstart/pom.xml
index 4219ca8..e20eea5 100644
--- a/samples/quickstart/pom.xml
+++ b/samples/quickstart/pom.xml
@@ -59,10 +59,22 @@
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
+
+ <!-- configure logging -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>runtime</scope>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <scope>test</scope>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>runtime</scope>
</dependency>
</dependencies>
[10/13] shiro git commit: Backported guice samples
Posted by bd...@apache.org.
Backported guice samples
Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/1e6a89cb
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/1e6a89cb
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/1e6a89cb
Branch: refs/heads/1.3.x
Commit: 1e6a89cb56aa11865a2af2d2137cb49c8b60da72
Parents: 4929b4a
Author: Brian Demers <bd...@apache.org>
Authored: Fri Jul 8 14:52:48 2016 -0400
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 14:52:48 2016 -0400
----------------------------------------------------------------------
pom.xml | 6 +
samples/guice/pom.xml | 132 +++++++++++++++++++
.../guice/SampleShiroGuiceBootstrap.java | 44 +++++++
.../SampleShiroNativeSessionsServletModule.java | 75 +++++++++++
.../samples/guice/SampleShiroServletModule.java | 91 +++++++++++++
.../guice/src/main/resources/log4j.properties | 47 +++++++
samples/guice/src/main/webapp/WEB-INF/shiro.ini | 53 ++++++++
samples/guice/src/main/webapp/WEB-INF/web.xml | 43 ++++++
samples/guice/src/main/webapp/account/index.jsp | 36 +++++
samples/guice/src/main/webapp/home.jsp | 69 ++++++++++
samples/guice/src/main/webapp/include.jsp | 22 ++++
samples/guice/src/main/webapp/index.jsp | 21 +++
samples/guice/src/main/webapp/login.jsp | 110 ++++++++++++++++
samples/guice/src/main/webapp/style.css | 47 +++++++
.../samples/guice/AbstractContainerTest.java | 89 +++++++++++++
.../samples/guice/ContainerIntegrationTest.java | 79 +++++++++++
samples/pom.xml | 2 +
samples/quickstart-guice/pom.xml | 78 +++++++++++
.../src/main/java/QuickstartGuice.java | 118 +++++++++++++++++
.../src/main/java/QuickstartShiroModule.java | 37 ++++++
.../src/main/resources/log4j.properties | 36 +++++
.../src/main/resources/shiro.ini | 58 ++++++++
22 files changed, 1293 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a9bcfc1..05b7836 100644
--- a/pom.xml
+++ b/pom.xml
@@ -844,6 +844,12 @@
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>taglibs</groupId>
+ <artifactId>standard</artifactId>
+ <version>1.1.2</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/pom.xml
----------------------------------------------------------------------
diff --git a/samples/guice/pom.xml b/samples/guice/pom.xml
new file mode 100644
index 0000000..935fb27
--- /dev/null
+++ b/samples/guice/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.shiro.samples</groupId>
+ <artifactId>shiro-samples</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>samples-guice</artifactId>
+ <name>Apache Shiro :: Samples :: Guice Web</name>
+ <packaging>war</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <forkMode>never</forkMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>maven-jetty-plugin</artifactId>
+ <version>${jetty.version}</version>
+ <configuration>
+ <contextPath>/</contextPath>
+ <connectors>
+ <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
+ <port>9080</port>
+ <maxIdleTime>60000</maxIdleTime>
+ </connector>
+ </connectors>
+ <requestLog implementation="org.mortbay.jetty.NCSARequestLog">
+ <filename>./target/yyyy_mm_dd.request.log</filename>
+ <retainDays>90</retainDays>
+ <append>true</append>
+ <extended>false</extended>
+ <logTimeZone>GMT</logTimeZone>
+ </requestLog>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>jstl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.sourceforge.htmlunit</groupId>
+ <artifactId>htmlunit</artifactId>
+ <version>2.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-guice</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ <version>${jetty.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jsp-2.1-jetty</artifactId>
+ <version>${jetty.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>taglibs</groupId>
+ <artifactId>standard</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java
new file mode 100644
index 0000000..7bd5480
--- /dev/null
+++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.samples.guice;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import org.apache.shiro.guice.web.ShiroWebModule;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+
+public class SampleShiroGuiceBootstrap extends GuiceServletContextListener {
+
+ private ServletContext servletContext;
+
+ @Override
+ public void contextInitialized(final ServletContextEvent servletContextEvent) {
+ this.servletContext = servletContextEvent.getServletContext();
+ super.contextInitialized(servletContextEvent);
+ }
+
+ @Override
+ protected Injector getInjector() {
+ return Guice.createInjector(new SampleShiroServletModule(servletContext), ShiroWebModule.guiceFilterModule());
+// return Guice.createInjector(new SampleShiroNativeSessionsServletModule(servletContext), ShiroWebModule.guiceFilterModule());
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java
new file mode 100644
index 0000000..5ab0057
--- /dev/null
+++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.samples.guice;
+
+import com.google.inject.Provides;
+import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.name.Names;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.guice.web.ShiroWebModule;
+import org.apache.shiro.realm.text.IniRealm;
+import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.web.servlet.Cookie;
+import org.apache.shiro.web.servlet.SimpleCookie;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class SampleShiroNativeSessionsServletModule extends ShiroWebModule {
+ private final ServletContext servletContext;
+
+ public SampleShiroNativeSessionsServletModule(ServletContext servletContext) {
+ super(servletContext);
+
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ protected void configureShiroWeb() {
+ bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/login.jsp");
+ try {
+ this.bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
+ } catch (NoSuchMethodException e) {
+ addError("Could not locate proper constructor for IniRealm.", e);
+ }
+
+ this.addFilterChain("/login.jsp", AUTHC);
+ this.addFilterChain("/logout", LOGOUT);
+ this.addFilterChain("/account/**", AUTHC);
+ this.addFilterChain("/remoting/**", AUTHC, config(ROLES, "b2bClient"), config(PERMS, "remote:invoke:lan,wan"));
+ }
+
+ @Provides
+ @Singleton
+ Ini loadShiroIni() throws MalformedURLException {
+ URL iniUrl = servletContext.getResource("/WEB-INF/shiro.ini");
+ return Ini.fromResourcePath("url:" + iniUrl.toExternalForm());
+ }
+
+ @Override
+ protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind) {
+ bind.to(DefaultWebSessionManager.class);
+ bindConstant().annotatedWith(Names.named("shiro.globalSessionTimeout")).to(5000L);
+ bind(DefaultWebSessionManager.class);
+ bind(Cookie.class).toInstance(new SimpleCookie("myCookie"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java
new file mode 100644
index 0000000..01a0113
--- /dev/null
+++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.samples.guice;
+
+import com.google.inject.Provides;
+import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.name.Names;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.config.ConfigurationException;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.guice.web.ShiroWebModule;
+import org.apache.shiro.realm.text.IniRealm;
+import org.apache.shiro.web.mgt.CookieRememberMeManager;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.mgt.WebSecurityManager;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class SampleShiroServletModule extends ShiroWebModule {
+ private final ServletContext servletContext;
+
+ public SampleShiroServletModule(ServletContext servletContext) {
+ super(servletContext);
+
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ protected void configureShiroWeb() {
+ bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/login.jsp");
+ try {
+ this.bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
+ } catch (NoSuchMethodException e) {
+ addError("Could not locate proper constructor for IniRealm.", e);
+ }
+
+ this.addFilterChain("/login.jsp", AUTHC);
+ this.addFilterChain("/logout", LOGOUT);
+ this.addFilterChain("/account/**", AUTHC);
+ this.addFilterChain("/remoting/**", AUTHC, config(ROLES, "b2bClient"), config(PERMS, "remote:invoke:lan,wan"));
+ }
+
+ @Provides
+ @Singleton
+ Ini loadShiroIni() throws MalformedURLException {
+ URL iniUrl = servletContext.getResource("/WEB-INF/shiro.ini");
+ return Ini.fromResourcePath("url:" + iniUrl.toExternalForm());
+ }
+
+ @Override
+ protected void bindWebSecurityManager(AnnotatedBindingBuilder<? super WebSecurityManager> bind)
+ {
+ try
+ {
+ String cipherKey = loadShiroIni().getSectionProperty( "main", "securityManager.rememberMeManager.cipherKey" );
+
+ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+ CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
+ rememberMeManager.setCipherKey( Base64.decode( cipherKey ) );
+ securityManager.setRememberMeManager(rememberMeManager);
+ bind.toInstance(securityManager);
+ }
+ catch ( MalformedURLException e )
+ {
+ // for now just throw, you could just call
+ // super.bindWebSecurityManager(bind) if you do not need rememberMe functionality
+ throw new ConfigurationException( "securityManager.rememberMeManager.cipherKey must be set in shiro.ini." );
+ }
+
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/resources/log4j.properties b/samples/guice/src/main/resources/log4j.properties
new file mode 100644
index 0000000..b10506b
--- /dev/null
+++ b/samples/guice/src/main/resources/log4j.properties
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=TRACE, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%c]: %m%n
+
+# =============================================================================
+# 3rd Party Libraries
+# OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
+# =============================================================================
+# ehcache caching manager:
+log4j.logger.net.sf.ehcache=WARN
+
+# Most all Apache libs:
+log4j.logger.org.apache=WARN
+
+# Quartz Enterprise Scheular (java 'cron' utility)
+log4j.logger.org.quartz=WARN
+
+# =============================================================================
+# Apache Shiro
+# =============================================================================
+# Shiro security framework
+log4j.logger.org.apache.shiro=TRACE
+#log4j.logger.org.apache.shiro.realm.text.PropertiesRealm=INFO
+#log4j.logger.org.apache.shiro.cache.ehcache.EhCache=INFO
+#log4j.logger.org.apache.shiro.io=INFO
+#log4j.logger.org.apache.shiro.web.servlet=INFO
+log4j.logger.org.apache.shiro.util.ThreadContext=INFO
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/WEB-INF/shiro.ini
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/WEB-INF/shiro.ini b/samples/guice/src/main/webapp/WEB-INF/shiro.ini
new file mode 100644
index 0000000..db453e7
--- /dev/null
+++ b/samples/guice/src/main/webapp/WEB-INF/shiro.ini
@@ -0,0 +1,53 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# INI configuration is very powerful and flexible, while still remaining succinct.
+# Please http://shiro.apache.org/configuration.html and
+# http://shiro.apache.org/web.html for more.
+
+[main]
+shiro.loginUrl = /login.jsp
+
+# We need to set the cipherKey, if you want the rememberMe cookie to work after restarting or on multiple nodes.
+# YOU MUST SET THIS TO A UNIQUE STRING
+securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA==
+
+[users]
+# format: username = password, role1, role2, ..., roleN
+root = secret,admin
+guest = guest,guest
+presidentskroob = 12345,president
+darkhelmet = ludicrousspeed,darklord,schwartz
+lonestarr = vespa,goodguy,schwartz
+
+[roles]
+# format: roleName = permission1, permission2, ..., permissionN
+admin = *
+schwartz = lightsaber:*
+goodguy = winnebago:drive:eagle5
+
+[urls]
+# The /login.jsp is not restricted to authenticated users (otherwise no one could log in!), but
+# the 'authc' filter must still be specified for it so it can process that url's
+# login submissions. It is 'smart' enough to allow those requests through as specified by the
+# shiro.loginUrl above.
+/login.jsp = authc
+/logout = logout
+/account/** = authc
+/remoting/** = authc, roles[b2bClient], perms["remote:invoke:lan,wan"]
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/WEB-INF/web.xml b/samples/guice/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..46567d4
--- /dev/null
+++ b/samples/guice/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <listener>
+ <listener-class>org.apache.shiro.samples.guice.SampleShiroGuiceBootstrap</listener-class>
+ </listener>
+
+ <filter>
+ <filter-name>GuiceFilter</filter-name>
+ <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>GuiceFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <welcome-file-list>
+ <welcome-file>index.jsp</welcome-file>
+ </welcome-file-list>
+
+</web-app>
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/account/index.jsp
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/account/index.jsp b/samples/guice/src/main/webapp/account/index.jsp
new file mode 100644
index 0000000..4f6c9d8
--- /dev/null
+++ b/samples/guice/src/main/webapp/account/index.jsp
@@ -0,0 +1,36 @@
+<%--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ --%>
+<%@ include file="../include.jsp" %>
+
+<html>
+<head>
+ <link type="text/css" rel="stylesheet" href="<c:url value="/style.css"/>"/>
+</head>
+<body>
+
+<h2>Users only</h2>
+
+<p>You are currently logged in.</p>
+
+<p><a href="<c:url value="/home.jsp"/>">Return to the home page.</a></p>
+
+<p><a href="<c:url value="/logout"/>">Log out.</a></p>
+
+</body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/home.jsp
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/home.jsp b/samples/guice/src/main/webapp/home.jsp
new file mode 100644
index 0000000..61dee25
--- /dev/null
+++ b/samples/guice/src/main/webapp/home.jsp
@@ -0,0 +1,69 @@
+<%--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ --%>
+<%@ include file="include.jsp" %>
+
+<html>
+<head>
+ <link type="text/css" rel="stylesheet" href="<c:url value="/style.css"/>"/>
+ <title>Apache Shiro Quickstart</title>
+</head>
+<body>
+
+<h1>Apache Shiro Quickstart</h1>
+
+<p>Hi <shiro:guest>Guest</shiro:guest><shiro:user><shiro:principal/></shiro:user>!
+ ( <shiro:user><a href="<c:url value="/logout"/>">Log out</a></shiro:user>
+ <shiro:guest><a href="<c:url value="/login.jsp"/>">Log in</a> (sample accounts provided)</shiro:guest> )
+</p>
+
+<p>Welcome to the Apache Shiro Quickstart sample application.
+ This page represents the home page of any web application.</p>
+
+<shiro:user><p>Visit your <a href="<c:url value="/account"/>">account page</a>.</p></shiro:user>
+<shiro:guest><p>If you want to access the user-only <a href="<c:url value="/account"/>">account page</a>,
+ you will need to log-in first.</p></shiro:guest>
+
+<h2>Roles</h2>
+
+<p>To show some taglibs, here are the roles you have and don't have. Log out and log back in under different user
+ accounts to see different roles.</p>
+
+<h3>Roles you have</h3>
+
+<p>
+ <shiro:hasRole name="admin">admin<br/></shiro:hasRole>
+ <shiro:hasRole name="president">president<br/></shiro:hasRole>
+ <shiro:hasRole name="darklord">darklord<br/></shiro:hasRole>
+ <shiro:hasRole name="goodguy">goodguy<br/></shiro:hasRole>
+ <shiro:hasRole name="schwartz">schwartz<br/></shiro:hasRole>
+</p>
+
+<h3>Roles you DON'T have</h3>
+
+<p>
+ <shiro:lacksRole name="admin">admin<br/></shiro:lacksRole>
+ <shiro:lacksRole name="president">president<br/></shiro:lacksRole>
+ <shiro:lacksRole name="darklord">darklord<br/></shiro:lacksRole>
+ <shiro:lacksRole name="goodguy">goodguy<br/></shiro:lacksRole>
+ <shiro:lacksRole name="schwartz">schwartz<br/></shiro:lacksRole>
+</p>
+
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/include.jsp
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/include.jsp b/samples/guice/src/main/webapp/include.jsp
new file mode 100644
index 0000000..fddd239
--- /dev/null
+++ b/samples/guice/src/main/webapp/include.jsp
@@ -0,0 +1,22 @@
+<%--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ --%>
+<%@ page import="org.apache.shiro.SecurityUtils" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/index.jsp
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/index.jsp b/samples/guice/src/main/webapp/index.jsp
new file mode 100644
index 0000000..9dedd19
--- /dev/null
+++ b/samples/guice/src/main/webapp/index.jsp
@@ -0,0 +1,21 @@
+<%--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ --%>
+
+<%-- Forward the user to the home page --%>
+<jsp:forward page="home.jsp"/>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/login.jsp
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/login.jsp b/samples/guice/src/main/webapp/login.jsp
new file mode 100644
index 0000000..c80ee54
--- /dev/null
+++ b/samples/guice/src/main/webapp/login.jsp
@@ -0,0 +1,110 @@
+<%--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ --%>
+<%@ include file="include.jsp" %>
+
+<html>
+<head>
+ <link type="text/css" rel="stylesheet" href="<c:url value="/style.css"/>"/>
+</head>
+<body>
+
+<h2>Please Log in</h2>
+
+<shiro:guest>
+ <p>Here are a few sample accounts to play with in the default text-based Realm (used for this
+ demo and test installs only). Do you remember the movie these names came from? ;)</p>
+
+
+ <style type="text/css">
+ table.sample {
+ border-width: 1px;
+ border-style: outset;
+ border-color: blue;
+ border-collapse: separate;
+ background-color: rgb(255, 255, 240);
+ }
+
+ table.sample th {
+ border-width: 1px;
+ padding: 1px;
+ border-style: none;
+ border-color: blue;
+ background-color: rgb(255, 255, 240);
+ }
+
+ table.sample td {
+ border-width: 1px;
+ padding: 1px;
+ border-style: none;
+ border-color: blue;
+ background-color: rgb(255, 255, 240);
+ }
+ </style>
+
+
+ <table class="sample">
+ <thead>
+ <tr>
+ <th>Username</th>
+ <th>Password</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>root</td>
+ <td>secret</td>
+ </tr>
+ <tr>
+ <td>presidentskroob</td>
+ <td>12345</td>
+ </tr>
+ <tr>
+ <td>darkhelmet</td>
+ <td>ludicrousspeed</td>
+ </tr>
+ <tr>
+ <td>lonestarr</td>
+ <td>vespa</td>
+ </tr>
+ </tbody>
+ </table>
+ <br/><br/>
+</shiro:guest>
+
+<form name="loginform" action="" method="post">
+ <table align="left" border="0" cellspacing="0" cellpadding="3">
+ <tr>
+ <td>Username:</td>
+ <td><input type="text" name="username" maxlength="30"></td>
+ </tr>
+ <tr>
+ <td>Password:</td>
+ <td><input type="password" name="password" maxlength="30"></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="left"><input type="checkbox" name="rememberMe"><font size="2">Remember Me</font></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><input type="submit" name="submit" value="Login"></td>
+ </tr>
+ </table>
+</form>
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/main/webapp/style.css
----------------------------------------------------------------------
diff --git a/samples/guice/src/main/webapp/style.css b/samples/guice/src/main/webapp/style.css
new file mode 100644
index 0000000..1308e3f
--- /dev/null
+++ b/samples/guice/src/main/webapp/style.css
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+body {
+ margin: 15px 0 0 15px;
+ padding: 1px; /*background: #2370cf;*/
+ font: 12px 'Lucida Grande', Geneva, Verdana, Arial, sans-serif;
+ color: #000;
+}
+
+table, td {
+ font: 12px 'Lucida Grande', Geneva, Verdana, Arial, sans-serif;
+ color: #000;
+}
+
+h1 {
+ font: 24px;
+}
+
+img {
+ border: thin black solid;
+}
+
+#contentBox {
+ text-align: center;
+ width: 50%;
+ margin: auto;
+ margin-top: 50px;
+ color: black;
+ background: #eee;
+ border: thick black solid;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/test/java/org/apache/shiro/samples/guice/AbstractContainerTest.java
----------------------------------------------------------------------
diff --git a/samples/guice/src/test/java/org/apache/shiro/samples/guice/AbstractContainerTest.java b/samples/guice/src/test/java/org/apache/shiro/samples/guice/AbstractContainerTest.java
new file mode 100644
index 0000000..0806fcc
--- /dev/null
+++ b/samples/guice/src/test/java/org/apache/shiro/samples/guice/AbstractContainerTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.samples.guice;
+
+import com.gargoylesoftware.htmlunit.WebClient;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.webapp.WebAppContext;
+
+import java.net.BindException;
+
+import static org.junit.Assert.assertTrue;
+
+public abstract class AbstractContainerTest {
+ public static final int MAX_PORT = 9200;
+
+ protected static PauseableServer server;
+
+ private static int port = 9180;
+
+ protected final WebClient webClient = new WebClient();
+
+ @BeforeClass
+ public static void startContainer() throws Exception {
+ while (server == null && port < MAX_PORT) {
+ try {
+ server = createAndStartServer(port);
+ } catch (BindException e) {
+ System.err.printf("Unable to listen on port %d. Trying next port.", port);
+ port++;
+ }
+ }
+ assertTrue(server.isStarted());
+ }
+
+ private static PauseableServer createAndStartServer(final int port) throws Exception {
+ PauseableServer server = new PauseableServer();
+ Connector connector = new SelectChannelConnector();
+ connector.setPort(port);
+ server.setConnectors(new Connector[]{connector});
+ server.setHandler(new WebAppContext("src/main/webapp", "/"));
+ server.start();
+ return server;
+ }
+
+ protected static String getBaseUri() {
+ return "http://localhost:" + port + "/";
+ }
+
+ @Before
+ public void beforeTest() {
+ webClient.setThrowExceptionOnFailingStatusCode(true);
+ }
+
+ public void pauseServer(boolean paused) {
+ if (server != null) server.pause(paused);
+ }
+
+ public static class PauseableServer extends Server {
+ public synchronized void pause(boolean paused) {
+ try {
+ if (paused) for (Connector connector : getConnectors())
+ connector.stop();
+ else for (Connector connector : getConnectors())
+ connector.start();
+ } catch (Exception e) {
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationTest.java b/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationTest.java
new file mode 100644
index 0000000..2694b3a
--- /dev/null
+++ b/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.samples.guice;
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import com.gargoylesoftware.htmlunit.WebAssert;
+import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlInput;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+public class ContainerIntegrationTest extends AbstractContainerTest {
+
+ @Before
+ public void logOut() throws IOException {
+ // Make sure we are logged out
+ final HtmlPage homePage = webClient.getPage(getBaseUri());
+ try {
+ homePage.getAnchorByHref("/logout").click();
+ }
+ catch (ElementNotFoundException e) {
+ //Ignore
+ }
+ }
+
+ @Test
+ public void logIn() throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {
+
+ HtmlPage page = webClient.getPage(getBaseUri() + "login.jsp");
+ HtmlForm form = page.getFormByName("loginform");
+ form.<HtmlInput>getInputByName("username").setValueAttribute("root");
+ form.<HtmlInput>getInputByName("password").setValueAttribute("secret");
+ page = form.<HtmlInput>getInputByName("submit").click();
+ // This'll throw an expection if not logged in
+ page.getAnchorByHref("/logout");
+ }
+
+ @Test
+ public void logInAndRememberMe() throws Exception {
+ HtmlPage page = webClient.getPage(getBaseUri() + "login.jsp");
+ HtmlForm form = page.getFormByName("loginform");
+ form.<HtmlInput>getInputByName("username").setValueAttribute("root");
+ form.<HtmlInput>getInputByName("password").setValueAttribute("secret");
+ HtmlCheckBoxInput checkbox = form.getInputByName("rememberMe");
+ checkbox.setChecked(true);
+ page = form.<HtmlInput>getInputByName("submit").click();
+ server.stop();
+ server.start();
+ page = webClient.getPage(getBaseUri());
+ // page.getAnchorByHref("/logout");
+ WebAssert.assertLinkPresentWithText(page, "Log out");
+ page = page.getAnchorByHref("/account").click();
+ // login page should be shown again - user remembered but not authenticated
+ WebAssert.assertFormPresent(page, "loginform");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/pom.xml
----------------------------------------------------------------------
diff --git a/samples/pom.xml b/samples/pom.xml
index ae8e708..0d53558 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -39,6 +39,8 @@
<module>spring-client</module>
<module>spring</module>
<module>spring-hibernate</module>
+ <module>guice</module>
+ <module>quickstart-guice</module>
</modules>
<reporting>
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/quickstart-guice/pom.xml
----------------------------------------------------------------------
diff --git a/samples/quickstart-guice/pom.xml b/samples/quickstart-guice/pom.xml
new file mode 100644
index 0000000..66eae14
--- /dev/null
+++ b/samples/quickstart-guice/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.shiro.samples</groupId>
+ <artifactId>shiro-samples</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>samples-quickstart-guice</artifactId>
+ <name>Apache Shiro :: Samples :: Quick Start Guice</name>
+ <packaging>jar</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <classpathScope>test</classpathScope>
+ <mainClass>QuickstartGuice</mainClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-guice</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/quickstart-guice/src/main/java/QuickstartGuice.java
----------------------------------------------------------------------
diff --git a/samples/quickstart-guice/src/main/java/QuickstartGuice.java b/samples/quickstart-guice/src/main/java/QuickstartGuice.java
new file mode 100644
index 0000000..5fcb1e3
--- /dev/null
+++ b/samples/quickstart-guice/src/main/java/QuickstartGuice.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.*;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple Quickstart application showing how to use Shiro's API with Guice integration.
+ *
+ * @since 0.9 RC2
+ */
+public class QuickstartGuice {
+
+ private static final transient Logger log = LoggerFactory.getLogger(QuickstartGuice.class);
+
+
+ public static void main(String[] args) {
+
+ // We will utilize standard Guice bootstrapping to create a Shiro SecurityManager.
+ Injector injector = Guice.createInjector(new QuickstartShiroModule());
+ SecurityManager securityManager = injector.getInstance(SecurityManager.class);
+
+ // for this simple example quickstart, make the SecurityManager
+ // accessible as a JVM singleton. Most applications wouldn't do this
+ // and instead rely on their container configuration or web.xml for
+ // webapps. That is outside the scope of this simple quickstart, so
+ // we'll just do the bare minimum so you can continue to get a feel
+ // for things.
+ SecurityUtils.setSecurityManager(securityManager);
+
+ // Now that a simple Shiro environment is set up, let's see what you can do:
+
+ // get the currently executing user:
+ Subject currentUser = SecurityUtils.getSubject();
+
+ // Do some stuff with a Session (no need for a web or EJB container!!!)
+ Session session = currentUser.getSession();
+ session.setAttribute("someKey", "aValue");
+ String value = (String) session.getAttribute("someKey");
+ if (value.equals("aValue")) {
+ log.info("Retrieved the correct value! [" + value + "]");
+ }
+
+ // let's login the current user so we can check against roles and permissions:
+ if (!currentUser.isAuthenticated()) {
+ UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
+ token.setRememberMe(true);
+ try {
+ currentUser.login(token);
+ } catch (UnknownAccountException uae) {
+ log.info("There is no user with username of " + token.getPrincipal());
+ } catch (IncorrectCredentialsException ice) {
+ log.info("Password for account " + token.getPrincipal() + " was incorrect!");
+ } catch (LockedAccountException lae) {
+ log.info("The account for username " + token.getPrincipal() + " is locked. " +
+ "Please contact your administrator to unlock it.");
+ }
+ // ... catch more exceptions here (maybe custom ones specific to your application?
+ catch (AuthenticationException ae) {
+ //unexpected condition? error?
+ }
+ }
+
+ //say who they are:
+ //print their identifying principal (in this case, a username):
+ log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
+
+ //test a role:
+ if (currentUser.hasRole("schwartz")) {
+ log.info("May the Schwartz be with you!");
+ } else {
+ log.info("Hello, mere mortal.");
+ }
+
+ //test a typed permission (not instance-level)
+ if (currentUser.isPermitted("lightsaber:weild")) {
+ log.info("You may use a lightsaber ring. Use it wisely.");
+ } else {
+ log.info("Sorry, lightsaber rings are for schwartz masters only.");
+ }
+
+ //a (very powerful) Instance Level permission:
+ if (currentUser.isPermitted("winnebago:drive:eagle5")) {
+ log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
+ "Here are the keys - have fun!");
+ } else {
+ log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
+ }
+
+ //all done - log out!
+ currentUser.logout();
+
+ System.exit(0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java
----------------------------------------------------------------------
diff --git a/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java b/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java
new file mode 100644
index 0000000..01caf68
--- /dev/null
+++ b/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import com.google.inject.Provides;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.guice.ShiroModule;
+import org.apache.shiro.realm.text.IniRealm;
+
+public class QuickstartShiroModule extends ShiroModule {
+ protected void configureShiro() {
+ try {
+ bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
+ } catch (NoSuchMethodException e) {
+ addError(e);
+ }
+ }
+
+ @Provides
+ Ini loadShiroIni() {
+ return Ini.fromResourcePath("classpath:shiro.ini");
+ }
+}
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/quickstart-guice/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/samples/quickstart-guice/src/main/resources/log4j.properties b/samples/quickstart-guice/src/main/resources/log4j.properties
new file mode 100644
index 0000000..779033d
--- /dev/null
+++ b/samples/quickstart-guice/src/main/resources/log4j.properties
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
+
+# General Apache libraries
+log4j.logger.org.apache=WARN
+
+# Spring
+log4j.logger.org.springframework=WARN
+
+# Default Shiro logging
+log4j.logger.org.apache.shiro=TRACE
+
+# Disable verbose logging
+log4j.logger.org.apache.shiro.util.ThreadContext=WARN
+log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
http://git-wip-us.apache.org/repos/asf/shiro/blob/1e6a89cb/samples/quickstart-guice/src/main/resources/shiro.ini
----------------------------------------------------------------------
diff --git a/samples/quickstart-guice/src/main/resources/shiro.ini b/samples/quickstart-guice/src/main/resources/shiro.ini
new file mode 100644
index 0000000..adca38c
--- /dev/null
+++ b/samples/quickstart-guice/src/main/resources/shiro.ini
@@ -0,0 +1,58 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# =============================================================================
+# Quickstart INI Realm configuration
+#
+# For those that might not understand the references in this file, the
+# definitions are all based on the classic Mel Brooks' film "Spaceballs". ;)
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# Users and their assigned roles
+#
+# Each line conforms to the format defined in the
+# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
+# -----------------------------------------------------------------------------
+[users]
+# user 'root' with password 'secret' and the 'admin' role
+root = secret, admin
+# user 'guest' with the password 'guest' and the 'guest' role
+guest = guest, guest
+# user 'presidentskroob' with password '12345' ("That's the same combination on
+# my luggage!!!" ;)), and role 'president'
+presidentskroob = 12345, president
+# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
+darkhelmet = ludicrousspeed, darklord, schwartz
+# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
+lonestarr = vespa, goodguy, schwartz
+
+# -----------------------------------------------------------------------------
+# Roles with assigned permissions
+#
+# Each line conforms to the format defined in the
+# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
+# -----------------------------------------------------------------------------
+[roles]
+# 'admin' role has all permissions, indicated by the wildcard '*'
+admin = *
+# The 'schwartz' role can do anything (*) with any lightsaber:
+schwartz = lightsaber:*
+# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
+# license plate 'eagle5' (instance specific id)
+goodguy = winnebago:drive:eagle5