package org.apache.slide.projector.processor.process;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.slide.projector.ConfigurableProcessor;
import org.apache.slide.projector.ConfigurationException;
import org.apache.slide.projector.Context;
import org.apache.slide.projector.EnvironmentConsumer;
import org.apache.slide.projector.EnvironmentProvider;
import org.apache.slide.projector.ProcessException;
import org.apache.slide.projector.Processor;
import org.apache.slide.projector.Result;
import org.apache.slide.projector.Store;
import org.apache.slide.projector.URI;
import org.apache.slide.projector.descriptor.ContextException;
import org.apache.slide.projector.descriptor.ParameterDescriptor;
import org.apache.slide.projector.descriptor.ProvidedEnvironmentDescriptor;
import org.apache.slide.projector.descriptor.RequiredEnvironmentDescriptor;
import org.apache.slide.projector.descriptor.ResultDescriptor;
import org.apache.slide.projector.descriptor.ResultEntryDescriptor;
import org.apache.slide.projector.descriptor.StateDescriptor;
import org.apache.slide.projector.descriptor.StringValueDescriptor;
import org.apache.slide.projector.descriptor.ValidationException;
import org.apache.slide.projector.descriptor.ValueDescriptor;
import org.apache.slide.projector.descriptor.ValueFactoryManager;
import org.apache.slide.projector.engine.ProcessorManager;
import org.apache.slide.projector.i18n.DefaultMessage;
import org.apache.slide.projector.i18n.ErrorMessage;
import org.apache.slide.projector.i18n.ParameterMessage;
import org.apache.slide.projector.store.AbstractStore;
import org.apache.slide.projector.store.Cache;
import org.apache.slide.projector.util.StoreHelper;
import org.apache.slide.projector.value.DocumentValue;
import org.apache.slide.projector.value.StreamableValue;
import org.apache.slide.projector.value.StringValue;
import org.apache.slide.projector.value.Value;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.xpath.XPath;
import de.zeigermann.xml.simpleImporter.ConversionHelpers;
public class Process implements ConfigurableProcessor, EnvironmentConsumer, EnvironmentProvider {
private static Logger logger = Logger.getLogger(Process.class.getName());
public final static String STEP = "step";
private final static String OK = "ok";
protected Map steps;
protected String firstStep;
protected RequiredEnvironmentDescriptor[] requiredEnvironmentDescriptors;
protected ProvidedEnvironmentDescriptor[] providedEnvironmentDescriptors;
protected ParameterDescriptor[] parameterDescriptors;
protected ResultDescriptor resultDescriptor = ResultDescriptor.OK;
public void configure(StreamableValue config) throws ConfigurationException {
steps = new HashMap();
try {
DocumentValue documentResource = new DocumentValue(config);
Document document = documentResource.getDocument();
Element rootElement = document.getRootElement();
firstStep = rootElement.getAttributeValue("first-step");
List inputParamters = XPath.newInstance("/process/description/input/parameter").selectNodes(rootElement);
List parameterDescriptors = new ArrayList();
for ( Iterator i = inputParamters.iterator(); i.hasNext(); ) {
Element inputParameter = (Element)i.next();
String name = inputParameter.getAttributeValue("name");
String description = inputParameter.getAttributeValue("description");
ParameterDescriptor parameterDescriptor;
if ( description != null ) {
parameterDescriptor = new ParameterDescriptor(name, new ParameterMessage(description), null);
} else {
parameterDescriptor = new ParameterDescriptor(name, new ParameterMessage(ParameterMessage.NO_MESSAGE_AVAILABLE, new String[] {name}), null);
}
parameterDescriptors.add(parameterDescriptor);
Element valueDescriptorElement = (Element)inputParameter.getChildren().iterator().next();
ValueDescriptor valueDescriptor = ValueFactoryManager.getInstance().loadValueDescriptor(valueDescriptorElement);
parameterDescriptor.setValueDescriptor(valueDescriptor);
}
this.parameterDescriptors = (ParameterDescriptor [])parameterDescriptors.toArray(new ParameterDescriptor[parameterDescriptors.size()]);
List outputResults = XPath.newInstance("/process/description/output/result").selectNodes(rootElement);
List resultEntryDescriptors = new ArrayList();
for ( Iterator i = outputResults.iterator(); i.hasNext(); ) {
Element outputResult = (Element)i.next();
String name = outputResult.getAttributeValue("name");
String description = outputResult.getAttributeValue("description");
String contentType = outputResult.getAttributeValue("content-type");
boolean presentable = ConversionHelpers.getBoolean(outputResult.getAttributeValue("presentable"), false);
resultEntryDescriptors.add(new ResultEntryDescriptor(name, new DefaultMessage(description), contentType, presentable));
}
List stateElements = XPath.newInstance("/process/description/output/state").selectNodes(rootElement);
List states = new ArrayList();
for ( Iterator i = stateElements.iterator(); i.hasNext(); ) {
Element stateElement = (Element)i.next();
String description = stateElement.getAttributeValue("description");
states.add(new StateDescriptor(stateElement.getTextTrim(), new DefaultMessage(description)));
}
resultDescriptor = new ResultDescriptor((StateDescriptor [])states.toArray(new StateDescriptor[states.size()]), (ResultEntryDescriptor[])resultEntryDescriptors.toArray(new ResultEntryDescriptor[resultEntryDescriptors.size()]));
List providedEnvironmentElements = XPath.newInstance("/process/description/output/environment").selectNodes(rootElement);
List providedEnvironment = new ArrayList();
for ( Iterator i = providedEnvironmentElements.iterator(); i.hasNext(); ) {
Element environmentElement = (Element)i.next();
String key = environmentElement.getAttributeValue("key");
String storeName = environmentElement.getAttributeValue("store");
String description = environmentElement.getAttributeValue("description");
String contentType = environmentElement.getAttributeValue("content-type");
boolean presentable = ConversionHelpers.getBoolean(environmentElement.getAttributeValue("presentable"), false);
int store = StoreHelper.getStoreByName(storeName);
ProvidedEnvironmentDescriptor environmentDescriptor = new ProvidedEnvironmentDescriptor(key, new DefaultMessage(description), contentType, presentable);
environmentDescriptor.setStore(store);
providedEnvironment.add(environmentDescriptor);
}
providedEnvironmentDescriptors = (ProvidedEnvironmentDescriptor [])providedEnvironment.toArray(new ProvidedEnvironmentDescriptor[providedEnvironment.size()]);
List requiredEnvironmentElements = XPath.newInstance("/process/description/input/environment").selectNodes(rootElement);
List requiredEnvironment = new ArrayList();
for ( Iterator i = requiredEnvironmentElements.iterator(); i.hasNext(); ) {
Element requiredEnvironmentElement = (Element)i.next();
String name = requiredEnvironmentElement.getAttributeValue("name");
String storeName = requiredEnvironmentElement.getAttributeValue("store");
int store = StoreHelper.getStoreByName(storeName);
String description = requiredEnvironmentElement.getAttributeValue("description");
RequiredEnvironmentDescriptor environmentDescriptor;
if ( description != null ) {
environmentDescriptor = new RequiredEnvironmentDescriptor(name, store, new ParameterMessage(description), null);
} else {
environmentDescriptor = new RequiredEnvironmentDescriptor(name, store, new ParameterMessage(ParameterMessage.NO_MESSAGE_AVAILABLE, new String[] {name}), null);
}
requiredEnvironment.add(environmentDescriptor);
Element valueDescriptorElement = (Element)requiredEnvironmentElement.getChildren().iterator().next();
ValueDescriptor valueDescriptor = ValueFactoryManager.getInstance().loadValueDescriptor(valueDescriptorElement);
environmentDescriptor.setValueDescriptor(valueDescriptor);
}
requiredEnvironmentDescriptors = (RequiredEnvironmentDescriptor [])requiredEnvironment.toArray(new RequiredEnvironmentDescriptor[requiredEnvironment.size()]);
List stepElements = XPath.newInstance("/process/step").selectNodes(rootElement);
for ( Iterator i = stepElements.iterator(); i.hasNext(); ) {
Element stepElement = (Element)i.next();
Step step = new Step();
step.configure(stepElement);
steps.put(step.getName(), step);
}
} catch (Exception exception) {
logger.log(Level.SEVERE, "Error while parsing process configuration", exception);
throw new ConfigurationException(new ErrorMessage("process/configurationException"), exception);
}
}
public Result process(Map parameter, Context context) throws Exception {
URI processorUri = ProcessorManager.getInstance().getProcessorDescriptor(this).getUri();
context.setProcess(processorUri); // Remember current process in context
String nextStep = getStep(firstStep, context); // Lookup the first step of this process
Store stepStore = new Cache(); // This store is used to allow stack-like result/parameter delivery between steps
Result result = new Result(OK); // The result of this process processor
Result stepResult = null; // The result of the last executed step
Step step; // The current step
Store enclosingStepStore = context.getStore(Store.STEP);
Map enclosingParameters = ((Cache)context.getStore(Store.INPUT)).getMap();
context.setStepStore(stepStore);
context.setInputParameters(parameter);
do {
logger.log(Level.FINE, "Processing "+processorUri+", step=" + nextStep);
context.setStep(nextStep); // Remember current step in context
step = (Step)steps.get(nextStep);
if (step == null) throw new ProcessException(new ErrorMessage("stepNotFound", new String[]{nextStep}));
Processor processor = ProcessorManager.getInstance().getProcessor(step.getProcessorURI());
try {
Map processorParameters = loadParameters(step, processor, context);
if ( processor instanceof EnvironmentConsumer ) {
checkRequirements((EnvironmentConsumer)processor, context);
}
checkRoutings(step, processor);
try {
stepResult = ProcessorManager.process(processor, processorParameters, context);
try {
saveResults(step, stepResult, stepStore, result, getResultDescriptor().getResultEntryDescriptors(), context);
} catch ( ProcessException e ) {
throw new ProcessException(new ErrorMessage("saveFailed", new Object[] { step.getProcessorURI(), nextStep }), e );
}
nextStep = routeState(step, stepResult.getState());
} catch (Exception e) {
nextStep = routeException(step, e);
}
} catch ( ValidationException exception ) {
throw new ValidationException(new ErrorMessage("validationFailed", new Object[] { step.getProcessorURI(), nextStep }), exception);
}
} while (nextStep != null);
result.setState(getState(step, stepResult.getState()));
context.setStepStore(enclosingStepStore);
context.setInputParameters(enclosingParameters);
return result;
}
public ParameterDescriptor[] getParameterDescriptors() {
return parameterDescriptors;
}
public ResultDescriptor getResultDescriptor() {
return resultDescriptor;
}
public RequiredEnvironmentDescriptor[] getRequiredEnvironmentDescriptors() {
return requiredEnvironmentDescriptors;
}
public ProvidedEnvironmentDescriptor[] getProvidedEnvironmentDescriptors() {
return providedEnvironmentDescriptors;
}
static String getStep(String firstStep, Context context) {
Store sessionStore = context.getStore(Store.SESSION);
if ( sessionStore != null ) {
Value stepParameter = (Value)StoreHelper.get(sessionStore, context.getProcess().toString(), STEP);
if (stepParameter != null && stepParameter instanceof StringValue ) {
return stepParameter.toString();
}
}
return firstStep;
}
static void checkRoutings(Step step, Processor processor) throws ValidationException {
ResultDescriptor resultDescriptor = processor.getResultDescriptor();
StateDescriptor[] states = resultDescriptor.getStateDescriptors();
for ( int i = 0; i < states.length; i++ ) {
String state = states[i].getState();
List routings = step.getRoutingConfigurations();
boolean routingFound = false;
for ( Iterator j = routings.iterator(); j.hasNext() ; ) {
if ( ((RoutingConfiguration)j.next()).getState().equals(state) ) {
routingFound = true;
break;
}
}
if ( !routingFound ) {
throw new ValidationException(new ErrorMessage("stateNotRouted", new String[] { step.getName(), state }));
}
}
}
public static void checkRequirements(EnvironmentConsumer processor, Context context) throws ValidationException, IOException {
RequiredEnvironmentDescriptor[] requirementDescriptor = processor.getRequiredEnvironmentDescriptors();
for ( int i = 0; i < requirementDescriptor.length; i++ ) {
Store store = context.getStore(requirementDescriptor[i].getStore());
Object value = store.get(requirementDescriptor[i].getName());
if ( value == null ) {
if ( requirementDescriptor[i].isRequired() ) {
throw new ContextException(new ErrorMessage("requiredContextMissing", new Object[] { requirementDescriptor[i].getName(), Store.stores[requirementDescriptor[i].getStore()] }));
} else {
value = requirementDescriptor[i].getDefaultValue();
store.put(requirementDescriptor[i].getName(), value);
}
}
Value castedValue = requirementDescriptor[i].getValueDescriptor().valueOf(value, context);
requirementDescriptor[i].getValueDescriptor().validate(castedValue, context);
if ( castedValue != value ) {
store.put(requirementDescriptor[i].getName(), castedValue);
}
}
}
public static String evaluateKey(String key, Context context) {
int start, end = 0;
while ( (start = key.indexOf('{') ) != -1 ) {
end = key.indexOf('}');
if ( end == -1 ) break;
int delimiter = key.indexOf(':', start);
String storeToken = key.substring(start+1, delimiter);
String keyToken = key.substring(delimiter+1, end);
Store keyStore = context.getStore(StoreHelper.getStoreByName(storeToken));
String evaluatedKey = null;
if ( keyStore != null ) {
try {
Object dynamicKey = keyStore.get(keyToken);
evaluatedKey = StringValueDescriptor.ANY.valueOf(dynamicKey, context).toString();
} catch ( Exception e ) {
logger.log(Level.SEVERE, "Dynamic key '"+keyToken+"' could not be loaded from store '"+storeToken+"'", e);
}
}
if ( evaluatedKey != null ) {
key = key.substring(0, start)+evaluatedKey+key.substring(end+1);
} else {
key = key.substring(0, start)+key.substring(end+1);
}
}
return key;
}
public static Map loadParameters(Step step, Processor processor, Context context) throws Exception {
// Collect parameters for this processor
Map parameters = new HashMap();
ParameterDescriptor[] parameterDescriptors = processor.getParameterDescriptors();
for (int i = 0; i < parameterDescriptors.length; i++) {
String key = parameterDescriptors[i].getName();
ParameterConfiguration parameterConfiguration = (ParameterConfiguration)step.getParameterConfigurations().get(key);
if ( parameterConfiguration == null ) {
parameters.put(key, null);
} else if ( parameterConfiguration != null ) {
parameters.put(key, parameterConfiguration.getValue());
}
}
return parameters;
}
public static void saveResults(Step step, Result stepResult, Store stepStore, Result result, ResultEntryDescriptor[] resultEntryDescriptors, Context context) throws ProcessException {
// save results by using result configuration
for (Iterator i = step.getResultConfigurations().entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry)i.next();
String resultName = (String)entry.getKey();
Value resultValue = (Value)stepResult.getResultEntries().get(resultName);
((ResultConfiguration)entry.getValue()).storeValue(resultValue, stepStore, result, resultEntryDescriptors, context);
}
}
private static String routeState(Step step, String state) {
// find routing for result state
for (Iterator i = step.getRoutingConfigurations().iterator(); i.hasNext();) {
RoutingConfiguration routingConfiguration = (RoutingConfiguration)i.next();
if (state.equals(routingConfiguration.getState())) {
if (routingConfiguration.getStep() != null) {
return routingConfiguration.getStep();
}
}
}
return null;
}
private static String routeException(Step step, Exception e) throws Exception {
logger.log(Level.SEVERE, "Exception occured:", e);
for (Iterator i = step.getRoutingConfigurations().iterator(); i.hasNext();) {
RoutingConfiguration routingConfiguration = (RoutingConfiguration)i.next();
Class exception = routingConfiguration.getException();
if (exception != null && exception.isAssignableFrom(e.getClass())) {
return routingConfiguration.getStep();
}
}
throw(e);
}
private String getState(Step step, String state) throws ProcessException {
// find processor return state for result state
for (Iterator i = step.getRoutingConfigurations().iterator(); i.hasNext();) {
RoutingConfiguration routingConfiguration = (RoutingConfiguration)i.next();
if (routingConfiguration.getReturnValue() != null && state.equals(routingConfiguration.getState())) {
String returnState = routingConfiguration.getReturnValue();
// check if state is legal
StateDescriptor[] validStates = resultDescriptor.getStateDescriptors();
for (int j = 0; j < validStates.length; j++) {
if (validStates[j].getState().equals(returnState)) {
return returnState;
}
}
logger.log(Level.SEVERE, "State '" + returnState + "' not defined!");
throw new ProcessException(new ErrorMessage("stateNotDefined", new String[]{returnState}));
}
}
return OK;
}
private Result generateResult(String state, Result result) {
Result processResult = new Result(state);
// copy defined results from context store to result
ResultEntryDescriptor[] resultEntryDescriptors = getResultDescriptor().getResultEntryDescriptors();
for (int i = 0; i < resultEntryDescriptors.length; i++) {
ResultEntryDescriptor descriptor = resultEntryDescriptors[i];
Value resultValue = (Value)result.getResultEntries().get(descriptor.getName());
if (resultValue != null) {
processResult.addResultEntry(descriptor.getName(), resultValue);
}
}
return processResult;
}
public class ProcessStore extends AbstractStore {
protected Map map = new HashMap();
public void put(String key, Object value) throws IOException {
map.put(key, value);
}
public Object get(String key) throws IOException {
return map.get(key);
}
public void dispose(String key) throws IOException {
map.remove(key);
}
}
}