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 2017/04/12 14:24:53 UTC
shiro git commit: SHIRO-608: use a ServiceLoader to discover
WebEnvironments
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 <bd...@apache.org>
Authored: Fri Dec 16 11:21:24 2016 -0800
Committer: Brian Demers <bd...@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();
+ }
+}