1 /* 2 * Copyright (c) 2002-2006 by OpenSymphony 3 * All rights reserved. 4 */ 5 package com.opensymphony.xwork2.interceptor; 6 7 import com.opensymphony.xwork2.ActionContext; 8 import com.opensymphony.xwork2.ActionInvocation; 9 import com.opensymphony.xwork2.ValidationAware; 10 import com.opensymphony.xwork2.inject.Inject; 11 import com.opensymphony.xwork2.config.entities.ActionConfig; 12 import com.opensymphony.xwork2.config.entities.Parameterizable; 13 import com.opensymphony.xwork2.util; 14 import com.opensymphony.xwork2.util.reflection.ReflectionContextState; 15 import com.opensymphony.xwork2.util.logging.Logger; 16 import com.opensymphony.xwork2.util.logging.LoggerFactory; 17 18 import java.util.Collections; 19 import java.util.Map; 20 import java.util.TreeMap; 21 22 23 /** 24 * <!-- START SNIPPET: description --> 25 * 26 * This interceptor populates the action with the static parameters defined in the action configuration. If the action 27 * implements {@link Parameterizable}, a map of the static parameters will be also be passed directly to the action. 28 * The static params will be added to the request params map, unless "merge" is set to false. 29 * 30 * <p/> Parameters are typically defined with <param> elements within xwork.xml. 31 * 32 * <!-- END SNIPPET: description --> 33 * 34 * <p/> <u>Interceptor parameters:</u> 35 * 36 * <!-- START SNIPPET: parameters --> 37 * 38 * <ul> 39 * 40 * <li>None</li> 41 * 42 * </ul> 43 * 44 * <!-- END SNIPPET: parameters --> 45 * 46 * <p/> <u>Extending the interceptor:</u> 47 * 48 * <!-- START SNIPPET: extending --> 49 * 50 * <p/>There are no extension points to this interceptor. 51 * 52 * <!-- END SNIPPET: extending --> 53 * 54 * <p/> <u>Example code:</u> 55 * 56 * <pre> 57 * <!-- START SNIPPET: example --> 58 * <action name="someAction" class="com.examples.SomeAction"> 59 * <interceptor-ref name="staticParams"> 60 * <param name="parse">true</param> 61 * <param name="overwrite">false</param> 62 * </interceptor-ref> 63 * <result name="success">good_result.ftl</result> 64 * </action> 65 * <!-- END SNIPPET: example --> 66 * </pre> 67 * 68 * @author Patrick Lightbody 69 */ 70 public class StaticParametersInterceptor extends AbstractInterceptor { 71 72 private boolean parse; 73 private boolean overwrite; 74 private boolean merge = true; 75 76 static boolean devMode = false; 77 78 private static final Logger LOG = LoggerFactory.getLogger(StaticParametersInterceptor.class); 79 80 private ValueStackFactory valueStackFactory; 81 82 @Inject 83 public void setValueStackFactory(ValueStackFactory valueStackFactory) { 84 this.valueStackFactory = valueStackFactory; 85 } 86 87 @Inject("devMode") 88 public static void setDevMode(String mode) { 89 devMode = "true".equals(mode); 90 } 91 92 public void setParse(String value) { 93 this.parse = Boolean.valueOf(value).booleanValue(); 94 } 95 96 public void setMerge(String value) { 97 this.merge = Boolean.valueOf(value).booleanValue(); 98 } 99 100 /** 101 * Overwrites already existing parameters from other sources. 102 * Static parameters are the successor over previously set parameters, if true. 103 * 104 * @param value 105 */ 106 public void setOverwrite(String value) { 107 this.overwrite = Boolean.valueOf(value).booleanValue(); 108 } 109 110 @Override 111 public String intercept(ActionInvocation invocation) throws Exception { 112 ActionConfig config = invocation.getProxy().getConfig(); 113 Object action = invocation.getAction(); 114 115 final Map<String, String> parameters = config.getParams(); 116 117 if (LOG.isDebugEnabled()) { 118 LOG.debug("Setting static parameters " + parameters); 119 } 120 121 // for actions marked as Parameterizable, pass the static parameters directly 122 if (action instanceof Parameterizable) { 123 ((Parameterizable) action).setParams(parameters); 124 } 125 126 if (parameters != null) { 127 ActionContext ac = ActionContext.getContext(); 128 Map<String, Object> contextMap = ac.getContextMap(); 129 try { 130 ReflectionContextState.setCreatingNullObjects(contextMap, true); 131 ReflectionContextState.setReportingConversionErrors(contextMap, true); 132 final ValueStack stack = ac.getValueStack(); 133 134 ValueStack newStack = valueStackFactory.createValueStack(stack); 135 boolean clearableStack = newStack instanceof ClearableValueStack; 136 if (clearableStack) { 137 //if the stack's context can be cleared, do that to prevent OGNL 138 //from having access to objects in the stack, see XW-641 139 ((ClearableValueStack)newStack).clearContextValues(); 140 Map<String, Object> context = newStack.getContext(); 141 ReflectionContextState.setCreatingNullObjects(context, true); 142 ReflectionContextState.setDenyMethodExecution(context, true); 143 ReflectionContextState.setReportingConversionErrors(context, true); 144 145 //keep locale from original context 146 context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE)); 147 } 148 149 for (Map.Entry<String, String> entry : parameters.entrySet()) { 150 Object val = entry.getValue(); 151 if (parse && val instanceof String) { 152 val = TextParseUtil.translateVariables(val.toString(), stack); 153 } 154 try { 155 newStack.setValue(entry.getKey(), val); 156 } catch (RuntimeException e) { 157 if (devMode) { 158 String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{ 159 "Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage() 160 }); 161 LOG.error(developerNotification); 162 if (action instanceof ValidationAware) { 163 ((ValidationAware) action).addActionMessage(developerNotification); 164 } 165 } 166 } 167 } 168 169 if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null)) 170 stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS)); 171 172 if (merge) 173 addParametersToContext(ac, parameters); 174 } finally { 175 ReflectionContextState.setCreatingNullObjects(contextMap, false); 176 ReflectionContextState.setReportingConversionErrors(contextMap, false); 177 } 178 } 179 return invocation.invoke(); 180 } 181 182 183 /** 184 * @param ac The action context 185 * @return the parameters from the action mapping in the context. If none found, returns 186 * an empty map. 187 */ 188 protected Map<String, String> retrieveParameters(ActionContext ac) { 189 ActionConfig config = ac.getActionInvocation().getProxy().getConfig(); 190 if (config != null) { 191 return config.getParams(); 192 } else { 193 return Collections.emptyMap(); 194 } 195 } 196 197 /** 198 * Adds the parameters into context's ParameterMap. 199 * As default, static parameters will not overwrite existing paramaters from other sources. 200 * If you want the static parameters as successor over already existing parameters, set overwrite to <tt>true</tt>. 201 * 202 * @param ac The action context 203 * @param newParams The parameter map to apply 204 */ 205 protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) { 206 Map<String, Object> previousParams = ac.getParameters(); 207 208 Map<String, Object> combinedParams; 209 if ( overwrite ) { 210 if (previousParams != null) { 211 combinedParams = new TreeMap<String, Object>(previousParams); 212 } else { 213 combinedParams = new TreeMap<String, Object>(); 214 } 215 if ( newParams != null) { 216 combinedParams.putAll(newParams); 217 } 218 } else { 219 if (newParams != null) { 220 combinedParams = new TreeMap<String, Object>(newParams); 221 } else { 222 combinedParams = new TreeMap<String, Object>(); 223 } 224 if ( previousParams != null) { 225 combinedParams.putAll(previousParams); 226 } 227 } 228 ac.setParameters(combinedParams); 229 } 230 }