blob: d9d119024a8b33f442302596a01c57821d68f7e8 [file] [log] [blame]
/*
* 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
*
* @exception 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.
* @exception 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.
* @exception 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;
}
}