1 /*
2 * Copyright (c) 2002-2006 by OpenSymphony
3 * All rights reserved.
4 */
5
6 package com.opensymphony.xwork2.interceptor;
7
8 import com.opensymphony.xwork2.ActionContext;
9 import com.opensymphony.xwork2.ActionInvocation;
10 import com.opensymphony.xwork2.ValidationAware;
11 import com.opensymphony.xwork2.inject.Inject;
12 import com.opensymphony.xwork2.config.entities.ActionConfig;
13 import com.opensymphony.xwork2.util.ValueStack;
14 import com.opensymphony.xwork2.util.ClearableValueStack;
15 import com.opensymphony.xwork2.util.ValueStackFactory;
16 import com.opensymphony.xwork2.util.LocalizedTextUtil;
17 import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
18 import com.opensymphony.xwork2.util.logging.Logger;
19 import com.opensymphony.xwork2.util.logging.LoggerFactory;
20
21 import java.util.Map;
22
23
24 /**
25 * <!-- START SNIPPET: description -->
26 *
27 * The aim of this Interceptor is to alias a named parameter to a different named parameter. By acting as the glue
28 * between actions sharing similiar parameters (but with different names), it can help greatly with action chaining.
29 *
30 * <p/> Action's alias expressions should be in the form of <code>#{ "name1" : "alias1", "name2" : "alias2" }</code>.
31 * This means that assuming an action (or something else in the stack) has a value for the expression named <i>name1</i> and the
32 * action this interceptor is applied to has a setter named <i>alias1</i>, <i>alias1</i> will be set with the value from
33 * <i>name1</i>.
34 *
35 * <!-- END SNIPPET: description -->
36 *
37 * <p/> <u>Interceptor parameters:</u>
38 *
39 * <!-- START SNIPPET: parameters -->
40 *
41 * <ul>
42 *
43 * <li>aliasesKey (optional) - the name of the action parameter to look for the alias map (by default this is
44 * <i>aliases</i>).</li>
45 *
46 * </ul>
47 *
48 * <!-- END SNIPPET: parameters -->
49 *
50 * <p/> <u>Extending the interceptor:</u>
51 *
52 * <p/>
53 *
54 * <!-- START SNIPPET: extending -->
55 *
56 * This interceptor does not have any known extension points.
57 *
58 * <!-- END SNIPPET: extending -->
59 *
60 * <p/> <u>Example code:</u>
61 *
62 * <pre>
63 * <!-- START SNIPPET: example -->
64 * <action name="someAction" class="com.examples.SomeAction">
65 * <!-- The value for the foo parameter will be applied as if it were named bar -->
66 * <param name="aliases">#{ 'foo' : 'bar' }</param>
67 *
68 * <interceptor-ref name="alias"/>
69 * <interceptor-ref name="basicStack"/>
70 * <result name="success">good_result.ftl</result>
71 * </action>
72 * <!-- END SNIPPET: example -->
73 * </pre>
74 *
75 * @author Matthew Payne
76 */
77 public class AliasInterceptor extends AbstractInterceptor {
78
79 private static final Logger LOG = LoggerFactory.getLogger(AliasInterceptor.class);
80
81 private static final String DEFAULT_ALIAS_KEY = "aliases";
82 protected String aliasesKey = DEFAULT_ALIAS_KEY;
83
84 protected ValueStackFactory valueStackFactory;
85 static boolean devMode = false;
86
87 @Inject("devMode")
88 public static void setDevMode(String mode) {
89 devMode = "true".equals(mode);
90 }
91
92 @Inject
93 public void setValueStackFactory(ValueStackFactory valueStackFactory) {
94 this.valueStackFactory = valueStackFactory;
95 }
96
97 /**
98 * Sets the name of the action parameter to look for the alias map.
99 * <p/>
100 * Default is <code>aliases</code>.
101 *
102 * @param aliasesKey the name of the action parameter
103 */
104 public void setAliasesKey(String aliasesKey) {
105 this.aliasesKey = aliasesKey;
106 }
107
108 @Override public String intercept(ActionInvocation invocation) throws Exception {
109
110 ActionConfig config = invocation.getProxy().getConfig();
111 ActionContext ac = invocation.getInvocationContext();
112 Object action = invocation.getAction();
113
114 // get the action's parameters
115 final Map<String, String> parameters = config.getParams();
116
117 if (parameters.containsKey(aliasesKey)) {
118
119 String aliasExpression = parameters.get(aliasesKey);
120 ValueStack stack = ac.getValueStack();
121 Object obj = stack.findValue(aliasExpression);
122
123 if (obj != null && obj instanceof Map) {
124 //get secure stack
125 ValueStack newStack = valueStackFactory.createValueStack(stack);
126 boolean clearableStack = newStack instanceof ClearableValueStack;
127 if (clearableStack) {
128 //if the stack's context can be cleared, do that to prevent OGNL
129 //from having access to objects in the stack, see XW-641
130 ((ClearableValueStack)newStack).clearContextValues();
131 Map<String, Object> context = newStack.getContext();
132 ReflectionContextState.setCreatingNullObjects(context, true);
133 ReflectionContextState.setDenyMethodExecution(context, true);
134 ReflectionContextState.setReportingConversionErrors(context, true);
135
136 //keep locale from original context
137 context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));
138 }
139
140 // override
141 Map aliases = (Map) obj;
142 for (Object o : aliases.entrySet()) {
143 Map.Entry entry = (Map.Entry) o;
144 String name = entry.getKey().toString();
145 String alias = (String) entry.getValue();
146 Object value = stack.findValue(name);
147 if (null == value) {
148 // workaround
149 Map<String, Object> contextParameters = ActionContext.getContext().getParameters();
150
151 if (null != contextParameters) {
152 value = contextParameters.get(name);
153 }
154 }
155 if (null != value) {
156 try {
157 newStack.setValue(alias, value);
158 } catch (RuntimeException e) {
159 if (devMode) {
160 String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
161 "Unexpected Exception caught setting '" + entry.getKey() + "' on '" + action.getClass() + ": " + e.getMessage()
162 });
163 LOG.error(developerNotification);
164 if (action instanceof ValidationAware) {
165 ((ValidationAware) action).addActionMessage(developerNotification);
166 }
167 }
168 }
169 }
170 }
171
172 if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
173 stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));
174 } else {
175 LOG.debug("invalid alias expression:" + aliasesKey);
176 }
177 }
178
179 return invocation.invoke();
180 }
181
182 }