shiro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bdem...@apache.org
Subject shiro git commit: SHIRO-608: use a ServiceLoader to discover WebEnvironments
Date Wed, 12 Apr 2017 14:24:53 GMT
Repository: shiro
Updated Branches:
  refs/heads/master ea7eb0029 -> 09ebb5ca7


SHIRO-608: use a ServiceLoader to discover WebEnvironments

Added test for EnvironmentLoader when using a ServiceLoader

Fixes: SHIRO-608, #53


Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/09ebb5ca
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/09ebb5ca
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/09ebb5ca

Branch: refs/heads/master
Commit: 09ebb5ca7f81525cb7da67f2fea29648dc9e3fbd
Parents: ea7eb00
Author: Brian Demers <bdemers@apache.org>
Authored: Fri Dec 16 11:21:24 2016 -0800
Committer: Brian Demers <bdemers@apache.org>
Committed: Wed Apr 12 10:23:11 2017 -0400

----------------------------------------------------------------------
 .../apache/shiro/web/env/EnvironmentLoader.java | 109 ++++++++++++++--
 .../web/env/EnvironmentLoaderServiceTest.java   | 129 +++++++++++++++++++
 .../shiro/web/env/WebEnvironmentStub.java       |  70 ++++++++++
 3 files changed, 299 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/shiro/blob/09ebb5ca/web/src/main/java/org/apache/shiro/web/env/EnvironmentLoader.java
----------------------------------------------------------------------
diff --git a/web/src/main/java/org/apache/shiro/web/env/EnvironmentLoader.java b/web/src/main/java/org/apache/shiro/web/env/EnvironmentLoader.java
index 1835e43..4d334e7 100644
--- a/web/src/main/java/org/apache/shiro/web/env/EnvironmentLoader.java
+++ b/web/src/main/java/org/apache/shiro/web/env/EnvironmentLoader.java
@@ -28,6 +28,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.servlet.ServletContext;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ServiceLoader;
+
 
 /**
  * An {@code EnvironmentLoader} is responsible for loading a web application's Shiro {@link
WebEnvironment}
@@ -162,19 +167,105 @@ public class EnvironmentLoader {
      * @return the WebEnvironment implementation class to use
      * @see #ENVIRONMENT_CLASS_PARAM
      * @see IniWebEnvironment
+     * @see #determineWebEnvironment(ServletContext)
+     * @see #getDefaultWebEnvironmentClass()
+     * @deprecated This method is not longer used by Shiro, and will be removed in future
versions,
+     * use {@link #determineWebEnvironment(ServletContext)} or {@link #determineWebEnvironment(ServletContext)}
      */
+    @Deprecated
     protected Class<?> determineWebEnvironmentClass(ServletContext servletContext)
{
+        Class<? extends WebEnvironment> webEnvironmentClass = webEnvironmentClassFromServletContext(servletContext);
+        if( webEnvironmentClass != null) {
+            return webEnvironmentClass;
+        } else {
+
+            return getDefaultWebEnvironmentClass();
+        }
+    }
+
+    private Class<? extends WebEnvironment> webEnvironmentClassFromServletContext(ServletContext
servletContext) {
+
+        Class<? extends WebEnvironment> webEnvironmentClass = null;
         String className = servletContext.getInitParameter(ENVIRONMENT_CLASS_PARAM);
         if (className != null) {
             try {
-                return ClassUtils.forName(className);
+                webEnvironmentClass = ClassUtils.forName(className);
             } catch (UnknownClassException ex) {
                 throw new ConfigurationException(
                         "Failed to load custom WebEnvironment class [" + className + "]",
ex);
             }
-        } else {
-            return IniWebEnvironment.class;
         }
+        return webEnvironmentClass;
+    }
+
+    private WebEnvironment webEnvironmentFromServiceLoader() {
+
+        WebEnvironment webEnvironment = null;
+        // try to load WebEnvironment as a service
+        ServiceLoader<WebEnvironment> serviceLoader = ServiceLoader.load(WebEnvironment.class);
+        Iterator<WebEnvironment> iterator = serviceLoader.iterator();
+
+        // Use the first one
+        if (iterator.hasNext()) {
+            webEnvironment = iterator.next();
+        }
+        // if there are others, throw an error
+        if (iterator.hasNext()) {
+            List<String> allWebEnvironments = new ArrayList<String>();
+            allWebEnvironments.add(webEnvironment.getClass().getName());
+            while (iterator.hasNext()) {
+                allWebEnvironments.add(iterator.next().getClass().getName());
+            }
+            throw new ConfigurationException("ServiceLoader for class [" + WebEnvironment.class
+ "] returned more then one " +
+                    "result.  ServiceLoader must return zero or exactly one result for this
class. Select one using the " +
+                    "servlet init parameter '"+ ENVIRONMENT_CLASS_PARAM +"'. Found: " + allWebEnvironments);
+        }
+        return webEnvironment;
+    }
+
+    /**
+     * Returns the default WebEnvironment class, which is unless overridden: {@link IniWebEnvironment}.
+     * @return the default WebEnvironment class.
+     */
+    protected Class<? extends WebEnvironment> getDefaultWebEnvironmentClass() {
+        return IniWebEnvironment.class;
+    }
+
+    /**
+     * Return the WebEnvironment implementation class to use, based on the order of:
+     * <ul>
+     *     <li>A custom WebEnvironment class - specified in the {@code servletContext}
{@link #ENVIRONMENT_ATTRIBUTE_KEY} property</li>
+     *     <li>{@code ServiceLoader.load(WebEnvironment.class)} - (if more then one
instance is found a {@link ConfigurationException} will be thrown</li>
+     *     <li>A call to {@link #getDefaultWebEnvironmentClass()} (default: {@link
IniWebEnvironment})</li>
+     * </ul>
+     *
+     * @param servletContext current servlet context
+     * @return the WebEnvironment implementation class to use
+     * @see #ENVIRONMENT_CLASS_PARAM
+     * @param servletContext the {@code servletContext} to query the {@code ENVIRONMENT_ATTRIBUTE_KEY}
property from
+     * @return the {@code WebEnvironment} to be used
+     */
+    protected WebEnvironment determineWebEnvironment(ServletContext servletContext) {
+
+        Class<? extends WebEnvironment> webEnvironmentClass = webEnvironmentClassFromServletContext(servletContext);
+        WebEnvironment webEnvironment = null;
+
+        // try service loader next
+        if (webEnvironmentClass == null) {
+            webEnvironment = webEnvironmentFromServiceLoader();
+        }
+
+        // if webEnvironment is not set, and ENVIRONMENT_CLASS_PARAM prop was not set, use
the default
+        if (webEnvironmentClass == null && webEnvironment == null) {
+            webEnvironmentClass = getDefaultWebEnvironmentClass();
+        }
+
+        // at this point, we anything is set for the webEnvironmentClass, load it.
+        if (webEnvironmentClass != null) {
+            webEnvironment = (WebEnvironment) ClassUtils.newInstance(webEnvironmentClass);
+        }
+
+        return webEnvironment;
     }
 
     /**
@@ -193,23 +284,23 @@ public class EnvironmentLoader {
      */
     protected WebEnvironment createEnvironment(ServletContext sc) {
 
-        Class<?> clazz = determineWebEnvironmentClass(sc);
-        if (!MutableWebEnvironment.class.isAssignableFrom(clazz)) {
-            throw new ConfigurationException("Custom WebEnvironment class [" + clazz.getName()
+
+        WebEnvironment webEnvironment = determineWebEnvironment(sc);
+        if (!MutableWebEnvironment.class.isInstance(webEnvironment)) {
+            throw new ConfigurationException("Custom WebEnvironment class [" + webEnvironment.getClass().getName()
+
                     "] is not of required type [" + MutableWebEnvironment.class.getName()
+ "]");
         }
 
         String configLocations = sc.getInitParameter(CONFIG_LOCATIONS_PARAM);
         boolean configSpecified = StringUtils.hasText(configLocations);
 
-        if (configSpecified && !(ResourceConfigurable.class.isAssignableFrom(clazz)))
{
-            String msg = "WebEnvironment class [" + clazz.getName() + "] does not implement
the " +
+        if (configSpecified && !(ResourceConfigurable.class.isInstance(webEnvironment)))
{
+            String msg = "WebEnvironment class [" + webEnvironment.getClass().getName() +
"] does not implement the " +
                     ResourceConfigurable.class.getName() + "interface.  This is required
to accept any " +
                     "configured " + CONFIG_LOCATIONS_PARAM + "value(s).";
             throw new ConfigurationException(msg);
         }
 
-        MutableWebEnvironment environment = (MutableWebEnvironment) ClassUtils.newInstance(clazz);
+        MutableWebEnvironment environment = (MutableWebEnvironment) webEnvironment;
 
         environment.setServletContext(sc);
 

http://git-wip-us.apache.org/repos/asf/shiro/blob/09ebb5ca/web/src/test/java/org/apache/shiro/web/env/EnvironmentLoaderServiceTest.java
----------------------------------------------------------------------
diff --git a/web/src/test/java/org/apache/shiro/web/env/EnvironmentLoaderServiceTest.java
b/web/src/test/java/org/apache/shiro/web/env/EnvironmentLoaderServiceTest.java
new file mode 100644
index 0000000..1c1f975
--- /dev/null
+++ b/web/src/test/java/org/apache/shiro/web/env/EnvironmentLoaderServiceTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.web.env;
+
+import org.apache.shiro.config.ConfigurationException;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import javax.servlet.ServletContext;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import static org.easymock.EasyMock.expect;
+import static org.hamcrest.Matchers.*;
+import static org.hamcrest.MatcherAssert.*;
+
+/**
+ * Tests for {@link EnvironmentLoader} that depend on PowerMock the stub out a ServiceLoader.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(EnvironmentLoader.class)
+public class EnvironmentLoaderServiceTest {
+
+    @Test()
+    public void singleServiceTest() throws Exception {
+
+        List<WebEnvironmentStub> environmentList = Arrays.asList(new WebEnvironmentStub());
+
+        ServletContext servletContext = EasyMock.mock(ServletContext.class);
+        expect(servletContext.getInitParameter("shiroEnvironmentClass")).andReturn(null);
+        expect(servletContext.getInitParameter("shiroConfigLocations")).andReturn(null);
+
+        PowerMock.mockStaticPartialStrict(ServiceLoader.class, "load");
+
+        final ServiceLoader serviceLoader = PowerMock.createMock(ServiceLoader.class);
+
+        EasyMock.expect(ServiceLoader.load(WebEnvironment.class)).andReturn(serviceLoader);
+        EasyMock.expect(serviceLoader.iterator()).andReturn(environmentList.iterator());
+
+        EasyMock.replay(servletContext);
+        PowerMock.replayAll();
+
+        WebEnvironment resultEnvironment = new EnvironmentLoader().createEnvironment(servletContext);
+
+        PowerMock.verifyAll();
+        EasyMock.verify(servletContext);
+
+        assertThat(resultEnvironment, instanceOf(WebEnvironmentStub.class));
+        WebEnvironmentStub environmentStub = (WebEnvironmentStub) resultEnvironment;
+
+        assertThat(environmentStub.getServletContext(), sameInstance(servletContext));
+    }
+
+    @Test()
+    public void multipleServiceTest() throws Exception {
+
+        List<WebEnvironmentStub> environmentList = Arrays.asList(new WebEnvironmentStub(),
new WebEnvironmentStub());
+
+        ServletContext servletContext = EasyMock.mock(ServletContext.class);
+        expect(servletContext.getInitParameter("shiroEnvironmentClass")).andReturn(null);
+
+        PowerMock.mockStaticPartialStrict(ServiceLoader.class, "load");
+
+        final ServiceLoader serviceLoader = PowerMock.createMock(ServiceLoader.class);
+
+        EasyMock.expect(ServiceLoader.load(WebEnvironment.class)).andReturn(serviceLoader);
+        EasyMock.expect(serviceLoader.iterator()).andReturn(environmentList.iterator());
+
+        EasyMock.replay(servletContext);
+        PowerMock.replayAll();
+
+        try {
+            new EnvironmentLoader().createEnvironment(servletContext);
+            Assert.fail("Expected ConfigurationException to be thrown");
+        }
+        catch (ConfigurationException e) {
+            assertThat(e.getMessage(), stringContainsInOrder("zero or exactly one", "shiroEnvironmentClass"));
+        }
+
+        PowerMock.verifyAll();
+        EasyMock.verify(servletContext);
+    }
+
+    @Test()
+    public void loadFromInitParamTest() throws Exception {
+
+        ServletContext servletContext = EasyMock.mock(ServletContext.class);
+        expect(servletContext.getInitParameter("shiroEnvironmentClass")).andReturn(WebEnvironmentStub.class.getName());
+        expect(servletContext.getInitParameter("shiroConfigLocations")).andReturn(null);
+
+        PowerMock.mockStaticPartialStrict(ServiceLoader.class, "load");
+
+        EasyMock.replay(servletContext);
+        PowerMock.replayAll();
+
+        WebEnvironment resultEnvironment = new EnvironmentLoader().createEnvironment(servletContext);
+
+        PowerMock.verifyAll();
+        EasyMock.verify(servletContext);
+
+        assertThat(resultEnvironment, instanceOf(WebEnvironmentStub.class));
+        WebEnvironmentStub environmentStub = (WebEnvironmentStub) resultEnvironment;
+
+        assertThat(environmentStub.getServletContext(), sameInstance(servletContext));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/shiro/blob/09ebb5ca/web/src/test/java/org/apache/shiro/web/env/WebEnvironmentStub.java
----------------------------------------------------------------------
diff --git a/web/src/test/java/org/apache/shiro/web/env/WebEnvironmentStub.java b/web/src/test/java/org/apache/shiro/web/env/WebEnvironmentStub.java
new file mode 100644
index 0000000..ea050a0
--- /dev/null
+++ b/web/src/test/java/org/apache/shiro/web/env/WebEnvironmentStub.java
@@ -0,0 +1,70 @@
+/*
+ * 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.web.env;
+
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.web.filter.mgt.FilterChainResolver;
+import org.apache.shiro.web.mgt.WebSecurityManager;
+
+import javax.servlet.ServletContext;
+
+public class WebEnvironmentStub implements WebEnvironment, MutableWebEnvironment {
+
+    private FilterChainResolver filterChainResolver;
+
+    private WebSecurityManager webSecurityManager;
+
+    private ServletContext servletContext;
+
+
+    @Override
+    public FilterChainResolver getFilterChainResolver() {
+        return filterChainResolver;
+    }
+
+    @Override
+    public void setFilterChainResolver(FilterChainResolver filterChainResolver) {
+        this.filterChainResolver = filterChainResolver;
+    }
+
+    @Override
+    public ServletContext getServletContext() {
+        return servletContext;
+    }
+
+    @Override
+    public void setServletContext(ServletContext servletContext) {
+        this.servletContext = servletContext;
+    }
+
+    @Override
+    public WebSecurityManager getWebSecurityManager() {
+        return webSecurityManager;
+    }
+
+    @Override
+    public void setWebSecurityManager(WebSecurityManager webSecurityManager) {
+        this.webSecurityManager = webSecurityManager;
+    }
+
+    @Override
+    public SecurityManager getSecurityManager() {
+        return getWebSecurityManager();
+    }
+}


Mime
View raw message