1 /*
2 * $Id$
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21 package org.apache.struts.chain.commands.generic;
22
23 import java.lang.reflect.InvocationTargetException;
24
25 import org.apache.commons.beanutils.ConstructorUtils;
26 import org.apache.commons.chain.Catalog;
27 import org.apache.commons.chain.CatalogFactory;
28 import org.apache.commons.chain.Command;
29 import org.apache.commons.chain.Context;
30 import org.apache.commons.chain.Filter;
31 import org.apache.struts.chain.commands.util.ClassUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36 * Variant on chain LookupCommand which can optionally wrap the context it
37 * passes to the looked up command in an alternative class.
38 */
39 public class WrappingLookupCommand implements Filter<Context> {
40
41 /**
42 * The {@code Log} instance for this class.
43 */
44 private final Logger log =
45 LoggerFactory.getLogger(WrappingLookupCommand.class);
46
47 // ------------------------------------------------------ Instance Variables
48
49 /**
50 * Field for property.
51 */
52 private String catalogName = null;
53
54 /**
55 * Field for property.
56 */
57 private String name = null;
58
59 /**
60 * Field for property.
61 */
62 private String nameKey = null;
63
64 /**
65 * Field for property.
66 */
67 private String wrapperClassName = null;
68
69 /**
70 * Field for property.
71 */
72 private boolean optional = false;
73
74 /**
75 * Zero-argument constructor.
76 */
77 public WrappingLookupCommand() {
78 catalogName = null;
79 name = null;
80 nameKey = null;
81 optional = false;
82 }
83
84 /**
85 * Return CatalogName property.
86 *
87 * @return Value of CatalogName property.
88 */
89 public String getCatalogName() {
90 return catalogName;
91 }
92
93 /**
94 * Set CatalogName property.
95 *
96 * @param catalogName New value for CatalogName
97 */
98 public void setCatalogName(String catalogName) {
99 this.catalogName = catalogName;
100 }
101
102 /**
103 * Retrieve Name property.
104 *
105 * @return Value of Name property
106 */
107 public String getName() {
108 return name;
109 }
110
111 /**
112 * Set Name property.
113 *
114 * @param name New value for Name
115 */
116 public void setName(String name) {
117 this.name = name;
118 }
119
120 /**
121 * Return NameKey property.
122 *
123 * @return Value of NameKey property.
124 */
125 public String getNameKey() {
126 return nameKey;
127 }
128
129 /**
130 * Set NameKey property.
131 *
132 * @param nameKey New value for NameKey
133 */
134 public void setNameKey(String nameKey) {
135 this.nameKey = nameKey;
136 }
137
138 /**
139 * Test Optional property.
140 *
141 * @return TRUE if Optional is TRUE.
142 */
143 public boolean isOptional() {
144 return optional;
145 }
146
147 /**
148 * Set Optional property.
149 *
150 * @param optional New value for Optional
151 */
152 public void setOptional(boolean optional) {
153 this.optional = optional;
154 }
155
156 /**
157 * Return the WrapperClass property.
158 *
159 * @return The WrapperClass property
160 */
161 public String getWrapperClassName() {
162 return wrapperClassName;
163 }
164
165 /**
166 * Set WrappClassName property.
167 *
168 * @param wrapperClassName The name of a WrapperClass
169 */
170 public void setWrapperClassName(String wrapperClassName) {
171 this.wrapperClassName = wrapperClassName;
172 }
173
174 /**
175 * Invoke the Command for a Context, returning TRUE if processing should
176 * halt.
177 *
178 * @param context Our ActionContext
179 *
180 * @return TRUE if processing should halt
181 *
182 * @throws Exception On any error
183 */
184 public boolean execute(Context context)
185 throws Exception {
186 log.trace("execute [{}]", this);
187
188 Command<Context> command = getCommand(context);
189
190 if (command != null) {
191 return command.execute(getContext(context));
192 } else {
193 return CONTINUE_PROCESSING;
194 }
195 }
196
197 /**
198 * Process the Exception for any Command that is a filter.
199 *
200 * @param context Our ActionContext
201 * @param exception The Exception thrown by another Command in a Chain
202 *
203 * @return TRUE if there is a Filter to process
204 */
205 public boolean postprocess(Context context, Exception exception) {
206 Command<Context> command = getCommand(context);
207
208 if (command != null && command instanceof Filter) {
209 try {
210 final Filter<Context> filter = (Filter<Context>) command;
211 return filter.postprocess(getContext(context), exception);
212 } catch (NoSuchMethodException | IllegalAccessException |
213 InvocationTargetException | InstantiationException |
214 ClassNotFoundException ex) {
215 log.error("Error wrapping context in postprocess", ex);
216 }
217 }
218
219 return false;
220 }
221
222 /**
223 * Return the Command to process for this Context.
224 *
225 * @param context The Context we are processing
226 *
227 * @return The Command to process for this Context
228 */
229 protected Command<Context> getCommand(Context context) {
230 CatalogFactory<Context> catalogFactory = CatalogFactory.getInstance();
231 String catalogName = getCatalogName();
232 Catalog<Context> catalog;
233
234 if (catalogName == null) {
235 catalog = catalogFactory.getCatalog();
236 catalogName = "{default}"; // for debugging purposes
237 } else {
238 catalog = catalogFactory.getCatalog(catalogName);
239 }
240
241 if (catalog == null) {
242 throw new IllegalArgumentException("Cannot find catalog '"
243 + catalogName + "'");
244 }
245
246 Command<Context> command;
247 String name = getName();
248
249 if (name == null) {
250 name = (String) context.get(getNameKey());
251 }
252
253 if (name != null) {
254 log.debug("Lookup command {} in catalog {}",
255 name, catalogName);
256
257 command = catalog.getCommand(name);
258
259 log.debug("Found command {}; optional: {}",
260 command, isOptional());
261
262 if ((command == null) && !isOptional()) {
263 throw new IllegalArgumentException("Cannot find command " + "'"
264 + name + "' in catalog '" + catalogName + "'");
265 } else {
266 return command;
267 }
268 } else {
269 throw new IllegalArgumentException("No command name");
270 }
271 }
272
273 /**
274 * If the wrapperClassName property is not null, return a Context of the
275 * type specified by wrapperClassName, instantiated using a single-arg
276 * constructor which takes the context passed as an argument to this
277 * method.</p>
278 *
279 * <p>This method throws an exception if the wrapperClass cannot be found,
280 * or if there are any errors instantiating the wrapping context.</p>
281 *
282 * @param context Context we are processing
283 *
284 * @return Context wrapper
285 *
286 * @throws ClassNotFoundException On failed instantiation
287 * @throws InstantiationException On failed instantiation
288 * @throws InvocationTargetException On failed instantiation
289 * @throws IllegalAccessException On failed instantiation
290 * @throws NoSuchMethodException On failed instantiation
291 */
292 protected Context getContext(Context context)
293 throws ClassNotFoundException, InstantiationException,
294 InvocationTargetException, IllegalAccessException,
295 NoSuchMethodException {
296 if (wrapperClassName == null) {
297 log.debug("No defined wrapper class; "
298 + "returning original context.");
299
300 return context;
301 }
302
303 log.debug("Looking for wrapper class: {}", wrapperClassName);
304
305 Class<?> wrapperClass = ClassUtils.getApplicationClass(wrapperClassName);
306
307 log.debug("Instantiating wrapper class");
308
309 return (Context) ConstructorUtils.invokeConstructor(wrapperClass, context);
310 }
311 }