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 }