@@ -239,7 +242,7 @@
Person Manager
CRUD
-
Execute & Wait
+
Execute & Wait
Token
Model Driven
diff --git a/apps/showcase/src/main/webapp/WEB-INF/wait/complete.jsp b/apps/showcase/src/main/webapp/WEB-INF/wait/complete.jsp
index 74c97c74c..47cbb0674 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/wait/complete.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/wait/complete.jsp
@@ -1,19 +1,19 @@
+<%@ taglib prefix="s" uri="/struts-tags" %>
+
+
+
Struts2 Showcase - Execute and Wait Examples
+
+
+
+
+
+
+
+
+
+
+ These examples illustrate Struts build in support for execute and wait.
+
+
+ When you have a process that takes a long time your users can be impatient and starts to submit/click
+ again.
+
A good solution is to show the user a progress page (wait page) while the process takes it time.
+
+
+
+
Example 1 (no delay)
+
Example 2 (with delay)
+
Example 3 (with longer check delay)
+
+
+
+
+
diff --git a/apps/showcase/src/main/webapp/WEB-INF/wait/wait.jsp b/apps/showcase/src/main/webapp/WEB-INF/wait/wait.jsp
index e4b467968..57f169d87 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/wait/wait.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/wait/wait.jsp
@@ -1,19 +1,19 @@
-
-
-
Struts2 Showcase - Execute and Wait Examples
-
-
-
-
-
-
-
-
-
-
-
-
- These examples illustrate Struts build in support for execute and wait.
-
-
- When you have a process that takes a long time your users can be impatient and starts to submit/click again.
-
A good solution is to show the user a progress page (wait page) while the process takes it time.
-
-
-
-
Example 1 (no delay)
-
Example 2 (with delay)
-
Example 3 (with longer check delay)
-
-
-
-
-
diff --git a/apps/showcase/src/test/java/it/org/apache/struts2/showcase/DispatcherResultTest.java b/apps/showcase/src/test/java/it/org/apache/struts2/showcase/DispatcherResultTest.java
index 6564270c2..5907efdb4 100644
--- a/apps/showcase/src/test/java/it/org/apache/struts2/showcase/DispatcherResultTest.java
+++ b/apps/showcase/src/test/java/it/org/apache/struts2/showcase/DispatcherResultTest.java
@@ -32,7 +32,6 @@ public class DispatcherResultTest {
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/dispatcher/dispatch.action");
DomElement div = page.getElementById("dispatcher-result");
-
Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
}
}
@@ -40,16 +39,10 @@ public class DispatcherResultTest {
@Test
public void testDispatchingToAction() throws Exception {
try (final WebClient webClient = new WebClient()) {
- webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/dispatcher/forward.action");
- //DomElement div = page.getElementById("dispatcher-result");
- //Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
- // support for forwarding to another action is broken on StrutsPrepareFilter/StrutsExecuteFilter
- // it only works in StrutsPrepareAndExecuteFilter
- // this will be fixed in Struts 6.1.x
-
- Assert.assertEquals(404, page.getWebResponse().getStatusCode());
+ DomElement div = page.getElementById("dispatcher-result");
+ Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
}
}
diff --git a/core/src/test/java/org/apache/struts2/dispatcher/ServletDispatchedTestAssertInterceptor.java b/apps/showcase/src/test/java/it/org/apache/struts2/showcase/ForwardTest.java
similarity index 50%
rename from core/src/test/java/org/apache/struts2/dispatcher/ServletDispatchedTestAssertInterceptor.java
rename to apps/showcase/src/test/java/it/org/apache/struts2/showcase/ForwardTest.java
index d3a24d281..b466010fd 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/ServletDispatchedTestAssertInterceptor.java
+++ b/apps/showcase/src/test/java/it/org/apache/struts2/showcase/ForwardTest.java
@@ -1,4 +1,6 @@
/*
+ * $Id$
+ *
* 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
@@ -16,41 +18,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.struts2.dispatcher;
+package it.org.apache.struts2.showcase;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.DomElement;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.Assert;
+import org.junit.Test;
-import org.apache.struts2.TestAction;
+public class ForwardTest {
-import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.interceptor.Interceptor;
+ @Test
+ public void testServletForwardingToAction() throws Exception {
+ try (final WebClient webClient = new WebClient()) {
+ // Struts excluded URL, as defined by struts.action.excludePattern
+ final HtmlPage page = webClient.getPage(ParameterUtils.getBaseUrl() + "/testServlet/forward");
-
-/**
- */
-public class ServletDispatchedTestAssertInterceptor implements Interceptor {
-
- private static final long serialVersionUID = 1980347231443329805L;
-
- public ServletDispatchedTestAssertInterceptor() {
- super();
- }
-
- public void destroy() {
- }
-
- public void init() {
- }
-
- public String intercept(ActionInvocation invocation) throws Exception {
- Assert.assertTrue(invocation.getAction() instanceof TestAction);
-
- TestAction testAction = (TestAction) invocation.getAction();
-
- Assert.assertEquals("bar", testAction.getFoo());
-
- String result = invocation.invoke();
-
- return result;
+ DomElement div = page.getElementById("dispatcher-result");
+ Assert.assertEquals("This page is a result of \"dispatching\" to it from an action", div.asNormalizedText());
+ }
}
}
diff --git a/assembly/README.md b/assembly/README.md
new file mode 100644
index 000000000..2bcd93d08
--- /dev/null
+++ b/assembly/README.md
@@ -0,0 +1,3 @@
+# Struts 2 Assemblies
+This module is used to prepare ZIP archives with different set of JARs, like code source, Javadocs, etc.
+It's a part of the release process, it shouldn't be used directly by users.
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 6773c74bd..6211d0a69 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -24,7 +24,7 @@
org.apache.struts
struts2-parent
- 6.1.0-SNAPSHOT
+ 6.2.0-SNAPSHOT
struts2-assembly
diff --git a/assembly/src/main/assembly/min-lib.xml b/assembly/src/main/assembly/min-lib.xml
index 3e6e4a176..3cae96356 100644
--- a/assembly/src/main/assembly/min-lib.xml
+++ b/assembly/src/main/assembly/min-lib.xml
@@ -36,6 +36,7 @@
org.apache.struts:struts2-core
org.freemarker:freemarker
org.apache.commons:commons-lang3
+
org.apache.commons:commons-text
org.apache.logging.log4j:log4j-api
ognl:ognl
commons-fileupload:commons-fileupload
diff --git a/bom/README.md b/bom/README.md
new file mode 100644
index 000000000..02bacfe8d
--- /dev/null
+++ b/bom/README.md
@@ -0,0 +1,20 @@
+# Struts 2 BOM
+This is a Bill-Of-Materials to be used with Maven based project. It allows to import all the Struts 2
+dependencies at once and used them in your project where needed.
+
+## Installation
+You must add a proper import statement into your `pom.xml` as presented below:
+
+```xml
+
+
+
+ org.apache.struts
+ struts2-bom
+ ${struts2.version}
+ pom
+ import
+
+
+
+```
diff --git a/bom/pom.xml b/bom/pom.xml
index e25ca89dd..289e83b02 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -29,7 +29,7 @@
struts2-bom
-
6.1.0-SNAPSHOT
+
6.2.0-SNAPSHOT
pom
Struts 2 Bill of Materials
@@ -44,7 +44,7 @@
- 6.1.0-SNAPSHOT
+ 6.2.0-SNAPSHOT
true
true
@@ -181,6 +181,11 @@
struts2-velocity-plugin
${struts-version.version}
+
+ org.apache.struts
+ struts2-xslt-plugin
+ ${struts-version.version}
+
diff --git a/bundles/admin/pom.xml b/bundles/admin/pom.xml
index 395dcb80f..53b96ee2e 100644
--- a/bundles/admin/pom.xml
+++ b/bundles/admin/pom.xml
@@ -24,7 +24,7 @@
org.apache.struts
struts2-osgi-bundles
- 6.1.0-SNAPSHOT
+ 6.2.0-SNAPSHOT
struts2-osgi-admin-bundle
diff --git a/bundles/admin/src/main/resources/struts.xml b/bundles/admin/src/main/resources/struts.xml
index e106e8bdf..2bb1bf134 100644
--- a/bundles/admin/src/main/resources/struts.xml
+++ b/bundles/admin/src/main/resources/struts.xml
@@ -22,13 +22,13 @@
+ "https://struts.apache.org/dtds/struts-2.5.dtd">
-
+
{1}
viewBundle.ftl
diff --git a/bundles/demo/pom.xml b/bundles/demo/pom.xml
index 7258e0dd7..f82c46bb4 100644
--- a/bundles/demo/pom.xml
+++ b/bundles/demo/pom.xml
@@ -24,7 +24,7 @@
org.apache.struts
struts2-osgi-bundles
- 6.1.0-SNAPSHOT
+ 6.2.0-SNAPSHOT
struts2-osgi-demo-bundle
diff --git a/bundles/demo/src/main/resources/struts.xml b/bundles/demo/src/main/resources/struts.xml
index 0e4d78967..003635f16 100644
--- a/bundles/demo/src/main/resources/struts.xml
+++ b/bundles/demo/src/main/resources/struts.xml
@@ -21,11 +21,11 @@
-->
+ "https://struts.apache.org/dtds/struts-2.5.dtd">
-
@@ -52,4 +52,4 @@
-
\ No newline at end of file
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index af4fd2d53..b68c3e997 100755
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -24,7 +24,7 @@
org.apache.struts
struts2-parent
- 6.1.0-SNAPSHOT
+ 6.2.0-SNAPSHOT
struts2-osgi-bundles
diff --git a/core/README.md b/core/README.md
new file mode 100644
index 000000000..0d10dcbc9
--- /dev/null
+++ b/core/README.md
@@ -0,0 +1,6 @@
+# Struts 2 Core
+This is a core of the Apache Struts framework and all other modules depend on it.
+It requires Java 8 at minimum and a Servlet container supporting Java Servlet API 3.1 at least.
+
+## Installation
+Just drop this plugin into `WEB-INF/lib` folder or add it as Maven dependency
diff --git a/core/pom.xml b/core/pom.xml
index d7cee0c16..07eb192bc 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -24,7 +24,7 @@
org.apache.struts
struts2-parent
- 6.1.0-SNAPSHOT
+ 6.2.0-SNAPSHOT
struts2-core
jar
@@ -41,6 +41,7 @@
org.apache.maven.plugins
maven-surefire-plugin
+ @{argLine}
maven.testng.output.dir
@@ -54,9 +55,6 @@
${project.build.testOutputDirectory}/xwork - jar.jar
${project.build.testOutputDirectory}/xwork - zip.zip
-
- **/*Test.java
-
**/XWorkTestCase.java
**/TestBean.java
@@ -339,8 +337,7 @@
org.testng
testng
- compile
- true
+ test
diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
index ea2076d4a..772e0adb0 100644
--- a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
+++ b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java
@@ -24,6 +24,7 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.interceptor.ConditionalInterceptor;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.interceptor.WithLazyParams;
@@ -73,7 +74,7 @@ public class DefaultActionInvocation implements ActionInvocation {
protected UnknownHandlerManager unknownHandlerManager;
protected OgnlUtil ognlUtil;
protected AsyncManager asyncManager;
- protected Callable asyncAction;
+ protected Callable> asyncAction;
protected WithLazyParams.LazyParamInjector lazyParamInjector;
public DefaultActionInvocation(final Map extraContext, final boolean pushAction) {
@@ -101,7 +102,7 @@ public class DefaultActionInvocation implements ActionInvocation {
this.container = cont;
}
- @Inject(required=false)
+ @Inject(required = false)
public void setActionEventListener(ActionEventListener listener) {
this.actionEventListener = listener;
}
@@ -111,7 +112,7 @@ public class DefaultActionInvocation implements ActionInvocation {
this.ognlUtil = ognlUtil;
}
- @Inject(required=false)
+ @Inject(required = false)
public void setAsyncManager(AsyncManager asyncManager) {
this.asyncManager = asyncManager;
}
@@ -214,7 +215,7 @@ public class DefaultActionInvocation implements ActionInvocation {
} catch (NullPointerException e) {
LOG.debug("Got NPE trying to read result configuration for resultCode [{}]", resultCode);
}
-
+
if (resultConfig == null) {
// If no result is found for the given resultCode, try to get a wildcard '*' match.
resultConfig = results.get("*");
@@ -248,7 +249,12 @@ public class DefaultActionInvocation implements ActionInvocation {
if (interceptor instanceof WithLazyParams) {
interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
}
- resultCode = interceptor.intercept(DefaultActionInvocation.this);
+ if (interceptor instanceof ConditionalInterceptor) {
+ resultCode = executeConditional((ConditionalInterceptor) interceptor);
+ } else {
+ LOG.debug("Executing normal interceptor: {}", interceptorMapping.getName());
+ resultCode = interceptor.intercept(this);
+ }
} else {
resultCode = invokeActionOnly();
}
@@ -268,9 +274,7 @@ public class DefaultActionInvocation implements ActionInvocation {
if (preResultListeners != null) {
LOG.trace("Executing PreResultListeners for result [{}]", result);
- for (Object preResultListener : preResultListeners) {
- PreResultListener listener = (PreResultListener) preResultListener;
-
+ for (PreResultListener listener : preResultListeners) {
listener.beforeResult(this, resultCode);
}
}
@@ -289,6 +293,16 @@ public class DefaultActionInvocation implements ActionInvocation {
return resultCode;
}
+ protected String executeConditional(ConditionalInterceptor conditionalInterceptor) throws Exception {
+ if (conditionalInterceptor.shouldIntercept(this)) {
+ LOG.debug("Executing conditional interceptor: {}", conditionalInterceptor.getClass().getSimpleName());
+ return conditionalInterceptor.intercept(this);
+ } else {
+ LOG.debug("Interceptor: {} is disabled, skipping to next", conditionalInterceptor.getClass().getSimpleName());
+ return this.invoke();
+ }
+ }
+
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
@@ -314,7 +328,7 @@ public class DefaultActionInvocation implements ActionInvocation {
gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
}
- gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
+ gripe += e.getMessage();
throw new StrutsException(gripe, e, proxy.getConfig());
}
@@ -363,7 +377,7 @@ public class DefaultActionInvocation implements ActionInvocation {
result.execute(this);
} else if (resultCode != null && !Action.NONE.equals(resultCode)) {
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
- + " and result " + getResultCode(), proxy.getConfig());
+ + " and result " + getResultCode(), proxy.getConfig());
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
@@ -464,6 +478,7 @@ public class DefaultActionInvocation implements ActionInvocation {
/**
* Save the result to be used later.
+ *
* @param actionConfig current ActionConfig
* @param methodResult the result of the action.
* @return the result code to process.
@@ -476,7 +491,7 @@ public class DefaultActionInvocation implements ActionInvocation {
container.inject(explicitResult);
return null;
} else if (methodResult instanceof Callable) {
- asyncAction = (Callable) methodResult;
+ asyncAction = (Callable>) methodResult;
return null;
} else {
return (String) methodResult;
diff --git a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
index 330e96412..ec7a8d755 100644
--- a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
+++ b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java
@@ -34,22 +34,22 @@ import java.util.Locale;
import java.util.Map;
/**
- * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
+ * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
* the generic test setup for logic.
*
* @author plightbo
*/
public abstract class XWorkTestCase extends TestCase {
-
+
protected ConfigurationManager configurationManager;
protected Configuration configuration;
protected Container container;
protected ActionProxyFactory actionProxyFactory;
-
+
public XWorkTestCase() {
super();
}
-
+
@Override
protected void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
@@ -57,7 +57,7 @@ public abstract class XWorkTestCase extends TestCase {
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
-
+
@Override
protected void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
@@ -66,34 +66,33 @@ public abstract class XWorkTestCase extends TestCase {
container = null;
actionProxyFactory = null;
}
-
+
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
-
- protected void loadButAdd(final Class> type, final Object impl) {
+
+ protected void loadButAdd(final Class type, final T impl) {
loadButAdd(type, Container.DEFAULT_NAME, impl);
}
-
- protected void loadButAdd(final Class> type, final String name, final Object impl) {
+
+ protected void loadButAdd(final Class type, final String name, final T impl) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
- public void register(ContainerBuilder builder,
- LocatableProperties props) throws ConfigurationException {
+ public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
if (impl instanceof String || ClassUtils.isPrimitiveOrWrapper(impl.getClass())) {
props.setProperty(name, "" + impl);
} else {
- builder.factory(type, name, new Factory() {
- public Object create(Context context) throws Exception {
+ builder.factory(type, name, new Factory() {
+ public T create(Context context) throws Exception {
return impl;
}
@Override
- public Class type() {
- return impl.getClass();
+ public Class type() {
+ return (Class) impl.getClass();
}
}, Scope.SINGLETON);
}
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/ConfigurationManager.java b/core/src/main/java/com/opensymphony/xwork2/config/ConfigurationManager.java
index 41e24b1b3..49517eedf 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/ConfigurationManager.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/ConfigurationManager.java
@@ -22,12 +22,12 @@ import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
import com.opensymphony.xwork2.config.providers.StrutsDefaultConfigurationProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.apache.struts2.StrutsConstants;
+import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.Optional;
+
+import static org.apache.struts2.StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD;
/**
@@ -42,12 +42,11 @@ public class ConfigurationManager {
protected static final Logger LOG = LogManager.getLogger(ConfigurationManager.class);
protected Configuration configuration;
- protected Lock providerLock = new ReentrantLock();
- private List containerProviders = new CopyOnWriteArrayList<>();
- private List packageProviders = new CopyOnWriteArrayList<>();
+ private List containerProviders = new ArrayList<>();
+ private List packageProviders = new ArrayList<>();
protected String defaultFrameworkBeanName;
- private boolean providersChanged = false;
- private boolean reloadConfigs = true; // for the first time
+ private boolean providersChanged = true;
+ private boolean alwaysReloadConfigs = false;
public ConfigurationManager(String name) {
this.defaultFrameworkBeanName = name;
@@ -59,54 +58,69 @@ public class ConfigurationManager {
* @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
*/
public synchronized Configuration getConfiguration() {
- if (configuration == null) {
- setConfiguration(createConfiguration(defaultFrameworkBeanName));
- try {
- configuration.reloadContainer(getContainerProviders());
- } catch (ConfigurationException e) {
- setConfiguration(null);
- throw new ConfigurationException("Unable to load configuration.", e);
- }
- } else {
+ if (wasConfigInitialised()) {
conditionalReload();
}
-
return configuration;
}
+ /**
+ * @return whether configuration was initialised (was null)
+ */
+ private boolean wasConfigInitialised() {
+ if (configuration == null) {
+ initialiseConfiguration();
+ return false;
+ }
+ return true;
+ }
+
+ protected void initialiseConfiguration() {
+ if (containerProviders.isEmpty()) {
+ addDefaultContainerProviders();
+ }
+ configuration = createConfiguration(defaultFrameworkBeanName);
+ try {
+ reload();
+ } catch (ConfigurationException e) {
+ configuration.destroy();
+ configuration = null;
+ providersChanged = true;
+ throw new ConfigurationException("Unable to load configuration.", e);
+ }
+ }
+
+ protected void addDefaultContainerProviders() {
+ containerProviders.add(new StrutsDefaultConfigurationProvider());
+ }
+
protected Configuration createConfiguration(String beanName) {
return new DefaultConfiguration(beanName);
}
+ /**
+ * Clear all container providers and destroy managing Configuration instance
+ */
+ public synchronized void destroyConfiguration() {
+ clearContainerProviders();
+ if (configuration != null) {
+ configuration.destroy();
+ configuration = null;
+ }
+ }
+
public synchronized void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
/**
- *
- * Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method
- * will return a list containing only a default ConfigurationProvider, {@link StrutsDefaultConfigurationProvider}.
- * If a custom ConfigurationProvider has been added, then the StrutsDefaultConfigurationProvider must be added by hand.
- *
- *
- *
- * TODO: The lazy instantiation of XmlConfigurationProvider should be refactored to be elsewhere. The behavior described above seems unintuitive.
- *
+ * Get the current list of ConfigurationProviders.
*
* @return the list of registered ConfigurationProvider objects
* @see ConfigurationProvider
*/
- public List getContainerProviders() {
- providerLock.lock();
- try {
- if (containerProviders.size() == 0) {
- containerProviders.add(new StrutsDefaultConfigurationProvider());
- }
-
- return containerProviders;
- } finally {
- providerLock.unlock();
- }
+ public synchronized List getContainerProviders() {
+ return new ArrayList<>(containerProviders);
}
/**
@@ -114,14 +128,9 @@ public class ConfigurationManager {
*
* @param containerProviders list of {@link ConfigurationProvider} to be set
*/
- public void setContainerProviders(List containerProviders) {
- providerLock.lock();
- try {
- this.containerProviders = new CopyOnWriteArrayList<>(containerProviders);
- providersChanged = true;
- } finally {
- providerLock.unlock();
- }
+ public synchronized void setContainerProviders(List containerProviders) {
+ this.containerProviders = new ArrayList<>(containerProviders);
+ providersChanged = true;
}
/**
@@ -130,22 +139,32 @@ public class ConfigurationManager {
*
* @param provider the ConfigurationProvider to register
*/
- public void addContainerProvider(ContainerProvider provider) {
+ public synchronized void addContainerProvider(ContainerProvider provider) {
if (!containerProviders.contains(provider)) {
containerProviders.add(provider);
providersChanged = true;
}
}
- public void clearContainerProviders() {
- for (ContainerProvider containerProvider : containerProviders) {
- clearContainerProvider(containerProvider);
+ public synchronized void removeContainerProvider(ContainerProvider provider) {
+ if (containerProviders.remove(provider)) {
+ destroyContainerProvider(provider);
+ providersChanged = true;
}
+ }
+
+ public synchronized void clearContainerProviders() {
+ destroyContainerProviders();
containerProviders.clear();
providersChanged = true;
}
- private void clearContainerProvider(ContainerProvider containerProvider) {
+ private void destroyContainerProviders() {
+ LOG.debug("Destroying all providers.");
+ containerProviders.forEach(this::destroyContainerProvider);
+ }
+
+ private void destroyContainerProvider(ContainerProvider containerProvider) {
try {
containerProvider.destroy();
} catch (Exception e) {
@@ -153,80 +172,61 @@ public class ConfigurationManager {
}
}
- /**
- * Destroy its managing Configuration instance
- */
- public synchronized void destroyConfiguration() {
- clearContainerProviders(); // let's destroy the ConfigurationProvider first
- containerProviders = new CopyOnWriteArrayList<>();
- if (configuration != null)
- configuration.destroy(); // let's destroy it first, before nulling it.
- configuration = null;
- }
-
-
/**
* Reloads the Configuration files if the configuration files indicate that they need to be reloaded.
*/
public synchronized void conditionalReload() {
- if (reloadConfigs || providersChanged) {
+ if (alwaysReloadConfigs || providersChanged) {
LOG.debug("Checking ConfigurationProviders for reload.");
- List providers = getContainerProviders();
- boolean reload = needReloadContainerProviders(providers);
- if (!reload) {
- reload = needReloadPackageProviders();
+ if (needReloadContainerProviders() || needReloadPackageProviders()) {
+ destroyAndReload();
}
- if (reload) {
- reloadProviders(providers);
- }
- updateReloadConfigsFlag();
providersChanged = false;
}
}
- private void updateReloadConfigsFlag() {
- reloadConfigs = Boolean.parseBoolean(configuration.getContainer().getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD));
- if (LOG.isDebugEnabled()) {
- LOG.debug("Updating [{}], current value is [{}], new value [{}]",
- StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, String.valueOf(reloadConfigs), String.valueOf(reloadConfigs));
+ private void updateAlwaysReloadFlag() {
+ boolean newValue = Boolean.parseBoolean(configuration.getContainer()
+ .getInstance(String.class, STRUTS_CONFIGURATION_XML_RELOAD));
+ if (alwaysReloadConfigs != newValue) {
+ LOG.debug(
+ "Updating [{}], current value is [{}], new value [{}]",
+ STRUTS_CONFIGURATION_XML_RELOAD,
+ String.valueOf(alwaysReloadConfigs),
+ String.valueOf(newValue));
+ alwaysReloadConfigs = newValue;
}
}
private boolean needReloadPackageProviders() {
- if (packageProviders != null) {
- for (PackageProvider provider : packageProviders) {
- if (provider.needsReload()) {
- LOG.info("Detected package provider [{}] needs to be reloaded. Reloading all providers.", provider);
- return true;
- }
- }
+ Optional provider = packageProviders.stream().filter(PackageProvider::needsReload).findAny();
+ if (provider.isPresent()) {
+ LOG.info("Detected package provider [{}] needs to be reloaded.", provider.get());
+ return true;
}
return false;
}
- private boolean needReloadContainerProviders(List providers) {
- for (ContainerProvider provider : providers) {
- if (provider.needsReload()) {
- LOG.info("Detected container provider [{}] needs to be reloaded. Reloading all providers.", provider);
- return true;
- }
+ private boolean needReloadContainerProviders() {
+ Optional provider = containerProviders.stream().filter(ContainerProvider::needsReload).findAny();
+ if (provider.isPresent()) {
+ LOG.info("Detected container provider [{}] needs to be reloaded.", provider.get());
+ return true;
}
return false;
}
- private void reloadProviders(List providers) {
- for (ContainerProvider containerProvider : containerProviders) {
- try {
- containerProvider.destroy();
- } catch (Exception e) {
- LOG.warn("error while destroying configuration provider [{}]", containerProvider, e);
- }
- }
- packageProviders = this.configuration.reloadContainer(providers);
+ public synchronized void destroyAndReload() {
+ destroyContainerProviders();
+ reload();
}
public synchronized void reload() {
- packageProviders = getConfiguration().reloadContainer(getContainerProviders());
+ if (wasConfigInitialised()) {
+ LOG.debug("Reloading all providers.");
+ packageProviders = configuration.reloadContainer(containerProviders);
+ providersChanged = false;
+ updateAlwaysReloadFlag();
+ }
}
-
}
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/ContainerProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/ContainerProvider.java
index 1fc4cbacb..e43bf8134 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/ContainerProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/ContainerProvider.java
@@ -24,7 +24,7 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
/**
* Provides beans and constants/properties for the Container
- *
+ *
* @since 2.1
*/
public interface ContainerProvider {
@@ -32,29 +32,29 @@ public interface ContainerProvider {
/**
* Called before removed from the configuration manager
*/
- public void destroy();
-
+ void destroy();
+
/**
* Initializes with the configuration
* @param configuration The configuration
* @throws ConfigurationException If anything goes wrong
*/
- public void init(Configuration configuration) throws ConfigurationException;
-
+ void init(Configuration configuration) throws ConfigurationException;
+
/**
* Tells whether the ContainerProvider should reload its configuration
*
* @return true, whether the ContainerProvider should reload its configuration, falseotherwise.
*/
- public boolean needsReload();
-
+ boolean needsReload();
+
/**
* Registers beans and properties for the Container
- *
+ *
* @param builder The builder to register beans with
* @param props The properties to register constants with
* @throws ConfigurationException If anything goes wrong
*/
- public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
-
+ void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
+
}
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
index 4d16ee81f..9174e651b 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
@@ -20,26 +20,18 @@ package com.opensymphony.xwork2.config.entities;
import com.opensymphony.xwork2.util.location.Located;
import com.opensymphony.xwork2.util.location.Location;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
import java.io.Serializable;
import java.util.*;
-
/**
* Configuration for Package.
*
*
* In the xml configuration file this is defined as the package tag.
*
- *
- * @author Rainer Hermanns
- * @version $Revision$
*/
-public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
-
- private static final Logger LOG = LogManager.getLogger(PackageConfig.class);
+public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
protected Map actionConfigs;
protected Map globalResultConfigs;
@@ -422,8 +414,7 @@ public class PackageConfig extends Located implements Comparable, Serializable,
return "PackageConfig: [" + name + "] for namespace [" + namespace + "] with parents [" + parents + "]";
}
- public int compareTo(Object o) {
- PackageConfig other = (PackageConfig) o;
+ public int compareTo(PackageConfig other) {
String full = namespace + "!" + name;
String otherFull = other.namespace + "!" + other.name;
@@ -443,7 +434,6 @@ public class PackageConfig extends Located implements Comparable, Serializable,
public static class Builder implements InterceptorLocator {
protected PackageConfig target;
- private boolean strictDMI = true;
public Builder(String name) {
target = new PackageConfig(name);
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
index 91b1b9a33..71fdf2ff8 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
@@ -298,8 +298,8 @@ public class DefaultConfiguration implements Configuration {
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
- builder.factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
- builder.factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
+ builder.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
+ builder.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
builder.factory(OgnlUtil.class, Scope.SINGLETON);
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
index 1442e920f..1f49cc11b 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
@@ -21,26 +21,16 @@ package com.opensymphony.xwork2.config.providers;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
-import com.opensymphony.xwork2.LocaleProviderFactory;
-import com.opensymphony.xwork2.StrutsTextProviderFactory;
-import com.opensymphony.xwork2.TextProviderFactory;
-import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
-import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
-import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
-import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
-import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
-import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
-import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
-import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
-import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
-import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
-import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
+import com.opensymphony.xwork2.LocaleProviderFactory;
+import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
@@ -57,14 +47,7 @@ import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
import com.opensymphony.xwork2.conversion.impl.DateConverter;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
-import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
-import org.apache.struts2.components.date.DateFormatter;
-import org.apache.struts2.components.date.DateTimeFormatterAdapter;
-import org.apache.struts2.components.date.SimpleDateFormatAdapter;
-import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
-import org.apache.struts2.conversion.StrutsTypeConverterCreator;
-import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
@@ -73,34 +56,45 @@ import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.factory.ActionFactory;
import com.opensymphony.xwork2.factory.ConverterFactory;
import com.opensymphony.xwork2.factory.DefaultActionFactory;
-import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
+import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.InterceptorFactory;
import com.opensymphony.xwork2.factory.ResultFactory;
+import com.opensymphony.xwork2.factory.StrutsConverterFactory;
+import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
+import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
+import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
+import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
+import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ObjectProxy;
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
+import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor;
+import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
+import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.CompoundRoot;
-import com.opensymphony.xwork2.LocalizedTextProvider;
-import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
-import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
-import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
+import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
@@ -119,8 +113,19 @@ import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
+import org.apache.struts2.conversion.StrutsTypeConverterCreator;
+import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
+import org.apache.struts2.url.QueryStringBuilder;
+import org.apache.struts2.url.QueryStringParser;
+import org.apache.struts2.url.StrutsQueryStringBuilder;
+import org.apache.struts2.url.StrutsQueryStringParser;
+import org.apache.struts2.url.StrutsUrlDecoder;
+import org.apache.struts2.url.StrutsUrlEncoder;
+import org.apache.struts2.url.UrlDecoder;
+import org.apache.struts2.url.UrlEncoder;
import java.util.ArrayList;
import java.util.Collection;
@@ -153,88 +158,90 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
@Override
public void register(ContainerBuilder builder, LocatableProperties props)
- throws ConfigurationException {
+ throws ConfigurationException {
builder
- .factory(ObjectFactory.class)
- .factory(ActionFactory.class, DefaultActionFactory.class)
- .factory(ResultFactory.class, DefaultResultFactory.class)
- .factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
- .factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
- .factory(ConverterFactory.class, StrutsConverterFactory.class)
- .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
+ .factory(ObjectFactory.class)
+ .factory(ActionFactory.class, DefaultActionFactory.class)
+ .factory(ResultFactory.class, DefaultResultFactory.class)
+ .factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
+ .factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
+ .factory(ConverterFactory.class, StrutsConverterFactory.class)
+ .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
- .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
- .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
+ .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
+ .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
- .factory(XWorkConverter.class, Scope.SINGLETON)
- .factory(XWorkBasicConverter.class, Scope.SINGLETON)
- .factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
- .factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
- .factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
- .factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
- .factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
+ .factory(XWorkConverter.class, Scope.SINGLETON)
+ .factory(XWorkBasicConverter.class, Scope.SINGLETON)
+ .factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
+ .factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
+ .factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
+ .factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
+ .factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
- .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
- .factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
- .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
- .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
- .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
- .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
- .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
- .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
+ .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
+ .factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
+ .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
+ .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
+ .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
+ .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
+ .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
+ .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
- .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
+ .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
- // silly workarounds for ognl since there is no way to flush its caches
- .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
+ // silly workarounds for ognl since there is no way to flush its caches
+ .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
- .factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
- .factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
+ .factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
+ .factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
- .factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
+ .factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
- .factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
- .factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
- .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
+ .factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
+ .factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
+ .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
- .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
- .factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
- .factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
- .factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
+ .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
+ .factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
+ .factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
+ .factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
- .factory(ExpressionCacheFactory.class, "defaultOgnlExpressionCacheFactory", DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
- .factory(BeanInfoCacheFactory.class, "defaultOgnlBeanInfoCacheFactory", DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
- .factory(OgnlUtil.class, Scope.SINGLETON)
- .factory(CollectionConverter.class, Scope.SINGLETON)
- .factory(ArrayConverter.class, Scope.SINGLETON)
- .factory(DateConverter.class, Scope.SINGLETON)
- .factory(NumberConverter.class, Scope.SINGLETON)
- .factory(StringConverter.class, Scope.SINGLETON)
+ .factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
+ .factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
+ .factory(OgnlUtil.class, Scope.SINGLETON)
+ .factory(CollectionConverter.class, Scope.SINGLETON)
+ .factory(ArrayConverter.class, Scope.SINGLETON)
+ .factory(DateConverter.class, Scope.SINGLETON)
+ .factory(NumberConverter.class, Scope.SINGLETON)
+ .factory(StringConverter.class, Scope.SINGLETON)
- .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
- .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
- .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
- , Scope.SINGLETON)
+ .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
+ .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
+ .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
+ , Scope.SINGLETON)
- .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
+ .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
- .factory(DateFormatter.class, "simpleDateFormatter", SimpleDateFormatAdapter.class, Scope.SINGLETON)
- .factory(DateFormatter.class, "dateTimeFormatter", DateTimeFormatterAdapter.class, Scope.SINGLETON)
+ .factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
+ .factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
+ .factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
+ .factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
;
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java
index 8d9699aae..8c980b724 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java
@@ -44,7 +44,6 @@ import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.ClassPathFinder;
import com.opensymphony.xwork2.util.DomHelper;
-import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.location.Location;
import com.opensymphony.xwork2.util.location.LocationUtils;
@@ -75,6 +74,17 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Vector;
+import java.util.function.Consumer;
+
+import static com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet;
+import static java.lang.Boolean.parseBoolean;
+import static java.lang.Character.isLowerCase;
+import static java.lang.Character.toUpperCase;
+import static java.lang.String.format;
+import static java.util.Collections.emptyList;
+import static org.apache.commons.lang3.StringUtils.defaultString;
+import static org.apache.commons.lang3.StringUtils.isNotEmpty;
+import static org.apache.commons.lang3.StringUtils.trimToNull;
/**
@@ -164,13 +174,10 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
if (this == o) {
return true;
}
-
if (!(o instanceof XmlConfigurationProvider)) {
return false;
}
-
- final XmlConfigurationProvider xmlConfigurationProvider = (XmlConfigurationProvider) o;
-
+ XmlConfigurationProvider xmlConfigurationProvider = (XmlConfigurationProvider) o;
return Objects.equals(configFileName, xmlConfigurationProvider.configFileName);
}
@@ -179,6 +186,34 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
return ((configFileName != null) ? configFileName.hashCode() : 0);
}
+ public static void iterateElementChildren(Document doc, Consumer function) {
+ iterateElementChildren(doc.getDocumentElement(), function);
+ }
+
+ public static void iterateElementChildren(Node node, Consumer function) {
+ iterateChildren(node, childNode -> {
+ if (!(childNode instanceof Element)) {
+ return;
+ }
+ function.accept((Element) childNode);
+ });
+ }
+
+ public static void iterateChildren(Node node, Consumer function) {
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ function.accept(children.item(i));
+ }
+ }
+
+ public static void iterateChildrenByTagName(Element el, String tagName, Consumer function) {
+ NodeList childrenByTag = el.getElementsByTagName(tagName);
+ for (int i = 0; i < childrenByTag.getLength(); i++) {
+ Element childEl = (Element) childrenByTag.item(i);
+ function.accept(childEl);
+ }
+ }
+
private void loadDocuments(String configFileName) {
try {
loadedFileUrls.clear();
@@ -194,145 +229,129 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
LOG.trace("Parsing configuration file [{}]", configFileName);
Map loadedBeans = new HashMap<>();
for (Document doc : documents) {
- Element rootElement = doc.getDocumentElement();
- NodeList children = rootElement.getChildNodes();
- int childSize = children.getLength();
+ iterateElementChildren(doc, child -> {
+ switch (child.getNodeName()) {
+ case "bean-selection": {
+ registerBeanSelection(child, containerBuilder, props);
+ break;
+ }
+ case "bean": {
+ registerBean(child, loadedBeans, containerBuilder);
+ break;
+ }
+ case "constant": {
+ registerConstant(child, props);
+ break;
+ }
+ case "unknown-handler-stack":
+ registerUnknownHandlerStack(child);
+ break;
+ }
+ });
+ }
+ }
- for (int i = 0; i < childSize; i++) {
- Node childNode = children.item(i);
+ protected void registerBeanSelection(Element child, ContainerBuilder containerBuilder, LocatableProperties props) {
+ String name = child.getAttribute("name");
+ String impl = child.getAttribute("class");
+ try {
+ Class> classImpl = ClassLoaderUtil.loadClass(impl, getClass());
+ if (BeanSelectionProvider.class.isAssignableFrom(classImpl)) {
+ BeanSelectionProvider provider = (BeanSelectionProvider) classImpl.newInstance();
+ provider.register(containerBuilder, props);
+ } else {
+ throw new ConfigurationException(format("The bean-provider: name:%s class:%s does not implement %s", name, impl, BeanSelectionProvider.class.getName()), child);
+ }
+ } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
+ throw new ConfigurationException(format("Unable to load bean-provider: name:%s class:%s", name, impl), e, child);
+ }
+ }
- if (childNode instanceof Element) {
- Element child = (Element) childNode;
+ protected void registerBean(Element child, Map loadedBeans, ContainerBuilder containerBuilder) {
+ String type = child.getAttribute("type");
+ String name = child.getAttribute("name");
+ String impl = child.getAttribute("class");
+ String onlyStatic = child.getAttribute("static");
+ String scopeStr = child.getAttribute("scope");
+ boolean optional = "true".equals(child.getAttribute("optional"));
+ Scope scope = Scope.fromString(scopeStr);
- final String nodeName = child.getNodeName();
+ if (name.isEmpty()) {
+ name = Container.DEFAULT_NAME;
+ }
- if ("bean-selection".equals(nodeName)) {
- String name = child.getAttribute("name");
- String impl = child.getAttribute("class");
- try {
- Class classImpl = ClassLoaderUtil.loadClass(impl, getClass());
- if (BeanSelectionProvider.class.isAssignableFrom(classImpl)) {
- BeanSelectionProvider provider = (BeanSelectionProvider) classImpl.newInstance();
- provider.register(containerBuilder, props);
- } else {
- throw new ConfigurationException("The bean-provider: name:" + name + " class:" + impl + " does not implement " + BeanSelectionProvider.class.getName(), childNode);
- }
- } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
- throw new ConfigurationException("Unable to load bean-provider: name:" + name + " class:" + impl, e, childNode);
- }
- } else if ("bean".equals(nodeName)) {
- String type = child.getAttribute("type");
- String name = child.getAttribute("name");
- String impl = child.getAttribute("class");
- String onlyStatic = child.getAttribute("static");
- String scopeStr = child.getAttribute("scope");
- boolean optional = "true".equals(child.getAttribute("optional"));
- Scope scope;
- if ("prototype".equals(scopeStr)) {
- scope = Scope.PROTOTYPE;
- } else if ("request".equals(scopeStr)) {
- scope = Scope.REQUEST;
- } else if ("session".equals(scopeStr)) {
- scope = Scope.SESSION;
- } else if ("singleton".equals(scopeStr)) {
- scope = Scope.SINGLETON;
- } else if ("thread".equals(scopeStr)) {
- scope = Scope.THREAD;
- } else {
- scope = Scope.SINGLETON;
- }
-
- if (StringUtils.isEmpty(name)) {
- name = Container.DEFAULT_NAME;
- }
-
- try {
- Class classImpl = ClassLoaderUtil.loadClass(impl, getClass());
- Class classType = classImpl;
- if (StringUtils.isNotEmpty(type)) {
- classType = ClassLoaderUtil.loadClass(type, getClass());
- }
- if ("true".equals(onlyStatic)) {
- // Force loading of class to detect no class def found exceptions
- classImpl.getDeclaredClasses();
- containerBuilder.injectStatics(classImpl);
- } else {
- if (containerBuilder.contains(classType, name)) {
- Location loc = LocationUtils.getLocation(loadedBeans.get(classType.getName() + name));
- if (throwExceptionOnDuplicateBeans) {
- throw new ConfigurationException("Bean type " + classType + " with the name " +
- name + " has already been loaded by " + loc, child);
- }
- }
-
- // Force loading of class to detect no class def found exceptions
- classImpl.getDeclaredConstructors();
-
- LOG.debug("Loaded type: {} name: {} impl: {}", type, name, impl);
- containerBuilder.factory(classType, name, new LocatableFactory(name, classType, classImpl, scope, childNode), scope);
- }
- loadedBeans.put(classType.getName() + name, child);
- } catch (Throwable ex) {
- if (!optional) {
- throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
- } else {
- LOG.debug("Unable to load optional class: {}", impl);
- }
- }
- } else if ("constant".equals(nodeName)) {
- String name = child.getAttribute("name");
- String value = child.getAttribute("value");
-
- if (valueSubstitutor != null) {
- LOG.debug("Substituting value [{}] using [{}]", value, valueSubstitutor.getClass().getName());
- value = valueSubstitutor.substitute(value);
- }
-
- props.setProperty(name, value, childNode);
- } else if (nodeName.equals("unknown-handler-stack")) {
- List unknownHandlerStack = new ArrayList();
- NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref");
- int unknownHandlersSize = unknownHandlers.getLength();
-
- for (int k = 0; k < unknownHandlersSize; k++) {
- Element unknownHandler = (Element) unknownHandlers.item(k);
- Location location = LocationUtils.getLocation(unknownHandler);
- unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name"), location));
- }
-
- if (!unknownHandlerStack.isEmpty())
- configuration.setUnknownHandlerStack(unknownHandlerStack);
+ try {
+ Class> classImpl = ClassLoaderUtil.loadClass(impl, getClass());
+ Class> classType = classImpl;
+ if (!type.isEmpty()) {
+ classType = ClassLoaderUtil.loadClass(type, getClass());
+ }
+ if ("true".equals(onlyStatic)) {
+ // Force loading of class to detect no class def found exceptions
+ classImpl.getDeclaredClasses();
+ containerBuilder.injectStatics(classImpl);
+ } else {
+ if (containerBuilder.contains(classType, name)) {
+ Location loc = LocationUtils.getLocation(loadedBeans.get(classType.getName() + name));
+ if (throwExceptionOnDuplicateBeans) {
+ throw new ConfigurationException(format("Bean type %s with the name %s has already been loaded by %s", classType, name, loc), child);
}
}
+
+ // Force loading of class to detect no class def found exceptions
+ classImpl.getDeclaredConstructors();
+
+ LOG.debug("Loaded type: {} name: {} impl: {}", type, name, impl);
+ containerBuilder.factory(classType, name, new LocatableFactory<>(name, classType, classImpl, scope, child), scope);
+ }
+ loadedBeans.put(classType.getName() + name, child);
+ } catch (Throwable ex) {
+ if (!optional) {
+ throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, child);
+ } else {
+ LOG.debug("Unable to load optional class: {}", impl);
}
}
}
+ protected void registerConstant(Element child, LocatableProperties props) {
+ String name = child.getAttribute("name");
+ String value = child.getAttribute("value");
+
+ if (valueSubstitutor != null) {
+ LOG.debug("Substituting value [{}] using [{}]", value, valueSubstitutor.getClass().getName());
+ value = valueSubstitutor.substitute(value);
+ }
+
+ props.setProperty(name, value, child);
+ }
+
+ protected void registerUnknownHandlerStack(Element child) {
+ List unknownHandlerStack = new ArrayList<>();
+
+ iterateChildrenByTagName(child, "unknown-handler-ref", unknownHandler -> {
+ Location location = LocationUtils.getLocation(unknownHandler);
+ unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name"), location));
+ });
+
+ if (!unknownHandlerStack.isEmpty()) {
+ configuration.setUnknownHandlerStack(unknownHandlerStack);
+ }
+ }
+
public void loadPackages() throws ConfigurationException {
- List reloads = new ArrayList();
+ List reloads = new ArrayList<>();
verifyPackageStructure();
for (Document doc : documents) {
- Element rootElement = doc.getDocumentElement();
- NodeList children = rootElement.getChildNodes();
- int childSize = children.getLength();
-
- for (int i = 0; i < childSize; i++) {
- Node childNode = children.item(i);
-
- if (childNode instanceof Element) {
- Element child = (Element) childNode;
-
- final String nodeName = child.getNodeName();
-
- if ("package".equals(nodeName)) {
- PackageConfig cfg = addPackage(child);
- if (cfg.isNeedsRefresh()) {
- reloads.add(child);
- }
+ iterateElementChildren(doc, child -> {
+ if ("package".equals(child.getNodeName())) {
+ PackageConfig cfg = addPackage(child);
+ if (cfg.isNeedsRefresh()) {
+ reloads.add(child);
}
}
- }
+ });
loadExtraConfiguration(doc);
}
@@ -353,30 +372,21 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
DirectedGraph graph = new DirectedGraph<>();
for (Document doc : documents) {
- Element rootElement = doc.getDocumentElement();
- NodeList children = rootElement.getChildNodes();
- int childSize = children.getLength();
- for (int i = 0; i < childSize; i++) {
- Node childNode = children.item(i);
- if (childNode instanceof Element) {
- Element child = (Element) childNode;
-
- final String nodeName = child.getNodeName();
-
- if ("package".equals(nodeName)) {
- String packageName = child.getAttribute("name");
- declaredPackages.put(packageName, child);
- graph.addNode(packageName);
-
- String extendsAttribute = child.getAttribute("extends");
- List parents = ConfigurationUtil.buildParentListFromString(extendsAttribute);
- for (String parent : parents) {
- graph.addNode(parent);
- graph.addEdge(packageName, parent);
- }
- }
+ iterateElementChildren(doc, child -> {
+ if (!"package".equals(child.getNodeName())) {
+ return;
}
- }
+
+ String packageName = child.getAttribute("name");
+ declaredPackages.put(packageName, child);
+ graph.addNode(packageName);
+
+ String extendsAttribute = child.getAttribute("extends");
+ for (String parent : ConfigurationUtil.buildParentListFromString(extendsAttribute)) {
+ graph.addNode(parent);
+ graph.addEdge(packageName, parent);
+ }
+ });
}
CycleDetector detector = new CycleDetector<>(graph);
@@ -391,30 +401,27 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
}
private void reloadRequiredPackages(List reloads) {
- if (reloads.size() > 0) {
- List result = new ArrayList<>();
- for (Element pkg : reloads) {
- PackageConfig cfg = addPackage(pkg);
- if (cfg.isNeedsRefresh()) {
- result.add(pkg);
- }
- }
- if ((result.size() > 0) && (result.size() != reloads.size())) {
- reloadRequiredPackages(result);
- return;
- }
+ if (reloads.isEmpty()) {
+ return;
+ }
- // Print out error messages for all misconfigured inheritance packages
- if (result.size() > 0) {
- for (Element rp : result) {
- String parent = rp.getAttribute("extends");
- if (parent != null) {
- List parents = ConfigurationUtil.buildParentsFromString(configuration, parent);
- if (parents != null && parents.size() <= 0) {
- LOG.error("Unable to find parent packages {}", parent);
- }
- }
- }
+ List result = new ArrayList<>();
+ for (Element pkg : reloads) {
+ PackageConfig cfg = addPackage(pkg);
+ if (cfg.isNeedsRefresh()) {
+ result.add(pkg);
+ }
+ }
+ if (!result.isEmpty() && result.size() != reloads.size()) {
+ reloadRequiredPackages(result);
+ return;
+ }
+
+ // Print out error messages for all misconfigured inheritance packages
+ for (Element rp : result) {
+ String parent = rp.getAttribute("extends");
+ if (!parent.isEmpty() && ConfigurationUtil.buildParentsFromString(configuration, parent).isEmpty()) {
+ LOG.error("Unable to find parent packages {}", parent);
}
}
}
@@ -426,7 +433,6 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
* @return true if the file has been changed since the last time we read it
*/
public boolean needsReload() {
-
for (String url : loadedFileUrls) {
if (fileManager.fileNeedsReloading(url)) {
return true;
@@ -439,28 +445,16 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
String name = actionElement.getAttribute("name");
String className = actionElement.getAttribute("class");
//methodName should be null if it's not set
- String methodName = StringUtils.trimToNull(actionElement.getAttribute("method"));
+ String methodName = trimToNull(actionElement.getAttribute("method"));
Location location = DomHelper.getLocationObject(actionElement);
if (location == null) {
LOG.warn("Location null for {}", className);
}
- // if there isn't a class name specified for an then try to
- // use the default-class-ref from the
- if (StringUtils.isEmpty(className)) {
- // if there is a package default-class-ref use that, otherwise use action support
- /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) {
- className = packageContext.getDefaultClassRef();
- } else {
- className = ActionSupport.class.getName();
- }*/
-
- } else {
- if (!verifyAction(className, name, location)) {
- LOG.error("Unable to verify action [{}] with class [{}], from [{}]", name, className, location);
- return;
- }
+ if (!className.isEmpty() && !verifyAction(className, name, location)) {
+ LOG.error("Unable to verify action [{}] with class [{}], from [{}]", name, className, location);
+ return;
}
Map results;
@@ -489,7 +483,7 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
packageContext.addActionConfig(name, actionConfig);
LOG.debug("Loaded {}{} in '{}' package: {}",
- StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "",
+ isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "",
name, packageContext.getName(), actionConfig);
}
@@ -500,11 +494,11 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
}
try {
if (objectFactory.isNoArgConstructorRequired()) {
- Class clazz = objectFactory.getClassInstance(className);
+ Class> clazz = objectFactory.getClassInstance(className);
if (!Modifier.isPublic(clazz.getModifiers())) {
throw new ConfigurationException("Action class [" + className + "] is not public", loc);
}
- clazz.getConstructor(new Class[]{});
+ clazz.getConstructor();
}
} catch (ClassNotFoundException e) {
LOG.debug("Class not found for action [{}]", className, e);
@@ -568,12 +562,7 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
loadGlobalExceptionMappings(newPackage, packageElement);
// get actions
- NodeList actionList = packageElement.getElementsByTagName("action");
-
- for (int i = 0; i < actionList.getLength(); i++) {
- Element actionElement = (Element) actionList.item(i);
- addAction(actionElement, newPackage);
- }
+ iterateChildrenByTagName(packageElement, "action", actionElement -> addAction(actionElement, newPackage));
// load the default action reference for this package
loadDefaultActionRef(newPackage, packageElement);
@@ -584,92 +573,82 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
}
protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
- NodeList resultTypeList = element.getElementsByTagName("result-type");
-
- for (int i = 0; i < resultTypeList.getLength(); i++) {
- Element resultTypeElement = (Element) resultTypeList.item(i);
+ iterateChildrenByTagName(element, "result-type", resultTypeElement -> {
String name = resultTypeElement.getAttribute("name");
String className = resultTypeElement.getAttribute("class");
String def = resultTypeElement.getAttribute("default");
Location loc = DomHelper.getLocationObject(resultTypeElement);
- Class clazz = verifyResultType(className, loc);
- if (clazz != null) {
- String paramName = null;
- try {
- paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
- } catch (Throwable t) {
- LOG.debug("The result type [{}] doesn't have a default param [DEFAULT_PARAM] defined!", className, t);
- }
- ResultTypeConfig.Builder resultType = new ResultTypeConfig.Builder(name, className).defaultResultParam(paramName)
- .location(DomHelper.getLocationObject(resultTypeElement));
-
- Map params = XmlHelper.getParams(resultTypeElement);
-
- if (!params.isEmpty()) {
- resultType.addParams(params);
- }
- packageContext.addResultTypeConfig(resultType.build());
-
- // set the default result type
- if (BooleanUtils.toBoolean(def)) {
- packageContext.defaultResultType(name);
- }
+ Class> clazz = verifyResultType(className, loc);
+ if (clazz == null) {
+ return;
}
- }
+ String paramName = null;
+ try {
+ paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
+ } catch (Throwable t) {
+ LOG.debug("The result type [{}] doesn't have a default param [DEFAULT_PARAM] defined!", className, t);
+ }
+ ResultTypeConfig.Builder resultType = new ResultTypeConfig.Builder(name, className).defaultResultParam(paramName)
+ .location(DomHelper.getLocationObject(resultTypeElement));
+
+ Map params = XmlHelper.getParams(resultTypeElement);
+
+ if (!params.isEmpty()) {
+ resultType.addParams(params);
+ }
+ packageContext.addResultTypeConfig(resultType.build());
+
+ // set the default result type
+ if (BooleanUtils.toBoolean(def)) {
+ packageContext.defaultResultType(name);
+ }
+ });
}
- protected Class verifyResultType(String className, Location loc) {
+ protected Class> verifyResultType(String className, Location loc) {
try {
return objectFactory.getClassInstance(className);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
LOG.warn("Result class [{}] doesn't exist ({}) at {}, ignoring", className, e.getClass().getSimpleName(), loc, e);
}
-
return null;
}
protected List buildInterceptorList(Element element, PackageConfig.Builder context) throws ConfigurationException {
List interceptorList = new ArrayList<>();
- NodeList interceptorRefList = element.getElementsByTagName("interceptor-ref");
- for (int i = 0; i < interceptorRefList.getLength(); i++) {
- Element interceptorRefElement = (Element) interceptorRefList.item(i);
-
- if (interceptorRefElement.getParentNode().equals(element) || interceptorRefElement.getParentNode().getNodeName().equals(element.getNodeName())) {
- List interceptors = lookupInterceptorReference(context, interceptorRefElement);
- interceptorList.addAll(interceptors);
+ iterateChildrenByTagName(element, "interceptor-ref", interceptorRefElement -> {
+ Node parNode = interceptorRefElement.getParentNode();
+ if (!parNode.equals(element) && !parNode.getNodeName().equals(element.getNodeName())) {
+ return;
}
- }
+ List interceptors = lookupInterceptorReference(context, interceptorRefElement);
+ interceptorList.addAll(interceptors);
+ });
return interceptorList;
}
/**
- *
- * This method builds a package context by looking for the parents of this new package.
- *
- *
- *
- * If no parents are found, it will return a root package.
- *
+ * This method builds a package context by looking for the parents of this new package.
+ * If no parents are found, it will return a root package.
*
* @param packageElement the package element
- *
* @return the package config builder
*/
protected PackageConfig.Builder buildPackageContext(Element packageElement) {
String parent = packageElement.getAttribute("extends");
String abstractVal = packageElement.getAttribute("abstract");
- boolean isAbstract = Boolean.parseBoolean(abstractVal);
- String name = StringUtils.defaultString(packageElement.getAttribute("name"));
- String namespace = StringUtils.defaultString(packageElement.getAttribute("namespace"));
+ boolean isAbstract = parseBoolean(abstractVal);
+ String name = defaultString(packageElement.getAttribute("name"));
+ String namespace = defaultString(packageElement.getAttribute("namespace"));
- // Strict DMI is enabled by default, it can disabled by user
+ // Strict DMI is enabled by default, it can be disabled by user
boolean strictDMI = true;
if (packageElement.hasAttribute("strict-method-invocation")) {
- strictDMI = Boolean.parseBoolean(packageElement.getAttribute("strict-method-invocation"));
+ strictDMI = parseBoolean(packageElement.getAttribute("strict-method-invocation"));
}
PackageConfig.Builder cfg = new PackageConfig.Builder(name)
@@ -678,27 +657,30 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
.strictMethodInvocation(strictDMI)
.location(DomHelper.getLocationObject(packageElement));
- if (StringUtils.isNotEmpty(StringUtils.defaultString(parent))) { // has parents, let's look it up
- List parents = new ArrayList<>();
- for (String parentPackageName : ConfigurationUtil.buildParentListFromString(parent)) {
- if (configuration.getPackageConfigNames().contains(parentPackageName)) {
- parents.add(configuration.getPackageConfig(parentPackageName));
- } else if (declaredPackages.containsKey(parentPackageName)) {
- if (configuration.getPackageConfig(parentPackageName) == null) {
- addPackage(declaredPackages.get(parentPackageName));
- }
- parents.add(configuration.getPackageConfig(parentPackageName));
- } else {
- throw new ConfigurationException("Parent package is not defined: " + parentPackageName);
+ if (parent.isEmpty()) {
+ return cfg;
+ }
+
+ // has parents, let's look it up
+ List parents = new ArrayList<>();
+ for (String parentPackageName : ConfigurationUtil.buildParentListFromString(parent)) {
+ if (configuration.getPackageConfigNames().contains(parentPackageName)) {
+ parents.add(configuration.getPackageConfig(parentPackageName));
+ } else if (declaredPackages.containsKey(parentPackageName)) {
+ if (configuration.getPackageConfig(parentPackageName) == null) {
+ addPackage(declaredPackages.get(parentPackageName));
}
-
- }
-
- if (parents.size() <= 0) {
- cfg.needsRefresh(true);
+ parents.add(configuration.getPackageConfig(parentPackageName));
} else {
- cfg.addParents(parents);
+ throw new ConfigurationException("Parent package is not defined: " + parentPackageName);
}
+
+ }
+
+ if (parents.isEmpty()) {
+ cfg.needsRefresh(true);
+ } else {
+ cfg.addParents(parents);
}
return cfg;
@@ -707,129 +689,125 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
/**
* Build a map of ResultConfig objects from below a given XML element.
*
- * @param element the given XML element
+ * @param element the given XML element
* @param packageContext the package context
- *
* @return map of result config objects
*/
protected Map buildResults(Element element, PackageConfig.Builder packageContext) {
- NodeList resultEls = element.getElementsByTagName("result");
-
Map results = new LinkedHashMap<>();
- for (int i = 0; i < resultEls.getLength(); i++) {
- Element resultElement = (Element) resultEls.item(i);
+ iterateChildrenByTagName(element, "result", resultElement -> {
+ Node parNode = resultElement.getParentNode();
+ if (!parNode.equals(element) && !parNode.getNodeName().equals(element.getNodeName())) {
+ return;
+ }
- if (resultElement.getParentNode().equals(element) || resultElement.getParentNode().getNodeName().equals(element.getNodeName())) {
- String resultName = resultElement.getAttribute("name");
- String resultType = resultElement.getAttribute("type");
+ String resultName = resultElement.getAttribute("name");
+ String resultType = resultElement.getAttribute("type");
- // if you don't specify a name on , it defaults to "success"
- if (StringUtils.isEmpty(resultName)) {
- resultName = Action.SUCCESS;
- }
+ // if you don't specify a name on , it defaults to "success"
+ if (StringUtils.isEmpty(resultName)) {
+ resultName = Action.SUCCESS;
+ }
- // there is no result type, so let's inherit from the parent package
- if (StringUtils.isEmpty(resultType)) {
- resultType = packageContext.getFullDefaultResultType();
-
- // now check if there is a result type now
- if (StringUtils.isEmpty(resultType)) {
- // uh-oh, we have a problem
- throw new ConfigurationException("No result type specified for result named '"
- + resultName + "', perhaps the parent package does not specify the result type?", resultElement);
- }
- }
-
-
- ResultTypeConfig config = packageContext.getResultType(resultType);
-
- if (config == null) {
- throw new ConfigurationException("There is no result type defined for type '" + resultType
- + "' mapped with name '" + resultName + "'."
- + " Did you mean '" + guessResultType(resultType) + "'?", resultElement);
- }
-
- String resultClass = config.getClassName();
-
- // invalid result type specified in result definition
- if (resultClass == null) {
- throw new ConfigurationException("Result type '" + resultType + "' is invalid");
- }
-
- Map resultParams = XmlHelper.getParams(resultElement);
-
- if (resultParams.size() == 0) // maybe we just have a body - therefore a default parameter
- {
- // if something then we add a parameter of 'something' as this is the most used result param
- if (resultElement.getChildNodes().getLength() >= 1) {
- resultParams = new LinkedHashMap<>();
-
- String paramName = config.getDefaultResultParam();
- if (paramName != null) {
- StringBuilder paramValue = new StringBuilder();
- for (int j = 0; j < resultElement.getChildNodes().getLength(); j++) {
- if (resultElement.getChildNodes().item(j).getNodeType() == Node.TEXT_NODE) {
- String val = resultElement.getChildNodes().item(j).getNodeValue();
- if (val != null) {
- paramValue.append(val);
- }
- }
- }
- String val = paramValue.toString().trim();
- if (val.length() > 0) {
- resultParams.put(paramName, val);
- }
- } else {
- LOG.debug("No default parameter defined for result [{}] of type [{}] ", config.getName(), config.getClassName());
- }
- }
- }
-
- // create new param map, so that the result param can override the config param
- Map params = new LinkedHashMap();
- Map configParams = config.getParams();
- if (configParams != null) {
- params.putAll(configParams);
- }
- params.putAll(resultParams);
-
- Set resultNamesSet = TextParseUtil.commaDelimitedStringToSet(resultName);
- if (resultNamesSet.isEmpty()) {
- resultNamesSet.add(resultName);
- }
-
- for (String name : resultNamesSet) {
- ResultConfig resultConfig = new ResultConfig.Builder(name, resultClass)
- .addParams(params)
- .location(DomHelper.getLocationObject(element))
- .build();
- results.put(resultConfig.getName(), resultConfig);
+ // there is no result type, so let's inherit from the parent package
+ if (resultType.isEmpty()) {
+ resultType = packageContext.getFullDefaultResultType();
+ // now check if there is a result type now
+ if (resultType.isEmpty()) {
+ throw new ConfigurationException("No result type specified for result named '"
+ + resultName + "', perhaps the parent package does not specify the result type?", resultElement);
}
}
- }
+
+ ResultTypeConfig config = packageContext.getResultType(resultType);
+ if (config == null) {
+ throw new ConfigurationException(format("There is no result type defined for type '%s' mapped with name '%s'. Did you mean '%s'?", resultType, resultName, guessResultType(resultType)), resultElement);
+ }
+
+ String resultClass = config.getClassName();
+ if (resultClass == null) {
+ throw new ConfigurationException("Result type '" + resultType + "' is invalid");
+ }
+
+ Map params = buildResultParams(resultElement, config);
+
+ Set resultNamesSet = commaDelimitedStringToSet(resultName);
+ if (resultNamesSet.isEmpty()) {
+ resultNamesSet.add(resultName);
+ }
+
+ for (String name : resultNamesSet) {
+ ResultConfig resultConfig = new ResultConfig.Builder(name, resultClass)
+ .addParams(params)
+ .location(DomHelper.getLocationObject(element))
+ .build();
+ results.put(resultConfig.getName(), resultConfig);
+ }
+ });
return results;
}
- protected String guessResultType(String type) {
- StringBuilder sb = null;
- if (type != null) {
- sb = new StringBuilder();
- boolean capNext = false;
- for (int x=0; x buildResultParams(Element resultElement, ResultTypeConfig config) {
+ Map resultParams = XmlHelper.getParams(resultElement);
+
+ // maybe we just have a body - therefore a default parameter
+ if (resultParams.isEmpty() && resultElement.getChildNodes().getLength() > 0) {
+ // if something then we add a parameter of 'something' as this is the most used result param
+ resultParams = new LinkedHashMap<>();
+
+ String paramName = config.getDefaultResultParam();
+ if (paramName != null) {
+ StringBuilder paramValue = new StringBuilder();
+ iterateChildren(resultElement, child -> {
+ if (child.getNodeType() == Node.TEXT_NODE) {
+ String val = child.getNodeValue();
+ if (val != null) {
+ paramValue.append(val);
+ }
+ }
+ });
+ String val = paramValue.toString().trim();
+ if (val.length() > 0) {
+ resultParams.put(paramName, val);
}
- sb.append(c);
+ } else {
+ LOG.debug(
+ "No default parameter defined for result [{}] of type [{}] ",
+ config.getName(),
+ config.getClassName());
}
}
- return (sb != null ? sb.toString() : null);
+
+ // create new param map, so that the result param can override the config param
+ Map params = new LinkedHashMap<>();
+ Map configParams = config.getParams();
+ if (configParams != null) {
+ params.putAll(configParams);
+ }
+ params.putAll(resultParams);
+ return params;
+ }
+
+ protected static String guessResultType(String type) {
+ if (type == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ boolean capNext = false;
+ for (int x = 0; x < type.length(); x++) {
+ char c = type.charAt(x);
+ if (c == '-') {
+ capNext = true;
+ continue;
+ } else if (isLowerCase(c) && capNext) {
+ c = toUpperCase(c);
+ capNext = false;
+ }
+ sb.append(c);
+ }
+ return sb.toString();
}
/**
@@ -841,31 +819,28 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
* @return list of exception mapping config objects
*/
protected List buildExceptionMappings(Element element, PackageConfig.Builder packageContext) {
- NodeList exceptionMappingEls = element.getElementsByTagName("exception-mapping");
-
List exceptionMappings = new ArrayList<>();
- for (int i = 0; i < exceptionMappingEls.getLength(); i++) {
- Element ehElement = (Element) exceptionMappingEls.item(i);
-
- if (ehElement.getParentNode().equals(element) || ehElement.getParentNode().getNodeName().equals(element.getNodeName())) {
- String emName = ehElement.getAttribute("name");
- String exceptionClassName = ehElement.getAttribute("exception");
- String exceptionResult = ehElement.getAttribute("result");
-
- Map params = XmlHelper.getParams(ehElement);
-
- if (StringUtils.isEmpty(emName)) {
- emName = exceptionResult;
- }
-
- ExceptionMappingConfig ehConfig = new ExceptionMappingConfig.Builder(emName, exceptionClassName, exceptionResult)
- .addParams(params)
- .location(DomHelper.getLocationObject(ehElement))
- .build();
- exceptionMappings.add(ehConfig);
+ iterateChildrenByTagName(element, "exception-mapping", ehElement -> {
+ Node parNode = ehElement.getParentNode();
+ if (!parNode.equals(element) && !parNode.getNodeName().equals(element.getNodeName())) {
+ return;
}
- }
+
+ String emName = ehElement.getAttribute("name");
+ String exceptionClassName = ehElement.getAttribute("exception");
+ String exceptionResult = ehElement.getAttribute("result");
+ Map params = XmlHelper.getParams(ehElement);
+ if (emName.isEmpty()) {
+ emName = exceptionResult;
+ }
+
+ ExceptionMappingConfig ehConfig = new ExceptionMappingConfig.Builder(emName, exceptionClassName, exceptionResult)
+ .addParams(params)
+ .location(DomHelper.getLocationObject(ehElement))
+ .build();
+ exceptionMappings.add(ehConfig);
+ });
return exceptionMappings;
}
@@ -878,26 +853,10 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
// user defined 'allowed-methods' so used them whatever Strict DMI was enabled or not
allowedMethods = new HashSet<>(packageContext.getGlobalAllowedMethods());
// Fix for WW-5029 (concatenate all possible text node children)
- final Node allowedMethodsNode = allowedMethodsEls.item(0);
- if (allowedMethodsNode != null) {
- final NodeList allowedMethodsChildren = allowedMethodsNode.getChildNodes();
- final StringBuilder allowedMethodsSB = new StringBuilder();
- for (int i = 0; i < allowedMethodsChildren.getLength(); i++) {
- Node allowedMethodsChildNode = allowedMethodsChildren.item(i);
- if (allowedMethodsChildNode != null && allowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
- String childNodeValue = allowedMethodsChildNode.getNodeValue();
- childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
- if (childNodeValue.length() > 0) {
- allowedMethodsSB.append(childNodeValue);
- }
- }
- }
- if (allowedMethodsSB.length() > 0) {
- allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(allowedMethodsSB.toString()));
- }
- }
+ Node allowedMethodsNode = allowedMethodsEls.item(0);
+ addAllowedMethodsToSet(allowedMethodsNode, allowedMethods);
} else if (packageContext.isStrictMethodInvocation()) {
- // user enabled Strict DMI but didn't defined action specific 'allowed-methods' so we use 'global-allowed-methods' only
+ // user enabled Strict DMI but didn't define action specific 'allowed-methods' so we use 'global-allowed-methods' only
allowedMethods = new HashSet<>(packageContext.getGlobalAllowedMethods());
} else {
// Strict DMI is disabled so any method can be called
@@ -929,7 +888,7 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
}
/**
- * Load all of the global results for this package from the XML element.
+ * Load all the global results for this package from the XML element.
*
* @param packageContext the package context
* @param packageElement the given XML element
@@ -950,25 +909,28 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
if (globalAllowedMethodsElms.getLength() > 0) {
Set globalAllowedMethods = new HashSet<>();
// Fix for WW-5029 (concatenate all possible text node children)
- Node globaAllowedMethodsNode = globalAllowedMethodsElms.item(0);
- if (globaAllowedMethodsNode != null) {
- NodeList globaAllowedMethodsChildren = globaAllowedMethodsNode.getChildNodes();
- final StringBuilder globalAllowedMethodsSB = new StringBuilder();
- for (int i = 0; i < globaAllowedMethodsChildren.getLength(); i++) {
- Node globalAllowedMethodsChildNode = globaAllowedMethodsChildren.item(i);
- if (globalAllowedMethodsChildNode != null && globalAllowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
- String childNodeValue = globalAllowedMethodsChildNode.getNodeValue();
- childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
- if (childNodeValue.length() > 0) {
- globalAllowedMethodsSB.append(childNodeValue);
- }
- }
- }
- if (globalAllowedMethodsSB.length() > 0) {
- globalAllowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(globalAllowedMethodsSB.toString()));
+ Node globalAllowedMethodsNode = globalAllowedMethodsElms.item(0);
+ addAllowedMethodsToSet(globalAllowedMethodsNode, globalAllowedMethods);
+ packageContext.addGlobalAllowedMethods(globalAllowedMethods);
+ }
+ }
+
+ protected static void addAllowedMethodsToSet(Node allowedMethodsNode, Set allowedMethodsSet) {
+ if (allowedMethodsNode == null) {
+ return;
+ }
+ StringBuilder allowedMethodsSB = new StringBuilder();
+ iterateChildren(allowedMethodsNode, allowedMethodsChildNode -> {
+ if (allowedMethodsChildNode != null && allowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
+ String childNodeValue = allowedMethodsChildNode.getNodeValue();
+ childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
+ if (childNodeValue.length() > 0) {
+ allowedMethodsSB.append(childNodeValue);
}
}
- packageContext.addGlobalAllowedMethods(globalAllowedMethods);
+ });
+ if (allowedMethodsSB.length() > 0) {
+ allowedMethodsSet.addAll(commaDelimitedStringToSet(allowedMethodsSB.toString()));
}
}
@@ -981,7 +943,7 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
}
/**
- * Load all of the global results for this package from the XML element.
+ * Load all the global results for this package from the XML element.
*
* @param packageContext the package context
* @param packageElement the given XML element
@@ -998,37 +960,26 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
protected InterceptorStackConfig loadInterceptorStack(Element element, PackageConfig.Builder context) throws ConfigurationException {
String name = element.getAttribute("name");
-
InterceptorStackConfig.Builder config = new InterceptorStackConfig.Builder(name)
.location(DomHelper.getLocationObject(element));
- NodeList interceptorRefList = element.getElementsByTagName("interceptor-ref");
- for (int j = 0; j < interceptorRefList.getLength(); j++) {
- Element interceptorRefElement = (Element) interceptorRefList.item(j);
+ iterateChildrenByTagName(element, "interceptor-ref", interceptorRefElement -> {
List interceptors = lookupInterceptorReference(context, interceptorRefElement);
config.addInterceptors(interceptors);
- }
+ });
return config.build();
}
protected void loadInterceptorStacks(Element element, PackageConfig.Builder context) throws ConfigurationException {
- NodeList interceptorStackList = element.getElementsByTagName("interceptor-stack");
-
- for (int i = 0; i < interceptorStackList.getLength(); i++) {
- Element interceptorStackElement = (Element) interceptorStackList.item(i);
-
+ iterateChildrenByTagName(element, "interceptor-stack", interceptorStackElement -> {
InterceptorStackConfig config = loadInterceptorStack(interceptorStackElement, context);
-
context.addInterceptorStackConfig(config);
- }
+ });
}
protected void loadInterceptors(PackageConfig.Builder context, Element element) throws ConfigurationException {
- NodeList interceptorList = element.getElementsByTagName("interceptor");
-
- for (int i = 0; i < interceptorList.getLength(); i++) {
- Element interceptorElement = (Element) interceptorList.item(i);
+ iterateChildrenByTagName(element, "interceptor", interceptorElement -> {
String name = interceptorElement.getAttribute("name");
String className = interceptorElement.getAttribute("class");
@@ -1039,109 +990,103 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
.build();
context.addInterceptorConfig(config);
- }
+ });
loadInterceptorStacks(element, context);
}
private List loadConfigurationFiles(String fileName, Element includeElement) {
+ if (includedFileNames.contains(fileName)) {
+ return emptyList();
+ }
+ LOG.debug("Loading action configurations from: {}", fileName);
+ includedFileNames.add(fileName);
+
+ Iterator urls = getURLs(fileName);
+ if (urls == null) {
+ return emptyList();
+ }
+
+ List docs = getDocs(urls, fileName, includeElement);
+ List finalDocs = getFinalDocs(docs);
+ LOG.debug("Loaded action configuration from: {}", fileName);
+ return finalDocs;
+ }
+
+ private Iterator getURLs(String fileName) {
+ Iterator urls = null;
+ try {
+ urls = getConfigurationUrls(fileName);
+ } catch (IOException ex) {
+ LOG.debug("Ignoring file that does not exist: " + fileName, ex);
+ }
+ if (urls != null && !urls.hasNext()) {
+ LOG.debug("Ignoring file that has no URLs: " + fileName);
+ urls = null;
+ }
+ return urls;
+ }
+
+ private List getDocs(Iterator urls, String fileName, Element includeElement) {
List docs = new ArrayList<>();
- List finalDocs = new ArrayList<>();
- if (!includedFileNames.contains(fileName)) {
- LOG.debug("Loading action configurations from: {}", fileName);
- includedFileNames.add(fileName);
-
- Iterator urls = null;
+ while (urls.hasNext()) {
InputStream is = null;
-
- IOException ioException = null;
- try {
- urls = getConfigurationUrls(fileName);
- } catch (IOException ex) {
- ioException = ex;
- }
-
- if (urls == null || !urls.hasNext()) {
- LOG.debug("Ignoring file that does not exist: " + fileName, ioException);
- return docs;
- }
-
URL url = null;
- while (urls.hasNext()) {
- try {
- url = urls.next();
- is = fileManager.loadFile(url);
-
- InputSource in = new InputSource(is);
-
- in.setSystemId(url.toString());
-
- Document helperDoc = DomHelper.parse(in, dtdMappings);
- if (helperDoc != null) {
- docs.add(helperDoc);
- }
-
- loadedFileUrls.add(url.toString());
- } catch (StrutsException e) {
- if (includeElement != null) {
- throw new ConfigurationException("Unable to load " + url, e, includeElement);
- } else {
- throw new ConfigurationException("Unable to load " + url, e);
- }
- } catch (Exception e) {
- throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- LOG.error("Unable to close input stream", e);
- }
+ try {
+ url = urls.next();
+ is = fileManager.loadFile(url);
+ InputSource in = new InputSource(is);
+ in.setSystemId(url.toString());
+ Document helperDoc = DomHelper.parse(in, dtdMappings);
+ if (helperDoc != null) {
+ docs.add(helperDoc);
+ }
+ loadedFileUrls.add(url.toString());
+ } catch (StrutsException e) {
+ if (includeElement != null) {
+ throw new ConfigurationException("Unable to load " + url, e, includeElement);
+ } else {
+ throw new ConfigurationException("Unable to load " + url, e);
+ }
+ } catch (Exception e) {
+ throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOG.error("Unable to close input stream", e);
}
}
}
+ }
+ return docs;
+ }
- //sort the documents, according to the "order" attribute
- Collections.sort(docs, new Comparator() {
- public int compare(Document doc1, Document doc2) {
- return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
+ private List getFinalDocs(List docs) {
+ List finalDocs = new ArrayList<>();
+ docs.sort(Comparator.comparing(XmlHelper::getLoadOrder));
+ for (Document doc : docs) {
+ iterateElementChildren(doc, child -> {
+ if (!"include".equals(child.getNodeName())) {
+ return;
+ }
+
+ String includeFileName = child.getAttribute("file");
+ if (includeFileName.indexOf('*') == -1) {
+ finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
+ return;
+ }
+ // handleWildCardIncludes(includeFileName, docs, child);
+ ClassPathFinder wildcardFinder = new ClassPathFinder();
+ wildcardFinder.setPattern(includeFileName);
+ Vector wildcardMatches = wildcardFinder.findMatches();
+ for (String match : wildcardMatches) {
+ finalDocs.addAll(loadConfigurationFiles(match, child));
}
});
-
- for (Document doc : docs) {
- Element rootElement = doc.getDocumentElement();
- NodeList children = rootElement.getChildNodes();
- int childSize = children.getLength();
-
- for (int i = 0; i < childSize; i++) {
- Node childNode = children.item(i);
-
- if (childNode instanceof Element) {
- Element child = (Element) childNode;
-
- final String nodeName = child.getNodeName();
-
- if ("include".equals(nodeName)) {
- String includeFileName = child.getAttribute("file");
- if (includeFileName.indexOf('*') != -1) {
- // handleWildCardIncludes(includeFileName, docs, child);
- ClassPathFinder wildcardFinder = new ClassPathFinder();
- wildcardFinder.setPattern(includeFileName);
- Vector wildcardMatches = wildcardFinder.findMatches();
- for (String match : wildcardMatches) {
- finalDocs.addAll(loadConfigurationFiles(match, child));
- }
- } else {
- finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
- }
- }
- }
- }
- finalDocs.add(doc);
- }
-
- LOG.debug("Loaded action configuration from: {}", fileName);
+ finalDocs.add(doc);
}
return finalDocs;
}
@@ -1163,7 +1108,7 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
* Looks up the Interceptor Class from the interceptor-ref name and creates an instance, which is added to the
* provided List, or, if this is a ref to a stack, it adds the Interceptor instances from the List to this stack.
*
- * @param context The PackageConfig to lookup the interceptor from
+ * @param context The PackageConfig to look up the interceptor from
* @param interceptorRefElement Element to pull interceptor ref data from
* @return A list of Interceptor objects
* @throws ConfigurationException in case of configuration errors
@@ -1182,8 +1127,6 @@ public abstract class XmlConfigurationProvider implements ConfigurationProvider
@Override
public String toString() {
- return "XmlConfigurationProvider{" +
- "configFileName='" + configFileName + '\'' +
- '}';
+ return format("XmlConfigurationProvider{configFileName='%s'}", configFileName);
}
}
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
index ad3d43c2f..455f8a449 100644
--- a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
@@ -19,10 +19,26 @@ import com.opensymphony.xwork2.inject.util.ReferenceCache;
import java.io.Serializable;
import java.lang.annotation.Annotation;
-import java.lang.reflect.*;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ReflectPermission;
import java.security.AccessControlException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
/**
* Default {@link Container} implementation.
@@ -39,11 +55,7 @@ class ContainerImpl implements Container {
this.factories = factories;
final Map, Set> map = new HashMap<>();
for (Key> key : factories.keySet()) {
- Set names = map.get(key.getType());
- if (names == null) {
- names = new HashSet<>();
- map.put(key.getType(), names);
- }
+ Set names = map.computeIfAbsent(key.getType(), k -> new HashSet<>());
names.add(key.getName());
}
@@ -63,20 +75,20 @@ class ContainerImpl implements Container {
* Field and method injectors.
*/
final Map, List> injectors =
- new ReferenceCache, List>() {
- @Override
- protected List create(Class> key) {
- List injectors = new ArrayList<>();
- addInjectors(key, injectors);
- return injectors;
- }
- };
+ new ReferenceCache, List>() {
+ @Override
+ protected List create(Class> key) {
+ List injectors = new ArrayList<>();
+ addInjectors(key, injectors);
+ return injectors;
+ }
+ };
/**
* Recursively adds injectors for fields and methods from the given class to the given list. Injects parent classes
* before sub classes.
*/
- void addInjectors(Class clazz, List injectors) {
+ void addInjectors(Class> clazz, List injectors) {
if (clazz == Object.class) {
return;
}
@@ -97,38 +109,24 @@ class ContainerImpl implements Container {
addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
}
- callInContext(new ContextualCallable() {
- public Void call(InternalContext context) {
- for (Injector injector : injectors) {
- injector.inject(context, null);
- }
- return null;
+ callInContext((ContextualCallable) context -> {
+ for (Injector injector : injectors) {
+ injector.inject(context, null);
}
+ return null;
});
}
void addInjectorsForMethods(Method[] methods, boolean statics, List injectors) {
- addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
- new InjectorFactory() {
- public Injector create(ContainerImpl container, Method method,
- String name) throws MissingDependencyException {
- return new MethodInjector(container, method, name);
- }
- });
+ addInjectorsForMembers(Arrays.asList(methods), statics, injectors, MethodInjector::new);
}
void addInjectorsForFields(Field[] fields, boolean statics, List injectors) {
- addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
- new InjectorFactory() {
- public Injector create(ContainerImpl container, Field field,
- String name) throws MissingDependencyException {
- return new FieldInjector(container, field, name);
- }
- });
+ addInjectorsForMembers(Arrays.asList(fields), statics, injectors, FieldInjector::new);
}
void addInjectorsForMembers(
- List members, boolean statics, List injectors, InjectorFactory injectorFactory) {
+ List members, boolean statics, List injectors, InjectorFactory injectorFactory) {
for (M member : members) {
if (isStatic(member) == statics) {
Inject inject = member.getAnnotation(Inject.class);
@@ -148,12 +146,12 @@ class ContainerImpl implements Container {
interface InjectorFactory {
Injector create(ContainerImpl container, M member, String name)
- throws MissingDependencyException;
+ throws MissingDependencyException;
}
/**
* Determines if a given {@link Member} is static or not.
- *
+ *
* @param member checked for the static modifier.
* @return true if member is static, false otherwise.
*/
@@ -163,13 +161,13 @@ class ContainerImpl implements Container {
/**
* Determines if a given {@link Member} is considered to be public for reflection usage or not.
- *
+ *
* @param member checked to see if it is public for reflection usage.
* @return true if member is public for reflection usage, false otherwise.
*/
private static boolean isPublicForReflection(Member member) {
return Modifier.isPublic(member.getModifiers()) &&
- Modifier.isPublic(member.getDeclaringClass().getModifiers());
+ Modifier.isPublic(member.getDeclaringClass().getModifiers());
}
static class FieldInjector implements Injector {
@@ -179,7 +177,7 @@ class ContainerImpl implements Container {
final ExternalContext> externalContext;
public FieldInjector(ContainerImpl container, Field field, String name)
- throws MissingDependencyException {
+ throws MissingDependencyException {
this.field = field;
if (!isPublicForReflection(field) && !field.isAccessible()) {
SecurityManager sm = System.getSecurityManager();
@@ -190,7 +188,7 @@ class ContainerImpl implements Container {
field.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access field: "
- + field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
+ + field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
}
}
@@ -225,8 +223,12 @@ class ContainerImpl implements Container {
* @param parameterTypes parameter types
* @return injections
*/
- ParameterInjector>[]
- getParametersInjectors(M member, Annotation[][] annotations, Class[] parameterTypes, String defaultName) throws MissingDependencyException {
+ ParameterInjector>[] getParametersInjectors(
+ M member,
+ Annotation[][] annotations,
+ Class>[] parameterTypes,
+ String defaultName
+ ) throws MissingDependencyException {
final List> parameterInjectors = new ArrayList<>();
final Iterator annotationsIterator = Arrays.asList(annotations).iterator();
@@ -247,12 +249,11 @@ class ContainerImpl implements Container {
}
final ExternalContext externalContext = ExternalContext.newInstance(member, key, this);
- return new ParameterInjector(externalContext, factory);
+ return new ParameterInjector<>(externalContext, factory);
}
- @SuppressWarnings("unchecked")
private ParameterInjector>[] toArray(List> parameterInjections) {
- return parameterInjections.toArray(new ParameterInjector[parameterInjections.size()]);
+ return parameterInjections.toArray(new ParameterInjector[0]);
}
/**
@@ -261,7 +262,7 @@ class ContainerImpl implements Container {
Inject findInject(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Inject.class) {
- return Inject.class.cast(annotation);
+ return (Inject) annotation;
}
}
return null;
@@ -283,7 +284,7 @@ class ContainerImpl implements Container {
method.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access method: "
- + name + "(" + method.getName() + ")", e);
+ + name + "(" + method.getName() + ")", e);
}
}
@@ -292,7 +293,7 @@ class ContainerImpl implements Container {
throw new DependencyException(method + " has no parameters to inject.");
}
parameterInjectors = container.getParametersInjectors(
- method, method.getParameterAnnotations(), parameterTypes, name);
+ method, method.getParameterAnnotations(), parameterTypes, name);
}
@Override
@@ -305,14 +306,12 @@ class ContainerImpl implements Container {
}
}
- Map, ConstructorInjector> constructors =
- new ReferenceCache, ConstructorInjector>() {
- @Override
- @SuppressWarnings("unchecked")
- protected ConstructorInjector> create(Class> implementation) {
- return new ConstructorInjector(ContainerImpl.this, implementation);
- }
- };
+ Map, ConstructorInjector>> constructors = new ReferenceCache, ConstructorInjector>>() {
+ @Override
+ protected ConstructorInjector> create(Class> implementation) {
+ return new ConstructorInjector<>(ContainerImpl.this, implementation);
+ }
+ };
static class ConstructorInjector {
@@ -334,7 +333,7 @@ class ContainerImpl implements Container {
constructor.setAccessible(true);
} catch (AccessControlException e) {
throw new DependencyException("Security manager in use, could not access constructor: "
- + implementation.getName() + "(" + constructor.getName() + ")", e);
+ + implementation.getName() + "(" + constructor.getName() + ")", e);
}
}
@@ -359,14 +358,14 @@ class ContainerImpl implements Container {
}
ParameterInjector>[] constructParameterInjector(
- Inject inject, ContainerImpl container, Constructor constructor) throws MissingDependencyException {
+ Inject inject, ContainerImpl container, Constructor constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
- ? null // default constructor.
- : container.getParametersInjectors(
- constructor,
- constructor.getParameterAnnotations(),
- constructor.getParameterTypes(),
- inject.value()
+ ? null // default constructor.
+ : container.getParametersInjectors(
+ constructor,
+ constructor.getParameterAnnotations(),
+ constructor.getParameterTypes(),
+ inject.value()
);
}
@@ -378,7 +377,7 @@ class ContainerImpl implements Container {
if (constructor.getAnnotation(Inject.class) != null) {
if (found != null) {
throw new DependencyException("More than one constructor annotated"
- + " with @Inject found in " + implementation + ".");
+ + " with @Inject found in " + implementation + ".");
}
found = constructor;
}
@@ -466,7 +465,7 @@ class ContainerImpl implements Container {
}
}
- private static Object[] getParameters(Member member, InternalContext context, ParameterInjector[] parameterInjectors) {
+ private static Object[] getParameters(Member member, InternalContext context, ParameterInjector>[] parameterInjectors) {
if (parameterInjectors == null) {
return null;
}
@@ -494,13 +493,12 @@ class ContainerImpl implements Container {
}
}
- @SuppressWarnings("unchecked")
T getInstance(Class type, String name, InternalContext context) {
final ExternalContext> previous = context.getExternalContext();
final Key key = Key.newInstance(type, name);
context.setExternalContext(ExternalContext.newInstance(null, key, this));
try {
- final InternalFactory o = getFactory(key);
+ final InternalFactory extends T> o = getFactory(key);
if (o != null) {
return getFactory(key).create(context);
} else {
@@ -517,39 +515,25 @@ class ContainerImpl implements Container {
@Override
public void inject(final Object o) {
- callInContext(new ContextualCallable() {
- public Void call(InternalContext context) {
- inject(o, context);
- return null;
- }
+ callInContext((ContextualCallable) context -> {
+ inject(o, context);
+ return null;
});
}
@Override
public T inject(final Class implementation) {
- return callInContext(new ContextualCallable() {
- public T call(InternalContext context) {
- return inject(implementation, context);
- }
- });
+ return callInContext(context -> inject(implementation, context));
}
@Override
public T getInstance(final Class type, final String name) {
- return callInContext(new ContextualCallable() {
- public T call(InternalContext context) {
- return getInstance(type, name, context);
- }
- });
+ return callInContext(context -> getInstance(type, name, context));
}
@Override
public T getInstance(final Class type) {
- return callInContext(new ContextualCallable() {
- public T call(InternalContext context) {
- return getInstance(type, context);
- }
- });
+ return callInContext(context -> getInstance(type, context));
}
@Override
@@ -561,12 +545,7 @@ class ContainerImpl implements Container {
return names;
}
- ThreadLocal