Commits

mrdon  committed 0f065fb

Making all configuration objects immutable, adding the capture and processing
of allowed methods in action configuration

XW-594 XW-595

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@1703e221344d-f017-0410-9bd5-d282ab1896d7

  • Participants
  • Parent commits bb12b36

Comments (0)

Files changed (52)

File src/java/com/opensymphony/xwork2/ActionChainResult.java

             LOG.debug("Chaining to action " + finalActionName);
         }
 
-        proxy = actionProxyFactory.createActionProxy(finalNamespace, finalActionName, extraContext);
-        if (null != finalMethodName) {
-            proxy.setMethod(finalMethodName);
-        }
+        proxy = actionProxyFactory.createActionProxy(finalNamespace, finalActionName, finalMethodName, extraContext);
         proxy.execute();
     }
 

File src/java/com/opensymphony/xwork2/ActionInvocation.java

      */
     void setActionEventListener(ActionEventListener listener);
 
-    void init(ActionProxy proxy) throws Exception;
+    void init(ActionProxy proxy) ;
 
 }

File src/java/com/opensymphony/xwork2/ActionProxy.java

 public interface ActionProxy {
 
     /**
-     * Called after all dependencies are set.
-     *
-     * @throws Exception can be thrown.
-     */
-    void prepare() throws Exception;
-    
-    /**
      * Gets the Action instance for this Proxy.
      *
      * @return the Action instance
     String execute() throws Exception;
 
     /**
-     * Sets the method to execute for the action invocation. If no method is specified, the method provided by
-     * in the action's configuration will be used.
-     *
-     * @param method the string name of the method to invoke
-     */
-    void setMethod(String method);
-
-    /**
      * Gets the method name to execute, or <tt>null</tt> if no method has been specified (meaning <code>execute</code> will be invoked).
      *
      * @return the method to execute

File src/java/com/opensymphony/xwork2/ActionProxyFactory.java

      * @param actionName   the name of the action
      * @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
      * @return ActionProxy  the created action proxy
-     * @throws Exception can be thrown
+     * @deprecated Since 2.1.1, use {@link #createActionProxy(String,String,String,Map) instead}
      */
-    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext) throws Exception;
+    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext);
+
+    /**
+     * Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
+     * should be fully initialized when it is returned, including having an {@link ActionInvocation} instance associated.
+     * <p/>
+     * <b>Note:</b> This is the most used create method.
+     *
+     * @param namespace    the namespace of the action, can be <tt>null</tt>
+     * @param actionName   the name of the action
+     * @param methodName   the name of the method to execute
+     * @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
+     * @return ActionProxy  the created action proxy
+     * @since 2.1.1
+     */
+    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext);
 
     /**
      * Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
      * @param executeResult flag which tells whether the result should be executed after the action
      * @param cleanupContext flag which tells whether the original context should be preserved during execution of the proxy.
      * @return ActionProxy  the created action proxy
-     * @throws Exception can be thrown
+     * @deprecated Since 2.1.1, use {@link #createActionProxy(String,String,String,Map,boolean,boolean)} instead
      */
-    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception;
+    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext);
 
-    public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName,
-            Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception;
+    /**
+     * Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
+     * should be fully initialized when it is returned, including having an {@link ActionInvocation} instance associated.
+     *
+     * @param namespace    the namespace of the action, can be <tt>null</tt>
+     * @param actionName   the name of the action
+     * @param methodName   the name of the method to execute
+     * @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
+     * @param executeResult flag which tells whether the result should be executed after the action
+     * @param cleanupContext flag which tells whether the original context should be preserved during execution of the proxy.
+     * @return ActionProxy  the created action proxy
+     * @since 2.1.1
+     */
+    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext);
+
+
+     /**
+     * Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
+     * should be fully initialized when it is returned, including passed {@link ActionInvocation} instance.
+     *
+     * @param actionInvocation the action invocation instance to associate with
+     * @param namespace    the namespace of the action, can be <tt>null</tt>
+     * @param actionName   the name of the action
+     * @param methodName   the name of the method to execute
+     * @param executeResult flag which tells whether the result should be executed after the action
+     * @param cleanupContext flag which tells whether the original context should be preserved during execution of the proxy.
+     * @return ActionProxy  the created action proxy
+     * @since 2.1.1
+     */
+    public ActionProxy createActionProxy(ActionInvocation actionInvocation, String namespace, String actionName, String methodName,
+                                         boolean executeResult, boolean cleanupContext);
     
 }

File src/java/com/opensymphony/xwork2/DefaultActionInvocation.java

     protected Container container;
     protected UnknownHandler unknownHandler;
 
-    public DefaultActionInvocation(final Map extraContext, final boolean pushAction) throws Exception {
+    public DefaultActionInvocation(final Map extraContext, final boolean pushAction) {
         DefaultActionInvocation.this.extraContext = extraContext;
         DefaultActionInvocation.this.pushAction = pushAction;
     }
         }
     }
 
-    public void init(ActionProxy proxy) throws Exception {
+    public void init(ActionProxy proxy) {
         this.proxy = proxy;
         Map contextMap = createContextMap();
 

File src/java/com/opensymphony/xwork2/DefaultActionProxy.java

     protected ActionConfig config;
     protected ActionInvocation invocation;
     protected UnknownHandler unknownHandler;
-    protected Map extraContext;
     protected String actionName;
     protected String namespace;
     protected String method;
      * The reason for the builder methods is so that you can use a subclass to create your own DefaultActionProxy instance
      * (like a RMIActionProxy).
      */
-    protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception {
+    protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
         
         this.invocation = inv;
 		this.cleanupContext = cleanupContext;
 		this.actionName = actionName;
 		this.namespace = namespace;
 		this.executeResult = executeResult;
-		this.extraContext = extraContext;
-    	
+        this.method = methodName;
     }
     
     @Inject
         return method;
     }
 
-    public void setMethod(String method) {
-        this.method = method;
-        resolveMethod();
-    }
-
     private void resolveMethod() {
         // if the method is set to null, use the one from the configuration
         // if the one from the configuration is also null, use "execute"
         }
     }
 
-    public void prepare() throws Exception {
+    protected void prepare()  {
         String profileKey = "create DefaultActionProxy: ";
         try {
             UtilTimerStack.push(profileKey);
                 }
                 throw new ConfigurationException(message);
             }
+
+            resolveMethod();
             
-            
+            if (!config.isAllowedMethod(method)) {
+                throw new ConfigurationException("Invalid method: "+method+" for action "+actionName);
+            }
+
             invocation.init(this);
-            resolveMethod();
+
         } finally {
             UtilTimerStack.pop(profileKey);
         }

File src/java/com/opensymphony/xwork2/DefaultActionProxyFactory.java

         this.container = container;
     }
     
-    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext) throws Exception {
-        return createActionProxy(namespace, actionName, extraContext, true, true);
+    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext) {
+        return createActionProxy(namespace, actionName, null, extraContext, true, true);
     }
 
-    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception {
+    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext) {
+        return createActionProxy(namespace, actionName, methodName, extraContext, true, true);
+    }
+
+    public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) {
+        return createActionProxy(namespace, actionName, null, extraContext, executeResult, cleanupContext);
+    }
+
+    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
         
         ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
         container.inject(inv);
-        return createActionProxy(inv, namespace, actionName, extraContext, executeResult, cleanupContext);
+        return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
     }
     
-    public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception {
+    public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, boolean executeResult, boolean cleanupContext) {
         
-        ActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, extraContext, executeResult, cleanupContext);
+        return createActionProxy(inv, namespace, actionName, null, executeResult, cleanupContext);
+    }
+
+    public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
+
+        DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
         container.inject(proxy);
         proxy.prepare();
         return proxy;

File src/java/com/opensymphony/xwork2/XWork.java

         Configuration config = configurationManager.getConfiguration();
         try {
             ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
-                    namespace, name, extraContext, true, false);
-            proxy.setMethod(method);
+                    namespace, name, method, extraContext, true, false);
         
             proxy.execute();
         } catch (Exception e) {

File src/java/com/opensymphony/xwork2/config/Configuration.java

      */
     List<PackageProvider> reloadContainer(List<ContainerProvider> containerProviders) throws ConfigurationException;
 
-    void removePackageConfig(String name);
-
     /**
      * @return the container
      */

File src/java/com/opensymphony/xwork2/config/entities/ActionConfig.java

 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.*;
  * @author Rainer Hermanns
  * @version $Revision$
  */
-public class ActionConfig extends Located implements InterceptorListHolder, Parameterizable, Serializable {
+public class ActionConfig extends Located implements Serializable {
+
+    public static final String WILDCARD = "*";
 
     protected List<InterceptorMapping> interceptors;
-    protected Map<String, Object> params;
+    protected Map<String, String> params;
     protected Map<String, ResultConfig> results;
     protected List<ExceptionMappingConfig> exceptionMappings;
     protected String className;
     protected String methodName;
     protected String packageName;
+    protected String name;
+    protected Set<String> allowedMethods;
 
-
-    public ActionConfig() {
-        params = new LinkedHashMap<String, Object>();
+    protected ActionConfig(String packageName, String name, String className) {
+        this.packageName = packageName;
+        this.name = name;
+        this.className = className;
+        params = new LinkedHashMap<String, String>();
         results = new LinkedHashMap<String, ResultConfig>();
         interceptors = new ArrayList<InterceptorMapping>();
         exceptionMappings = new ArrayList<ExceptionMappingConfig>();
-    }
-
-    //Helper constuctor to maintain backward compatibility with objects that create ActionConfigs
-    //TODO this should be removed if these changes are rolled in to xwork CVS
-    public ActionConfig(String methodName, Class clazz, Map<String, Object> parameters, Map<String, ResultConfig> results, List<InterceptorMapping> interceptors) {
-        this(methodName, clazz.getName(), parameters, results, interceptors);
-    }
-
-    public ActionConfig(String methodName, Class clazz, Map<String, Object> parameters, Map<String, ResultConfig> results, List<InterceptorMapping> interceptors, List<ExceptionMappingConfig> exceptionMappings) {
-        this(methodName, clazz.getName(), parameters, results, interceptors, exceptionMappings);
-    }
-
-    public ActionConfig(String methodName, String className, Map<String, Object> parameters, Map<String, ResultConfig> results, List<InterceptorMapping> interceptors) {
-        this(methodName, className, "", parameters, results, interceptors, Collections.EMPTY_LIST);
-    }
-
-    public ActionConfig(String methodName, String className, Map<String, Object> parameters, Map<String, ResultConfig> results, List<InterceptorMapping> interceptors, List<ExceptionMappingConfig> exceptionMappings) {
-        this(methodName, className, "", parameters, results, interceptors, exceptionMappings);
-    }
-
-    public ActionConfig(String methodName, String className, String packageName, Map<String, Object> parameters, Map<String, ResultConfig> results, List<InterceptorMapping> interceptors) {
-        this(methodName, className, packageName, parameters, results, interceptors, Collections.EMPTY_LIST);
+        allowedMethods = new HashSet<String>();
+        allowedMethods.add(WILDCARD);
     }
 
     /**
      * @param orig The ActionConfig to clone
      * @Since 2.1
      */
-    public ActionConfig(ActionConfig orig) {
-        this(orig.getMethodName(), orig.getClassName(), orig.getPackageName(), new LinkedHashMap<String,Object>(orig.getParams()),
-                new LinkedHashMap<String,ResultConfig>(orig.getResults()),
-                new ArrayList<InterceptorMapping>(orig.getInterceptors()), new ArrayList<ExceptionMappingConfig>(orig.getExceptionMappings()));
-    }
-
-    public ActionConfig(String methodName, String className, String packageName, Map<String, Object> parameters,
-                        Map<String, ResultConfig> results, List<InterceptorMapping> interceptors, List<ExceptionMappingConfig> exceptionMappings) {
-        this.methodName = methodName;
-        this.interceptors = interceptors;
-        this.params = parameters;
-        this.results = results;
-        this.className = className;
-        this.exceptionMappings = exceptionMappings;
-        this.packageName = packageName;
+    protected ActionConfig(ActionConfig orig) {
+        this.name = orig.name;
+        this.className = orig.className;
+        this.methodName = orig.methodName;
+        this.packageName = orig.packageName;
+        this.params = new LinkedHashMap<String,String>(orig.params);
+        this.interceptors = new ArrayList<InterceptorMapping>(orig.interceptors);
+        this.results = new LinkedHashMap<String,ResultConfig>(orig.results);
+        this.exceptionMappings = new ArrayList<ExceptionMappingConfig>(orig.exceptionMappings);
+        this.allowedMethods = new HashSet<String>(orig.allowedMethods);
     }
-    
-    public void setClassName(String className) {
-        this.className = className;
+
+    public String getName() {
+        return name;
     }
 
     public String getClassName() {
     }
 
     public List<InterceptorMapping> getInterceptors() {
-        if (interceptors == null) {
-            interceptors = new ArrayList<InterceptorMapping>();
-        }
-
         return interceptors;
     }
 
-    public void setMethodName(String methodName) {
-        this.methodName = methodName;
+    public Set<String> getAllowedMethods() {
+        return allowedMethods;
     }
 
     /**
     }
 
     /**
-     * @param packageName The packageName to set.
-     */
-    public void setPackageName(String packageName) {
-        this.packageName = packageName;
-    }
-
-    /**
      * @return Returns the packageName.
      */
     public String getPackageName() {
         return packageName;
     }
 
-    public void setParams(Map<String, Object> params) {
-        this.params = params;
-    }
-
-    public Map<String, Object> getParams() {
-        if (params == null) {
-            params = new LinkedHashMap<String, Object>();
-        }
-
+    public Map<String, String> getParams() {
         return params;
     }
 
-    public void setResults(Map<String, ResultConfig> results) {
-        this.results = results;
-    }
-
     public Map<String, ResultConfig> getResults() {
-        if (results == null) {
-            results = new LinkedHashMap<String, ResultConfig>();
-        }
-
         return results;
     }
 
-    public void addExceptionMapping(ExceptionMappingConfig exceptionMapping) {
-        getExceptionMappings().add(exceptionMapping);
-    }
-
-    public void addExceptionMappings(List<? extends ExceptionMappingConfig> mappings) {
-        getExceptionMappings().addAll(mappings);
-    }
-
-    public void addInterceptor(InterceptorMapping interceptor) {
-        getInterceptors().add(interceptor);
-    }
-
-    public void addInterceptors(List<InterceptorMapping> interceptors) {
-        getInterceptors().addAll(interceptors);
-    }
-
-    public void addParam(String name, Object value) {
-        getParams().put(name, value);
-    }
-
-    public void addResultConfig(ResultConfig resultConfig) {
-        getResults().put(resultConfig.getName(), resultConfig);
+    public boolean isAllowedMethod(String method) {
+        if (allowedMethods.size() == 1 && WILDCARD.equals(allowedMethods.iterator().next())) {
+            return true;
+        } else {
+            return allowedMethods.contains(method);
+        }
     }
 
     public boolean equals(Object o) {
             return false;
         }
 
+        if ((name != null) ? (!name.equals(actionConfig.name)) : (actionConfig.name != null)) {
+            return false;
+        }
+
         if ((interceptors != null) ? (!interceptors.equals(actionConfig.interceptors)) : (actionConfig.interceptors != null))
         {
             return false;
         if ((results != null) ? (!results.equals(actionConfig.results)) : (actionConfig.results != null)) {
             return false;
         }
-        
+
+        if ((allowedMethods != null) ? (!allowedMethods.equals(actionConfig.allowedMethods)) : (actionConfig.allowedMethods != null)) {
+            return false;
+        }
+
         return true;
     }
 
+
     public int hashCode() {
         int result;
-        result = ((interceptors != null) ? interceptors.hashCode() : 0);
-        result = (29 * result) + ((params != null) ? params.hashCode() : 0);
-        result = (29 * result) + ((results != null) ? results.hashCode() : 0);
-        result = (29 * result) + ((methodName != null) ? methodName.hashCode() : 0);
-
+        result = (interceptors != null ? interceptors.hashCode() : 0);
+        result = 31 * result + (params != null ? params.hashCode() : 0);
+        result = 31 * result + (results != null ? results.hashCode() : 0);
+        result = 31 * result + (exceptionMappings != null ? exceptionMappings.hashCode() : 0);
+        result = 31 * result + (className != null ? className.hashCode() : 0);
+        result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
+        result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (allowedMethods != null ? allowedMethods.hashCode() : 0);
         return result;
     }
 
     public String toString() {
         StringBuffer sb = new StringBuffer();
         sb.append("{ActionConfig ");
+        sb.append(name).append(" (");
         sb.append(className);
         if (methodName != null) {
             sb.append(".").append(methodName).append("()");
         }
+        sb.append(")");
         sb.append(" - ").append(location);
         sb.append("}");
         return sb.toString();
     }
+
+    /**
+     * 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{
+
+        private ActionConfig target;
+
+        public Builder(ActionConfig toClone) {
+            target = new ActionConfig(toClone);
+        }
+
+        public Builder(String packageName, String name, String className) {
+            target = new ActionConfig(packageName, name, className);
+        }
+
+        public Builder packageName(String name) {
+            target.packageName = name;
+            return this;
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        public Builder className(String name) {
+            target.className = name;
+            return this;
+        }
+
+        public Builder methodName(String method) {
+            target.methodName = method;
+            return this;
+        }
+
+        public Builder addExceptionMapping(ExceptionMappingConfig exceptionMapping) {
+            target.exceptionMappings.add(exceptionMapping);
+            return this;
+        }
+
+        public Builder addExceptionMappings(Collection<? extends ExceptionMappingConfig> mappings) {
+            target.exceptionMappings.addAll(mappings);
+            return this;
+        }
+
+        public Builder exceptionMappings(Collection<? extends ExceptionMappingConfig> mappings) {
+            target.exceptionMappings.clear();
+            target.exceptionMappings.addAll(mappings);
+            return this;
+        }
+
+        public Builder addInterceptor(InterceptorMapping interceptor) {
+            target.interceptors.add(interceptor);
+            return this;
+        }
+
+        public Builder addInterceptors(List<InterceptorMapping> interceptors) {
+            target.interceptors.addAll(interceptors);
+            return this;
+        }
+
+        public Builder interceptors(List<InterceptorMapping> interceptors) {
+            target.interceptors.clear();
+            target.interceptors.addAll(interceptors);
+            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 addResultConfig(ResultConfig resultConfig) {
+            target.results.put(resultConfig.getName(), resultConfig);
+            return this;
+        }
+
+        public Builder addResultConfigs(Collection<ResultConfig> configs) {
+            for (ResultConfig rc : configs) {
+                target.results.put(rc.getName(), rc);
+            }
+            return this;
+        }
+
+        public Builder addResultConfigs(Map<String,ResultConfig> configs) {
+            target.results.putAll(configs);
+            return this;
+        }
+
+        public Builder addAllowedMethod(String methodName) {
+            target.allowedMethods.add(methodName);
+            return this;
+        }
+
+        public Builder addAllowedMethod(Collection<String> methods) {
+            target.allowedMethods.addAll(methods);
+            return this;
+        }
+
+        public Builder location(Location loc) {
+            target.location = loc;
+            return this;
+        }
+
+        public ActionConfig build() {
+            target.params = Collections.unmodifiableMap(target.params);
+            target.results = Collections.unmodifiableMap(target.results);
+            target.interceptors = Collections.unmodifiableList(target.interceptors);
+            target.exceptionMappings = Collections.unmodifiableList(target.exceptionMappings);
+            target.allowedMethods = Collections.unmodifiableSet(target.allowedMethods);
+            ActionConfig result = target;
+            target = new ActionConfig(target);
+            return result;
+        }
+    }
 }

File src/java/com/opensymphony/xwork2/config/entities/ExceptionMappingConfig.java

 
 package com.opensymphony.xwork2.config.entities;
 
-import java.util.Map;
-import java.util.LinkedHashMap;
+import java.util.*;
 import java.io.Serializable;
 
 import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
 
 /**
  * Configuration for exception mapping.
     private String name;
     private String exceptionClassName;
     private String result;
-    private Map params;
+    private Map<String,String> params;
 
 
-    public ExceptionMappingConfig() {
-    }
-
-    public ExceptionMappingConfig(String name, String exceptionClassName, String result) {
-        this(name, exceptionClassName, result, new LinkedHashMap());
-    }
-
-    public ExceptionMappingConfig(String name, String exceptionClassName, String result, Map params) {
+    protected ExceptionMappingConfig(String name, String exceptionClassName, String result) {
         this.name = name;
         this.exceptionClassName = exceptionClassName;
         this.result = result;
-        this.params = params;
+        this.params = new LinkedHashMap<String,String>();
     }
 
-    public String getName() {
-        return name;
+    protected ExceptionMappingConfig(ExceptionMappingConfig target) {
+        this.name = target.name;
+        this.exceptionClassName = target.exceptionClassName;
+        this.result = target.result;
+        this.params = new LinkedHashMap<String,String>(target.params);
     }
 
-    public void setName(String name) {
-        this.name = name;
+    public String getName() {
+        return name;
     }
 
     public String getExceptionClassName() {
         return exceptionClassName;
     }
 
-    public void setExceptionClassName(String exceptionClassName) {
-        this.exceptionClassName = exceptionClassName;
-    }
-
     public String getResult() {
         return result;
     }
 
-    public void setResult(String result) {
-        this.result = result;
-    }
-
-    public Map getParams() {
-        if (params == null) {
-            params = new LinkedHashMap();
-        }
-
+    public Map<String,String> getParams() {
         return params;
     }
 
-    public void addParam(String name, Object value) {
-        getParams().put(name, value);
-    }
 
     public boolean equals(Object o) {
         if (this == o) {
         return hashCode;
     }
 
+    /**
+     * 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{
+
+        private ExceptionMappingConfig target;
+
+        public Builder(ExceptionMappingConfig toClone) {
+            target = new ExceptionMappingConfig(toClone);
+        }
+
+        public Builder(String name, String exceptionClassName, String result) {
+            target = new ExceptionMappingConfig(name, exceptionClassName, result);
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        public Builder exceptionClassName(String name) {
+            target.exceptionClassName = name;
+            return this;
+        }
+
+        public Builder result(String result) {
+            target.result = result;
+            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 ExceptionMappingConfig build() {
+            target.params = Collections.unmodifiableMap(target.params);
+            ExceptionMappingConfig result = target;
+            target = new ExceptionMappingConfig(target);
+            return result;
+        }
+    }
+
 }

File src/java/com/opensymphony/xwork2/config/entities/InterceptorConfig.java

 import java.util.HashMap;
 import java.util.Map;
 import java.util.LinkedHashMap;
+import java.util.Collections;
 import java.io.Serializable;
 
 import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
 
 /**
  * Configuration for Interceptors.
  *
  * @author Mike
  */
-public class InterceptorConfig extends Located implements Parameterizable, Serializable {
+public class InterceptorConfig extends Located implements Serializable {
 
-    Map params;
+    Map<String,String> params;
     String className;
     String name;
 
 
-    public InterceptorConfig() {
-    }
-
-    public InterceptorConfig(String name, Class clazz, Map params) {
-        this.name = name;
-        this.className = clazz.getName();
-        this.params = params;
-    }
-
-    public InterceptorConfig(String name, String className, Map params) {
+    protected InterceptorConfig(String name, String className) {
+        this.params = new LinkedHashMap<String,String>();
         this.name = name;
         this.className = className;
-        this.params = params;
     }
-    
-    public InterceptorConfig(InterceptorConfig parent, Map params) {
-        this.name = parent.getName();
-        this.className = parent.getClassName();
-        this.params = new HashMap(parent.getParams());
-        this.params.putAll(params);
-    }
-
 
-    public void setClassName(String className) {
-        this.className = className;
+    protected InterceptorConfig(InterceptorConfig orig) {
+        this.name = orig.name;
+        this.className = orig.className;
+        this.params = new LinkedHashMap<String,String>(orig.params);
     }
 
+
     public String getClassName() {
         return className;
     }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
     public String getName() {
         return name;
     }
 
-    public void setParams(Map params) {
-        this.params = params;
-    }
-
-    public Map getParams() {
-        if (params == null) {
-            params = new LinkedHashMap();
-        }
-
+    public Map<String,String> getParams() {
         return params;
     }
 
-    public void addParam(String name, Object value) {
-        getParams().put(name, value);
-    }
-
     public boolean equals(Object o) {
         if (this == o) {
             return true;
 
         return result;
     }
+
+    /**
+     * 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 InterceptorConfig target;
+
+        public Builder(String name, String className) {
+            target = new InterceptorConfig(name, className);
+        }
+
+        public Builder(InterceptorConfig orig) {
+            target = new InterceptorConfig(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 InterceptorConfig build() {
+            target.params = Collections.unmodifiableMap(target.params);
+            InterceptorConfig result = target;
+            target = new InterceptorConfig(target);
+            return result;
+        }
+    }
 }

File src/java/com/opensymphony/xwork2/config/entities/InterceptorListHolder.java

  */
 public interface InterceptorListHolder {
 
-    void addInterceptor(InterceptorMapping interceptor);
+    InterceptorListHolder addInterceptor(InterceptorMapping interceptor);
 
-    void addInterceptors(List<InterceptorMapping> interceptors);
+    InterceptorListHolder addInterceptors(List<InterceptorMapping> interceptors);
 }

File src/java/com/opensymphony/xwork2/config/entities/InterceptorLocator.java

+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);
+}

File src/java/com/opensymphony/xwork2/config/entities/InterceptorMapping.java

     private String name;
     private Interceptor interceptor;
 
-    public InterceptorMapping() {
-    }
-
     public InterceptorMapping(String name, Interceptor interceptor) {
         this.name = name;
         this.interceptor = interceptor;
         return name;
     }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
     public Interceptor getInterceptor() {
         return interceptor;
     }
 
-    public void setInterceptor(Interceptor interceptor) {
-        this.interceptor = interceptor;
-    }
-
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;

File src/java/com/opensymphony/xwork2/config/entities/InterceptorStackConfig.java

 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.List;
+import java.util.Collections;
 
 
 /**
  * @author Mike
  * @author Rainer Hermanns
  */
-public class InterceptorStackConfig extends Located implements InterceptorListHolder, Serializable {
+public class InterceptorStackConfig extends Located implements Serializable {
 
     private List<InterceptorMapping> interceptors;
     private String name;
 
 
-    public InterceptorStackConfig() {
+    protected InterceptorStackConfig() {
         this.interceptors = new ArrayList<InterceptorMapping>();
     }
 
-    public InterceptorStackConfig(String name) {
-        this();
-        this.name = name;
+    protected InterceptorStackConfig(InterceptorStackConfig orig) {
+        this.name = orig.name;
+        this.interceptors = new ArrayList<InterceptorMapping>(orig.interceptors);
     }
 
 
         return interceptors;
     }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
     public String getName() {
         return name;
     }
 
-    public void addInterceptor(InterceptorMapping interceptor) {
-        this.interceptors.add(interceptor);
-    }
-
-    public void addInterceptors(List<InterceptorMapping> interceptors) {
-        this.interceptors.addAll(interceptors);
-    }
-
     public boolean equals(Object o) {
         if (this == o) {
             return true;
 
         return result;
     }
+
+    /**
+     * 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 {
+        private InterceptorStackConfig target;
+
+        public Builder(String name) {
+            target = new InterceptorStackConfig();
+            target.name = name;
+        }
+
+        public Builder name(String name) {
+            target.name = name;
+            return this;
+        }
+
+        public Builder addInterceptor(InterceptorMapping interceptor) {
+            target.interceptors.add(interceptor);
+            return this;
+        }
+
+        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() {
+            target.interceptors = Collections.unmodifiableList(target.interceptors);
+            InterceptorStackConfig result = target;
+            target = new InterceptorStackConfig(target);
+            return result;
+        }
+    }
 }

File src/java/com/opensymphony/xwork2/config/entities/PackageConfig.java

 
 import com.opensymphony.xwork2.util.TextUtils;
 import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
 import com.opensymphony.xwork2.util.logging.Logger;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
  * @author Rainer Hermanns
  * @version $Revision$
  */
-public class PackageConfig extends Located implements Comparable, Serializable {
+public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
 
     private static final Logger LOG = LoggerFactory.getLogger(PackageConfig.class);
 
-
-    private Map<String, ActionConfig> actionConfigs = new LinkedHashMap<String, ActionConfig>();
-    private Map<String, ResultConfig> globalResultConfigs = new LinkedHashMap<String, ResultConfig>();
-    private Map interceptorConfigs = new LinkedHashMap();
-    private Map<String, ResultTypeConfig> resultTypeConfigs = new LinkedHashMap<String, ResultTypeConfig>();
-    private List globalExceptionMappingConfigs = new ArrayList();
-    private List<PackageConfig> parents = new ArrayList<PackageConfig>();
+    private Map<String, ActionConfig> actionConfigs;
+    private Map<String, ResultConfig> globalResultConfigs;
+    private Map<String, Object> interceptorConfigs;
+    private Map<String, ResultTypeConfig> resultTypeConfigs;
+    private List<ExceptionMappingConfig> globalExceptionMappingConfigs;
+    private List<PackageConfig> parents;
     private String defaultInterceptorRef;
     private String defaultActionRef;
     private String defaultResultType;
     private boolean needsRefresh;
 
 
-    public PackageConfig() {
-    }
-
-    public PackageConfig(String name) {
+    protected PackageConfig(String name) {
         this.name = name;
-    }
-
-    public PackageConfig(String name, String namespace, boolean isAbstract) {
-        this(name);
-        this.namespace = TextUtils.noNull(namespace);
-        this.isAbstract = isAbstract;
-    }
-
-    public PackageConfig(String name, String namespace, boolean isAbstract, List parents) {
-        this(name, namespace, isAbstract);
-
-        if (parents != null) {
-            for (Iterator iterator = parents.iterator(); iterator.hasNext();) {
-                PackageConfig parent = (PackageConfig) iterator.next();
-                addParent(parent);
-            }
-        }
-    }
-
-
-    public void setAbstract(boolean isAbstract) {
-        this.isAbstract = isAbstract;
+        actionConfigs = new LinkedHashMap<String, ActionConfig>();
+        globalResultConfigs = new LinkedHashMap<String, ResultConfig>();
+        interceptorConfigs = new LinkedHashMap<String, Object>();
+        resultTypeConfigs = new LinkedHashMap<String, ResultTypeConfig>();
+        globalExceptionMappingConfigs = new ArrayList<ExceptionMappingConfig>();
+        parents = new ArrayList<PackageConfig>();
+    }
+
+
+    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<String, ActionConfig>(orig.actionConfigs);
+        this.globalResultConfigs = new LinkedHashMap<String, ResultConfig>(orig.globalResultConfigs);
+        this.interceptorConfigs = new LinkedHashMap<String, Object>(orig.interceptorConfigs);
+        this.resultTypeConfigs = new LinkedHashMap<String, ResultTypeConfig>(orig.resultTypeConfigs);
+        this.globalExceptionMappingConfigs = new ArrayList<ExceptionMappingConfig>(orig.globalExceptionMappingConfigs);
+        this.parents = new ArrayList<PackageConfig>(orig.parents);
     }
 
     public boolean isAbstract() {
     }
 
 
-    public void setDefaultInterceptorRef(String name) {
-        defaultInterceptorRef = name;
-    }
-
     public String getDefaultInterceptorRef() {
         return defaultInterceptorRef;
     }
 
-    public void setDefaultActionRef(String name) {
-        defaultActionRef = name;
-    }
-
     public String getDefaultActionRef() {
         return defaultActionRef;
     }
 
-    public void setDefaultClassRef( String defaultClassRef ) {
-       this.defaultClassRef = defaultClassRef;
-    }
-    
     public String getDefaultClassRef() {
        return defaultClassRef;
     }
     
     /**
-     * sets the default Result type for this package
-     *
-     * @param defaultResultType
-     */
-    public void setDefaultResultType(String defaultResultType) {
-        this.defaultResultType = defaultResultType;
-    }
-
-    /**
      * Returns the default result type for this package.
      */
     public String getDefaultResultType() {
         return interceptorConfigs;
     }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
     public String getName() {
         return name;
     }
 
-    public void setNamespace(String namespace) {
-        if (namespace == null) {
-            this.namespace = "";
-        } else {
-            this.namespace = namespace;
-        }
-    }
-
     public String getNamespace() {
         return namespace;
     }
         return needsRefresh;
     }
 
-    public void setNeedsRefresh(boolean needsRefresh) {
-        this.needsRefresh = needsRefresh;
-    }
-
     /**
      * gets the ExceptionMappingConfigs local to this package
      *
         return globalExceptionMappingConfigs;
     }
 
-    public void addActionConfig(String name, ActionConfig action) {
-        actionConfigs.put(name, action);
-    }
-
-    public void addAllParents(List<PackageConfig> parents) {
-        for (PackageConfig config : parents) {
-            addParent(config);
-        }
-    }
-
-    public void addGlobalResultConfig(ResultConfig resultConfig) {
-        globalResultConfigs.put(resultConfig.getName(), resultConfig);
-    }
-
-    public void addGlobalResultConfigs(Map resultConfigs) {
-        globalResultConfigs.putAll(resultConfigs);
-    }
-
-    public void addExceptionMappingConfig(ExceptionMappingConfig exceptionMappingConfig) {
-        globalExceptionMappingConfigs.add(exceptionMappingConfig);
-    }
-
-    public void addGlobalExceptionMappingConfigs(List exceptionMappingConfigs) {
-        globalExceptionMappingConfigs.addAll(exceptionMappingConfigs);
-    }
-
-    public void addInterceptorConfig(InterceptorConfig config) {
-        interceptorConfigs.put(config.getName(), config);
-    }
-
-    public void addInterceptorStackConfig(InterceptorStackConfig config) {
-        interceptorConfigs.put(config.getName(), config);
-    }
-
-    public void addParent(PackageConfig parent) {
-        if (this.equals(parent)) {
-            LOG.error("A package cannot extend itself: " + name);
-        }
-
-        parents.add(0, parent);
-    }
-
-    public void addResultTypeConfig(ResultTypeConfig config) {
-        resultTypeConfigs.put(config.getName(), config);
-    }
-
     public boolean equals(Object o) {
         if (this == o) {
             return true;
         return result;
     }
 
-    public void removeParent(PackageConfig parent) {
-        parents.remove(parent);
-    }
-
     public String toString() {
-        return "{PackageConfig Name:" + name + " namespace:" + namespace + " abstract:" + isAbstract + " parents:" + parents + "}";
+        return "{PackageConfig Name:" + name + " namespace:" + namespace + " parents:" + parents + "}";
     }
 
     public int compareTo(Object o) {
         return full.compareTo(otherFull);
     }
 
+    public Object getInterceptorConfig(String name) {
+        return getInterceptorConfigs().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 {
+
+        private PackageConfig target;
+
+        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 resultConfigs) {
+            target.globalResultConfigs.putAll(resultConfigs);
+            return this;
+        }
+
+        public Builder addExceptionMappingConfig(ExceptionMappingConfig exceptionMappingConfig) {
+            target.globalExceptionMappingConfigs.add(exceptionMappingConfig);
+            return this;
+        }
+
+        public Builder addGlobalExceptionMappingConfigs(List 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) {
+            if (this.equals(parent)) {
+                LOG.error("A package cannot extend itself: " + target.name);
+            }
+
+            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 PackageConfig build() {
+            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);
+
+            PackageConfig result = target;
+            target = new PackageConfig(result);
+            return result;
+        }
+
+        public String toString() {
+            return "[BUILDER] "+target.toString();
+        }
+    }
+
 }

File src/java/com/opensymphony/xwork2/config/entities/ResultConfig.java

 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.LinkedHashMap;
 import java.util.Map;
+import java.util.Collections;
 
 
 /**
  *
  * @author Mike
  */
-public class ResultConfig extends Located implements Parameterizable, Serializable {
+public class ResultConfig extends Located implements Serializable {
 
-    private Map params;
+    private Map<String,String> params;
     private String className;
     private String name;
 
 
-    public ResultConfig() {
-        params = new LinkedHashMap();
-    }
-
-    public ResultConfig(String name, String clazz) {
-        this(name, clazz, new LinkedHashMap());
-    }
-
-    public ResultConfig(String name, String className, Map params) {
+    protected ResultConfig(String name, String className) {
         this.name = name;
         this.className = className;
-        this.params = params;
+        params = new LinkedHashMap();
     }
 
-
-    public void setClassName(String className) {
-        this.className = className;
+    protected ResultConfig(ResultConfig orig) {
+        this.params = orig.params;
+        this.name = orig.name;
+        this.className = orig.className;
     }
 
     public String getClassName() {
         return className;
     }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
     public String getName() {
         return name;
     }
 
-    public void setParams(Map params) {
-        this.params = params;
-    }
-
-    public Map getParams() {
-        if (params == null) {
-            params = new LinkedHashMap();
-        }
-
+    public Map<String,String> getParams() {
         return params;
     }
 
-    public void addParam(String name, Object value) {
-        getParams().put(name, value);
-    }
 
     public boolean equals(Object o) {
         if (this == o) {
 
         return result;
     }
+
+    /**
+     * 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) {
+            target.params.putAll(params);
+            return this;
+        }
+
+        public Builder location(Location loc) {
+            target.location = loc;
+            return this;
+        }
+
+        public ResultConfig build() {
+            target.params = Collections.unmodifiableMap(target.params);
+            ResultConfig result = target;
+            target = new ResultConfig(target);
+            return result;
+        }
+    }
 }

File src/java/com/opensymphony/xwork2/config/entities/ResultTypeConfig.java

 
 import java.util.Map;
 import java.util.LinkedHashMap;
+import java.util.Collections;
 import java.io.Serializable;
 
 import com.opensymphony.xwork2.util.location.Located;
+import com.opensymphony.xwork2.util.location.Location;
 
 
 /**
  */
 public class ResultTypeConfig extends Located implements Serializable {
 
-    private String clazz;
+    private String className;
     private String name;
     private String defaultResultParam;
 
-    private Map params;
+    private Map<String,String> params;