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:36 UTC
[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.
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;
}