You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by ku...@apache.org on 2024/11/02 03:11:59 UTC

(struts) branch 7.0.x/merge-master-2024-11-02 created (now 3bd473b2d)

This is an automated email from the ASF dual-hosted git repository.

kusal pushed a change to branch 7.0.x/merge-master-2024-11-02
in repository https://gitbox.apache.org/repos/asf/struts.git


      at 3bd473b2d Merge remote-tracking branch 'origin/master' into 7.0.x/merge-master-2024-11-02

This branch includes the following new commits:

     new 3bd473b2d Merge remote-tracking branch 'origin/master' into 7.0.x/merge-master-2024-11-02

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(struts) 01/01: Merge remote-tracking branch 'origin/master' into 7.0.x/merge-master-2024-11-02

Posted by ku...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kusal pushed a commit to branch 7.0.x/merge-master-2024-11-02
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 3bd473b2d398fbd7614275b02c31eb01b53a2ac1
Merge: 99434d99b 1908cbab8
Author: Kusal Kithul-Godage <gi...@kusal.io>
AuthorDate: Sat Nov 2 14:11:48 2024 +1100

    Merge remote-tracking branch 'origin/master' into 7.0.x/merge-master-2024-11-02

 core/src/main/java/org/apache/struts2/ActionContext.java   |  5 +++++
 core/src/main/java/org/apache/struts2/ModelDriven.java     |  3 +++
 .../org/apache/struts2/config/entities/ResultConfig.java   |  6 +++---
 .../apache/struts2/config/impl/DefaultConfiguration.java   |  4 ++--
 .../org/apache/struts2/factory/DefaultResultFactory.java   |  3 +++
 .../interceptor/parameter/ParametersInterceptor.java       |  1 +
 .../interceptor/ChainingInterceptorWithConfigTest.java     |  7 ++++---
 .../convention/PackageBasedActionConfigBuilderTest.java    |  4 ++--
 .../org/apache/struts2/rest/RestActionInvocationTest.java  | 14 ++++++--------
 9 files changed, 29 insertions(+), 18 deletions(-)

diff --cc core/src/main/java/org/apache/struts2/ActionContext.java
index 56163eacc,335c24e95..2f023bc51
--- a/core/src/main/java/org/apache/struts2/ActionContext.java
+++ b/core/src/main/java/org/apache/struts2/ActionContext.java
@@@ -543,9 -543,15 +543,14 @@@ public class ActionContext implements S
  
      @Override
      public final boolean equals(Object obj) {
 -        if (!(obj instanceof ActionContext)) {
 +        if (!(obj instanceof ActionContext other)) {
              return false;
          }
 -        ActionContext other = (ActionContext) obj;
          return Objects.equals(getContextMap(), other.getContextMap());
      }
+ 
+     @Override
+     public final int hashCode() {
+         return Objects.hash(getContextMap());
+     }
  }
diff --cc core/src/main/java/org/apache/struts2/config/entities/ResultConfig.java
index 5e2a1daae,000000000..40d20838e
mode 100644,000000..100644
--- a/core/src/main/java/org/apache/struts2/config/entities/ResultConfig.java
+++ b/core/src/main/java/org/apache/struts2/config/entities/ResultConfig.java
@@@ -1,163 -1,0 +1,163 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *  http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.struts2.config.entities;
 +
 +import org.apache.struts2.util.location.Located;
 +import org.apache.struts2.util.location.Location;
 +
 +import java.io.Serializable;
 +import java.util.Collections;
 +import java.util.LinkedHashMap;
 +import java.util.Map;
 +import java.util.Objects;
 +
 +
 +/**
 + * Configuration for Result.
 + *
 + * <p>
 + * In the xml configuration file this is defined as the <code>result</code> tag.
 + * </p>
 + *
 + * @author Mike
 + */
 +public class ResultConfig extends Located implements Serializable {
 +
-     protected Map<String,String> params;
++    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() {
++    public Map<String, String> getParams() {
 +        return params;
 +    }
 +
 +    @Override
 +    public boolean equals(Object o) {
 +        if (this == o) {
 +            return true;
 +        }
 +
 +        if (!(o instanceof ResultConfig resultConfig)) {
 +            return false;
 +        }
 +
 +        if (!Objects.equals(className, resultConfig.className)) {
 +            return false;
 +        }
 +
 +        if (!Objects.equals(name, resultConfig.name)) {
 +            return false;
 +        }
 +
 +        if (!Objects.equals(params, resultConfig.params)) {
 +            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 {
 +        private 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) {
++        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;
 +        }
 +
 +        private void embalmTarget() {
 +            target.params = Collections.unmodifiableMap(target.params);
 +        }
 +    }
 +}
diff --cc core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java
index b7bc93425,000000000..3ea854b21
mode 100644,000000..100644
--- a/core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java
+++ b/core/src/main/java/org/apache/struts2/config/impl/DefaultConfiguration.java
@@@ -1,674 -1,0 +1,674 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *  http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.struts2.config.impl;
 +
 +import org.apache.struts2.ActionContext;
 +import org.apache.struts2.DefaultLocaleProviderFactory;
 +import org.apache.struts2.DefaultTextProvider;
 +import org.apache.struts2.FileManager;
 +import org.apache.struts2.FileManagerFactory;
 +import org.apache.struts2.LocaleProviderFactory;
 +import org.apache.struts2.LocalizedTextProvider;
 +import org.apache.struts2.ObjectFactory;
 +import org.apache.struts2.StrutsTextProviderFactory;
 +import org.apache.struts2.TextProvider;
 +import org.apache.struts2.TextProviderFactory;
 +import org.apache.struts2.config.Configuration;
 +import org.apache.struts2.config.ConfigurationException;
 +import org.apache.struts2.config.ContainerProvider;
 +import org.apache.struts2.config.FileManagerFactoryProvider;
 +import org.apache.struts2.config.FileManagerProvider;
 +import org.apache.struts2.config.PackageProvider;
 +import org.apache.struts2.config.RuntimeConfiguration;
 +import org.apache.struts2.config.entities.ActionConfig;
 +import org.apache.struts2.config.entities.InterceptorMapping;
 +import org.apache.struts2.config.entities.PackageConfig;
 +import org.apache.struts2.config.entities.ResultConfig;
 +import org.apache.struts2.config.entities.ResultTypeConfig;
 +import org.apache.struts2.config.entities.UnknownHandlerConfig;
 +import org.apache.struts2.config.providers.EnvsValueSubstitutor;
 +import org.apache.struts2.config.providers.InterceptorBuilder;
 +import org.apache.struts2.config.providers.ValueSubstitutor;
 +import org.apache.struts2.conversion.ConversionAnnotationProcessor;
 +import org.apache.struts2.conversion.ConversionFileProcessor;
 +import org.apache.struts2.conversion.ConversionPropertiesProcessor;
 +import org.apache.struts2.conversion.ObjectTypeDeterminer;
 +import org.apache.struts2.conversion.TypeConverter;
 +import org.apache.struts2.conversion.TypeConverterCreator;
 +import org.apache.struts2.conversion.TypeConverterHolder;
 +import org.apache.struts2.conversion.impl.ArrayConverter;
 +import org.apache.struts2.conversion.impl.CollectionConverter;
 +import org.apache.struts2.conversion.impl.DateConverter;
 +import org.apache.struts2.conversion.impl.DefaultConversionAnnotationProcessor;
 +import org.apache.struts2.conversion.impl.DefaultConversionFileProcessor;
 +import org.apache.struts2.conversion.impl.DefaultObjectTypeDeterminer;
 +import org.apache.struts2.conversion.impl.NumberConverter;
 +import org.apache.struts2.conversion.impl.StringConverter;
 +import org.apache.struts2.conversion.impl.XWorkBasicConverter;
 +import org.apache.struts2.conversion.impl.XWorkConverter;
 +import org.apache.struts2.factory.ActionFactory;
 +import org.apache.struts2.factory.ConverterFactory;
 +import org.apache.struts2.factory.DefaultActionFactory;
 +import org.apache.struts2.factory.DefaultInterceptorFactory;
- import org.apache.struts2.factory.DefaultResultFactory;
 +import org.apache.struts2.factory.DefaultUnknownHandlerFactory;
 +import org.apache.struts2.factory.DefaultValidatorFactory;
 +import org.apache.struts2.factory.InterceptorFactory;
 +import org.apache.struts2.factory.ResultFactory;
 +import org.apache.struts2.factory.StrutsConverterFactory;
 +import org.apache.struts2.factory.UnknownHandlerFactory;
 +import org.apache.struts2.factory.ValidatorFactory;
 +import org.apache.struts2.inject.Container;
 +import org.apache.struts2.inject.ContainerBuilder;
 +import org.apache.struts2.inject.Context;
 +import org.apache.struts2.inject.Factory;
 +import org.apache.struts2.inject.Scope;
 +import org.apache.struts2.ognl.BeanInfoCacheFactory;
 +import org.apache.struts2.ognl.DefaultOgnlBeanInfoCacheFactory;
 +import org.apache.struts2.ognl.DefaultOgnlExpressionCacheFactory;
 +import org.apache.struts2.ognl.ExpressionCacheFactory;
 +import org.apache.struts2.ognl.OgnlCacheFactory;
 +import org.apache.struts2.ognl.OgnlReflectionProvider;
 +import org.apache.struts2.ognl.OgnlUtil;
 +import org.apache.struts2.ognl.OgnlValueStackFactory;
 +import org.apache.struts2.ognl.SecurityMemberAccess;
 +import org.apache.struts2.ognl.accessor.CompoundRootAccessor;
 +import org.apache.struts2.ognl.accessor.RootAccessor;
 +import org.apache.struts2.ognl.accessor.XWorkMethodAccessor;
 +import org.apache.struts2.util.OgnlTextParser;
 +import org.apache.struts2.util.PatternMatcher;
 +import org.apache.struts2.util.StrutsLocalizedTextProvider;
 +import org.apache.struts2.util.TextParser;
 +import org.apache.struts2.util.ValueStack;
 +import org.apache.struts2.util.ValueStackFactory;
 +import org.apache.struts2.util.fs.DefaultFileManager;
 +import org.apache.struts2.util.fs.DefaultFileManagerFactory;
 +import org.apache.struts2.util.location.LocatableProperties;
 +import org.apache.struts2.util.reflection.ReflectionProvider;
 +import ognl.MethodAccessor;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.logging.log4j.LogManager;
 +import org.apache.logging.log4j.Logger;
 +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.factory.StrutsResultFactory;
 +import org.apache.struts2.ognl.OgnlGuard;
 +import org.apache.struts2.ognl.ProviderAllowlist;
 +import org.apache.struts2.ognl.StrutsOgnlGuard;
 +import org.apache.struts2.ognl.ThreadAllowlist;
 +
 +import java.io.Serial;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.LinkedHashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Optional;
 +import java.util.Set;
 +import java.util.TreeMap;
 +import java.util.TreeSet;
 +
 +
 +/**
 + * DefaultConfiguration
 + *
 + * @author Jason Carreira
 + *         Created Feb 24, 2003 7:38:06 AM
 + */
 +public class DefaultConfiguration implements Configuration {
 +
 +    public static final Map<String, Object> BOOTSTRAP_CONSTANTS;
 +
 +    static {
 +        Map<String, Object> constants = new HashMap<>();
 +        constants.put(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE);
 +        constants.put(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, Boolean.FALSE);
 +        constants.put(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE);
 +        constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
 +        constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, 10000);
 +        constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
 +        constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, 10000);
 +        constants.put(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE);
 +        BOOTSTRAP_CONSTANTS = Collections.unmodifiableMap(constants);
 +    }
 +
 +    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(Container.DEFAULT_NAME);
 +    }
 +
 +    public DefaultConfiguration(String defaultBeanName) {
 +        this.defaultFrameworkBeanName = defaultBeanName;
 +    }
 +
 +
 +    @Override
 +    public PackageConfig getPackageConfig(String name) {
 +        return packageContexts.get(name);
 +    }
 +
 +    @Override
 +    public List<UnknownHandlerConfig> getUnknownHandlerStack() {
 +        return unknownHandlerStack;
 +    }
 +
 +    @Override
 +    public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
 +        this.unknownHandlerStack = unknownHandlerStack;
 +    }
 +
 +    @Override
 +    public Set<String> getPackageConfigNames() {
 +        return packageContexts.keySet();
 +    }
 +
 +    @Override
 +    public Map<String, PackageConfig> getPackageConfigs() {
 +        return packageContexts;
 +    }
 +
 +    @Override
 +    public Set<String> getLoadedFileNames() {
 +        return loadedFileNames;
 +    }
 +
 +    @Override
 +    public RuntimeConfiguration getRuntimeConfiguration() {
 +        return runtimeConfiguration;
 +    }
 +
 +    /**
 +     * @return the container
 +     */
 +    @Override
 +    public Container getContainer() {
 +        return container;
 +    }
 +
 +    @Override
 +    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);
 +    }
 +
 +    @Override
 +    public PackageConfig removePackageConfig(String packageName) {
 +        return packageContexts.remove(packageName);
 +    }
 +
 +    /**
 +     * Allows the configuration to clean up any resources used
 +     */
 +    @Override
 +    public void destroy() {
 +        packageContexts.clear();
 +        loadedFileNames.clear();
 +    }
 +
 +    @Override
 +    public void rebuildRuntimeConfiguration() {
 +        runtimeConfiguration = buildRuntimeConfiguration();
 +    }
 +
 +    /**
 +     * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
 +     * buildRuntimeConfiguration().
 +     *
 +     * @param providers list of ContainerProvider
 +     * @return list of package providers
 +     *
 +     * @throws ConfigurationException in case of any configuration errors
 +     */
 +    @Override
 +    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>() {
 +            @Override
 +            public Configuration create(Context context) throws Exception {
 +                return DefaultConfiguration.this;
 +            }
 +
 +            @Override
 +            public Class<? extends Configuration> type() {
 +                return DefaultConfiguration.this.getClass();
 +            }
 +        });
 +
 +        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.clear();
 +            }
 +        }
 +        return packageProviders;
 +    }
 +
 +    protected ActionContext setContext(Container cont) {
 +        ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
 +        return ActionContext.of(vs.getContext()).bind();
 +    }
 +
 +    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;
 +            }
 +        }
 +
 +        bootstrapFactories(builder);
 +        bootstrapTypeConverters(builder);
 +
 +        if (!fmFactoryRegistered) {
 +            builder.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON);
 +        }
 +
 +        for (Map.Entry<String, Object> entry : BOOTSTRAP_CONSTANTS.entrySet()) {
 +            builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
 +        }
 +
 +        return builder.create(true);
 +    }
 +
 +    public static ContainerBuilder bootstrapFactories(ContainerBuilder builder) {
 +        return builder
 +                // TODO: SpringObjectFactoryTest fails when these are SINGLETON
 +                .factory(ObjectFactory.class, Scope.PROTOTYPE)
 +                .factory(ActionFactory.class, DefaultActionFactory.class, Scope.PROTOTYPE)
-                 .factory(ResultFactory.class, DefaultResultFactory.class, Scope.PROTOTYPE)
++                .factory(ResultFactory.class, StrutsResultFactory.class, Scope.PROTOTYPE)
 +                .factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.PROTOTYPE)
 +                .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.PROTOTYPE)
 +                .factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.PROTOTYPE)
 +                .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.PROTOTYPE)
 +
 +                .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
 +                .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
 +                .factory(ValueStackFactory.class, OgnlValueStackFactory.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(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(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
 +
 +                .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
 +                .factory(RootAccessor.class, CompoundRootAccessor.class, Scope.SINGLETON)
 +                .factory(MethodAccessor.class, XWorkMethodAccessor.class, Scope.SINGLETON)
 +
 +                .factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
 +                .factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
 +                .factory(OgnlUtil.class, Scope.SINGLETON)
 +                .factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
 +                .factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
 +                .factory(ProviderAllowlist.class, Scope.SINGLETON)
 +                .factory(ThreadAllowlist.class, Scope.SINGLETON)
 +
 +                .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
 +    }
 +
 +    public static ContainerBuilder bootstrapTypeConverters(ContainerBuilder builder) {
 +        return builder
 +                .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON)
 +                .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON)
 +                .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON)
 +                .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON)
 +                .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
 +    }
 +
 +    /**
 +     * <p>
 +     * 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>
 +     *
 +     * <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.
 +     * </p>
 +     *
 +     * @return runtime configuration
 +     * @throws ConfigurationException in case of any configuration errors
 +     */
 +    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);
 +        boolean appendNamedParameters = Boolean.parseBoolean(
 +                container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS)
 +        );
 +        boolean fallbackToEmptyNamespace = Boolean.parseBoolean(
 +                Optional.ofNullable(container.getInstance(String.class, StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE)).orElse("true")
 +        );
 +
 +        return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs),
 +                Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters, fallbackToEmptyNamespace);
 +    }
 +
 +    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 org.apache.struts2.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));
 +            }
 +        }
 +
 +        String methodRegex = container.getInstance(String.class, StrutsConstants.STRUTS_SMI_METHOD_REGEX);
 +        if (methodRegex == null) {
 +            methodRegex = ActionConfig.DEFAULT_METHOD_REGEX;
 +        }
 +
 +        LOG.debug("Using pattern [{}] to match allowed methods when SMI is disabled!", methodRegex);
 +
 +        return new ActionConfig.Builder(baseConfig)
 +            .addParams(params)
 +            .addResultConfigs(results)
 +            .defaultClassName(packageContext.getDefaultClassRef())  // fill in default if non class has been provided
 +            .interceptors(interceptors)
 +            .setStrictMethodInvocation(packageContext.isStrictMethodInvocation())
 +            .setDefaultMethodRegex(methodRegex)
 +            .addExceptionMappings(packageContext.getAllExceptionMappingConfigs())
 +            .build();
 +    }
 +
 +
 +    private static class RuntimeConfigurationImpl implements RuntimeConfiguration {
 +
 +        private final Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
 +        private final Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
 +        private final NamespaceMatcher namespaceMatcher;
 +        private final Map<String, String> namespaceConfigs;
 +        private final boolean fallbackToEmptyNamespace;
 +
 +        public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
 +                                        Map<String, String> namespaceConfigs,
 +                                        PatternMatcher<int[]> matcher,
 +                                        boolean appendNamedParameters,
 +                                        boolean fallbackToEmptyNamespace)
 +        {
 +            this.namespaceActionConfigs = namespaceActionConfigs;
 +            this.namespaceConfigs = namespaceConfigs;
 +            this.fallbackToEmptyNamespace = fallbackToEmptyNamespace;
 +
 +            this.namespaceActionConfigMatchers = new LinkedHashMap<>();
 +            this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters);
 +
 +            for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
 +                ActionConfigMatcher configMatcher = new ActionConfigMatcher(matcher, entry.getValue(), true, appendNamedParameters);
 +                namespaceActionConfigMatchers.put(entry.getKey(), configMatcher);
 +            }
 +        }
 +
 +
 +        /**
 +         * 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
 +         */
 +        @Override
 +        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 && shouldFallbackToEmptyNamespace(namespace)) {
 +                config = findActionConfigInNamespace("", name);
 +            }
 +
 +            return config;
 +        }
 +
 +        private boolean shouldFallbackToEmptyNamespace(String namespace) {
 +            return StringUtils.isNotBlank(namespace) && ("/".equals(namespace) || fallbackToEmptyNamespace);
 +        }
 +
 +        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
 +         */
 +        @Override
 +        public Map<String, Map<String, ActionConfig>>  getActionConfigs() {
 +            return namespaceActionConfigs;
 +        }
 +
 +        @Override
 +        public String toString() {
 +            StringBuilder buff = new StringBuilder("RuntimeConfiguration - actions are\n");
 +
 +            for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
 +                Map<String, ActionConfig> actionConfigs = entry.getValue();
 +
 +                for (String s : actionConfigs.keySet()) {
 +                    buff.append(entry.getKey()).append("/").append(s).append("\n");
 +                }
 +            }
 +
 +            return buff.toString();
 +        }
 +    }
 +
 +    class ContainerProperties extends LocatableProperties {
 +        @Serial
 +        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)));
 +            }
 +        }
 +    }
 +}
diff --cc core/src/main/java/org/apache/struts2/factory/DefaultResultFactory.java
index 202514ccd,000000000..54efb6972
mode 100644,000000..100644
--- a/core/src/main/java/org/apache/struts2/factory/DefaultResultFactory.java
+++ b/core/src/main/java/org/apache/struts2/factory/DefaultResultFactory.java
@@@ -1,78 -1,0 +1,81 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *  http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.struts2.factory;
 +
 +import org.apache.struts2.ObjectFactory;
 +import org.apache.struts2.Result;
 +import org.apache.struts2.config.ConfigurationException;
 +import org.apache.struts2.config.entities.ResultConfig;
 +import org.apache.struts2.inject.Inject;
 +import org.apache.struts2.util.reflection.ReflectionException;
 +import org.apache.struts2.util.reflection.ReflectionExceptionHandler;
 +import org.apache.struts2.util.reflection.ReflectionProvider;
 +
 +import java.util.Map;
 +
 +/**
 + * Default implementation
++ *
++ * @deprecated since 6.7.0, use {@link StrutsResultFactory} instead.
 + */
++@Deprecated
 +public class DefaultResultFactory implements ResultFactory {
 +
 +    private ObjectFactory objectFactory;
 +    private ReflectionProvider reflectionProvider;
 +
 +    @Inject
 +    public void setObjectFactory(ObjectFactory objectFactory) {
 +        this.objectFactory = objectFactory;
 +    }
 +
 +    @Inject
 +    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
 +        this.reflectionProvider = reflectionProvider;
 +    }
 +
 +    public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
 +        String resultClassName = resultConfig.getClassName();
 +        if (resultClassName == null) {
 +            return null;
 +        }
 +
 +        Object o = objectFactory.buildBean(resultClassName, extraContext);
 +
 +        if (!(o instanceof Result result)) {
 +            throw new ConfigurationException("Class [" + resultClassName + "] does not implement Result", resultConfig);
 +        }
 +
 +        Map<String, String> params = resultConfig.getParams();
 +        if (params != null) {
 +            for (Map.Entry<String, String> paramEntry : params.entrySet()) {
 +                try {
 +                    reflectionProvider.setProperty(paramEntry.getKey(), paramEntry.getValue(), result, extraContext, true);
 +                } catch (ReflectionException ex) {
 +                    if (result instanceof ReflectionExceptionHandler reflectionExceptionHandler) {
 +                        reflectionExceptionHandler.handle(ex);
 +                    }
 +                }
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +}
diff --cc core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorWithConfigTest.java
index 5d2e21dbc,000000000..211e7329b
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorWithConfigTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorWithConfigTest.java
@@@ -1,127 -1,0 +1,128 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *  http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.struts2.interceptor;
 +
 +import org.apache.struts2.Action;
 +import org.apache.struts2.ActionChainResult;
 +import org.apache.struts2.ActionProxy;
 +import org.apache.struts2.ActionProxyFactory;
 +import org.apache.struts2.DefaultActionProxyFactory;
 +import org.apache.struts2.ObjectFactory;
 +import org.apache.struts2.SimpleAction;
 +import org.apache.struts2.XWorkTestCase;
 +import org.apache.struts2.config.Configuration;
 +import org.apache.struts2.config.ConfigurationException;
 +import org.apache.struts2.config.ConfigurationProvider;
 +import org.apache.struts2.config.entities.ActionConfig;
 +import org.apache.struts2.config.entities.InterceptorConfig;
 +import org.apache.struts2.config.entities.InterceptorMapping;
 +import org.apache.struts2.config.entities.PackageConfig;
 +import org.apache.struts2.config.entities.ResultConfig;
 +import org.apache.struts2.config.providers.XmlConfigurationProvider;
 +import org.apache.struts2.inject.ContainerBuilder;
 +import org.apache.struts2.util.location.LocatableProperties;
 +import org.apache.struts2.TestResult;
 +import org.apache.struts2.config.StrutsXmlConfigurationProvider;
 +
 +import java.util.Collections;
 +import java.util.HashMap;
++import java.util.Map;
 +
 +
 +/**
 + * Unit test for {@link ChainingInterceptor} with a configuration provider.
 + */
 +public class ChainingInterceptorWithConfigTest extends XWorkTestCase {
 +
 +    static String CHAINED_ACTION = "chainedAction";
 +    static String CHAINTO_ACTION = "chaintoAction";
 +    ObjectFactory objectFactory;
 +
 +    public void testTwoExcludesPropertiesChained() throws Exception {
 +        assertNotNull(objectFactory);
 +        ActionProxy proxy = actionProxyFactory.createActionProxy("", CHAINED_ACTION, null, null);
 +        SimpleAction chainedAction = (SimpleAction) proxy.getAction();
 +        chainedAction.setBar(1);
 +        chainedAction.setFoo(1);
 +        chainedAction.setBlah("WW-4528");
 +        proxy.execute();
 +    }
 +
 +    @Override
 +    protected void setUp() throws Exception {
 +        super.setUp();
 +        XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("xwork-default.xml");
 +        container.inject(provider);
 +        this.objectFactory = container.getInstance(ObjectFactory.class);
 +        loadConfigurationProviders(provider, new MockConfigurationProvider());
 +    }
 +
 +
 +    private class MockConfigurationProvider implements ConfigurationProvider {
 +        private Configuration config;
 +
 +        public void init(Configuration configuration) throws ConfigurationException {
 +            this.config = configuration;
 +        }
 +
 +        public boolean needsReload() {
 +            return false;
 +        }
 +
 +        public void destroy() {
 +        }
 +
 +
 +        public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
 +            if (!builder.contains(ObjectFactory.class)) {
 +                builder.factory(ObjectFactory.class);
 +            }
 +            if (!builder.contains(ActionProxyFactory.class)) {
 +                builder.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class);
 +            }
 +        }
 +
 +        public void loadPackages() throws ConfigurationException {
 +            HashMap<String, String> interceptorParams = new HashMap<>();
 +            interceptorParams.put("excludes", "blah,bar");
 +
-             HashMap successParams1 = new HashMap();
++            Map<String, String> successParams1 = new HashMap<>();
 +            successParams1.put("propertyName", "baz");
-             successParams1.put("expectedValue", 1);
++            successParams1.put("expectedValue", "1");
 +
-             HashMap successParams2 = new HashMap();
++            Map<String, String> successParams2 = new HashMap<>();
 +            successParams2.put("propertyName", "blah");
 +            successParams2.put("expectedValue", null);
 +
 +            InterceptorConfig chainingInterceptorConfig = new InterceptorConfig.Builder("chainStack", ChainingInterceptor.class.getName()).build();
 +            PackageConfig packageConfig = new PackageConfig.Builder("default")
 +                .addActionConfig(CHAINED_ACTION, new ActionConfig.Builder("defaultPackage", CHAINED_ACTION, SimpleAction.class.getName())
 +                    .addResultConfig(new ResultConfig.Builder(Action.ERROR, ActionChainResult.class.getName()).addParam("actionName", CHAINTO_ACTION).build())
 +                    .build())
 +                .addActionConfig(CHAINTO_ACTION, new ActionConfig.Builder("defaultPackage", CHAINTO_ACTION, SimpleAction.class.getName())
 +                    .addInterceptors(Collections.singletonList(new InterceptorMapping("chainStack", objectFactory.buildInterceptor(chainingInterceptorConfig, interceptorParams))))
 +                    .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, TestResult.class.getName()).addParams(successParams1).build())
 +                    .addResultConfig(new ResultConfig.Builder(Action.ERROR, TestResult.class.getName()).addParams(successParams2).build())
 +                    .build())
 +                .build();
 +            config.addPackageConfig("defaultPackage", packageConfig);
 +            config.addPackageConfig("default", new PackageConfig.Builder(packageConfig).name("default").build());
 +        }
 +    }
 +}
diff --cc plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
index fb80fae66,b6a9de4be..6aff13694
--- a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
+++ b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
@@@ -18,31 -18,30 +18,30 @@@
   */
  package org.apache.struts2.convention;
  
 -import com.opensymphony.xwork2.ActionChainResult;
 -import com.opensymphony.xwork2.ActionContext;
 -import com.opensymphony.xwork2.FileManager;
 -import com.opensymphony.xwork2.FileManagerFactory;
 -import com.opensymphony.xwork2.ObjectFactory;
 -import com.opensymphony.xwork2.Result;
 -import com.opensymphony.xwork2.config.Configuration;
 -import com.opensymphony.xwork2.config.entities.ActionConfig;
 -import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
 -import com.opensymphony.xwork2.config.entities.InterceptorConfig;
 -import com.opensymphony.xwork2.config.entities.InterceptorMapping;
 -import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
 -import com.opensymphony.xwork2.config.entities.PackageConfig;
 -import com.opensymphony.xwork2.config.entities.ResultConfig;
 -import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
 -import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
 -import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
 -import com.opensymphony.xwork2.inject.Container;
 -import com.opensymphony.xwork2.inject.Scope.Strategy;
 -import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
 -import com.opensymphony.xwork2.util.TextParseUtil;
 -import com.opensymphony.xwork2.util.fs.DefaultFileManager;
 -import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
 -import com.opensymphony.xwork2.util.reflection.ReflectionException;
 +import org.apache.struts2.ActionChainResult;
 +import org.apache.struts2.ActionContext;
 +import org.apache.struts2.FileManager;
 +import org.apache.struts2.FileManagerFactory;
 +import org.apache.struts2.ObjectFactory;
 +import org.apache.struts2.Result;
 +import org.apache.struts2.config.Configuration;
 +import org.apache.struts2.config.entities.ActionConfig;
 +import org.apache.struts2.config.entities.ExceptionMappingConfig;
 +import org.apache.struts2.config.entities.InterceptorConfig;
 +import org.apache.struts2.config.entities.InterceptorMapping;
 +import org.apache.struts2.config.entities.InterceptorStackConfig;
 +import org.apache.struts2.config.entities.PackageConfig;
 +import org.apache.struts2.config.entities.ResultConfig;
 +import org.apache.struts2.config.entities.ResultTypeConfig;
 +import org.apache.struts2.config.impl.DefaultConfiguration;
 +import org.apache.struts2.factory.DefaultInterceptorFactory;
- import org.apache.struts2.factory.DefaultResultFactory;
 +import org.apache.struts2.inject.Container;
 +import org.apache.struts2.inject.Scope.Strategy;
 +import org.apache.struts2.ognl.OgnlReflectionProvider;
 +import org.apache.struts2.util.TextParseUtil;
 +import org.apache.struts2.util.fs.DefaultFileManager;
 +import org.apache.struts2.util.fs.DefaultFileManagerFactory;
 +import org.apache.struts2.util.reflection.ReflectionException;
  import junit.framework.TestCase;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.struts2.convention.actions.DefaultResultPathAction;