001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.chain.generic;
018
019
020 import org.apache.commons.chain.Catalog;
021 import org.apache.commons.chain.CatalogFactory;
022 import org.apache.commons.chain.Command;
023 import org.apache.commons.chain.Context;
024 import org.apache.commons.chain.Filter;
025
026
027 /**
028 * <p>Look up a specified {@link Command} (which could also be a
029 * {@link org.apache.commons.chain.Chain})
030 * in a {@link Catalog}, and delegate execution to it. If the delegated-to
031 * {@link Command} is also a {@link Filter}, its <code>postprocess()</code>
032 * method will also be invoked at the appropriate time.</p>
033 *
034 * <p>The name of the {@link Command} can be specified either directly (via
035 * the <code>name</code> property) or indirectly (via the <code>nameKey</code>
036 * property). Exactly one of these must be set.</p>
037 *
038 * <p>If the <code>optional</code> property is set to <code>true</code>,
039 * failure to find the specified command in the specified catalog will be
040 * silently ignored. Otherwise, a lookup failure will trigger an
041 * <code>IllegalArgumentException</code>.</p>
042 *
043 * @author Craig R. McClanahan
044 * @version $Revision: 532951 $ $Date: 2007-04-27 04:59:00 +0100 (Fri, 27 Apr 2007) $
045 */
046
047 public class LookupCommand implements Filter {
048
049
050 // -------------------------------------------------------------- Constructors
051
052 /**
053 * Create an instance, setting its <code>catalogFactory</code> property to the
054 * value of <code>CatalogFactory.getInstance()</code>.
055 *
056 * @since Chain 1.1
057 */
058 public LookupCommand() {
059 this(CatalogFactory.getInstance());
060 }
061
062 /**
063 * Create an instance and initialize the <code>catalogFactory</code> property
064 * to given <code>factory</code>/
065 *
066 * @param factory The Catalog Factory.
067 *
068 * @since Chain 1.1
069 */
070 public LookupCommand(CatalogFactory factory) {
071 this.catalogFactory = factory;
072 }
073
074
075 // -------------------------------------------------------------- Properties
076
077 private CatalogFactory catalogFactory = null;
078
079 /**
080 * <p>Set the {@link CatalogFactory} from which lookups will be
081 * performed.</p>
082 *
083 * @param catalogFactory The Catalog Factory.
084 *
085 * @since Chain 1.1
086 */
087 public void setCatalogFactory(CatalogFactory catalogFactory) {
088 this.catalogFactory = catalogFactory;
089 }
090
091 /**
092 * Return the {@link CatalogFactory} from which lookups will be performed.
093 * @return The Catalog factory.
094 *
095 * @since Chain 1.1
096 */
097 public CatalogFactory getCatalogFactory() {
098
099 return this.catalogFactory;
100 }
101
102
103 private String catalogName = null;
104
105 /**
106 * <p>Return the name of the {@link Catalog} to be searched, or
107 * <code>null</code> to search the default {@link Catalog}.</p>
108 * @return The Catalog name.
109 */
110 public String getCatalogName() {
111
112 return (this.catalogName);
113
114 }
115
116
117 /**
118 * <p>Set the name of the {@link Catalog} to be searched, or
119 * <code>null</code> to search the default {@link Catalog}.</p>
120 *
121 * @param catalogName The new {@link Catalog} name or <code>null</code>
122 */
123 public void setCatalogName(String catalogName) {
124
125 this.catalogName = catalogName;
126
127 }
128
129
130 private String name = null;
131
132
133 /**
134 * <p>Return the name of the {@link Command} that we will look up and
135 * delegate execution to.</p>
136 * @return The name of the Command.
137 */
138 public String getName() {
139
140 return (this.name);
141
142 }
143
144
145 /**
146 * <p>Set the name of the {@link Command} that we will look up and
147 * delegate execution to.</p>
148 *
149 * @param name The new command name
150 */
151 public void setName(String name) {
152
153 this.name = name;
154
155 }
156
157
158 private String nameKey = null;
159
160
161 /**
162 * <p>Return the context attribute key under which the {@link Command}
163 * name is stored.</p>
164 * @return The context key of the Command.
165 */
166 public String getNameKey() {
167
168 return (this.nameKey);
169
170 }
171
172
173 /**
174 * <p>Set the context attribute key under which the {@link Command}
175 * name is stored.</p>
176 *
177 * @param nameKey The new context attribute key
178 */
179 public void setNameKey(String nameKey) {
180
181 this.nameKey = nameKey;
182
183 }
184
185
186 private boolean optional = false;
187
188
189 /**
190 * <p>Return <code>true</code> if locating the specified command
191 * is optional.</p>
192 * @return <code>true</code> if the Command is optional.
193 */
194 public boolean isOptional() {
195
196 return (this.optional);
197
198 }
199
200
201 /**
202 * <p>Set the optional flag for finding the specified command.</p>
203 *
204 * @param optional The new optional flag
205 */
206 public void setOptional(boolean optional) {
207
208 this.optional = optional;
209
210 }
211
212 private boolean ignoreExecuteResult = false;
213
214 /**
215 * <p>Return <code>true</code> if this command should ignore
216 * the return value from executing the looked-up command.
217 * Defaults to <code>false</code>, which means that the return result
218 * of executing this lookup will be whatever is returned from that
219 * command.</p>
220 * @return <code>true</code> if result of the looked up Command
221 * should be ignored.
222 *
223 * @since Chain 1.1
224 */
225 public boolean isIgnoreExecuteResult() {
226 return ignoreExecuteResult;
227 }
228
229 /**
230 * <p>Set the rules for whether or not this class will ignore or
231 * pass through the value returned from executing the looked up
232 * command.</p>
233 * <p>If you are looking up a chain which may be "aborted" and
234 * you do not want this class to stop chain processing, then this
235 * value should be set to <code>true</code></p>
236 * @param ignoreReturn <code>true</code> if result of the
237 * looked up Command should be ignored.
238 *
239 * @since Chain 1.1
240 */
241 public void setIgnoreExecuteResult(boolean ignoreReturn) {
242 this.ignoreExecuteResult = ignoreReturn;
243 }
244
245 private boolean ignorePostprocessResult = false;
246
247 /**
248 * <p>Return <code>true</code> if this command is a Filter and
249 * should ignore the return value from executing the looked-up Filter's
250 * <code>postprocess()</code> method.
251 * Defaults to <code>false</code>, which means that the return result
252 * of executing this lookup will be whatever is returned from that
253 * Filter.</p>
254 * @return <code>true</code> if result of the looked up Filter's
255 * <code>postprocess()</code> method should be ignored.
256 *
257 * @since Chain 1.1
258 */
259 public boolean isIgnorePostprocessResult() {
260 return ignorePostprocessResult;
261 }
262
263 /**
264 * <p>Set the rules for whether or not this class will ignore or
265 * pass through the value returned from executing the looked up
266 * Filter's <code>postprocess()</code> method.</p>
267 * <p>If you are looking up a Filter which may be "aborted" and
268 * you do not want this class to stop chain processing, then this
269 * value should be set to <code>true</code></p>
270 * @param ignorePostprocessResult <code>true</code> if result of the
271 * looked up Filter's <code>postprocess()</code> method should be ignored.
272 *
273 * @since Chain 1.1
274 */
275 public void setIgnorePostprocessResult(boolean ignorePostprocessResult) {
276 this.ignorePostprocessResult = ignorePostprocessResult;
277 }
278 // ---------------------------------------------------------- Filter Methods
279
280
281 /**
282 * <p>Look up the specified command, and (if found) execute it.
283 * Unless <code>ignoreExecuteResult</code> is set to <code>true</code>,
284 * return the result of executing the found command. If no command
285 * is found, return <code>false</code>, unless the <code>optional</code>
286 * property is <code>false</code>, in which case an <code>IllegalArgumentException</code>
287 * will be thrown.
288 * </p>
289 *
290 * @param context The context for this request
291 *
292 * @exception IllegalArgumentException if no such {@link Command}
293 * can be found and the <code>optional</code> property is set
294 * to <code>false</code>
295 * @return the result of executing the looked-up command, or
296 * <code>false</code> if no command is found or if the command
297 * is found but the <code>ignoreExecuteResult</code> property of this
298 * instance is <code>true</code>
299 * @throws Exception if and error occurs in the looked-up Command.
300 */
301 public boolean execute(Context context) throws Exception {
302
303 Command command = getCommand(context);
304 if (command != null) {
305 boolean result = (command.execute(context));
306 if (isIgnoreExecuteResult()) {
307 return false;
308 }
309 return result;
310 } else {
311 return (false);
312 }
313
314 }
315
316
317 /**
318 * <p>If the executed command was itself a {@link Filter}, call the
319 * <code>postprocess()</code> method of that {@link Filter} as well.</p>
320 *
321 * @param context The context for this request
322 * @param exception Any <code>Exception</code> thrown by command execution
323 *
324 * @return the result of executing the <code>postprocess</code> method
325 * of the looked-up command, unless <code>ignorePostprocessResult</code> is
326 * <code>true</code>. If no command is found, return <code>false</code>,
327 * unless the <code>optional</code> property is <code>false</code>, in which
328 * case <code>IllegalArgumentException</code> will be thrown.
329 */
330 public boolean postprocess(Context context, Exception exception) {
331
332 Command command = getCommand(context);
333 if (command != null) {
334 if (command instanceof Filter) {
335 boolean result = (((Filter) command).postprocess(context, exception));
336 if (isIgnorePostprocessResult()) {
337 return false;
338 }
339 return result;
340 }
341 }
342 return (false);
343
344 }
345
346
347 // --------------------------------------------------------- Private Methods
348
349
350 /**
351 * <p>Return the {@link Catalog} to look up the {@link Command} in.</p>
352 *
353 * @param context {@link Context} for this request
354 * @return The catalog.
355 * @exception IllegalArgumentException if no {@link Catalog}
356 * can be found
357 *
358 * @since Chain 1.2
359 */
360 protected Catalog getCatalog(Context context) {
361 CatalogFactory lookupFactory = this.catalogFactory;
362 if (lookupFactory == null) {
363 lookupFactory = CatalogFactory.getInstance();
364 }
365
366 String catalogName = getCatalogName();
367 Catalog catalog = null;
368 if (catalogName == null) {
369 // use default catalog
370 catalog = lookupFactory.getCatalog();
371 } else {
372 catalog = lookupFactory.getCatalog(catalogName);
373 }
374 if (catalog == null) {
375 if (catalogName == null) {
376 throw new IllegalArgumentException
377 ("Cannot find default catalog");
378 } else {
379 throw new IllegalArgumentException
380 ("Cannot find catalog '" + catalogName + "'");
381 }
382 }
383
384 return catalog;
385 }
386
387 /**
388 * <p>Return the {@link Command} instance to be delegated to.</p>
389 *
390 * @param context {@link Context} for this request
391 * @return The looked-up Command.
392 * @exception IllegalArgumentException if no such {@link Command}
393 * can be found and the <code>optional</code> property is set
394 * to <code>false</code>
395 */
396 protected Command getCommand(Context context) {
397
398 Catalog catalog = getCatalog(context);
399
400 Command command = null;
401 String name = getCommandName(context);
402 if (name != null) {
403 command = catalog.getCommand(name);
404 if ((command == null) && !isOptional()) {
405 if (catalogName == null) {
406 throw new IllegalArgumentException
407 ("Cannot find command '" + name
408 + "' in default catalog");
409 } else {
410 throw new IllegalArgumentException
411 ("Cannot find command '" + name
412 + "' in catalog '" + catalogName + "'");
413 }
414 }
415 return (command);
416 } else {
417 throw new IllegalArgumentException("No command name");
418 }
419
420 }
421
422 /**
423 * <p>Return the name of the {@link Command} instance to be delegated to.</p>
424 *
425 * @param context {@link Context} for this request
426 * @return The name of the {@link Command} instance
427 *
428 * @since Chain 1.2
429 */
430 protected String getCommandName(Context context) {
431
432 String name = getName();
433 if (name == null) {
434 name = (String) context.get(getNameKey());
435 }
436 return name;
437
438 }
439
440 }