| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.chain2.base; |
| |
| import org.apache.commons.chain2.Catalog; |
| import org.apache.commons.chain2.CatalogFactory; |
| import org.apache.commons.chain2.Command; |
| import org.apache.commons.chain2.Context; |
| import org.apache.commons.chain2.Filter; |
| import org.apache.commons.chain2.Processing; |
| import org.apache.commons.chain2.impl.CatalogFactoryBase; |
| |
| import java.util.Map; |
| |
| /** |
| * <p>Look up a specified {@link Command} (which could also be a |
| * {@link org.apache.commons.chain2.Chain}) |
| * in a {@link Catalog}, and delegate execution to it. If the delegated-to |
| * {@link Command} is also a {@link Filter}, its <code>postprocess()</code> |
| * method will also be invoked at the appropriate time.</p> |
| * |
| * <p>The name of the {@link Command} can be specified either directly (via |
| * the <code>name</code> property) or indirectly (via the <code>nameKey</code> |
| * property). Exactly one of these must be set.</p> |
| * |
| * <p>If the <code>optional</code> property is set to <code>true</code>, |
| * failure to find the specified command in the specified catalog will be |
| * silently ignored. Otherwise, a lookup failure will trigger an |
| * <code>IllegalArgumentException</code>.</p> |
| * |
| * @param <K> Context key type |
| * @param <V> Context value type |
| * @param <C> Type of the context associated with this command |
| * |
| * @version $Id$ |
| */ |
| public class LookupCommand<K, V, C extends Map<K, V>> implements Filter<K, V, C> { |
| |
| // -------------------------------------------------------------- Constructors |
| |
| /** |
| * Create an instance, setting its <code>catalogFactory</code> property to the |
| * value of <code>CatalogFactory.getInstance()</code>. |
| * |
| * @since Chain 1.1 |
| */ |
| public LookupCommand() { |
| this(CatalogFactoryBase.<K, V, C>getInstance()); |
| } |
| |
| /** |
| * Create an instance and initialize the <code>catalogFactory</code> property |
| * to given <code>factory</code>/ |
| * |
| * @param factory The Catalog Factory. |
| * |
| * @since Chain 1.1 |
| */ |
| public LookupCommand(CatalogFactory<K, V, C> factory) { |
| this.catalogFactory = factory; |
| } |
| |
| // -------------------------------------------------------------- Properties |
| |
| private CatalogFactory<K, V, C> catalogFactory = null; |
| |
| /** |
| * <p>Set the {@link CatalogFactoryBase} from which lookups will be |
| * performed.</p> |
| * |
| * @param catalogFactory The Catalog Factory. |
| * |
| * @since Chain 1.1 |
| */ |
| public void setCatalogFactory(CatalogFactory<K, V, C> catalogFactory) { |
| this.catalogFactory = catalogFactory; |
| } |
| |
| /** |
| * Return the {@link CatalogFactoryBase} from which lookups will be performed. |
| * @return The Catalog factory. |
| * |
| * @since Chain 1.1 |
| */ |
| public CatalogFactory<K, V, C> getCatalogFactory() { |
| return this.catalogFactory; |
| } |
| |
| private String catalogName = null; |
| |
| /** |
| * <p>Return the name of the {@link Catalog} to be searched, or |
| * <code>null</code> to search the default {@link Catalog}.</p> |
| * @return The Catalog name. |
| */ |
| public String getCatalogName() { |
| return this.catalogName; |
| } |
| |
| /** |
| * <p>Set the name of the {@link Catalog} to be searched, or |
| * <code>null</code> to search the default {@link Catalog}.</p> |
| * |
| * @param catalogName The new {@link Catalog} name or <code>null</code> |
| */ |
| public void setCatalogName(String catalogName) { |
| this.catalogName = catalogName; |
| } |
| |
| private String name = null; |
| |
| /** |
| * <p>Return the name of the {@link Command} that we will look up and |
| * delegate execution to.</p> |
| * @return The name of the Command. |
| */ |
| public String getName() { |
| return this.name; |
| } |
| |
| /** |
| * <p>Set the name of the {@link Command} that we will look up and |
| * delegate execution to.</p> |
| * |
| * @param name The new command name |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| private String nameKey = null; |
| |
| /** |
| * <p>Return the context attribute key under which the {@link Command} |
| * name is stored.</p> |
| * @return The context key of the Command. |
| */ |
| public String getNameKey() { |
| return this.nameKey; |
| } |
| |
| /** |
| * <p>Set the context attribute key under which the {@link Command} |
| * name is stored.</p> |
| * |
| * @param nameKey The new context attribute key |
| */ |
| public void setNameKey(String nameKey) { |
| this.nameKey = nameKey; |
| } |
| |
| private boolean optional = false; |
| |
| /** |
| * <p>Return <code>true</code> if locating the specified command |
| * is optional.</p> |
| * @return <code>true</code> if the Command is optional. |
| */ |
| public boolean isOptional() { |
| return this.optional; |
| } |
| |
| /** |
| * <p>Set the optional flag for finding the specified command.</p> |
| * |
| * @param optional The new optional flag |
| */ |
| public void setOptional(boolean optional) { |
| this.optional = optional; |
| } |
| |
| private boolean ignoreExecuteResult = false; |
| |
| /** |
| * <p>Return <code>true</code> if this command should ignore |
| * the return value from executing the looked-up command. |
| * Defaults to <code>false</code>, which means that the return result |
| * of executing this lookup will be whatever is returned from that |
| * command.</p> |
| * @return <code>true</code> if result of the looked up Command |
| * should be ignored. |
| * |
| * @since Chain 1.1 |
| */ |
| public boolean isIgnoreExecuteResult() { |
| return ignoreExecuteResult; |
| } |
| |
| /** |
| * <p>Set the rules for whether or not this class will ignore or |
| * pass through the value returned from executing the looked up |
| * command.</p> |
| * <p>If you are looking up a chain which may be "aborted" and |
| * you do not want this class to stop chain processing, then this |
| * value should be set to <code>true</code></p> |
| * @param ignoreReturn <code>true</code> if result of the |
| * looked up Command should be ignored. |
| * |
| * @since Chain 1.1 |
| */ |
| public void setIgnoreExecuteResult(boolean ignoreReturn) { |
| this.ignoreExecuteResult = ignoreReturn; |
| } |
| |
| private boolean ignorePostprocessResult = false; |
| |
| /** |
| * <p>Return <code>true</code> if this command is a Filter and |
| * should ignore the return value from executing the looked-up Filter's |
| * <code>postprocess()</code> method. |
| * Defaults to <code>false</code>, which means that the return result |
| * of executing this lookup will be whatever is returned from that |
| * Filter.</p> |
| * @return <code>true</code> if result of the looked up Filter's |
| * <code>postprocess()</code> method should be ignored. |
| * |
| * @since Chain 1.1 |
| */ |
| public boolean isIgnorePostprocessResult() { |
| return ignorePostprocessResult; |
| } |
| |
| /** |
| * <p>Set the rules for whether or not this class will ignore or |
| * pass through the value returned from executing the looked up |
| * Filter's <code>postprocess()</code> method.</p> |
| * <p>If you are looking up a Filter which may be "aborted" and |
| * you do not want this class to stop chain processing, then this |
| * value should be set to <code>true</code></p> |
| * @param ignorePostprocessResult <code>true</code> if result of the |
| * looked up Filter's <code>postprocess()</code> method should be ignored. |
| * |
| * @since Chain 1.1 |
| */ |
| public void setIgnorePostprocessResult(boolean ignorePostprocessResult) { |
| this.ignorePostprocessResult = ignorePostprocessResult; |
| } |
| |
| // ---------------------------------------------------------- Filter Methods |
| |
| /** |
| * <p>Look up the specified command, and (if found) execute it. |
| * Unless <code>ignoreExecuteResult</code> is set to <code>true</code>, |
| * return the result of executing the found command. If no command |
| * is found, return {@link Processing#CONTINUE}, unless the <code>optional</code> |
| * property is <code>false</code>, in which case an <code>IllegalArgumentException</code> |
| * will be thrown. |
| * </p> |
| * |
| * @param context The context for this request |
| * |
| * @throws IllegalArgumentException if no such {@link Command} |
| * can be found and the <code>optional</code> property is set |
| * to <code>false</code> |
| * @return the result of executing the looked-up command, or |
| * <code>CONTINUE</code> if no command is found or if the command |
| * is found but the <code>ignoreExecuteResult</code> property of this |
| * instance is <code>true</code> |
| * @throws org.apache.commons.chain2.ChainException if and error occurs in the looked-up Command. |
| */ |
| public Processing execute(C context) { |
| Command<K, V, C> command = getCommand(context); |
| if (command != null) { |
| Processing result = command.execute(context); |
| if (isIgnoreExecuteResult()) { |
| return Processing.CONTINUE; |
| } |
| return result; |
| } |
| return Processing.CONTINUE; |
| } |
| |
| |
| /** |
| * <p>If the executed command was itself a {@link Filter}, call the |
| * <code>postprocess()</code> method of that {@link Filter} as well.</p> |
| * |
| * @param context The context for this request |
| * @param exception Any <code>Exception</code> thrown by command execution |
| * |
| * @return the result of executing the <code>postprocess</code> method |
| * of the looked-up command, unless <code>ignorePostprocessResult</code> is |
| * <code>true</code>. If no command is found, return <code>false</code>, |
| * unless the <code>optional</code> property is <code>false</code>, in which |
| * case <code>IllegalArgumentException</code> will be thrown. |
| */ |
| public boolean postprocess(C context, Exception exception) { |
| Command<K, V, C> command = getCommand(context); |
| if (command != null) { |
| if (command instanceof Filter) { |
| boolean result = ((Filter<K, V, C>) command).postprocess(context, exception); |
| return !isIgnorePostprocessResult() && result; |
| } |
| } |
| return false; |
| } |
| |
| // --------------------------------------------------------- Private Methods |
| |
| /** |
| * <p>Return the {@link Catalog} to look up the {@link Command} in.</p> |
| * |
| * @param context {@link Context} for this request |
| * @return The catalog. |
| * @throws IllegalArgumentException if no {@link Catalog} |
| * can be found |
| * |
| * @since Chain 1.2 |
| */ |
| protected Catalog<K, V, C> getCatalog(C context) { |
| CatalogFactory<K, V, C> lookupFactory = this.catalogFactory; |
| if (lookupFactory == null) { |
| lookupFactory = CatalogFactoryBase.getInstance(); |
| } |
| |
| String catalogName = getCatalogName(); |
| Catalog<K, V, C> catalog = null; |
| if (catalogName == null) { |
| // use default catalog |
| catalog = lookupFactory.getCatalog(); |
| } else { |
| catalog = lookupFactory.getCatalog(catalogName); |
| } |
| if (catalog == null) { |
| if (catalogName == null) { |
| throw new IllegalArgumentException |
| ("Cannot find default catalog"); |
| } else { |
| throw new IllegalArgumentException |
| ("Cannot find catalog '" + catalogName + "'"); |
| } |
| } |
| |
| return catalog; |
| } |
| |
| /** |
| * <p>Return the {@link Command} instance to be delegated to.</p> |
| * |
| * @param context {@link Context} for this request |
| * @return The looked-up Command. |
| * @throws IllegalArgumentException if no such {@link Command} |
| * can be found and the <code>optional</code> property is set |
| * to <code>false</code> |
| */ |
| protected Command<K, V, C> getCommand(C context) { |
| Catalog<K, V, C> catalog = getCatalog(context); |
| |
| Command<K, V, C> command; |
| String name = getCommandName(context); |
| if (name != null) { |
| command = catalog.getCommand(name); |
| if (command == null && !isOptional()) { |
| if (catalogName == null) { |
| throw new IllegalArgumentException |
| ("Cannot find command '" + name |
| + "' in default catalog"); |
| } else { |
| throw new IllegalArgumentException |
| ("Cannot find command '" + name |
| + "' in catalog '" + catalogName + "'"); |
| } |
| } |
| return command; |
| } |
| throw new IllegalArgumentException("No command name"); |
| } |
| |
| /** |
| * <p>Return the name of the {@link Command} instance to be delegated to.</p> |
| * |
| * @param context {@link Context} for this request |
| * @return The name of the {@link Command} instance |
| * |
| * @since Chain 1.2 |
| */ |
| protected String getCommandName(C context) { |
| String name = getName(); |
| if (name == null) { |
| name = (String) context.get(getNameKey()); |
| } |
| return name; |
| } |
| |
| } |