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    }