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/10/14 19:36:22 UTC

[19/20] shiro git commit: SHIRO-493 - Adding new methods and deprecating old to ShiroWebModule to support Guice 4

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 53c8946..d85823e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -110,6 +110,17 @@
 
     </properties>
 
+    <modules>
+        <module>core</module>
+        <module>web</module>
+        <module>support</module>
+        <module>samples</module>
+        <module>tools</module>
+        <module>all</module>
+        <module>integration-tests</module>
+        <module>test-coverage</module>
+    </modules>
+
     <mailingLists>
         <mailingList>
             <name>Apache Shiro Users Mailing List</name>
@@ -197,16 +208,6 @@
         </developer>
     </developers>
 
-    <modules>
-        <module>core</module>
-        <module>web</module>
-        <module>support</module>
-        <module>samples</module>
-        <module>tools</module>
-        <module>all</module>
-        <module>test-coverage</module>
-    </modules>
-
     <build>
         <pluginManagement>
             <plugins>
@@ -384,6 +385,25 @@
                         </excludes>
                     </configuration>
                 </plugin>
+                <plugin>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-maven-plugin</artifactId>
+                    <version>${jetty.version}</version>
+                    <configuration>
+                        <contextPath>/</contextPath>
+                        <httpConnector>
+                            <port>9080</port>
+                            <idleTimeout>60000</idleTimeout>
+                        </httpConnector>
+                        <requestLog implementation="org.eclipse.jetty.server.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>
         </pluginManagement>
         <plugins>
@@ -445,6 +465,13 @@
                                 <goal>prepare-agent</goal>
                             </goals>
                         </execution>
+                        <execution>
+                            <id>prepare-agent-integration</id>
+                            <phase>pre-integration-test</phase>
+                            <goals>
+                                <goal>prepare-agent-integration</goal>
+                            </goals>
+                        </execution>
                     </executions>
                 </plugin>
             <!-- Allow Groovy tests to run: -->
@@ -485,7 +512,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>2.3.1</version>
+                <version>3.0.2</version>
                 <configuration>
                     <archive>
                         <manifest>
@@ -1000,6 +1027,12 @@
                 <version>1.1.2</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>com.github.mjeanroy</groupId>
+                <artifactId>junit-servers-jetty</artifactId>
+                <version>0.4.2</version>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
     
@@ -1093,7 +1126,7 @@
             </plugin>
             <plugin>
                 <artifactId>maven-surefire-report-plugin</artifactId>
-                <version>2.5</version>
+                <version>2.19.1</version>
             </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
@@ -1143,8 +1176,9 @@
                 <artifactId>jacoco-maven-plugin</artifactId>
                 <reportSets>
                     <reportSet>
-                        <id>javadoc-aggregate</id>
+                        <id>jacoco-aggregate</id>
                         <reports>
+                            <report>report-integration</report>
                             <report>report</report>
                         </reports>
                     </reportSet>

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/samples/guice/pom.xml
----------------------------------------------------------------------
diff --git a/samples/guice/pom.xml b/samples/guice/pom.xml
index ce9d56c..e51c0e0 100644
--- a/samples/guice/pom.xml
+++ b/samples/guice/pom.xml
@@ -32,30 +32,9 @@
 
 	<build>
 	    <plugins>
-	        <plugin>
-	            <artifactId>maven-surefire-plugin</artifactId>
-	            <configuration>
-	                <forkMode>never</forkMode>
-	            </configuration>
-	        </plugin>
 			<plugin>
 				<groupId>org.eclipse.jetty</groupId>
 				<artifactId>jetty-maven-plugin</artifactId>
-				<version>${jetty.version}</version>
-				<configuration>
-					<contextPath>/</contextPath>
-					<httpConnector>
-						<port>9080</port>
-						<idleTimeout>60000</idleTimeout>
-					</httpConnector>
-					<requestLog implementation="org.eclipse.jetty.server.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>

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/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
index 01a0113..945ae70 100644
--- 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
@@ -31,6 +31,7 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
 import org.apache.shiro.web.mgt.WebSecurityManager;
 
 import javax.inject.Singleton;
+import javax.servlet.Filter;
 import javax.servlet.ServletContext;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -56,7 +57,8 @@ public class SampleShiroServletModule extends ShiroWebModule {
         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"));
+
+        this.addFilterChain("/remoting/**", filterConfig(AUTHC), filterConfig(ROLES, "b2bClient"), filterConfig(PERMS, "remote:invoke:lan,wan"));
     }
 
     @Provides

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/samples/jaxrs/pom.xml
----------------------------------------------------------------------
diff --git a/samples/jaxrs/pom.xml b/samples/jaxrs/pom.xml
index c2ef72f..ad3716e 100644
--- a/samples/jaxrs/pom.xml
+++ b/samples/jaxrs/pom.xml
@@ -62,12 +62,6 @@
         </pluginManagement>
         <plugins>
             <plugin>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <forkMode>never</forkMode>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-maven-plugin</artifactId>
             </plugin>

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/samples/servlet-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/samples/servlet-plugin/pom.xml b/samples/servlet-plugin/pom.xml
index 75c7cb6..c03d4f1 100644
--- a/samples/servlet-plugin/pom.xml
+++ b/samples/servlet-plugin/pom.xml
@@ -34,12 +34,6 @@
     <build>
         <plugins>
             <plugin>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <forkMode>never</forkMode>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-maven-plugin</artifactId>
                 <version>${jetty.version}</version>

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/samples/web/pom.xml
----------------------------------------------------------------------
diff --git a/samples/web/pom.xml b/samples/web/pom.xml
index 8b045fd..8d93a9a 100644
--- a/samples/web/pom.xml
+++ b/samples/web/pom.xml
@@ -40,12 +40,6 @@
     <build>
         <plugins>
             <plugin>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <forkMode>never</forkMode>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-maven-plugin</artifactId>
                 <version>${jetty.version}</version>

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/pom.xml
----------------------------------------------------------------------
diff --git a/support/guice/pom.xml b/support/guice/pom.xml
index 78a97f8..f91cb4e 100644
--- a/support/guice/pom.xml
+++ b/support/guice/pom.xml
@@ -31,10 +31,6 @@
     <name>Apache Shiro :: Support :: Guice</name>
     <packaging>bundle</packaging>
 
-    <properties>
-        <guice.version>3.0</guice.version>
-    </properties>
-
     <dependencies>
         <dependency>
             <groupId>org.apache.shiro</groupId>
@@ -68,6 +64,16 @@
             <artifactId>jcl-over-slf4j</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -89,7 +95,18 @@
                     </instructions>
                 </configuration>
             </plugin>
+            <plugin>
+                <!-- Package tests so we can re-run them with guice4 -->
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
-
 </project>

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java
----------------------------------------------------------------------
diff --git a/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java b/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java
index 00c40a4..2eac73d 100644
--- a/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java
+++ b/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java
@@ -18,20 +18,33 @@
  */
 package org.apache.shiro.guice;
 
+import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 import java.util.WeakHashMap;
 
 import javax.annotation.PreDestroy;
 
+import com.google.inject.Provider;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.name.Names;
+import com.google.inject.spi.InjectionListener;
+import com.google.inject.spi.TypeEncounter;
+import com.google.inject.spi.TypeListener;
 import org.apache.shiro.config.ConfigurationException;
 import org.apache.shiro.env.Environment;
+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.mgt.DefaultSecurityManager;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.realm.Realm;
 import org.apache.shiro.session.mgt.DefaultSessionManager;
 import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.util.ClassUtils;
 import org.apache.shiro.util.Destroyable;
 
 import com.google.inject.Key;
@@ -57,6 +70,9 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable {
         bindSessionManager(bind(SessionManager.class));
         bindEnvironment(bind(Environment.class));
         bindListener(BeanTypeListener.MATCHER, new BeanTypeListener());
+        bindEventBus(bind(EventBus.class));
+        bindListener(Matchers.any(), new SubscribedEventTypeListener());
+        bindListener(Matchers.any(), new EventBusAwareTypeListener());
         final DestroyableInjectionListener.DestroyableRegistry registry = new DestroyableInjectionListener.DestroyableRegistry() {
             public void add(Destroyable destroyable) {
                 ShiroModule.this.add(destroyable);
@@ -70,6 +86,7 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable {
         bindListener(LifecycleTypeListener.MATCHER, new LifecycleTypeListener(registry));
 
         expose(SecurityManager.class);
+        expose(EventBus.class);
 
         configureShiro();
         bind(realmCollectionKey())
@@ -153,6 +170,15 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable {
     }
 
     /**
+     * Binds the EventBus.  Override this method in order to provide your own {@link EventBus} binding.
+     * @param bind
+     * @since 1.4
+     */
+    protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) {
+        bind.to(DefaultEventBus.class).asEagerSingleton();
+    }
+
+    /**
      * Destroys all beans created within this module that implement {@link org.apache.shiro.util.Destroyable}.  Should be called when this
      * module will no longer be used.
      *
@@ -167,4 +193,39 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable {
     public void add(Destroyable destroyable) {
         this.destroyables.add(destroyable);
     }
+
+    private class SubscribedEventTypeListener implements TypeListener {
+        @Override
+        public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
+
+            final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class);
+
+            List<Method> methods = ClassUtils.getAnnotatedMethods(typeLiteral.getRawType(), Subscribe.class);
+            if (methods != null && !methods.isEmpty()) {
+                typeEncounter.register( new InjectionListener<I>() {
+                    @Override
+                    public void afterInjection(Object o) {
+                        eventBusProvider.get().register(o);
+                    }
+                });
+            }
+        }
+    }
+
+    private class EventBusAwareTypeListener implements TypeListener {
+        @Override
+        public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
+
+            final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class);
+
+            if (EventBusAware.class.isAssignableFrom(typeLiteral.getRawType())) {
+                typeEncounter.register( new InjectionListener<I>() {
+                    @Override
+                    public void afterInjection(Object o) {
+                        ((EventBusAware)o).setEventBus(eventBusProvider.get());
+                    }
+                });
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
----------------------------------------------------------------------
diff --git a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
index 45bc916..15431e5 100644
--- a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
+++ b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
@@ -18,10 +18,7 @@
  */
 package org.apache.shiro.guice.web;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.*;
 
 import javax.servlet.Filter;
 import javax.servlet.ServletContext;
@@ -31,6 +28,7 @@ import org.apache.shiro.env.Environment;
 import org.apache.shiro.guice.ShiroModule;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.util.StringUtils;
 import org.apache.shiro.web.env.WebEnvironment;
 import org.apache.shiro.web.filter.PathMatchingFilter;
 import org.apache.shiro.web.filter.authc.AnonymousFilter;
@@ -94,7 +92,7 @@ public abstract class ShiroWebModule extends ShiroModule {
      * We use a LinkedHashMap here to ensure that iterator order is the same as add order.  This is important, as the
      * FilterChainResolver uses iterator order when searching for a matching chain.
      */
-    private final Map<String, Key<? extends Filter>[]> filterChains = new LinkedHashMap<String, Key<? extends Filter>[]>();
+    private final Map<String, FilterConfig<? extends Filter>[]> filterChains = new LinkedHashMap<String, FilterConfig<? extends Filter>[]>();
     private final ServletContext servletContext;
 
     public ShiroWebModule(ServletContext servletContext) {
@@ -134,37 +132,65 @@ public abstract class ShiroWebModule extends ShiroModule {
 
         this.configureShiroWeb();
 
-        setupFilterChainConfigs();
-
-        bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(filterChains));
+        bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(setupFilterChainConfigs()));
     }
 
-    private void setupFilterChainConfigs() {
-        Map<Key<? extends PathMatchingFilter>, Map<String, String>> configs = new HashMap<Key<? extends PathMatchingFilter>, Map<String, String>>();
+    private Map<String, Key<? extends Filter>[]> setupFilterChainConfigs() {
+
+        // loop through and build a map of Filter Key -> Map<Path, Config>
+        Map<Key<? extends Filter>, Map<String, String>> filterToPathToConfig = new HashMap<Key<? extends Filter>, Map<String, String>>();
+
+        // At the same time build a map to return with Path -> Key[]
+        Map<String, Key<? extends Filter>[]> resultConfigMap = new HashMap<String, Key<? extends Filter>[]>();
+
+        for (Map.Entry<String, FilterConfig<? extends Filter>[]> filterChain : filterChains.entrySet()) {
+
+            String path = filterChain.getKey();
+
+            // collect the keys used for this path
+            List<Key<? extends Filter>> keysForPath = new ArrayList<Key<? extends Filter>>();
 
-        for (Map.Entry<String, Key<? extends Filter>[]> filterChain : filterChains.entrySet()) {
             for (int i = 0; i < filterChain.getValue().length; i++) {
-                Key<? extends Filter> key = filterChain.getValue()[i];
-                if (key instanceof FilterConfigKey) {
-                    FilterConfigKey<? extends PathMatchingFilter> configKey = (FilterConfigKey<? extends PathMatchingFilter>) key;
-                    key = configKey.getKey();
-                    filterChain.getValue()[i] = key;
-                    if (!PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
-                        throw new ConfigurationException("Config information requires a PathMatchingFilter - can't apply to " + key.getTypeLiteral().getRawType());
-                    }
-                    if (configs.get(castToPathMatching(key)) == null) configs.put(castToPathMatching(key), new HashMap<String, String>());
-                    configs.get(castToPathMatching(key)).put(filterChain.getKey(), configKey.getConfigValue());
-                } else if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
-	                  if (configs.get(castToPathMatching(key)) == null) configs.put(castToPathMatching(key), new HashMap<String, String>());
-                    configs.get(castToPathMatching(key)).put(filterChain.getKey(), "");
+                FilterConfig<? extends Filter> filterConfig = filterChain.getValue()[i];
+
+                Key<? extends Filter> key = filterConfig.getKey();
+                String config = filterConfig.getConfigValue();
+
+                // initialize key in filterToPathToConfig, if it doesn't exist
+                if (filterToPathToConfig.get(key) == null) {
+                    filterToPathToConfig.put((key), new HashMap<String, String>());
+                }
+                // now set the value
+                filterToPathToConfig.get(key).put(path, config);
+
+                // Config error if someone configured a non PathMatchingFilter with a config value
+                if (StringUtils.hasText(config) && !PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
+                    throw new ConfigurationException("Config information requires a PathMatchingFilter - can't apply to " + key.getTypeLiteral().getRawType());
                 }
+
+                // store the key in keysForPath
+                keysForPath.add(key);
             }
+
+            // map the current path to all of its Keys
+            resultConfigMap.put(path, keysForPath.toArray(new Key[keysForPath.size()]));
         }
-        for (Key<? extends PathMatchingFilter> filterKey : configs.keySet()) {
-            bindPathMatchingFilter(filterKey, configs.get(filterKey));
+
+        // now we find only the PathMatchingFilter and configure bindings
+        // non PathMatchingFilter, can be loaded with the default provider via the class name
+        for (Key<? extends Filter> key : filterToPathToConfig.keySet()) {
+            if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
+                bindPathMatchingFilter(castToPathMatching(key), filterToPathToConfig.get(key));
+            }
+            else {
+                bind(key);
+            }
         }
+
+        return resultConfigMap;
     }
 
+
     private <T extends PathMatchingFilter> void bindPathMatchingFilter(Key<T> filterKey, Map<String, String> configs) {
         bind(filterKey).toProvider(new PathMatchingFilterProvider<T>(filterKey, configs)).asEagerSingleton();
     }
@@ -218,6 +244,126 @@ public abstract class ShiroWebModule extends ShiroModule {
         bind.to(WebGuiceEnvironment.class).asEagerSingleton();
     }
 
+    protected final void addFilterChain(String pattern, Key<? extends Filter> key) {
+        // check for legacy API
+        if (key instanceof FilterConfigKey) {
+            addLegacyFilterChain(pattern, (FilterConfigKey) key);
+        }
+        else {
+            addFilterChain(pattern, new FilterConfig<Filter>((Key<Filter>) key, ""));
+        }
+    }
+
+    /**
+     * Maps 'n' number of <code>filterConfig</code>s to a specific path pattern.<BR/>
+     * For example, a path of '/my_private_resource/**' to 'filterConfig(AUTHC)' would require
+     * any resource under the path '/my_private_resource' would be processed through the {@link FormAuthenticationFilter}.
+     *
+     * @param pattern URL patter to be mapped to a FilterConfig, e.g. '/my_private-path/**'
+     * @param filterConfigs FilterConfiguration representing the Filter and config to be used when processing resources on <code>pattern</code>.
+     * @since 1.4
+     */
+    protected final void addFilterChain(String pattern, FilterConfig<? extends Filter>... filterConfigs) {
+        filterChains.put(pattern, filterConfigs);
+    }
+
+    /**
+     * Builds a FilterConfig from a Filer and configuration String
+     * @param baseKey The Key of the Filter class to be used.
+     * @param <T> A Servlet Filter class.
+     * @return A FilterConfig used to map a String path to this configuration.
+     * @since 1.4
+     */
+    protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey, String configValue) {
+        return new FilterConfig<T>(baseKey, configValue);
+    }
+
+    /**
+     * Builds a FilterConfig from a Filer and configuration String
+     * @param baseKey The Key of the Filter class to be used.
+     * @param <T> A Servlet Filter class.
+     * @return A FilterConfig used to map a String path to this configuration.
+     * @since 1.4
+     */
+    protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey) {
+        return filterConfig(baseKey, "");
+    }
+
+    /**
+     * Builds a FilterConfig from a Filer and configuration String
+     * @param typeLiteral The TyleLiteral of the filter key to be used.
+     * @param configValue the configuration used.
+     * @param <T> A Servlet Filter class.
+     * @return A FilterConfig used to map a String path to this configuration.
+     * @since 1.4
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected static <T extends Filter> FilterConfig<T> filterConfig(TypeLiteral<T> typeLiteral, String configValue) {
+        return filterConfig(Key.get(typeLiteral), configValue);
+    }
+
+    /**
+     * Builds a FilterConfig from a Filer and configuration String
+     * @param type The filter to be used.
+     * @param configValue the configuration used.
+     * @param <T> A Servlet Filter class.
+     * @return A FilterConfig used to map a String path to this configuration.
+     * @since 1.4
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected static <T extends Filter> FilterConfig<T> filterConfig(Class<T> type, String configValue) {
+        return filterConfig(Key.get(type), configValue);
+    }
+
+
+    /**
+     * Filter configuration which pairs a Filter class with its configuration used on a path.
+     * @param <T> The Servlet Filter class.
+     * @since 1.4
+     */
+    public static class FilterConfig<T extends Filter> {
+        private Key<T> key;
+        private String configValue;
+
+        private FilterConfig(Key<T> key, String configValue) {
+            super();
+            this.key = key;
+            this.configValue = configValue;
+        }
+
+        public Key<T> getKey() {
+            return key;
+        }
+
+        public String getConfigValue() {
+            return configValue;
+        }
+    }
+
+
+
+
+
+
+
+    // legacy methods
+
+
+    static boolean isGuiceVersion3() {
+        try {
+            Class.forName("com.google.inject.multibindings.MapKey");
+            return false;
+        } catch (ClassNotFoundException e) {
+            return true;
+        }
+    }
+
+    private void addLegacyFilterChain(String pattern, FilterConfigKey filterConfigKey) {
+
+        FilterConfig<Filter> filterConfig = new FilterConfig<Filter>(filterConfigKey.getKey(), filterConfigKey.getConfigValue());
+        addFilterChain(pattern, filterConfig);
+    }
+
     /**
      * Adds a filter chain to the shiro configuration.
      * <p/>
@@ -228,24 +374,52 @@ public abstract class ShiroWebModule extends ShiroModule {
      * @param keys
      */
     @SuppressWarnings({"UnusedDeclaration"})
+    @Deprecated
     protected final void addFilterChain(String pattern, Key<? extends Filter>... keys) {
-        filterChains.put(pattern, keys);
+
+        // We need to extract the keys and FilterConfigKey and convert to the new format.
+
+        FilterConfig[] filterConfigs = new FilterConfig[keys.length];
+        for (int ii = 0; ii < keys.length; ii++) {
+            Key<? extends Filter> key = keys[ii];
+            // If this is a path matching filter, we need to remember the config
+            if (key instanceof FilterConfigKey) {
+                // legacy config
+                FilterConfigKey legacyKey = (FilterConfigKey) key;
+                filterConfigs[ii] = new FilterConfig(legacyKey.getKey(), legacyKey.getConfigValue());
+            }
+            else {
+                // Some other type of Filter key, no config
+                filterConfigs[ii] = new FilterConfig(key, "");
+            }
+        }
+
+        filterChains.put(pattern, filterConfigs);
     }
 
+    @Deprecated
     protected static <T extends PathMatchingFilter> Key<T> config(Key<T> baseKey, String configValue) {
+
+        if( !isGuiceVersion3()) {
+            throw new ConfigurationException("Method ShiroWebModule.config(Key<? extends PathMatchingFilter>, String configValue), is not supported when using Guice 4+");
+        }
+
         return new FilterConfigKey<T>(baseKey, configValue);
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
+    @Deprecated
     protected static <T extends PathMatchingFilter> Key<T> config(TypeLiteral<T> typeLiteral, String configValue) {
         return config(Key.get(typeLiteral), configValue);
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
+    @Deprecated
     protected static <T extends PathMatchingFilter> Key<T> config(Class<T> type, String configValue) {
         return config(Key.get(type), configValue);
     }
 
+    @Deprecated
     private static class FilterConfigKey<T extends PathMatchingFilter> extends Key<T> {
         private Key<T> key;
         private String configValue;
@@ -264,4 +438,5 @@ public abstract class ShiroWebModule extends ShiroModule {
             return configValue;
         }
     }
+
 }

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java
----------------------------------------------------------------------
diff --git a/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java b/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java
index 4ff8b56..eee6230 100644
--- a/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java
+++ b/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java
@@ -44,4 +44,13 @@ class SimpleFilterChain implements FilterChain {
             originalChain.doFilter(request, response);
         }
     }
+
+    /**
+     * Exposed for testing, not part of public API.
+     * @return an Iterater of filters.
+     */
+    Iterator<? extends Filter> getFilters() {
+        return chain;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java
----------------------------------------------------------------------
diff --git a/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java b/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java
index 4507076..526f4f7 100644
--- a/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java
+++ b/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java
@@ -28,6 +28,10 @@ import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 import org.apache.shiro.env.Environment;
+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.mgt.DefaultSecurityManager;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.realm.Realm;
@@ -37,11 +41,13 @@ import org.apache.shiro.subject.Subject;
 import org.apache.shiro.util.Destroyable;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
 import java.util.Collection;
+import java.util.Map;
 
 import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
 
 public class ShiroModuleTest {
 
@@ -204,6 +210,82 @@ public class ShiroModuleTest {
         verify(myDestroyable);
     }
 
+    /**
+     * @since 1.4
+     * @throws Exception
+     */
+    @Test
+    public void testEventListener() throws Exception {
+
+        final MockRealm mockRealm = createMock(MockRealm.class);
+        final EventBus eventBus = createMock(EventBus.class);
+
+        // expect both objects to be registered
+        eventBus.register(anyObject(MockEventListener1.class));
+        eventBus.register(anyObject(MockEventListener2.class));
+        replay(eventBus);
+
+        final ShiroModule shiroModule = new ShiroModule() {
+            @Override
+            protected void configureShiro() {
+                bindRealm().to(MockRealm.class);
+
+                // bind our event listeners
+                binder().bind(MockEventListener1.class).asEagerSingleton();
+                binder().bind(MockEventListener2.class).asEagerSingleton();
+            }
+
+            @Override
+            protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) {
+                bind.toInstance(eventBus);
+            }
+
+            @Provides
+            public MockRealm createRealm() {
+                return mockRealm;
+            }
+
+        };
+        Guice.createInjector(shiroModule);
+
+        verify(eventBus);
+
+    }
+
+    /**
+     * @since 1.4
+     * @throws Exception
+     */
+    @Test
+    public void testEventBusAware() throws Exception {
+
+        final MockRealm mockRealm = createMock(MockRealm.class);
+
+        final ShiroModule shiroModule = new ShiroModule() {
+            @Override
+            protected void configureShiro() {
+                bindRealm().to(MockRealm.class);
+
+                binder().bind(MockEventBusAware.class).asEagerSingleton();
+                expose(MockEventBusAware.class);
+            }
+
+            @Provides
+            public MockRealm createRealm() {
+                return mockRealm;
+            }
+
+        };
+        Injector injector = Guice.createInjector(shiroModule);
+        EventBus eventBus = injector.getInstance(EventBus.class);
+        SecurityManager securityManager = injector.getInstance(SecurityManager.class);
+
+        MockEventBusAware eventBusAware = injector.getInstance(MockEventBusAware.class);
+
+        assertSame(eventBus, eventBusAware.eventBus);
+        assertSame(eventBus, ((DefaultSecurityManager)securityManager).getEventBus());
+    }
+
     public static interface MockRealm extends Realm {
 
     }
@@ -227,4 +309,27 @@ public class ShiroModuleTest {
 
     public static interface MyDestroyable extends Destroyable {
     }
+
+    public static class MockEventListener1 {
+        @Subscribe
+        public void listenToAllAndDoNothing(Object o) {}
+    }
+
+    public static class MockEventListener2 {
+        @Subscribe
+        public void listenToAllAndDoNothing(Object o) {}
+    }
+
+    public static class MockEventBusAware implements EventBusAware {
+        private EventBus eventBus;
+
+        public EventBus getEventBus() {
+            return eventBus;
+        }
+
+        @Override
+        public void setEventBus(EventBus eventBus) {
+            this.eventBus = eventBus;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
----------------------------------------------------------------------
diff --git a/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java b/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
index 98add89..c1b3c32 100644
--- a/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
+++ b/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
@@ -45,7 +45,8 @@ public class FilterConfigTest {
                 bindRealm().to(ShiroModuleTest.MockRealm.class);
 
                 addFilterChain("/index.html", AUTHC_BASIC);
-                addFilterChain("/index2.html", config(PERMS, "permission"));
+//                addFilterChain("/index2.html", config(PERMS, "permission"));
+                addFilterChain("/index2.html", filterConfig(PERMS, "permission"));
             }
 
             @Provides

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
----------------------------------------------------------------------
diff --git a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
index a3a3f76..ff50d0f 100644
--- a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
+++ b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
@@ -21,6 +21,7 @@ package org.apache.shiro.guice.web;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
+import com.google.inject.Key;
 import com.google.inject.Provides;
 import com.google.inject.binder.AnnotatedBindingBuilder;
 import org.apache.shiro.guice.ShiroModuleTest;
@@ -28,21 +29,38 @@ import org.apache.shiro.env.Environment;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.realm.Realm;
 import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.web.env.EnvironmentLoader;
 import org.apache.shiro.web.env.WebEnvironment;
+import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
+import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
+import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
 import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
 import org.apache.shiro.web.mgt.WebSecurityManager;
 import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
 import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
+import org.easymock.EasyMock;
+import org.junit.Assume;
 import org.junit.Test;
 
 import javax.inject.Named;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
 import java.util.Collection;
+import java.util.Iterator;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
 
-import static org.easymock.EasyMock.createMock;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 public class ShiroWebModuleTest {
 
@@ -146,6 +164,162 @@ public class ShiroWebModuleTest {
         assertTrue( environment == webEnvironment );
     }
 
+    /**
+     * @since 1.4
+     */
+    @Test
+    public void testAddFilterChainGuice3and4() {
+
+        final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+
+        servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
+        expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
+        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_custom_filter");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc_basic");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_perms");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/multiple_configs");
+        replay(servletContext, request);
+
+        Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {
+            @Override
+            protected void configureShiroWeb() {
+                bindRealm().to(ShiroModuleTest.MockRealm.class);
+                expose(FilterChainResolver.class);
+                this.addFilterChain("/test_authc/**", filterConfig(AUTHC));
+                this.addFilterChain("/test_custom_filter/**", Key.get(CustomFilter.class));
+                this.addFilterChain("/test_authc_basic/**", AUTHC_BASIC);
+                this.addFilterChain("/test_perms/**", filterConfig(PERMS, "remote:invoke:lan,wan"));
+                this.addFilterChain("/multiple_configs/**", filterConfig(AUTHC), filterConfig(ROLES, "b2bClient"), filterConfig(PERMS, "remote:invoke:lan,wan"));
+            }
+
+            @Provides
+            public ShiroModuleTest.MockRealm createRealm() {
+                return mockRealm;
+            }
+        });
+
+        FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class);
+        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
+        SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver;
+
+        // test the /test_authc resource
+        FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class));
+
+        // test the /test_custom_filter resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(CustomFilter.class));
+
+        // test the /test_authc_basic resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(BasicHttpAuthenticationFilter.class));
+
+        // test the /test_perms resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(PermissionsAuthorizationFilter.class));
+
+        // test the /multiple_configs resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(RolesAuthorizationFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(PermissionsAuthorizationFilter.class));
+
+        verify(servletContext, request);
+    }
+
+    /**
+     * @since 1.4
+     */
+    @Test
+    public void testAddFilterChainGuice3Only() {
+
+        Assume.assumeTrue("This test only runs agains Guice 3.x", ShiroWebModule.isGuiceVersion3());
+
+        final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+
+        servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
+        expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
+        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_custom_filter");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_perms");
+        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/multiple_configs");
+        replay(servletContext, request);
+
+        Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {
+            @Override
+            protected void configureShiroWeb() {
+                bindRealm().to(ShiroModuleTest.MockRealm.class);
+                expose(FilterChainResolver.class);
+                this.addFilterChain("/test_authc/**", AUTHC);
+                this.addFilterChain("/test_custom_filter/**", Key.get(CustomFilter.class));
+                this.addFilterChain("/test_perms/**", config(PERMS, "remote:invoke:lan,wan"));
+                this.addFilterChain("/multiple_configs/**", AUTHC, config(ROLES, "b2bClient"), config(PERMS, "remote:invoke:lan,wan"));
+            }
+
+            @Provides
+            public ShiroModuleTest.MockRealm createRealm() {
+                return mockRealm;
+            }
+        });
+
+        FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class);
+        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
+        SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver;
+
+        // test the /test_authc resource
+        FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class));
+
+        // test the /test_custom_filter resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(CustomFilter.class));
+
+        // test the /test_perms resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(PermissionsAuthorizationFilter.class));
+
+        // test the /multiple_configs resource
+        filterChain = simpleFilterChainResolver.getChain(request, null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(RolesAuthorizationFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(PermissionsAuthorizationFilter.class));
+
+        verify(servletContext, request);
+    }
+
+    private Filter getNextFilter(SimpleFilterChain filterChain) {
+
+        Iterator<? extends Filter> filters = filterChain.getFilters();
+        if (filters.hasNext()) {
+            return filters.next();
+        }
+
+        return null;
+    }
+
     public static class MyDefaultWebSecurityManager extends DefaultWebSecurityManager {
         @Inject
         public MyDefaultWebSecurityManager(Collection<Realm> realms) {
@@ -162,4 +336,16 @@ public class ShiroWebModuleTest {
             super(filterChainResolver, servletContext, securityManager);
         }
     }
+
+    public static class CustomFilter implements Filter {
+
+        @Override
+        public void init(FilterConfig filterConfig) throws ServletException {}
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {}
+
+        @Override
+        public void destroy() {}
+    }
 }

http://git-wip-us.apache.org/repos/asf/shiro/blob/a63e1d85/support/guice/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/support/guice/src/test/resources/log4j.properties b/support/guice/src/test/resources/log4j.properties
new file mode 100644
index 0000000..bbb023d
--- /dev/null
+++ b/support/guice/src/test/resources/log4j.properties
@@ -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.
+#
+
+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 %p [%c] - %m %n
+
+# Pattern to output: date priority [category] - message
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
+
+# General Apache libraries is WARN
+log4j.logger.org.apache=WARN
+
+log4j.logger.org.apache.shiro=TRACE
+log4j.logger.org.apache.shiro.util.ThreadContext=WARN
\ No newline at end of file