struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lukaszlen...@apache.org
Subject [47/57] [partial] struts git commit: Merges xwork packages into struts
Date Wed, 17 Jun 2015 21:09:47 GMT
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorListHolder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorListHolder.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorListHolder.java
new file mode 100644
index 0000000..4323116
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorListHolder.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.entities;
+
+import java.util.List;
+
+/**
+ * InterceptorListHolder
+ *
+ * @author Jason Carreira
+ *         Created Jun 1, 2003 1:02:48 AM
+ */
+public interface InterceptorListHolder {
+
+    InterceptorListHolder addInterceptor(InterceptorMapping interceptor);
+
+    InterceptorListHolder addInterceptors(List<InterceptorMapping> interceptors);
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorLocator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorLocator.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorLocator.java
new file mode 100644
index 0000000..aa74960
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorLocator.java
@@ -0,0 +1,14 @@
+package com.opensymphony.xwork2.config.entities;
+
+/**
+ * Defines an object that can be used to retrieve interceptor configuration
+ */
+public interface InterceptorLocator {
+
+    /**
+     * Gets an interceptor configuration object.
+     * @param name The interceptor or interceptor stack name
+     * @return Either an {@link InterceptorConfig} or {@link InterceptorStackConfig} object
+     */
+    Object getInterceptorConfig(String name);
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorMapping.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorMapping.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorMapping.java
new file mode 100644
index 0000000..846575e
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorMapping.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.xwork2.config.entities;
+
+import com.opensymphony.xwork2.interceptor.Interceptor;
+
+import java.io.Serializable;
+
+/**
+ * <code>InterceptorMapping</code>
+ *
+ * @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
+ * @version $Id$
+ */
+public class InterceptorMapping implements Serializable {
+
+    private String name;
+    private Interceptor interceptor;
+
+    public InterceptorMapping(String name, Interceptor interceptor) {
+        this.name = name;
+        this.interceptor = interceptor;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Interceptor getInterceptor() {
+        return interceptor;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final InterceptorMapping that = (InterceptorMapping) o;
+
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = (name != null ? name.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "InterceptorMapping: [" + name + "] => [" + interceptor.getClass().getName() + ']';
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorStackConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorStackConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorStackConfig.java
new file mode 100644
index 0000000..b9e2f78
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/InterceptorStackConfig.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.entities;
+
+import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Configuration for InterceptorStack.
+ * <p/>
+ * In the xml configuration file this is defined as the <code>interceptor-stack</code> tag.
+ *
+ * @author Mike
+ * @author Rainer Hermanns
+ */
+public class InterceptorStackConfig extends Located implements Serializable {
+
+    private static final long serialVersionUID = 2897260918170270343L;
+
+    /**
+     * A list of InterceptorMapping object
+     */
+    protected List<InterceptorMapping> interceptors;
+    protected String name;
+
+    /**
+     * Creates an InterceptorStackConfig object.
+     */
+    protected InterceptorStackConfig() {
+        this.interceptors = new ArrayList<InterceptorMapping>();
+    }
+
+    /**
+     * Creates an InterceptorStackConfig object with a particular <code>name</code>.
+     *
+     * @param name
+     */
+    protected InterceptorStackConfig(InterceptorStackConfig orig) {
+        this.name = orig.name;
+        this.interceptors = new ArrayList<>(orig.interceptors);
+        this.location = orig.location;
+    }
+
+
+    /**
+     * Returns a <code>Collection</code> of InterceptorMapping objects.
+     *
+     * @return
+     */
+    public Collection<InterceptorMapping> getInterceptors() {
+        return interceptors;
+    }
+
+    /**
+     * Get the name of this interceptor stack configuration.
+     *
+     * @return String
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * An InterceptorStackConfig object is equals with <code>o</code> only if
+     * <ul>
+     * <li>o is an InterceptorStackConfig object</li>
+     * <li>both names are equals</li>
+     * <li>all of their <code>InterceptorMapping</code>s are equals</li>
+     * </ul>
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof InterceptorStackConfig)) {
+            return false;
+        }
+
+        final InterceptorStackConfig interceptorStackConfig = (InterceptorStackConfig) o;
+
+        if ((interceptors != null) ? (!interceptors.equals(interceptorStackConfig.interceptors)) : (interceptorStackConfig.interceptors != null)) {
+            return false;
+        }
+
+        if ((name != null) ? (!name.equals(interceptorStackConfig.name)) : (interceptorStackConfig.name != null)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Generate hashcode based on <code>InterceptorStackConfig</code>'s name and its
+     * <code>InterceptorMapping</code>s.
+     */
+    @Override
+    public int hashCode() {
+        int result;
+        result = ((name != null) ? name.hashCode() : 0);
+        result = (29 * result) + ((interceptors != null) ? interceptors.hashCode() : 0);
+
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "InterceptorStackConfig: [" + name + "] contains " + interceptors;
+    }
+
+    /**
+     * The builder for this object.  An instance of this object is the only way to construct a new instance.  The
+     * purpose is to enforce the immutability of the object.  The methods are structured in a way to support chaining.
+     * After setting any values you need, call the {@link #build()} method to create the object.
+     */
+    public static class Builder implements InterceptorListHolder {
+        protected InterceptorStackConfig target;
+
+        public Builder(String name) {
+            target = new InterceptorStackConfig();
+            target.name = name;
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        /**
+         * Add an <code>InterceptorMapping</code> object.
+         */
+        public Builder addInterceptor(InterceptorMapping interceptor) {
+            target.interceptors.add(interceptor);
+            return this;
+        }
+
+        /**
+         * Add a List of <code>InterceptorMapping</code> objects.
+         */
+        public Builder addInterceptors(List<InterceptorMapping> interceptors) {
+            target.interceptors.addAll(interceptors);
+            return this;
+        }
+
+        public Builder location(Location loc) {
+            target.location = loc;
+            return this;
+        }
+
+        public InterceptorStackConfig build() {
+            embalmTarget();
+            InterceptorStackConfig result = target;
+            target = new InterceptorStackConfig(target);
+            return result;
+        }
+
+        protected void embalmTarget() {
+            target.interceptors = Collections.unmodifiableList(target.interceptors);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..d4a3c5c
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
@@ -0,0 +1,615 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 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.
+ * <p/>
+ * In the xml configuration file this is defined as the <code>package</code> tag.
+ *
+ * @author Rainer Hermanns
+ * @version $Revision$
+ */
+public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
+
+    private static final Logger LOG = LogManager.getLogger(PackageConfig.class);
+
+    protected Map<String, ActionConfig> actionConfigs;
+    protected Map<String, ResultConfig> globalResultConfigs;
+    protected Map<String, Object> interceptorConfigs;
+    protected Map<String, ResultTypeConfig> resultTypeConfigs;
+    protected List<ExceptionMappingConfig> globalExceptionMappingConfigs;
+    protected List<PackageConfig> parents;
+    protected String defaultInterceptorRef;
+    protected String defaultActionRef;
+    protected String defaultResultType;
+    protected String defaultClassRef;
+    protected String name;
+    protected String namespace = "";
+    protected boolean isAbstract = false;
+    protected boolean needsRefresh;
+
+    protected PackageConfig(String name) {
+        this.name = name;
+        actionConfigs = new LinkedHashMap<>();
+        globalResultConfigs = new LinkedHashMap<>();
+        interceptorConfigs = new LinkedHashMap<>();
+        resultTypeConfigs = new LinkedHashMap<>();
+        globalExceptionMappingConfigs = new ArrayList<>();
+        parents = new ArrayList<>();
+    }
+
+    protected PackageConfig(PackageConfig orig) {
+        this.defaultInterceptorRef = orig.defaultInterceptorRef;
+        this.defaultActionRef = orig.defaultActionRef;
+        this.defaultResultType = orig.defaultResultType;
+        this.defaultClassRef = orig.defaultClassRef;
+        this.name = orig.name;
+        this.namespace = orig.namespace;
+        this.isAbstract = orig.isAbstract;
+        this.needsRefresh = orig.needsRefresh;
+        this.actionConfigs = new LinkedHashMap<>(orig.actionConfigs);
+        this.globalResultConfigs = new LinkedHashMap<>(orig.globalResultConfigs);
+        this.interceptorConfigs = new LinkedHashMap<>(orig.interceptorConfigs);
+        this.resultTypeConfigs = new LinkedHashMap<>(orig.resultTypeConfigs);
+        this.globalExceptionMappingConfigs = new ArrayList<>(orig.globalExceptionMappingConfigs);
+        this.parents = new ArrayList<>(orig.parents);
+        this.location = orig.location;
+    }
+
+    public boolean isAbstract() {
+        return isAbstract;
+    }
+
+    public Map<String, ActionConfig> getActionConfigs() {
+        return actionConfigs;
+    }
+
+    /**
+     * returns the Map of all the ActionConfigs available in the current package.
+     * ActionConfigs defined in ancestor packages will be included in this Map.
+     *
+     * @return a Map of ActionConfig Objects with the action name as the key
+     * @see ActionConfig
+     */
+    public Map<String, ActionConfig> getAllActionConfigs() {
+        Map<String, ActionConfig> retMap = new LinkedHashMap<>();
+
+        if (!parents.isEmpty()) {
+            for (PackageConfig parent : parents) {
+                retMap.putAll(parent.getAllActionConfigs());
+            }
+        }
+
+        retMap.putAll(getActionConfigs());
+
+        return retMap;
+    }
+
+    /**
+     * returns the Map of all the global ResultConfigs available in the current package.
+     * Global ResultConfigs defined in ancestor packages will be included in this Map.
+     *
+     * @return a Map of Result Objects with the result name as the key
+     * @see ResultConfig
+     */
+    public Map<String, ResultConfig> getAllGlobalResults() {
+        Map<String, ResultConfig> retMap = new LinkedHashMap<>();
+
+        if (!parents.isEmpty()) {
+            for (PackageConfig parentConfig : parents) {
+                retMap.putAll(parentConfig.getAllGlobalResults());
+            }
+        }
+
+        retMap.putAll(getGlobalResultConfigs());
+
+        return retMap;
+    }
+
+    /**
+     * returns the Map of all InterceptorConfigs and InterceptorStackConfigs available in the current package.
+     * InterceptorConfigs defined in ancestor packages will be included in this Map.
+     *
+     * @return a Map of InterceptorConfig and InterceptorStackConfig Objects with the ref-name as the key
+     * @see InterceptorConfig
+     * @see InterceptorStackConfig
+     */
+    public Map<String, Object> getAllInterceptorConfigs() {
+        Map<String, Object> retMap = new LinkedHashMap<>();
+
+        if (!parents.isEmpty()) {
+            for (PackageConfig parentContext : parents) {
+                retMap.putAll(parentContext.getAllInterceptorConfigs());
+            }
+        }
+
+        retMap.putAll(getInterceptorConfigs());
+
+        return retMap;
+    }
+
+    /**
+     * returns the Map of all the ResultTypeConfigs available in the current package.
+     * ResultTypeConfigs defined in ancestor packages will be included in this Map.
+     *
+     * @return a Map of ResultTypeConfig Objects with the result type name as the key
+     * @see ResultTypeConfig
+     */
+    public Map<String, ResultTypeConfig> getAllResultTypeConfigs() {
+        Map<String, ResultTypeConfig> retMap = new LinkedHashMap<>();
+
+        if (!parents.isEmpty()) {
+            for (PackageConfig parentContext : parents) {
+                retMap.putAll(parentContext.getAllResultTypeConfigs());
+            }
+        }
+
+        retMap.putAll(getResultTypeConfigs());
+
+        return retMap;
+    }
+
+    /**
+     * returns the List of all the ExceptionMappingConfigs available in the current package.
+     * ExceptionMappingConfigs defined in ancestor packages will be included in this list.
+     *
+     * @return a List of ExceptionMappingConfigs Objects with the result type name as the key
+     * @see ExceptionMappingConfig
+     */
+    public List<ExceptionMappingConfig> getAllExceptionMappingConfigs() {
+        List<ExceptionMappingConfig> allExceptionMappings = new ArrayList<>();
+
+        if (!parents.isEmpty()) {
+            for (PackageConfig parentContext : parents) {
+                allExceptionMappings.addAll(parentContext.getAllExceptionMappingConfigs());
+            }
+        }
+
+        allExceptionMappings.addAll(getGlobalExceptionMappingConfigs());
+
+        return allExceptionMappings;
+    }
+
+
+    public String getDefaultInterceptorRef() {
+        return defaultInterceptorRef;
+    }
+
+    public String getDefaultActionRef() {
+        return defaultActionRef;
+    }
+
+    public String getDefaultClassRef() {
+        if ((defaultClassRef == null) && !parents.isEmpty()) {
+            for (PackageConfig parent : parents) {
+                String parentDefault = parent.getDefaultClassRef();
+                if (parentDefault != null) {
+                    return parentDefault;
+                }
+            }
+        }
+        return defaultClassRef;
+    }
+
+    /**
+     * Returns the default result type for this package.
+     */
+    public String getDefaultResultType() {
+        return defaultResultType;
+    }
+
+    /**
+     * gets the default interceptor-ref name. If this is not set on this PackageConfig, it searches the parent
+     * PackageConfigs in order until it finds one.
+     */
+    public String getFullDefaultInterceptorRef() {
+        if ((defaultInterceptorRef == null) && !parents.isEmpty()) {
+            for (PackageConfig parent : parents) {
+                String parentDefault = parent.getFullDefaultInterceptorRef();
+
+                if (parentDefault != null) {
+                    return parentDefault;
+                }
+            }
+        }
+
+        return defaultInterceptorRef;
+    }
+
+    /**
+     * gets the default action-ref name. If this is not set on this PackageConfig, it searches the parent
+     * PackageConfigs in order until it finds one.
+     */
+    public String getFullDefaultActionRef() {
+        if ((defaultActionRef == null) && !parents.isEmpty()) {
+            for (PackageConfig parent : parents) {
+                String parentDefault = parent.getFullDefaultActionRef();
+
+                if (parentDefault != null) {
+                    return parentDefault;
+                }
+            }
+        }
+        return defaultActionRef;
+    }
+
+    /**
+     * Returns the default result type for this package.
+     * <p/>
+     * If there is no default result type, but this package has parents - we will try to
+     * look up the default result type of a parent.
+     */
+    public String getFullDefaultResultType() {
+        if ((defaultResultType == null) && !parents.isEmpty()) {
+            for (PackageConfig parent : parents) {
+                String parentDefault = parent.getFullDefaultResultType();
+
+                if (parentDefault != null) {
+                    return parentDefault;
+                }
+            }
+        }
+
+        return defaultResultType;
+    }
+
+    /**
+     * gets the global ResultConfigs local to this package
+     *
+     * @return a Map of ResultConfig objects keyed by result name
+     * @see ResultConfig
+     */
+    public Map<String, ResultConfig> getGlobalResultConfigs() {
+        return globalResultConfigs;
+    }
+
+    /**
+     * gets the InterceptorConfigs and InterceptorStackConfigs local to this package
+     *
+     * @return a Map of InterceptorConfig and InterceptorStackConfig objects keyed by ref-name
+     * @see InterceptorConfig
+     * @see InterceptorStackConfig
+     */
+    public Map<String, Object> getInterceptorConfigs() {
+        return interceptorConfigs;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public List<PackageConfig> getParents() {
+        return new ArrayList<>(parents);
+    }
+
+    /**
+     * gets the ResultTypeConfigs local to this package
+     *
+     * @return a Map of ResultTypeConfig objects keyed by result name
+     * @see ResultTypeConfig
+     */
+    public Map<String, ResultTypeConfig> getResultTypeConfigs() {
+        return resultTypeConfigs;
+    }
+
+
+    public boolean isNeedsRefresh() {
+        return needsRefresh;
+    }
+
+    /**
+     * gets the ExceptionMappingConfigs local to this package
+     *
+     * @return a Map of ExceptionMappingConfig objects keyed by result name
+     * @see ExceptionMappingConfig
+     */
+    public List<ExceptionMappingConfig> getGlobalExceptionMappingConfigs() {
+        return globalExceptionMappingConfigs;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PackageConfig)) {
+            return false;
+        }
+
+        final PackageConfig packageConfig = (PackageConfig) o;
+
+        if (isAbstract != packageConfig.isAbstract) {
+            return false;
+        }
+
+        if ((actionConfigs != null) ? (!actionConfigs.equals(packageConfig.actionConfigs)) : (packageConfig.actionConfigs != null)) {
+            return false;
+        }
+
+        if ((defaultResultType != null) ? (!defaultResultType.equals(packageConfig.defaultResultType)) : (packageConfig.defaultResultType != null)) {
+            return false;
+        }
+
+        if ((defaultClassRef != null) ? (!defaultClassRef.equals(packageConfig.defaultClassRef)) : (packageConfig.defaultClassRef != null)) {
+            return false;
+        }
+
+        if ((globalResultConfigs != null) ? (!globalResultConfigs.equals(packageConfig.globalResultConfigs)) : (packageConfig.globalResultConfigs != null)) {
+            return false;
+        }
+
+        if ((interceptorConfigs != null) ? (!interceptorConfigs.equals(packageConfig.interceptorConfigs)) : (packageConfig.interceptorConfigs != null)) {
+            return false;
+        }
+
+        if ((name != null) ? (!name.equals(packageConfig.name)) : (packageConfig.name != null)) {
+            return false;
+        }
+
+        if ((namespace != null) ? (!namespace.equals(packageConfig.namespace)) : (packageConfig.namespace != null)) {
+            return false;
+        }
+
+        if ((parents != null) ? (!parents.equals(packageConfig.parents)) : (packageConfig.parents != null)) {
+            return false;
+        }
+
+        if ((resultTypeConfigs != null) ? (!resultTypeConfigs.equals(packageConfig.resultTypeConfigs)) : (packageConfig.resultTypeConfigs != null)) {
+            return false;
+        }
+
+        if ((globalExceptionMappingConfigs != null) ? (!globalExceptionMappingConfigs.equals(packageConfig.globalExceptionMappingConfigs)) : (packageConfig.globalExceptionMappingConfigs != null)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = ((name != null) ? name.hashCode() : 0);
+        result = (29 * result) + ((parents != null) ? parents.hashCode() : 0);
+        result = (29 * result) + ((actionConfigs != null) ? actionConfigs.hashCode() : 0);
+        result = (29 * result) + ((globalResultConfigs != null) ? globalResultConfigs.hashCode() : 0);
+        result = (29 * result) + ((interceptorConfigs != null) ? interceptorConfigs.hashCode() : 0);
+        result = (29 * result) + ((resultTypeConfigs != null) ? resultTypeConfigs.hashCode() : 0);
+        result = (29 * result) + ((globalExceptionMappingConfigs != null) ? globalExceptionMappingConfigs.hashCode() : 0);
+        result = (29 * result) + ((defaultResultType != null) ? defaultResultType.hashCode() : 0);
+        result = (29 * result) + ((defaultClassRef != null) ? defaultClassRef.hashCode() : 0);
+        result = (29 * result) + ((namespace != null) ? namespace.hashCode() : 0);
+        result = (29 * result) + (isAbstract ? 1 : 0);
+
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "PackageConfig: [" + name + "] for namespace [" + namespace + "] with parents [" + parents + "]";
+    }
+
+    public int compareTo(Object o) {
+        PackageConfig other = (PackageConfig) o;
+        String full = namespace + "!" + name;
+        String otherFull = other.namespace + "!" + other.name;
+
+        // note, this isn't perfect (could come from different parents), but it is "good enough"
+        return full.compareTo(otherFull);
+    }
+
+    public Object getInterceptorConfig(String name) {
+        return getAllInterceptorConfigs().get(name);
+    }
+
+    /**
+     * The builder for this object.  An instance of this object is the only way to construct a new instance.  The
+     * purpose is to enforce the immutability of the object.  The methods are structured in a way to support chaining.
+     * After setting any values you need, call the {@link #build()} method to create the object.
+     */
+    public static class Builder implements InterceptorLocator {
+
+        protected PackageConfig target;
+        private boolean strictDMI;
+
+        public Builder(String name) {
+            target = new PackageConfig(name);
+        }
+
+        public Builder(PackageConfig config) {
+            target = new PackageConfig(config);
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        public Builder isAbstract(boolean isAbstract) {
+            target.isAbstract = isAbstract;
+            return this;
+        }
+
+        public Builder defaultInterceptorRef(String name) {
+            target.defaultInterceptorRef = name;
+            return this;
+        }
+
+        public Builder defaultActionRef(String name) {
+            target.defaultActionRef = name;
+            return this;
+        }
+
+        public Builder defaultClassRef(String defaultClassRef) {
+            target.defaultClassRef = defaultClassRef;
+            return this;
+        }
+
+        /**
+         * sets the default Result type for this package
+         *
+         * @param defaultResultType
+         */
+        public Builder defaultResultType(String defaultResultType) {
+            target.defaultResultType = defaultResultType;
+            return this;
+        }
+
+        public Builder namespace(String namespace) {
+            if (namespace == null) {
+                target.namespace = "";
+            } else {
+                target.namespace = namespace;
+            }
+            return this;
+        }
+
+        public Builder needsRefresh(boolean needsRefresh) {
+            target.needsRefresh = needsRefresh;
+            return this;
+        }
+
+        public Builder addActionConfig(String name, ActionConfig action) {
+            target.actionConfigs.put(name, action);
+            return this;
+        }
+
+        public Builder addParents(List<PackageConfig> parents) {
+            for (PackageConfig config : parents) {
+                addParent(config);
+            }
+            return this;
+        }
+
+        public Builder addGlobalResultConfig(ResultConfig resultConfig) {
+            target.globalResultConfigs.put(resultConfig.getName(), resultConfig);
+            return this;
+        }
+
+        public Builder addGlobalResultConfigs(Map<String, ResultConfig> resultConfigs) {
+            target.globalResultConfigs.putAll(resultConfigs);
+            return this;
+        }
+
+        public Builder addExceptionMappingConfig(ExceptionMappingConfig exceptionMappingConfig) {
+            target.globalExceptionMappingConfigs.add(exceptionMappingConfig);
+            return this;
+        }
+
+        public Builder addGlobalExceptionMappingConfigs(List<ExceptionMappingConfig> exceptionMappingConfigs) {
+            target.globalExceptionMappingConfigs.addAll(exceptionMappingConfigs);
+            return this;
+        }
+
+        public Builder addInterceptorConfig(InterceptorConfig config) {
+            target.interceptorConfigs.put(config.getName(), config);
+            return this;
+        }
+
+        public Builder addInterceptorStackConfig(InterceptorStackConfig config) {
+            target.interceptorConfigs.put(config.getName(), config);
+            return this;
+        }
+
+        public Builder addParent(PackageConfig parent) {
+            target.parents.add(0, parent);
+            return this;
+        }
+
+        public Builder addResultTypeConfig(ResultTypeConfig config) {
+            target.resultTypeConfigs.put(config.getName(), config);
+            return this;
+        }
+
+        public Builder location(Location loc) {
+            target.location = loc;
+            return this;
+        }
+
+        public boolean isNeedsRefresh() {
+            return target.needsRefresh;
+        }
+
+        public String getDefaultClassRef() {
+            return target.defaultClassRef;
+        }
+
+        public String getName() {
+            return target.name;
+        }
+
+        public String getNamespace() {
+            return target.namespace;
+        }
+
+        public String getFullDefaultResultType() {
+            return target.getFullDefaultResultType();
+        }
+
+        public ResultTypeConfig getResultType(String type) {
+            return target.getAllResultTypeConfigs().get(type);
+        }
+
+        public Object getInterceptorConfig(String name) {
+            return target.getAllInterceptorConfigs().get(name);
+        }
+
+        public Builder strictMethodInvocation(boolean strict) {
+            strictDMI = strict;
+            return this;
+        }
+
+        public boolean isStrictMethodInvocation() {
+            return strictDMI;
+        }
+
+        public PackageConfig build() {
+            embalmTarget();
+            PackageConfig result = target;
+            target = new PackageConfig(result);
+            return result;
+        }
+
+        protected void embalmTarget() {
+            target.actionConfigs = Collections.unmodifiableMap(target.actionConfigs);
+            target.globalResultConfigs = Collections.unmodifiableMap(target.globalResultConfigs);
+            target.interceptorConfigs = Collections.unmodifiableMap(target.interceptorConfigs);
+            target.resultTypeConfigs = Collections.unmodifiableMap(target.resultTypeConfigs);
+            target.globalExceptionMappingConfigs = Collections.unmodifiableList(target.globalExceptionMappingConfigs);
+            target.parents = Collections.unmodifiableList(target.parents);
+        }
+
+        @Override
+        public String toString() {
+            return "[BUILDER] " + target.toString();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/Parameterizable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/Parameterizable.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/Parameterizable.java
new file mode 100644
index 0000000..42b6cb3
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/Parameterizable.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.entities;
+
+import java.util.Map;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * <p/>
+ * Actions implementing Parameterizable will receive a map of the static parameters defined in the action
+ * configuration.
+ * <p/>
+ * <p/> The {@link com.opensymphony.xwork2.interceptor.StaticParametersInterceptor} must be in the action's interceptor
+ * queue for this to work.
+ * <p/>
+ * <!-- END SNIPPET: javadoc -->
+ *
+ * @author Jason Carreira
+ */
+public interface Parameterizable {
+
+    public void addParam(String name, String value);
+
+    void setParams(Map<String, String> params);
+
+    Map<String, String> getParams();
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultConfig.java
new file mode 100644
index 0000000..b9ed588
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultConfig.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.entities;
+
+import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+/**
+ * Configuration for Result.
+ * <p/>
+ * In the xml configuration file this is defined as the <code>result</code> tag.
+ *
+ * @author Mike
+ */
+public class ResultConfig extends Located implements Serializable {
+
+    protected Map<String,String> params;
+    protected String className;
+    protected String name;
+
+    protected ResultConfig(String name, String className) {
+        this.name = name;
+        this.className = className;
+        params = new LinkedHashMap<>();
+    }
+
+    protected ResultConfig(ResultConfig orig) {
+        this.params = orig.params;
+        this.name = orig.name;
+        this.className = orig.className;
+        this.location = orig.location;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Map<String,String> getParams() {
+        return params;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof ResultConfig)) {
+            return false;
+        }
+
+        final ResultConfig resultConfig = (ResultConfig) o;
+
+        if ((className != null) ? (!className.equals(resultConfig.className)) : (resultConfig.className != null)) {
+            return false;
+        }
+
+        if ((name != null) ? (!name.equals(resultConfig.name)) : (resultConfig.name != null)) {
+            return false;
+        }
+
+        if ((params != null) ? (!params.equals(resultConfig.params)) : (resultConfig.params != null)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = ((name != null) ? name.hashCode() : 0);
+        result = (29 * result) + ((className != null) ? className.hashCode() : 0);
+        result = (29 * result) + ((params != null) ? params.hashCode() : 0);
+
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ResultConfig: [" + name + "] => [" + className + "] with params " + params;
+    }
+
+    /**
+     * The builder for this object.  An instance of this object is the only way to construct a new instance.  The
+     * purpose is to enforce the immutability of the object.  The methods are structured in a way to support chaining.
+     * After setting any values you need, call the {@link #build()} method to create the object.
+     */
+    public static final class Builder {
+        protected ResultConfig target;
+
+        public Builder(String name, String className) {
+            target = new ResultConfig(name, className);
+        }
+
+        public Builder(ResultConfig orig) {
+            target = new ResultConfig(orig);
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        public Builder className(String name) {
+            target.className = name;
+            return this;
+        }
+
+         public Builder addParam(String name, String value) {
+            target.params.put(name, value);
+            return this;
+        }
+
+        public Builder addParams(Map<String,String> params) {
+            target.params.putAll(params);
+            return this;
+        }
+
+        public Builder location(Location loc) {
+            target.location = loc;
+            return this;
+        }
+
+        public ResultConfig build() {
+            embalmTarget();
+            ResultConfig result = target;
+            target = new ResultConfig(target);
+            return result;
+        }
+
+        protected void embalmTarget() {
+            target.params = Collections.unmodifiableMap(target.params);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultTypeConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultTypeConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultTypeConfig.java
new file mode 100644
index 0000000..96ff4ff
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/ResultTypeConfig.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.entities;
+
+import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+/**
+ * Configuration class for result types.
+ * <p/>
+ * In the xml configuration file this is defined as the <code>result-type</code> tag.
+ *
+ * @author Mike
+ * @author Rainer Hermanns
+ * @author Neo
+ */
+public class ResultTypeConfig extends Located implements Serializable {
+
+    protected String className;
+    protected String name;
+    protected String defaultResultParam;
+    protected Map<String,String> params;
+
+    protected ResultTypeConfig(String name, String className) {
+        this.name = name;
+        this.className = className;
+        params = new LinkedHashMap<>();
+    }
+
+    protected ResultTypeConfig(ResultTypeConfig orig) {
+        this.name = orig.name;
+        this.className = orig.className;
+        this.defaultResultParam = orig.defaultResultParam;
+        this.params = orig.params;
+        this.location = orig.location;
+    }
+
+    public void setDefaultResultParam(String defaultResultParam) {
+        this.defaultResultParam = defaultResultParam;
+    }
+    
+    public String getDefaultResultParam() {
+        return this.defaultResultParam;
+    }
+
+    /**
+     * @deprecated Since 2.1, use {@link #getClassName()} instead
+     */
+    @Deprecated public String getClazz() {
+        return className;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Map<String,String> getParams() {
+        return this.params;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final ResultTypeConfig that = (ResultTypeConfig) o;
+
+        if (className != null ? !className.equals(that.className) : that.className != null) return false;
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (params != null ? !params.equals(that.params) : that.params != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = (className != null ? className.hashCode() : 0);
+        result = 29 * result + (name != null ? name.hashCode() : 0);
+        result = 29 * result + (params != null ? params.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ResultTypeConfig: [" + name + "] => [" + className + "] " +
+                "with defaultParam [" + defaultResultParam + "] with params " + params;
+    }
+
+    /**
+     * The builder for this object.  An instance of this object is the only way to construct a new instance.  The
+     * purpose is to enforce the immutability of the object.  The methods are structured in a way to support chaining.
+     * After setting any values you need, call the {@link #build()} method to create the object.
+     */
+    public static final class Builder {
+        protected ResultTypeConfig target;
+
+        public Builder(String name, String className) {
+            target = new ResultTypeConfig(name, className);
+        }
+
+        public Builder(ResultTypeConfig orig) {
+            target = new ResultTypeConfig(orig);
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        public Builder className(String name) {
+            target.className = name;
+            return this;
+        }
+
+         public Builder addParam(String name, String value) {
+            target.params.put(name, value);
+            return this;
+        }
+
+        public Builder addParams(Map<String,String> params) {
+            target.params.putAll(params);
+            return this;
+        }
+
+        public Builder defaultResultParam(String defaultResultParam) {
+            target.defaultResultParam = defaultResultParam;
+            return this;
+        }
+
+        public Builder location(Location loc) {
+            target.location = loc;
+            return this;
+        }
+
+        public ResultTypeConfig build() {
+            embalmTarget();
+            ResultTypeConfig result = target;
+            target = new ResultTypeConfig(target);
+            return result;
+        }
+
+        protected void embalmTarget() {
+            target.params = Collections.unmodifiableMap(target.params);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/UnknownHandlerConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/UnknownHandlerConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/UnknownHandlerConfig.java
new file mode 100644
index 0000000..3d6692f
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/UnknownHandlerConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.entities;
+
+import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
+
+public class UnknownHandlerConfig extends Located {
+
+    private String name;
+
+    public UnknownHandlerConfig(String name, Location location) {
+        this.name = name;
+        this.location = location;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return "UnknownHandlerConfig: [" + name + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/entities/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/package.html b/core/src/main/java/com/opensymphony/xwork2/config/entities/package.html
new file mode 100644
index 0000000..d05a357
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/package.html
@@ -0,0 +1,18 @@
+<body>
+
+<p>
+Configuration entity classes.  All objects ending in "Config" are immutable and must be constructed using
+their inner "Builder" class.  For example, a PackageConfig object can be created via:
+</p>
+<pre>
+    PackageConfig config = new PackageConfig.Builder("myPackage").build();
+</pre>
+<p>
+    The methods on the builder object are chainable to support constructions like this:
+</p>
+<pre>
+    ResultConfig config = new ResultConfig.Builder("success", "myapp.MyResult")
+        .addParam("location", "/foo.jsp")
+        .build();
+</pre>
+</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/impl/AbstractMatcher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/impl/AbstractMatcher.java b/core/src/main/java/com/opensymphony/xwork2/config/impl/AbstractMatcher.java
new file mode 100644
index 0000000..5deb7cb
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/AbstractMatcher.java
@@ -0,0 +1,267 @@
+/*
+ * $Id$
+ *
+ * Copyright 2003,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.impl;
+
+import com.opensymphony.xwork2.util.PatternMatcher;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * <p> Matches patterns against pre-compiled wildcard expressions pulled from
+ * target objects. It uses the wildcard matcher from the Apache Cocoon
+ * project. Patterns will be matched in the order they were added. The first 
+ * match wins, so more specific patterns should be defined before less specific 
+ * patterns.
+ * 
+ * @since 2.1
+ */
+public abstract class AbstractMatcher<E> implements Serializable {
+    /**
+     * <p> The logging instance </p>
+     */
+    private static final Logger log = LogManager.getLogger(AbstractMatcher.class);
+
+    /**
+     * <p> Handles all wildcard pattern matching. </p>
+     */
+    PatternMatcher<Object> wildcard;
+
+    /**
+     * <p> The compiled patterns and their associated target objects </p>
+     */
+    List<Mapping<E>> compiledPatterns = new ArrayList<>();
+    ;
+    
+    public AbstractMatcher(PatternMatcher<?> helper) {
+        this.wildcard = (PatternMatcher<Object>) helper;
+    }
+
+    /**
+     * <p>
+     * Finds and precompiles the wildcard patterns. Patterns will be evaluated
+     * in the order they were added. Only patterns that actually contain a
+     * wildcard will be compiled.
+     * </p>
+     * 
+     * <p>
+     * Patterns can optionally be matched "loosely". When the end of the pattern
+     * matches \*[^*]\*$ (wildcard, no wildcard, wildcard), if the pattern
+     * fails, it is also matched as if the last two characters didn't exist. The
+     * goal is to support the legacy "*!*" syntax, where the "!*" is optional.
+     * </p>
+     * 
+     * @param name The pattern
+     * @param target The object to associate with the pattern
+     * @param looseMatch
+     *            To loosely match wildcards or not
+     */
+    public void addPattern(String name, E target, boolean looseMatch) {
+
+        Object pattern;
+
+        if (!wildcard.isLiteral(name)) {
+            if (looseMatch && (name.length() > 0) && (name.charAt(0) == '/')) {
+                name = name.substring(1);
+            }
+
+            log.debug("Compiling pattern '{}'", name);
+
+            pattern = wildcard.compilePattern(name);
+            compiledPatterns.add(new Mapping<E>(name, pattern, target));
+
+            if (looseMatch) {
+                int lastStar = name.lastIndexOf('*');
+                if (lastStar > 1 && lastStar == name.length() - 1) {
+                    if (name.charAt(lastStar - 1) != '*') {
+                        pattern = wildcard.compilePattern(name.substring(0, lastStar - 1));
+                        compiledPatterns.add(new Mapping<E>(name, pattern, target));
+                    }
+                }
+            }
+        }
+    }
+    
+    public void freeze() {
+        compiledPatterns = Collections.unmodifiableList(new ArrayList<Mapping<E>>());
+    }
+
+    /**
+     * <p> Matches the path against the compiled wildcard patterns. </p>
+     *
+     * @param potentialMatch The portion of the request URI for selecting a config.
+     * @return The action config if matched, else null
+     */
+    public E match(String potentialMatch) {
+        E config = null;
+
+        if (compiledPatterns.size() > 0) {
+            log.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
+
+            Map<String,String> vars = new LinkedHashMap<String,String>();
+            for (Mapping<E> m : compiledPatterns) {
+                if (wildcard.match(vars, potentialMatch, m.getPattern())) {
+                    log.debug("Value matches pattern '{}'", m.getOriginalPattern());
+                    config = convert(potentialMatch, m.getTarget(), vars);
+                    break;
+                }
+            }
+        }
+
+        return config;
+    }
+
+    /**
+     * <p> Clones the target object and its children, replacing various
+     * properties with the values of the wildcard-matched strings. </p>
+     *
+     * @param path The requested path
+     * @param orig The original object
+     * @param vars A Map of wildcard-matched strings
+     * @return A cloned object with appropriate properties replaced with
+     *         wildcard-matched values
+     */
+    protected abstract E convert(String path, E orig, Map<String, String> vars);
+
+    /**
+     * <p> Replaces parameter values
+     * </p>
+     *
+     * @param orig  The original parameters with placeholder values
+     * @param vars  A Map of wildcard-matched strings
+     */
+    protected Map<String,String> replaceParameters(Map<String, String> orig, Map<String,String> vars) {
+        Map<String, String> map = new LinkedHashMap<>();
+        
+        //this will set the group index references, like {1}
+        for (String key : orig.keySet()) {
+            map.put(key, convertParam(orig.get(key), vars));
+        }
+        
+        //the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
+        //now add the non-numeric values
+        for (String key: vars.keySet()) {
+            if (!NumberUtils.isNumber(key)) {
+                map.put(key, vars.get(key));
+            }
+        }
+        
+        return map;
+    }
+
+    /**
+     * <p> Inserts into a value wildcard-matched strings where specified
+     * with the {x} syntax.  If a wildcard-matched value isn't found, the
+     * replacement token is turned into an empty string. 
+     * </p>
+     *
+     * @param val  The value to convert
+     * @param vars A Map of wildcard-matched strings
+     * @return The new value
+     */
+    protected String convertParam(String val, Map<String, String> vars) {
+        if (val == null) {
+            return null;
+        } 
+        
+        int len = val.length();
+        StringBuilder ret = new StringBuilder();
+        char c;
+        String varVal;
+        for (int x=0; x<len; x++) {
+            c = val.charAt(x);
+            if (x < len - 2 && 
+                    c == '{' && '}' == val.charAt(x+2)) {
+                varVal = (String)vars.get(String.valueOf(val.charAt(x + 1)));
+                if (varVal != null) {
+                    ret.append(varVal);
+                } 
+                x += 2;
+            } else {
+                ret.append(c);
+            }
+        }
+        
+        return ret.toString();
+    }
+
+    /**
+     * <p> Stores a compiled wildcard pattern and the object it came
+     * from. </p>
+     */
+    private static class Mapping<E> implements Serializable {
+        /**
+         * <p> The original pattern. </p>
+         */
+        private String original;
+
+        
+        /**
+         * <p> The compiled pattern. </p>
+         */
+        private Object pattern;
+
+        /**
+         * <p> The original object. </p>
+         */
+        private E config;
+
+        /**
+         * <p> Contructs a read-only Mapping instance. </p>
+         *
+         * @param original The original pattern
+         * @param pattern The compiled pattern
+         * @param config  The original object
+         */
+        public Mapping(String original, Object pattern, E config) {
+            this.original = original;
+            this.pattern = pattern;
+            this.config = config;
+        }
+
+        /**
+         * <p> Gets the compiled wildcard pattern. </p>
+         *
+         * @return The compiled pattern
+         */
+        public Object getPattern() {
+            return this.pattern;
+        }
+
+        /**
+         * <p> Gets the object that contains the pattern. </p>
+         *
+         * @return The associated object
+         */
+        public E getTarget() {
+            return this.config;
+        }
+        
+        /**
+         * <p> Gets the original wildcard pattern. </p>
+         *
+         * @return The original pattern
+         */
+        public String getOriginalPattern() {
+            return this.original;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/impl/ActionConfigMatcher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/impl/ActionConfigMatcher.java b/core/src/main/java/com/opensymphony/xwork2/config/impl/ActionConfigMatcher.java
new file mode 100644
index 0000000..e282c1a
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/ActionConfigMatcher.java
@@ -0,0 +1,152 @@
+/*
+ * $Id$
+ *
+ * Copyright 2003,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.impl;
+
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
+import com.opensymphony.xwork2.config.entities.ResultConfig;
+import com.opensymphony.xwork2.util.PatternMatcher;
+import com.opensymphony.xwork2.util.WildcardHelper;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p> Matches paths against pre-compiled wildcard expressions pulled from
+ * action configs. It uses the wildcard matcher from the Apache Cocoon
+ * project. Patterns will be matched in the order they exist in the 
+ * config file. The first match wins, so more specific patterns should be
+ * defined before less specific patterns.
+ */
+public class ActionConfigMatcher extends AbstractMatcher<ActionConfig> implements Serializable {
+   
+    /**
+     * <p> Finds and precompiles the wildcard patterns from the ActionConfig
+     * "path" attributes. ActionConfig's will be evaluated in the order they
+     * exist in the config file. Only paths that actually contain a
+     * wildcard will be compiled. Patterns will matched strictly.</p>
+     *
+     * @param configs An array of ActionConfig's to process
+     * @deprecated Since 2.1, use {@link #ActionConfigMatcher(PatternMatcher, Map, boolean)} instead
+     */
+    @Deprecated public ActionConfigMatcher(Map<String, ActionConfig> configs) {
+        this(configs, false);
+    }
+    
+    /**
+     * <p> Finds and precompiles the wildcard patterns from the ActionConfig
+     * "path" attributes. ActionConfig's will be evaluated in the order they
+     * exist in the config file. Only paths that actually contain a
+     * wildcard will be compiled. </p>
+     * 
+     * <p>Patterns can optionally be matched "loosely".  When
+     * the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
+     * wildcard), if the pattern fails, it is also matched as if the 
+     * last two characters didn't exist.  The goal is to support the 
+     * legacy "*!*" syntax, where the "!*" is optional.</p> 
+     *
+     * @param configs An array of ActionConfig's to process
+     * @param looseMatch To loosely match wildcards or not
+     * @deprecated Since 2.1, use {@link #ActionConfigMatcher(PatternMatcher, Map, boolean)} instead
+     */
+    @Deprecated public ActionConfigMatcher(Map<String, ActionConfig> configs,
+            boolean looseMatch) {
+
+        this(new WildcardHelper(), configs, looseMatch);
+    }
+    
+    /**
+     * <p> Finds and precompiles the wildcard patterns from the ActionConfig
+     * "path" attributes. ActionConfig's will be evaluated in the order they
+     * exist in the config file. Only paths that actually contain a
+     * wildcard will be compiled. </p>
+     * 
+     * <p>Patterns can optionally be matched "loosely".  When
+     * the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
+     * wildcard), if the pattern fails, it is also matched as if the 
+     * last two characters didn't exist.  The goal is to support the 
+     * legacy "*!*" syntax, where the "!*" is optional.</p> 
+     *
+     * @param configs An array of ActionConfig's to process
+     * @param looseMatch To loosely match wildcards or not
+     */
+    public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
+            Map<String, ActionConfig> configs,
+            boolean looseMatch) {
+        super(patternMatcher);
+        for (String name : configs.keySet()) {
+            addPattern(name, configs.get(name), looseMatch);
+        }
+    }
+
+    /**
+     * <p> Clones the ActionConfig and its children, replacing various
+     * properties with the values of the wildcard-matched strings. </p>
+     *
+     * @param path The requested path
+     * @param orig The original ActionConfig
+     * @param vars A Map of wildcard-matched strings
+     * @return A cloned ActionConfig with appropriate properties replaced with
+     *         wildcard-matched values
+     */
+    @Override public ActionConfig convert(String path, ActionConfig orig,
+        Map<String, String> vars) {
+
+        String methodName = convertParam(orig.getMethodName(), vars);
+        if (!orig.isAllowedMethod(methodName)) {
+            return null;
+        }
+
+        String className = convertParam(orig.getClassName(), vars);
+        String pkgName = convertParam(orig.getPackageName(), vars);
+
+        Map<String,String> params = replaceParameters(orig.getParams(), vars);
+
+        Map<String, ResultConfig> results = new LinkedHashMap<>();
+        for (String name : orig.getResults().keySet()) {
+            ResultConfig result = orig.getResults().get(name);
+            name = convertParam(name, vars);
+            ResultConfig r = new ResultConfig.Builder(name, convertParam(result.getClassName(), vars))
+                    .addParams(replaceParameters(result.getParams(), vars))
+                    .build();
+            results.put(name, r);
+        }
+
+        List<ExceptionMappingConfig> exs = new ArrayList<ExceptionMappingConfig>();
+        for (ExceptionMappingConfig ex : orig.getExceptionMappings()) {
+            String name = convertParam(ex.getName(), vars);
+            String exClassName = convertParam(ex.getExceptionClassName(), vars);
+            String exResult = convertParam(ex.getResult(), vars);
+            Map<String,String> exParams = replaceParameters(ex.getParams(), vars);
+            ExceptionMappingConfig e = new ExceptionMappingConfig.Builder(name, exClassName, exResult).addParams(exParams).build();
+            exs.add(e);
+        }
+
+        return new ActionConfig.Builder(pkgName, orig.getName(), className)
+                .methodName(methodName)
+                .addParams(params)
+                .addResultConfigs(results)
+                .addInterceptors(orig.getInterceptors())
+                .addExceptionMappings(exs)
+                .location(orig.getLocation())
+                .build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..7d5e5cc
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.config.impl;
+
+import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.config.*;
+import com.opensymphony.xwork2.config.entities.*;
+import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
+import com.opensymphony.xwork2.conversion.*;
+import com.opensymphony.xwork2.conversion.impl.*;
+import com.opensymphony.xwork2.factory.*;
+import com.opensymphony.xwork2.inject.*;
+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.util.*;
+import com.opensymphony.xwork2.util.fs.DefaultFileManager;
+import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import ognl.PropertyAccessor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.*;
+
+
+/**
+ * DefaultConfiguration
+ *
+ * @author Jason Carreira
+ *         Created Feb 24, 2003 7:38:06 AM
+ */
+public class DefaultConfiguration implements Configuration {
+
+    protected static final Logger LOG = LogManager.getLogger(DefaultConfiguration.class);
+
+
+    // Programmatic Action Configurations
+    protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<>();
+    protected RuntimeConfiguration runtimeConfiguration;
+    protected Container container;
+    protected String defaultFrameworkBeanName;
+    protected Set<String> loadedFileNames = new TreeSet<>();
+    protected List<UnknownHandlerConfig> unknownHandlerStack;
+
+
+    ObjectFactory objectFactory;
+
+    public DefaultConfiguration() {
+        this("xwork");
+    }
+
+    public DefaultConfiguration(String defaultBeanName) {
+        this.defaultFrameworkBeanName = defaultBeanName;
+    }
+
+
+    public PackageConfig getPackageConfig(String name) {
+        return packageContexts.get(name);
+    }
+
+    public List<UnknownHandlerConfig> getUnknownHandlerStack() {
+        return unknownHandlerStack;
+    }
+
+    public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
+        this.unknownHandlerStack = unknownHandlerStack;
+    }
+
+    public Set<String> getPackageConfigNames() {
+        return packageContexts.keySet();
+    }
+
+    public Map<String, PackageConfig> getPackageConfigs() {
+        return packageContexts;
+    }
+
+    public Set<String> getLoadedFileNames() {
+        return loadedFileNames;
+    }
+
+    public RuntimeConfiguration getRuntimeConfiguration() {
+        return runtimeConfiguration;
+    }
+
+    /**
+     * @return the container
+     */
+    public Container getContainer() {
+        return container;
+    }
+
+    public void addPackageConfig(String name, PackageConfig packageContext) {
+        PackageConfig check = packageContexts.get(name);
+        if (check != null) {
+            if (check.getLocation() != null && packageContext.getLocation() != null
+                    && check.getLocation().equals(packageContext.getLocation())) {
+                LOG.debug("The package name '{}' is already been loaded by the same location and could be removed: {}",
+                        name, packageContext.getLocation());
+            } else {
+                throw new ConfigurationException("The package name '" + name
+                        + "' at location "+packageContext.getLocation()
+                        + " is already been used by another package at location " + check.getLocation(),
+                        packageContext);
+            }
+        }
+        packageContexts.put(name, packageContext);
+    }
+
+    public PackageConfig removePackageConfig(String packageName) {
+        return packageContexts.remove(packageName);
+    }
+
+    /**
+     * Allows the configuration to clean up any resources used
+     */
+    public void destroy() {
+        packageContexts.clear();
+        loadedFileNames.clear();
+    }
+
+    public void rebuildRuntimeConfiguration() {
+        runtimeConfiguration = buildRuntimeConfiguration();
+    }
+
+    /**
+     * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
+     * buildRuntimeConfiguration().
+     *
+     * @throws ConfigurationException
+     */
+    public synchronized void reload(List<ConfigurationProvider> providers) throws ConfigurationException {
+
+        // Silly copy necessary due to lack of ability to cast generic lists
+        List<ContainerProvider> contProviders = new ArrayList<>();
+        contProviders.addAll(providers);
+
+        reloadContainer(contProviders);
+    }
+
+    /**
+     * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
+     * buildRuntimeConfiguration().
+     *
+     * @throws ConfigurationException
+     */
+    public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
+        packageContexts.clear();
+        loadedFileNames.clear();
+        List<PackageProvider> packageProviders = new ArrayList<>();
+
+        ContainerProperties props = new ContainerProperties();
+        ContainerBuilder builder = new ContainerBuilder();
+        Container bootstrap = createBootstrapContainer(providers);
+        for (final ContainerProvider containerProvider : providers)
+        {
+            bootstrap.inject(containerProvider);
+            containerProvider.init(this);
+            containerProvider.register(builder, props);
+        }
+        props.setConstants(builder);
+
+        builder.factory(Configuration.class, new Factory<Configuration>() {
+            public Configuration create(Context context) throws Exception {
+                return DefaultConfiguration.this;
+            }
+        });
+
+        ActionContext oldContext = ActionContext.getContext();
+        try {
+            // Set the bootstrap container for the purposes of factory creation
+
+            setContext(bootstrap);
+            container = builder.create(false);
+            setContext(container);
+            objectFactory = container.getInstance(ObjectFactory.class);
+
+            // Process the configuration providers first
+            for (final ContainerProvider containerProvider : providers)
+            {
+                if (containerProvider instanceof PackageProvider) {
+                    container.inject(containerProvider);
+                    ((PackageProvider)containerProvider).loadPackages();
+                    packageProviders.add((PackageProvider)containerProvider);
+                }
+            }
+
+            // Then process any package providers from the plugins
+            Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
+            for (String name : packageProviderNames) {
+                PackageProvider provider = container.getInstance(PackageProvider.class, name);
+                provider.init(this);
+                provider.loadPackages();
+                packageProviders.add(provider);
+            }
+
+            rebuildRuntimeConfiguration();
+        } finally {
+            if (oldContext == null) {
+                ActionContext.setContext(null);
+            }
+        }
+        return packageProviders;
+    }
+
+    protected ActionContext setContext(Container cont) {
+        ActionContext context = ActionContext.getContext();
+        if (context == null) {
+            ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
+            context = new ActionContext(vs.getContext());
+            ActionContext.setContext(context);
+        }
+        return context;
+    }
+
+    protected Container createBootstrapContainer(List<ContainerProvider> providers) {
+        ContainerBuilder builder = new ContainerBuilder();
+        boolean fmFactoryRegistered = false;
+        for (ContainerProvider provider : providers) {
+            if (provider instanceof FileManagerProvider) {
+                provider.register(builder, null);
+            }
+            if (provider instanceof FileManagerFactoryProvider) {
+                provider.register(builder, null);
+                fmFactoryRegistered = true;
+            }
+        }
+        builder.factory(ObjectFactory.class, Scope.SINGLETON);
+        builder.factory(ActionFactory.class, DefaultActionFactory.class, Scope.SINGLETON);
+        builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);
+        builder.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.SINGLETON);
+        builder.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class, Scope.SINGLETON);
+        builder.factory(ConverterFactory.class, DefaultConverterFactory.class, Scope.SINGLETON);
+        builder.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.SINGLETON);
+
+        builder.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON);
+        if (!fmFactoryRegistered) {
+            builder.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON);
+        }
+        builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
+        builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
+
+        builder.factory(XWorkConverter.class, Scope.SINGLETON);
+        builder.factory(ConversionPropertiesProcessor.class, DefaultConversionPropertiesProcessor.class, Scope.SINGLETON);
+        builder.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON);
+        builder.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON);
+        builder.factory(TypeConverterCreator.class, DefaultTypeConverterCreator.class, Scope.SINGLETON);
+        builder.factory(TypeConverterHolder.class, DefaultTypeConverterHolder.class, Scope.SINGLETON);
+
+        builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
+        builder.factory(TypeConverter.class, XWorkConstants.COLLECTION_CONVERTER,  CollectionConverter.class, Scope.SINGLETON);
+        builder.factory(TypeConverter.class, XWorkConstants.ARRAY_CONVERTER, ArrayConverter.class, Scope.SINGLETON);
+        builder.factory(TypeConverter.class, XWorkConstants.DATE_CONVERTER, DateConverter.class, Scope.SINGLETON);
+        builder.factory(TypeConverter.class, XWorkConstants.NUMBER_CONVERTER,  NumberConverter.class, Scope.SINGLETON);
+        builder.factory(TypeConverter.class, XWorkConstants.STRING_CONVERTER, StringConverter.class, Scope.SINGLETON);
+
+        builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
+        builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
+
+        builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
+        builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
+        builder.factory(OgnlUtil.class, Scope.SINGLETON);
+
+        builder.constant(XWorkConstants.DEV_MODE, "false");
+        builder.constant(XWorkConstants.LOG_MISSING_PROPERTIES, "false");
+        builder.constant(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, "false");
+        builder.constant(XWorkConstants.ENABLE_OGNL_EXPRESSION_CACHE, "true");
+        builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
+
+        return builder.create(true);
+    }
+
+    /**
+     * This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
+     * programmatic configuration data structures. All of the old runtime configuration will be discarded and rebuilt.
+     *
+     * <p>
+     * It basically flattens the data structures to make the information easier to access.  It will take
+     * an {@link ActionConfig} and combine its data with all inherited dast.  For example, if the {@link ActionConfig}
+     * is in a package that contains a global result and it also contains a result, the resulting {@link ActionConfig}
+     * will have two results.
+     */
+    protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
+        Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<>();
+        Map<String, String> namespaceConfigs = new LinkedHashMap<>();
+
+        for (PackageConfig packageConfig : packageContexts.values()) {
+
+            if (!packageConfig.isAbstract()) {
+                String namespace = packageConfig.getNamespace();
+                Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);
+
+                if (configs == null) {
+                    configs = new LinkedHashMap<>();
+                }
+
+                Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();
+
+                for (Object o : actionConfigs.keySet()) {
+                    String actionName = (String) o;
+                    ActionConfig baseConfig = actionConfigs.get(actionName);
+                    configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
+                }
+
+                namespaceActionConfigs.put(namespace, configs);
+                if (packageConfig.getFullDefaultActionRef() != null) {
+                    namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
+                }
+            }
+        }
+
+        PatternMatcher<int[]> matcher = container.getInstance(PatternMatcher.class);
+        return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs),
+                Collections.unmodifiableMap(namespaceConfigs), matcher);
+    }
+
+    private void setDefaultResults(Map<String, ResultConfig> results, PackageConfig packageContext) {
+        String defaultResult = packageContext.getFullDefaultResultType();
+
+        for (Map.Entry<String, ResultConfig> entry : results.entrySet()) {
+
+            if (entry.getValue() == null) {
+                ResultTypeConfig resultTypeConfig = packageContext.getAllResultTypeConfigs().get(defaultResult);
+                entry.setValue(new ResultConfig.Builder(null, resultTypeConfig.getClassName()).build());
+            }
+        }
+    }
+
+    /**
+     * Builds the full runtime actionconfig with all of the defaults and inheritance
+     *
+     * @param packageContext the PackageConfig which holds the base config we're building from
+     * @param baseConfig     the ActionConfig which holds only the configuration specific to itself, without the defaults
+     *                       and inheritance
+     * @return a full ActionConfig for runtime configuration with all of the inherited and default params
+     * @throws com.opensymphony.xwork2.config.ConfigurationException
+     *
+     */
+    private ActionConfig buildFullActionConfig(PackageConfig packageContext, ActionConfig baseConfig) throws ConfigurationException {
+        Map<String, String> params = new TreeMap<>(baseConfig.getParams());
+        Map<String, ResultConfig> results = new TreeMap<>();
+
+        if (!baseConfig.getPackageName().equals(packageContext.getName()) && packageContexts.containsKey(baseConfig.getPackageName())) {
+            results.putAll(packageContexts.get(baseConfig.getPackageName()).getAllGlobalResults());
+        } else {
+            results.putAll(packageContext.getAllGlobalResults());
+        }
+
+       	results.putAll(baseConfig.getResults());
+
+        setDefaultResults(results, packageContext);
+
+        List<InterceptorMapping> interceptors = new ArrayList<>(baseConfig.getInterceptors());
+
+        if (interceptors.size() <= 0) {
+            String defaultInterceptorRefName = packageContext.getFullDefaultInterceptorRef();
+
+            if (defaultInterceptorRefName != null) {
+                interceptors.addAll(InterceptorBuilder.constructInterceptorReference(new PackageConfig.Builder(packageContext), defaultInterceptorRefName,
+                        new LinkedHashMap<String, String>(), packageContext.getLocation(), objectFactory));
+            }
+        }
+
+        return new ActionConfig.Builder(baseConfig)
+            .addParams(params)
+            .addResultConfigs(results)
+            .defaultClassName(packageContext.getDefaultClassRef())  // fill in default if non class has been provided
+            .interceptors(interceptors)
+            .addExceptionMappings(packageContext.getAllExceptionMappingConfigs())
+            .build();
+    }
+
+
+    private static class RuntimeConfigurationImpl implements RuntimeConfiguration {
+
+        private Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
+        private Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
+        private NamespaceMatcher namespaceMatcher;
+        private Map<String, String> namespaceConfigs;
+
+        public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
+                                        Map<String, String> namespaceConfigs,
+                                        PatternMatcher<int[]> matcher) {
+            this.namespaceActionConfigs = namespaceActionConfigs;
+            this.namespaceConfigs = namespaceConfigs;
+
+            this.namespaceActionConfigMatchers = new LinkedHashMap<>();
+            this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());
+
+            for (String ns : namespaceActionConfigs.keySet()) {
+                namespaceActionConfigMatchers.put(ns, new ActionConfigMatcher(matcher, namespaceActionConfigs.get(ns), true));
+            }
+        }
+
+
+        /**
+         * Gets the configuration information for an action name, or returns null if the
+         * name is not recognized.
+         *
+         * @param name      the name of the action
+         * @param namespace the namespace for the action or null for the empty namespace, ""
+         * @return the configuration information for action requested
+         */
+        public ActionConfig getActionConfig(String namespace, String name) {
+            ActionConfig config = findActionConfigInNamespace(namespace, name);
+
+            // try wildcarded namespaces
+            if (config == null) {
+                NamespaceMatch match = namespaceMatcher.match(namespace);
+                if (match != null) {
+                    config = findActionConfigInNamespace(match.getPattern(), name);
+
+                    // If config found, place all the matches found in the namespace processing in the action's parameters
+                    if (config != null) {
+                        config = new ActionConfig.Builder(config)
+                                .addParams(match.getVariables())
+                                .build();
+                    }
+                }
+            }
+
+            // fail over to empty namespace
+            if (config == null && StringUtils.isNotBlank(namespace)) {
+                config = findActionConfigInNamespace("", name);
+            }
+
+
+            return config;
+        }
+
+        private ActionConfig findActionConfigInNamespace(String namespace, String name) {
+            ActionConfig config = null;
+            if (namespace == null) {
+                namespace = "";
+            }
+            Map<String, ActionConfig> actions = namespaceActionConfigs.get(namespace);
+            if (actions != null) {
+                config = actions.get(name);
+                // Check wildcards
+                if (config == null) {
+                    config = namespaceActionConfigMatchers.get(namespace).match(name);
+                    // fail over to default action
+                    if (config == null) {
+                        String defaultActionRef = namespaceConfigs.get(namespace);
+                        if (defaultActionRef != null) {
+                            config = actions.get(defaultActionRef);
+                        }
+                    }
+                }
+            }
+            return config;
+        }
+
+        /**
+         * Gets the configuration settings for every action.
+         *
+         * @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
+         */
+        public Map<String, Map<String, ActionConfig>>  getActionConfigs() {
+            return namespaceActionConfigs;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder buff = new StringBuilder("RuntimeConfiguration - actions are\n");
+
+            for (String namespace : namespaceActionConfigs.keySet()) {
+                Map<String, ActionConfig> actionConfigs = namespaceActionConfigs.get(namespace);
+
+                for (String s : actionConfigs.keySet()) {
+                    buff.append(namespace).append("/").append(s).append("\n");
+                }
+            }
+
+            return buff.toString();
+        }
+    }
+
+    class ContainerProperties extends LocatableProperties {
+        private static final long serialVersionUID = -7320625750836896089L;
+
+        @Override
+        public Object setProperty(String key, String value) {
+            String oldValue = getProperty(key);
+            if (LOG.isInfoEnabled() && oldValue != null && !oldValue.equals(value) && !defaultFrameworkBeanName.equals(oldValue)) {
+                LOG.info("Overriding property {} - old value: {} new value: {}", key, oldValue, value);
+            }
+            return super.setProperty(key, value);
+        }
+
+        public void setConstants(ContainerBuilder builder) {
+            for (Object keyobj : keySet()) {
+                String key = (String)keyobj;
+                builder.factory(String.class, key, new LocatableConstantFactory<>(getProperty(key), getPropertyLocation(key)));
+            }
+        }
+    }
+}


Mime
View raw message