/**
* Copyright 2011 meltmedia
*
* Licensed 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.xchain.namespaces.core;
import java.net.URI;
import org.xchain.Command;
import org.xchain.Filter;
import org.xchain.Locatable;
import org.xchain.framework.factory.CatalogFactory;
import org.xchain.framework.lifecycle.Execution;
import org.xchain.framework.util.ThreadLocalStack;
import javax.xml.namespace.QName;
import org.xchain.annotations.Attribute;
import org.xchain.annotations.AttributeType;
import org.xchain.annotations.Element;
import org.apache.commons.jxpath.JXPathContext;
/**
* <p>The <code>execute</code> command can execute another command chain. The command can be in the current
* catalog or in a different catalog. The command chain will continue if and only if the referenced command
* would continue the command chain.</p>
*
* <code class="source">
* <xchain:chain xmlns:xchain="http://www.xchain.org/core/1.0">
* ...
* <xchain:execute system-id="$myCatalog" name="$myCommand">
* ...
* </xchain:choose>
* </code>
*
* @author Christian Trimble
* @author Devon Tackett
*/
@Element(localName="execute")
public abstract class ExecuteCommand
implements Filter, Locatable
{
protected static ThreadLocalStack<Command> callStack = new ThreadLocalStack<Command>();
/**
* The system id of the catalog to search for the command. If no system id is provided, then the current catalog is
* searched. If the system id is relative, then it is resolved against the current catalog's system id.
*
* @param context the context that the command is executed in.
* @return the system id of the catalog.
*/
@Attribute(localName="system-id", type=AttributeType.JXPATH_VALUE)
public abstract String getSystemId( JXPathContext context );
public abstract boolean hasSystemId();
/**
* The qname of the command to execute. The qname is required.
*
* @param context the context that the command is executed in.
* @return the qname of the command.
*/
@Attribute(localName="name", type=AttributeType.JXPATH_VALUE)
public abstract QName getName( JXPathContext context );
public abstract boolean hasName();
/**
* Executes the command specified by the system-id and name attributes.
*/
public boolean execute( JXPathContext context )
throws Exception
{
Command command = null;
boolean result = false;
Exception exception = null;
if( hasSystemId() && hasName() ) {
command = CatalogFactory.getInstance().getCatalog(resolveSystemId(getSystemId(context))).getCommand(getName(context));
}
else if( hasName() ) {
command = CatalogFactory.getInstance().getCatalog(Execution.getSystemId()).getCommand(getName(context));
}
else {
throw new Exception("Call command must specify both a system id and a name.");
}
// set the context for the command, if needed.
try {
result = command.execute(context);
}
catch( Exception e ) {
exception = e;
}
finally {
callStack.push(command);
}
if( exception != null ) {
throw exception;
}
return result;
}
public boolean postProcess( JXPathContext context, Exception exception )
{
Command command = callStack.pop();
boolean result = false;
if( command instanceof Filter ) {
// set the context for the command.
try {
result = ((Filter)command).postProcess( context, exception );
}
catch( Exception e ) {
// ignore this exception.
}
}
return result;
}
public String resolveSystemId( String systemId )
{
return URI.create(Execution.getSystemId()).resolve(systemId).toString();
}
}