/*
* Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: ElementContext.java 3928 2008-04-22 16:25:18Z gbevin $
*/
package com.uwyn.rife.engine;
import com.uwyn.rife.config.RifeConfig;
import com.uwyn.rife.continuations.CallState;
import com.uwyn.rife.continuations.ContinuationConfigRuntime;
import com.uwyn.rife.continuations.ContinuationContext;
import com.uwyn.rife.continuations.ContinuationManager;
import com.uwyn.rife.continuations.exceptions.AnswerException;
import com.uwyn.rife.continuations.exceptions.CallException;
import com.uwyn.rife.continuations.exceptions.PauseException;
import com.uwyn.rife.continuations.exceptions.StepBackException;
import com.uwyn.rife.engine.exceptions.*;
import com.uwyn.rife.site.Constrained;
import com.uwyn.rife.site.ConstrainedProperty;
import com.uwyn.rife.site.ConstrainedUtils;
import com.uwyn.rife.template.*;
import com.uwyn.rife.template.exceptions.TemplateException;
import com.uwyn.rife.tools.*;
import com.uwyn.rife.tools.exceptions.BeanUtilsException;
import com.uwyn.rife.tools.exceptions.LightweightError;
import com.uwyn.rife.tools.exceptions.SerializationUtilsErrorException;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ElementContext
{
public static final String SUFFIX_QUERY = "QUERY:";
public static final String SUFFIX_FORM = "FORM:";
public static final String SUFFIX_PARAMS = "PARAMS:";
public static final String SUFFIX_PARAMSJS = "PARAMSJS:";
public static final String PREFIX_EXIT = "EXIT:";
public static final String PREFIX_EXIT_QUERY = PREFIX_EXIT+SUFFIX_QUERY;
public static final String PREFIX_EXIT_FORM = PREFIX_EXIT+SUFFIX_FORM;
public static final String PREFIX_EXIT_PARAMS = PREFIX_EXIT+SUFFIX_PARAMS;
public static final String PREFIX_EXIT_PARAMSJS = PREFIX_EXIT+SUFFIX_PARAMSJS;
public static final String PREFIX_SUBMISSION = "SUBMISSION:";
public static final String PREFIX_SUBMISSION_QUERY = PREFIX_SUBMISSION+SUFFIX_QUERY;
public static final String PREFIX_SUBMISSION_FORM = PREFIX_SUBMISSION+SUFFIX_FORM;
public static final String PREFIX_SUBMISSION_PARAMS = PREFIX_SUBMISSION+SUFFIX_PARAMS;
public static final String PREFIX_SUBMISSION_PARAMSJS = PREFIX_SUBMISSION+SUFFIX_PARAMSJS;
public static final String PREFIX_PARAM = "PARAM:";
public static final String PREFIX_INPUT = "INPUT:";
public static final String PREFIX_OUTPUT = "OUTPUT:";
public static final String PREFIX_INCOOKIE = "INCOOKIE:";
public static final String PREFIX_OUTCOOKIE = "OUTCOOKIE:";
public static final String PREFIX_ELEMENT = "ELEMENT:";
public static final String PREFIX_PROPERTY = "PROPERTY:";
public static final String PREFIX_OGNL_ROLEUSER = "OGNL:ROLEUSER:";
public static final String PREFIX_MVEL_ROLEUSER = "MVEL:ROLEUSER:";
public static final String PREFIX_GROOVY_ROLEUSER = "GROOVY:ROLEUSER:";
public static final String PREFIX_JANINO_ROLEUSER = "JANINO:ROLEUSER:";
public static final String ID_WEBAPP_ROOTURL = "WEBAPP:ROOTURL";
public static final String ID_SERVER_ROOTURL = "SERVER:ROOTURL";
public static final String TAG_PROPERTY = "^"+PREFIX_PROPERTY+"\\s*(.*?)\\s*$";
public static final String TAG_ELEMENT = "^("+PREFIX_ELEMENT+"\\s*([\\-\\+]?)\\s*(.*?)\\s*)(?::([^:]*))?$";
public static final String TAG_EXITFIELD = "^"+PREFIX_EXIT+"("+SUFFIX_QUERY+"|"+SUFFIX_FORM+"|"+SUFFIX_PARAMS+"|"+SUFFIX_PARAMSJS+")\\s*@([\\w\\.]*?)\\s*$";
public static final String TAG_SUBMISSIONFIELD = "^"+PREFIX_SUBMISSION+"("+SUFFIX_QUERY+"|"+SUFFIX_FORM+"|"+SUFFIX_PARAMS+"|"+SUFFIX_PARAMSJS+")\\s*@([\\w\\.]*?)\\s*$";
public static final String TAG_OGNL_ROLEUSER = "(?s)^("+PREFIX_OGNL_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
public static final String TAG_MVEL_ROLEUSER = "(?s)^("+PREFIX_MVEL_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
public static final String TAG_JANINO_ROLEUSER = "(?s)^("+PREFIX_JANINO_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
public static final String TAG_GROOVY_ROLEUSER = "(?s)^("+PREFIX_GROOVY_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
private static final ThreadLocal<ElementSupport> ACTIVE_ELEMENT_SUPPORT = new ThreadLocal<ElementSupport>();
private final ElementSupport mElement;
private final ElementInfo mElementInfo;
private final RequestState mRequestState;
private final OutputValues mOutputs;
private final OutcookieValues mOutcookies;
private Response mResponse = null;
private ArrayList<OutputListener> mOutputListeners = null;
private ArrayList<OutcookieListener> mOutcookieListeners = null;
private String mContextId = null;
private String mSubmission = null;
private boolean mSteppedBack = false;
public static ElementSupport getActiveElementSupport()
{
return ACTIVE_ELEMENT_SUPPORT.get();
}
ElementContext(ElementSupport element, RequestState state, Response response)
throws EngineException
{
assert element != null;
assert state != null;
assert response != null;
mElement = element;
synchronized (mElement)
{
mElement.setElementContext(this);
mElementInfo = mElement.getElementInfo();
mRequestState = state;
mResponse = response;
mOutputs = new OutputValues(this);
mOutcookies = new OutcookieValues(this);
// register the EmbeddingListener of the current state so that the
// output values are kept in sync with global vars and the outcookies
// are kept in sync with the global cookies
if (state.isEmbedded())
{
RequestState.EmbeddingListener listener = state.getEmbeddingListener();
addOutputListener(listener);
addOutcookieListener(listener);
}
// register the PrecedenceListener of the current state so that the
// output values are kept in sync with global vars and the outcookies
// are kept in sync with the global cookies
if (state.isPreceeding())
{
RequestState.PrecedenceListener listener = state.getPrecedenceListener();
addOutputListener(listener);
addOutcookieListener(listener);
}
ElementExecutionState element_state = getElementState();
// if the child element has been reached, replace the request parameters with the
// original child request parameters
if (element_state.isInheritanceTarget() &&
element_state.hasRequestParameterValue(ReservedParameters.CHILDREQUEST))
{
ChildRequestEncoder.decode(element.getElementInfo(), mRequestState);
}
// automatically set an output value if the input value has been provided
// and it's a global variable
String[] output_values = null;
for (String globalvar_name : mElementInfo.getGlobalVarNames())
{
if (element_state.hasInputValue(globalvar_name))
{
output_values = element_state.getInputValues(globalvar_name);
setAutomatedOutputValues(globalvar_name, output_values);
}
}
}
}
private void handleChildTriggerVariablesPre()
throws EngineException
{
ElementExecutionState element_state = getElementState();
// verify if no value are present that will automatically launch a child trigger
if (mElementInfo.getChildTriggerNames().size() > 0)
{
// check each child trigger for a corresponding value
// if it was found, launch the trigger
for (String child_trigger_name : mElementInfo.getChildTriggerNames())
{
// check if a provided input value is acceptable for this
// element as input or global variable
if (element_state.hasInputValue(child_trigger_name))
{
if (mElementInfo.containsGlobalVar(child_trigger_name) ||
mElementInfo.containsInput(child_trigger_name))
{
triggerChild(child_trigger_name, getInputValues(child_trigger_name));
}
}
// check if a provided cookie is acceptable for this
// element as incookie
else if (mRequestState.getCookie(child_trigger_name) != null)
{
if (mElementInfo.containsGlobalCookie(child_trigger_name) ||
mElementInfo.containsIncookie(child_trigger_name))
{
triggerChild(child_trigger_name, new String[] {getCookie(child_trigger_name).getValue()});
}
}
// check if a default input value is present that can trigger the child
else if (mElementInfo.hasInputDefaultValues(child_trigger_name))
{
triggerChild(child_trigger_name, mElementInfo.getInputDefaultValues(child_trigger_name));
}
// check if a default cookie value is present that can trigger the child
else if (mElementInfo.hasIncookieDefaultValue(child_trigger_name))
{
triggerChild(child_trigger_name, new String[] {mElementInfo.getIncookieDefaultValue(child_trigger_name)});
}
// check if a global var default is present for the trigger name
else if (mElementInfo.containsGlobalVar(child_trigger_name) &&
mElementInfo.hasGlobalVarDefaultValues(child_trigger_name))
{
triggerChild(child_trigger_name, mElementInfo.getGlobalVarDefaultValues(child_trigger_name));
}
// check if a global cookie default is present for the trigger name
else if (mElementInfo.containsGlobalCookie(child_trigger_name) &&
mElementInfo.hasGlobalCookieDefaultValue(child_trigger_name))
{
triggerChild(child_trigger_name, new String[] {mElementInfo.getGlobalCookieDefaultValue(child_trigger_name)});
}
}
}
}
private void handleChildTriggerVariablesPost()
throws EngineException
{
// verify if no value are present that will automatically launch a child trigger
if (mElementInfo.getChildTriggerNames().size() > 0)
{
// check each child trigger for a corresponding value
// if it was found, launch the trigger
for (String child_trigger_name : mElementInfo.getChildTriggerNames())
{
// check if an output default value is present
if (mElementInfo.hasOutputDefaultValues(child_trigger_name))
{
String[] output_defaultvalues = mElementInfo.getOutputDefaultValues(child_trigger_name);
triggerChild(child_trigger_name, output_defaultvalues);
}
}
}
}
ElementContext processContext()
throws EngineException
{
long start = mElementInfo.startTrace();
boolean clear_request = false;
ElementContext result = null;
ElementExecutionState element_state = getElementState();
try
{
ElementSupport previous_active_element;
synchronized (mElement)
{
// handle the inheritance structure
if (element_state.inInheritanceStructure())
{
// try to see if the top of the trigger list matches the current element
if (element_state.isNextTrigger(mElementInfo))
{
// if it does, and it's a child trigger, use the values to launch a child trigger
if (TriggerContext.TRIGGER_CHILD == element_state.getNextTriggerType() &&
mElementInfo.containsChildTrigger(element_state.getNextTriggerName()))
{
triggerChild(element_state.getNextTriggerName(), element_state.getNextTriggerValues());
}
else if (TriggerContext.TRIGGER_EXIT == element_state.getNextTriggerType() &&
mElementInfo.containsExit(element_state.getNextTriggerName()))
{
exit(element_state.getNextTriggerName());
}
}
handleChildTriggerVariablesPre();
}
// handle precedence
mRequestState.handlePrecedence(mResponse, getElementInfo());
// set the response content type if this has been specified by the element
if (mElementInfo.getContentType() != null &&
mElementInfo.getContentType().length() > 0)
{
mResponse.setContentType(mElementInfo.getContentType());
}
// obtain the continuation context
ContinuationContext continuation_context = mRequestState.getContinuationContext(mElementInfo);
ContinuationContext.setActiveContext(continuation_context);
ContinuationConfigRuntime.setActiveConfigRuntime(EngineContinuationConfigRuntimeSingleton.INSTANCE);
// register the element as the active one in this thread and remember
// which one that was previously active
previous_active_element = ACTIVE_ELEMENT_SUPPORT.get();
ACTIVE_ELEMENT_SUPPORT.set(mElement);
}
try
{
final ElementAware element_aware;
Method submission_handler = null;
synchronized (mElement)
{
// get the element aware interface
element_aware = mElement.getElementAware();
// check if there's are submissions
if (element_state.hasRequestParameterValue(ReservedParameters.SUBMISSION))
{
String[] submission_names = element_state.getRequestParameterValues(ReservedParameters.SUBMISSION);
String[] submission_contexts = element_state.getRequestParameterValues(ReservedParameters.SUBMISSIONCONTEXT);
String submission_context = null;
String submission_context_id = null;
String submission_target = null;
HashSet<String> non_requestparameter_inputs = null;
int counter = 0;
for (String submission_name : submission_names)
{
// get the submission context
if (null == submission_contexts ||
counter >= submission_contexts.length)
{
submission_context = getContextId();
}
else
{
try
{
byte[] decoded = Base64.decode(submission_contexts[counter].getBytes("UTF-8"));
if (decoded != null &&
decoded.length > 0)
{
submission_context = new String(decoded);
}
else
{
submission_context = getContextId();
}
}
catch (UnsupportedEncodingException e)
{
// should never happen
}
}
// split up in the submission context id and the submission target
int seperator_index = submission_context.indexOf("^");
if (-1 == seperator_index)
{
submission_context_id = submission_context;
submission_target = submission_context;
}
else
{
submission_context_id = submission_context.substring(0, seperator_index);
submission_target = submission_context.substring(seperator_index+1);
}
// check if the submission target corresponds to the context id
if (null == mSubmission &&
getContextId().equals(submission_context_id) &&
mElementInfo.hasSubmission(submission_name))
{
mSubmission = submission_name;
}
// if it doesn't, use the submission target element id to
// retrieve the submission declaration, and the parameters
// that have to be disabled as inputs for this element
else
{
// get the submission target
if (0 == submission_target.length())
{
submission_target = getElementInfo().getId();
}
// obtain the element info
ElementInfo submission_target_element = mElementInfo.getSite().resolveId(submission_target);
if (submission_target_element != null)
{
// get the submission declaration
Submission submission = submission_target_element.getSubmission(submission_name);
if (submission != null)
{
// get the submission parameter names
Collection<String> names = submission.getParameterNames();
if (names != null &&
names.size() > 0)
{
if (null == non_requestparameter_inputs)
{
non_requestparameter_inputs = new HashSet<String>();
}
// add the submission parameters to the collection
// of inputs that shouldn't retrieve their values
// from the request parameters
non_requestparameter_inputs.addAll(names);
}
}
}
}
counter++;
}
// register the non parameter inputs
element_state.setNonRequestParameterInputs(non_requestparameter_inputs);
}
// check if there's a submission handler
// check if a submission is present and a corresponding do*() method is
// available and retrieve the method
final String submission_name = getSubmission();
if (submission_name != null)
{
String submission_handler_name = "do"+StringUtils.capitalize(submission_name);
try
{
submission_handler = element_aware.getClass().getMethod(submission_handler_name, (Class[])null);
submission_handler.setAccessible(true);
}
catch (NoSuchMethodException e)
{
submission_handler = null;
}
catch (SecurityException e)
{
throw new EngineException(e);
}
}
// inject the properties request values into the element instance
new ElementInjector(this).performInjection(submission_name);
// update the target element in the response
mResponse.setLastElement(mElement);
// initialize the element in a fully setup context
mElement.initialize();
}
try
{
try
{
synchronized (mElement)
{
// call the processElement() method of the element itself if no handler could be found
if (null == submission_handler)
{
element_aware.processElement();
}
// call the submission handler if it's available
else
{
try
{
submission_handler.invoke(element_aware, (Object[])null);
}
catch (InvocationTargetException e)
{
if (e.getCause() instanceof LightweightError)
{
throw (LightweightError)e.getCause();
}
else if (e.getCause() instanceof EngineException)
{
throw (EngineException)e.getCause();
}
else
{
throw new EngineException(e.getCause());
}
}
catch (IllegalArgumentException e)
{
throw new EngineException(e);
}
catch (IllegalAccessException e)
{
throw new EngineException(e);
}
}
}
}
catch (CallException e)
{
ContinuationContext context = e.getContext();
// register context
ContinuationManager manager = mElementInfo.getSite().getContinuationManager();
manager.addContext(context);
synchronized (mElement)
{
String exit = (String)e.getTarget();
mElementInfo.validateExitName(exit);
if (null == mElementInfo.getFlowLink(exit))
{
throw new ExitNotAttachedException(mElementInfo.getDeclarationName(), exit);
}
// create a new call state
CallState call_state = new CallState(context.getId(), element_state.clone());
context.setCreatedCallState(call_state);
// setup the exit
result = setupExitContext(exit);
}
}
}
finally
{
synchronized (mElement)
{
String context_id = getContextId();
ElementResultState result_state = null;
// extract the preserved inputs of this element
Set<Map.Entry<String, String[]>> output_value_entries = mOutputs.aggregateValues().entrySet();
Map<String, String[]> preserved_inputs = collectPreservedInputs(output_value_entries);
// if a call continuation is active, also preserve the previous preserved inputs without overriding the new ones
ContinuationContext active_context = ContinuationContext.getActiveContext();
if (active_context != null &&
active_context.getActiveCallState() != null)
{
ElementResultState previous_result_state = mRequestState.getElementResultStatesRestored().get(context_id);
if (previous_result_state != null)
{
Map<String, String[]> previous_preserved_inputs = previous_result_state.getPreservedInputs();
if (previous_preserved_inputs != null)
{
if (null == preserved_inputs)
{
preserved_inputs = previous_preserved_inputs;
}
else
{
for (Map.Entry<String, String[]> entry : previous_preserved_inputs.entrySet())
{
if (!preserved_inputs.containsKey(entry.getKey()))
{
preserved_inputs.put(entry.getKey(), entry.getValue());
}
}
}
}
}
// preserve the continuation ID of this element
if (null == result_state)
{
result_state = mElementInfo.getStateStore().createNewResultState(context_id);
}
result_state.setContinuationId(active_context.getActiveCallState().getContinuationId());
}
// add the preserved inputs to the result state
if (preserved_inputs != null &&
preserved_inputs.size() > 0)
{
if (null == result_state)
{
result_state = mElementInfo.getStateStore().createNewResultState(context_id);
}
result_state.setPreservedInputs(preserved_inputs);
}
// state the resulting element state
if (result_state != null)
{
mRequestState.getElementResultStatesObtained().put(result_state);
}
// handle the setting of the getter outcookies
mOutcookies.processGetters();
// handle the firing of listener methods for getters outjection
mOutputs.processGetters();
}
// handle the outcookie and output childtriggers from the getters outjection
mOutcookies.processGetterChildTriggers();
mOutputs.processGetterChildTriggers();
}
}
catch (AnswerException e)
{
synchronized (mElement)
{
// only answer a call if a call state is available
// otherwise the answer will function as a regular return
if (e.getContext().getActiveCallState() != null)
{
throw e;
}
}
}
finally
{
synchronized (mElement)
{
// restore the previously active element
ACTIVE_ELEMENT_SUPPORT.set(previous_active_element);
}
}
synchronized (mElement)
{
// handle the child trigger values that could have an effect after
// the actual element logic
handleChildTriggerVariablesPost();
// handle the default outcookies
if (mElementInfo.hasOutcookieDefaults())
{
Cookie default_cookie = null;
for (Map.Entry<String, String> default_outcookie_entry : mElementInfo.getDefaultOutcookies().entrySet())
{
if (!mOutcookies.contains(default_outcookie_entry.getKey()))
{
default_cookie = new Cookie(default_outcookie_entry.getKey(), default_outcookie_entry.getValue());
default_cookie.setPath("");
setCookie(default_cookie);
}
}
}
// handle the default global cookies
if (mElementInfo.hasGlobalCookieDefaults())
{
Cookie default_cookie = null;
for (Map.Entry<String, String> default_globalcookie_entry : mElementInfo.getDefaultGlobalCookies().entrySet())
{
if (!mOutcookies.contains(default_globalcookie_entry.getKey()))
{
default_cookie = new Cookie(default_globalcookie_entry.getKey(), default_globalcookie_entry.getValue());
default_cookie.setPath("");
setCookie(default_cookie);
}
}
}
}
}
catch (PauseException e)
{
ContinuationContext context = e.getContext();
// register context
ContinuationManager manager = mElementInfo.getSite().getContinuationManager();
manager.addContext(context);
synchronized (mElement)
{
// preserve the continuation ID of this element
String context_id = getContextId();
ElementResultState result_state = mRequestState.getElementResultStatesObtained().get(context_id);
if (null == result_state)
{
result_state = mElementInfo.getStateStore().createNewResultState(context_id);
mRequestState.getElementResultStatesObtained().put(result_state);
}
result_state.setContinuationId(context.getId());
clear_request = true;
try
{
mResponse.flush();
}
catch (EngineException e2)
{
// if errors occurred during flushing it means that the client has
// disconnected, disregard them
}
mResponse = null;
}
}
catch (ChildTriggeredException e)
{
synchronized (mElement)
{
result = setupChildtriggerContext(e.getChildTriggerName(), e.getChildTriggerValues());
}
}
catch (ExitTriggeredException e)
{
synchronized (mElement)
{
result = handleExitTrigger(e.getExitName());
}
}
catch (StepBackException e)
{
ContinuationContext context = e.getContext();
// register context
ContinuationManager manager = mElementInfo.getSite().getContinuationManager();
manager.addContext(context);
synchronized (mElement)
{
// preserve the continuation ID of this element
String context_id = getContextId();
ElementResultState result_state = mRequestState.getElementResultStatesObtained().get(context_id);
if (null == result_state)
{
result_state = mElementInfo.getStateStore().createNewResultState(context_id);
mRequestState.getElementResultStatesObtained().put(result_state);
}
result_state.setContinuationId(context.getId());
// try to obtain the id of the previous continuation
String stepbackid = e.lookupStepBackId();
// there is no previous continuation, so start from the beginning again
if (null == stepbackid)
{
ContinuationContext.clearActiveContext();
mRequestState.setContinuationId(null);
}
else
{
mRequestState.setContinuationId(stepbackid);
}
result = new ElementContext(mElement, mRequestState, mResponse);
result.mSteppedBack = true;
}
}
catch (ForwardException e)
{
synchronized (mElement)
{
handleForward(e.getUrl());
}
}
catch (RedirectException e)
{
synchronized (mElement)
{
getResponse().sendRedirect(e.getUrl());
}
}
finally
{
synchronized (mElement)
{
try
{
// flush the output buffer
if (mResponse != null)
{
mResponse.flush();
}
// trace element activity
mElementInfo.outputTrace(start, mRequestState);
}
catch (EngineException e)
{
// if errors occurred during flushing it means that the client has
// disconnected, disregard them
}
finally
{
// clear the request if this is needed, this typically happens
// for continuations, to prevent the request to be cloned when
// element instances are cloned
if (clear_request)
{
mRequestState.clearRequest();
}
}
}
}
return result;
}
private ElementContext setupChildtriggerContext(String childtrigger, String[] values)
throws EngineException
{
ElementContext element_context = null;
Map<String, String[]> child_inputs = null;
ElementExecutionState element_state = getElementState();
if (element_state.inInheritanceStructure())
{
// construct the child element
String child_element_id = element_state.getInheritanceStack().pop();
ElementInfo child_element_info = mRequestState.getSite().resolveId(child_element_id);
// verify if the trigger wasn't raised automatically, if this is the
// case, the first trigger context has to be removed from the trigger
// list since it has been used by this element
if (element_state.isNextChildTrigger(mElementInfo, childtrigger))
{
// obtain the stored exit inputs
child_inputs = element_state.nextTrigger().getParameters();
}
// since the current element deferred the logical flow to its child,
// record the child trigger that caused this
else
{
child_inputs = collectChildInputValues(child_element_info);
// don't store an child trigger which isn't dependent on watched values in the
// trigger list, the whole element will be executed each time for those triggers
if (childtrigger != null)
{
element_state.addTrigger(TriggerContext.generateChildTrigger(mElementInfo, childtrigger, values, child_inputs));
}
}
// construct the child element's context
element_state.setTriggerInputs(child_inputs);
element_context = mRequestState.getElementContext(child_element_info, mResponse);
}
return element_context;
}
private ElementContext handleExitTrigger(String exit)
throws EngineException
{
// get the the flowlink to check first if it's a redirection
FlowLink flowlink = mElementInfo.getFlowLink(exit);
if (null == flowlink)
{
throw new ExitNotAttachedException(mElementInfo.getDeclarationName(), exit);
}
if (flowlink.isRedirect())
{
getResponse().sendRedirect(ElementContextFlowGeneration.generateExitQueryUrl(this, flowlink, null, mOutputs.aggregateValues(), null).toString());
return null;
}
else
{
return setupExitContext(exit);
}
}
private ElementContext setupExitContext(String exit)
throws EngineException
{
// get the element info of the exit target
FlowLink flowlink = mElementInfo.getFlowLink(exit);
if (null == flowlink)
{
throw new ExitNotAttachedException(mElementInfo.getDeclarationName(), exit);
}
ElementInfo target = flowlink.getExitTarget(mRequestState);
// handle embedding cancellation
if (mRequestState.isEmbedded() &&
flowlink.cancelEmbedding())
{
// this flag is set in the embedding context to indicate that the
// embedding needs to be cancelled, it will be checked by the
// processEmbeddedElement method of the embedding element
mRequestState.getEmbeddingContext().setCancelEmbedding(true);
// clear all current output buffers
RequestState request_state = mRequestState;
while (request_state.isEmbedded())
{
request_state.getEmbeddingContext().getElementContext().getResponse().clearBuffer();
request_state = request_state.getEmbeddingContext().getElementContext().getRequestState();
}
}
Map<String, String[]> exit_request_params = null;
Map<String, String[]> exit_inputs = null;
Stack<String> inheritance_stack = null;
Set<Map.Entry<String, String[]>> output_entries = mOutputs.aggregateValues().entrySet();
ElementExecutionState element_state = getElementState();
if (element_state.inInheritanceStructure() &&
!flowlink.cancelInheritance())
{
// verify if the trigger wasn't raised automatically, if this is the
// case, the first trigger context has to be removed from the trigger
// list since it has been used by this element
if (element_state.isNextExitTrigger(mElementInfo, exit))
{
exit_request_params = element_state.getRequestParameters();
// obtain the stored exit inputs
exit_inputs = element_state.nextTrigger().getParameters();
}
// since the current element deferred the logical flow to an exit,
// record this action in the trigger list
else
{
exit_request_params = new HashMap<String, String[]>();
exit_request_params.put(ReservedParameters.CHILDREQUEST, new String[] {getEncodedChildRequest()});
exit_request_params.put(ReservedParameters.TRIGGERLIST, element_state.getRequestParameterValues(ReservedParameters.TRIGGERLIST));
exit_inputs = collectExitInputValues(flowlink, output_entries, target, flowlink.isSnapback());
element_state.addTrigger(TriggerContext.generateExitTrigger(mElementInfo, exit, exit_inputs));
}
inheritance_stack = element_state.getInheritanceStack();
// check for successive inheritance stacks
Stack<ElementInfo> dest_inheritance_stack = target.getInheritanceStack();
if (dest_inheritance_stack != null)
{
for (ElementInfo element_info : dest_inheritance_stack)
{
target = element_info;
inheritance_stack.add(element_info.getId());
}
inheritance_stack.pop();
}
}
else
{
exit_request_params = new HashMap<String, String[]>();
exit_inputs = collectExitInputValues(flowlink, output_entries, target, flowlink.isSnapback());
mRequestState.setTarget(target);
// obtain the inheritance stack and if it exists the top parent
// this isn't done if an inheritance structure is already present
Stack<ElementInfo> dest_inheritance_stack = target.getInheritanceStack();
if (dest_inheritance_stack != null)
{
inheritance_stack = new Stack<String>();
for (ElementInfo element_info : dest_inheritance_stack)
{
target = element_info;
inheritance_stack.add(element_info.getId());
}
inheritance_stack.pop();
}
}
// the request method isn't get or post anymore since all parameters have
// been removed
element_state.setMethod(RequestMethod.EXIT);
// construct the target element's context
element_state.setRequestParameters(exit_request_params);
element_state.setInheritanceStack(inheritance_stack);
element_state.setTriggerInputs(exit_inputs);
// return the new element context
return mRequestState.getElementContext(target, mResponse);
}
private void handleForward(String url)
{
boolean is_absolute_url = true;
if (-1 == url.indexOf(":/"))
{
is_absolute_url = false;
StringBuilder absolute_url = new StringBuilder();
absolute_url.append(mRequestState.getWebappRootUrl(RifeConfig.Engine.getLocalForwardPort()));
if (url.startsWith("/"))
{
absolute_url.append(url.substring(1));
}
else
{
absolute_url.append(url);
}
url = absolute_url.toString();
}
try
{
Map<String, String> request_header_map = new HashMap<String, String>();
Request request = mRequestState.getRequest();
Enumeration request_header_names = request.getHeaderNames();
// convert the headers to a map
if (request_header_names.hasMoreElements())
{
String header_name = null;
String header_name_lowercase = null;
String header_value = null;
do
{
header_name = (String)request_header_names.nextElement();
if (null != header_name)
{
header_name_lowercase = header_name.toLowerCase();
header_value = request.getHeader(header_name);
if (is_absolute_url &&
("host".equals(header_name_lowercase) ||
"connection".equals(header_name_lowercase) ||
"keep-alive".equals(header_name_lowercase) ||
"content-type".equals(header_name_lowercase) ||
"content-length".equals(header_name_lowercase)))
{
continue;
}
request_header_map.put(header_name, header_value);
}
}
while (request_header_names.hasMoreElements());
}
// retrieve the page
HttpUtils.Page page = new HttpUtils.Request(url).headers(request_header_map).retrieve();
// incorporate the page data in the current request
if ((page.getResponseCode() / 100) != 2)
{
// report errors
mResponse.sendError(page.getResponseCode(), page.getResponseMessage());
}
else
{
// preserve the status code
mResponse.setStatus(page.getResponseCode());
}
if (page.getContent() != null)
{
mResponse.print(page.getContent());
}
for (Map.Entry<String, List<String>> header : page.getHeaders().entrySet())
{
if (header.getKey() != null)
{
String key = header.getKey().toLowerCase();
// strip out several duplicate headers
if (key.equals("accept-encoding") ||
key.equals("content-encoding") ||
key.equals("content-length") ||
key.equals("date") ||
key.equals("host") ||
key.equals("server") ||
key.equals("transfer-encoding"))
{
continue;
}
// handle the content type differently
if (key.equals("content-type"))
{
mResponse.setContentType(HttpUtils.extractMimeTypeFromContentType(page.getContentType()));
continue;
}
for (String header_value : header.getValue())
{
mResponse.addHeader(header.getKey(), header_value);
}
}
}
}
catch (IOException e)
{
mResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
}
RequestDispatcher dispatcher = mRequestState.getRequest().getRequestDispatcher(url);
if (null == dispatcher)
{
mResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
RequestState getRequestState()
{
return mRequestState;
}
ElementInfo getElementInfo()
{
return mElementInfo;
}
ElementSupport getElementSupport()
{
return mElement;
}
ElementExecutionState getElementState()
{
return getRequestState().getElementState();
}
Response getResponse()
{
return mResponse;
}
void setResponse(Response response)
{
mResponse = response;
}
void print(Template template)
throws TemplateException, EngineException
{
List<String> set_values = new EngineTemplateProcessor(this, template).processTemplate();
// set the content type
if (!mResponse.isContentTypeSet())
{
String content_type = template.getDefaultContentType();
if (null == content_type)
{
content_type = RifeConfig.Engine.getDefaultContentType();
}
mResponse.setContentType(content_type);
}
// print the element contents with the auto-generated values
mResponse.print(template);
// clean up the values that were set
template.removeValues(set_values);
}
void triggerChild(String childTriggerName, String[] childTriggerValues)
throws EngineException
{
mElement.enableRequestAccess(false);
try
{
if (mElement.childTriggered(childTriggerName, childTriggerValues))
{
throw new ChildTriggeredException(childTriggerName, childTriggerValues);
}
}
finally
{
mElement.enableRequestAccess(true);
}
}
boolean hasSubmission()
{
return null != getSubmission();
}
boolean hasSubmission(String name)
{
String submission = getSubmission();
return submission != null &&
submission.equals(name);
}
String getSubmission()
{
if (null == mSubmission)
{
return null;
}
return mSubmission;
}
private void validateParameter(String parameterName)
throws EngineException
{
assert parameterName != null;
assert parameterName.length() > 0;
boolean found = false;
for (Submission submission : mElementInfo.getSubmissions())
{
if (submission.containsParameter(parameterName))
{
found = true;
break;
}
}
if (!found)
{
throw new ParameterUnknownException(mElementInfo.getDeclarationName(), parameterName);
}
}
private void validateFile(String fileName)
throws EngineException
{
assert fileName != null;
assert fileName.length() > 0;
boolean found = false;
for (Submission submission : mElementInfo.getSubmissions())
{
if (submission.containsFile(fileName))
{
found = true;
break;
}
}
if (!found)
{
throw new FileUnknownException(mElementInfo.getDeclarationName(), fileName);
}
}
boolean isInputEmpty(String name)
throws EngineException
{
String input = getInput(name);
return null == input ||
input.trim().equals("");
}
String getInput(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateInputName(name);
String input = getElementState().getInput(name);
if (null == input &&
mElementInfo.hasInputDefaults())
{
String[] default_values = mElementInfo.getInputDefaultValues(name);
if (default_values != null)
{
input = default_values[0];
}
}
if (null == input &&
mElementInfo.hasGlobalVarDefaults())
{
String[] default_values = mElementInfo.getGlobalVarDefaultValues(name);
if (default_values != null)
{
input = default_values[0];
}
}
return input;
}
String[] getInputValues(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateInputName(name);
String[] input_values = getElementState().getInputValues(name);
if (null == input_values &&
mElementInfo.hasInputDefaults())
{
input_values = mElementInfo.getInputDefaultValues(name);
}
if (null == input_values &&
mElementInfo.hasGlobalVarDefaults())
{
input_values = mElementInfo.getGlobalVarDefaultValues(name);
}
return input_values;
}
boolean hasInputValue(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateInputName(name);
return getElementState().hasInputValue(name) || mElementInfo.hasInputDefaultValues(name) || mElementInfo.hasGlobalVarDefaultValues(name);
}
OutcookieValues getOutcookies() {
return mOutcookies;
}
OutputValues getOutputs()
{
return mOutputs;
}
void setOutput(String name, String value)
throws EngineException
{
assert name != null;
assert name.length() > 0;
assert value != null;
mElementInfo.validateOutputName(name);
// create a string array
String[] value_array = new String[]{value};
// store the value
setOutputValues(name, value_array);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(name))
{
triggerChild(name, value_array);
}
}
void setOutput(String name, String[] values)
throws EngineException
{
assert name != null;
assert name.length() > 0;
assert values != null;
assert values.length > 0;
mElementInfo.validateOutputName(name);
setOutputValues(name, values);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(name))
{
triggerChild(name, values);
}
}
void setOutput(String name, Object value, ConstrainedProperty constrainedProperty)
throws EngineException
{
assert name != null;
assert name.length() > 0;
assert value != null;
String[] value_array = getOutputObjectValues(name, value, constrainedProperty);
// store the value
setOutputValues(name, value_array);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(name))
{
triggerChild(name, value_array);
}
}
private String[] getOutputObjectValues(String name, Object value, ConstrainedProperty constrainedProperty)
throws EngineException
{
mElementInfo.validateOutputName(name);
// create a string array
String[] value_array;
// convert the value to a string representation
Class value_type = value.getClass();
if (value_type.isArray() ||
!(value instanceof Serializable) ||
ClassUtils.isBasic(value_type))
{
value_array = ArrayUtils.createStringArray(value, constrainedProperty);
}
else
{
try
{
value_array = new String[]{SerializationUtils.serializeToString((Serializable)value)};
}
catch (SerializationUtilsErrorException e)
{
throw new UnserializableOutputValueException(mElementInfo.getDeclarationName(), name, value, e);
}
}
return value_array;
}
void addOutputValue(String name, String value)
throws EngineException
{
assert name != null;
assert name.length() > 0;
assert value != null;
mElementInfo.validateOutputName(name);
String[] value_array = null;
String[] existing_values = mOutputs.get(name);
if (existing_values != null)
{
value_array = ArrayUtils.join(existing_values, value);
}
else
{
value_array = new String[]{value};
}
setOutputValues(name, value_array);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(name))
{
triggerChild(name, value_array);
}
}
void addOutputValues(String name, String[] values)
throws EngineException
{
assert name != null;
assert name.length() > 0;
assert values != null;
assert values.length > 0;
mElementInfo.validateOutputName(name);
String[] value_array = null;
String[] existing_values = mOutputs.get(name);
if (existing_values != null)
{
value_array = ArrayUtils.join(existing_values, values);
}
else
{
value_array = values;
}
setOutputValues(name, value_array);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(name))
{
triggerChild(name, value_array);
}
}
void addOutputValue(String name, Object value)
throws EngineException
{
assert name != null;
assert name.length() > 0;
assert value != null;
mElementInfo.validateOutputName(name);
// convert the value to a string representation
String value_text = null;
Class value_type = value.getClass();
if (!(value instanceof Serializable) ||
ClassUtils.isBasic(value_type))
{
value_text = String.valueOf(value);
}
else
{
try
{
value_text = SerializationUtils.serializeToString((Serializable)value);
}
catch (SerializationUtilsErrorException e)
{
throw new UnserializableOutputValueException(mElementInfo.getDeclarationName(), name, value, e);
}
}
addOutputValue(name, value_text);
}
void clearOutput(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateOutputName(name);
clearOutputValue(name);
}
void clearNamedOutputBean(String name)
throws EngineException
{
assert name != null;
mElementInfo.validateOutbeanName(name);
BeanDeclaration output_bean = mElementInfo.getNamedOutbeanInfo(name);
if (null == output_bean)
{
output_bean = mElementInfo.getNamedGlobalBeanInfo(name);
}
Class output_bean_class = null;
try
{
output_bean_class = Class.forName(output_bean.getClassname());
}
catch (ClassNotFoundException e)
{
throw new NamedOutbeanClassnameErrorException(mElementInfo.getDeclarationName(),name, output_bean.getClassname());
}
clearOutputBean(output_bean_class, output_bean.getPrefix());
}
void clearOutputBean(Class beanClass, String prefix)
throws EngineException
{
if (null == beanClass) throw new IllegalArgumentException("beanClass can't be null.");
try
{
// handle outputs
Collection<String> output_names = mElementInfo.getOutputNames();
String[] output_names_array = new String[output_names.size()];
output_names.toArray(output_names_array);
// handle globals
Collection<String> globalvar_names = mElementInfo.getGlobalVarNames();
String[] globalvar_names_array = new String[globalvar_names.size()];
globalvar_names.toArray(globalvar_names_array);
// merge the both arrays
String[] merged_names_array = ArrayUtils.join(output_names_array, globalvar_names_array);
// process all the possible output names
Set<String> property_names = BeanUtils.getPropertyNames(beanClass, merged_names_array, null, prefix);
for (String property_name : property_names)
{
clearOutput(property_name);
}
}
catch (BeanUtilsException e)
{
throw new BeanClassNamesErrorException(beanClass, e);
}
}
String[] getOutput(String name)
throws EngineException
{
mElementInfo.validateOutputName(name);
return mOutputs.aggregateValues().get(name);
}
Set<Map.Entry<String, String>> getIncookieEntries()
{
return collectCookieValues().entrySet();
}
boolean hasCookie(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateIncookieName(name);
return mRequestState.hasCookie(name) || mElementInfo.hasIncookieDefaultValue(name) || mElementInfo.hasGlobalCookieDefaultValue(name);
}
Cookie getCookie(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateIncookieName(name);
Cookie cookie = mRequestState.getCookie(name);
if (null == cookie )
{
if (mElementInfo.hasIncookieDefaultValue(name))
{
cookie = new Cookie(name, mElementInfo.getIncookieDefaultValue(name));
}
if (mElementInfo.hasGlobalCookieDefaultValue(name))
{
cookie = new Cookie(name, mElementInfo.getGlobalCookieDefaultValue(name));
}
}
return cookie;
}
String getCookieValue(String name)
throws EngineException
{
Cookie cookie = getCookie(name);
String value = null;
if (cookie != null)
{
value = cookie.getValue();
}
return value;
}
void setCookie(Cookie cookie)
throws EngineException
{
assert cookie != null;
assert cookie.getName() != null;
mElementInfo.validateOutcookieName(cookie.getName());
mOutcookies.put(cookie.getName(), cookie.getValue());
setCookieRaw(cookie);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(cookie.getName()))
{
triggerChild(cookie.getName(), new String[] {cookie.getValue()});
}
}
void setCookieRaw(Cookie cookie)
{
mResponse.addCookie(cookie);
mRequestState.setStateCookie(cookie);
fireOutcookieSet(cookie);
}
HashMap<String, String> collectCookieValues()
throws EngineException
{
HashMap<String, String> result = new HashMap<String, String>();
for (Map.Entry<String, String> entry : mElementInfo.getDefaultIncookies().entrySet())
{
result.put(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : mElementInfo.getDefaultGlobalCookies().entrySet())
{
result.put(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, Cookie> entry : mRequestState.getCookies().entrySet())
{
result.put(entry.getKey(), entry.getValue().getValue());
}
return result;
}
boolean hasParameterValue(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
validateParameter(name);
String submission = getSubmission();
if (null == submission)
{
return false;
}
return getElementState().hasRequestParameterValue(name) || mElementInfo.hasParameterDefaultValues(submission, name);
}
boolean isParameterEmpty(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
String parameter = getParameter(name);
if (null == parameter ||
parameter.trim().equals(""))
{
return true;
}
return false;
}
String getParameter(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
validateParameter(name);
String submission = getSubmission();
if (null == submission)
{
return null;
}
String parameter = getElementState().getRequestParameter(name);
if (null == parameter &&
mElementInfo.hasParameterDefaults(submission))
{
String[] default_values = mElementInfo.getParameterDefaultValues(submission, name);
if (default_values != null)
{
return default_values[0];
}
}
return parameter;
}
ArrayList<String> getParameterNames(String regexp)
throws EngineException
{
Pattern pattern = null;
if (regexp != null)
{
pattern = Pattern.compile("^"+regexp+"$");
}
ElementExecutionState element_state = getElementState();
ArrayList<String> result = new ArrayList<String>();
String submission_name = getSubmission();
if (null == submission_name)
{
return result;
}
Submission submission = mElementInfo.getSubmission(submission_name);
if (null == submission)
{
return result;
}
Collection<String> parameter_names = null;
// add all default parameters that match
parameter_names = submission.getParameterDefaultNames();
for (String parameter_name : parameter_names)
{
if (null == pattern ||
pattern.matcher(parameter_name).matches())
{
result.add(parameter_name);
}
}
// go over the possible parameters and check if they have values in the request
parameter_names = submission.getParameterNames();
for (String parameter_name : parameter_names)
{
if (element_state.hasRequestParameterValue(parameter_name) &&
!result.contains(parameter_name) &&
(null == pattern || pattern.matcher(parameter_name).matches()))
{
result.add(parameter_name);
}
}
// go over all the parameter regexps and find those that match with the parameters in the request
Matcher matcher = null;
for (Pattern parameter_regexp : submission.getParameterRegexps())
{
for (String parameter_name : element_state.getRequestParameterNames())
{
matcher = parameter_regexp.matcher(parameter_name);
if (matcher.matches())
{
if (element_state.hasRequestParameterValue(parameter_name) &&
!result.contains(parameter_name) &&
(null == pattern || pattern.matcher(parameter_name).matches()))
{
result.add(parameter_name);
}
}
}
}
return result;
}
String[] getParameterValues(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
validateParameter(name);
String submission = getSubmission();
if (null == submission)
{
return null;
}
String[] parameter_values = getElementState().getRequestParameterValues(name);
if (null == parameter_values &&
mElementInfo.hasParameterDefaults(submission))
{
return mElementInfo.getParameterDefaultValues(submission, name);
}
return parameter_values;
}
ArrayList<String> getUploadedFileNames(String regexp)
throws EngineException
{
Pattern pattern = null;
if (regexp != null)
{
pattern = Pattern.compile("^"+regexp+"$");
}
ArrayList<String> result = new ArrayList<String>();
String submission_name = getSubmission();
if (null == submission_name)
{
return result;
}
Submission submission = mElementInfo.getSubmission(submission_name);
if (null == submission)
{
return result;
}
Collection<String> file_names = null;
// go over the possible files and check if they have values in the request
file_names = submission.getFileNames();
for (String file_name : file_names)
{
if (mRequestState.hasUploadedFile(file_name) &&
!result.contains(file_name) &&
(null == pattern || pattern.matcher(file_name).matches()))
{
result.add(file_name);
}
}
// go over all the file regexps and find those that match with the files in the request
Matcher matcher = null;
for (Pattern file_regexp : submission.getFileRegexps())
{
for (String file_name : mRequestState.getUploadedFileNames())
{
matcher = file_regexp.matcher(file_name);
if (matcher.matches())
{
if (mRequestState.hasUploadedFile(file_name) &&
!result.contains(file_name) &&
(null == pattern || pattern.matcher(file_name).matches()))
{
result.add(file_name);
}
}
}
}
return result;
}
ArrayList<String> getUploadedFileNames()
throws EngineException
{
ArrayList<String> result = new ArrayList<String>();
String submission_name = getSubmission();
if (null == submission_name)
{
return result;
}
Submission submission = mElementInfo.getSubmission(submission_name);
if (null == submission)
{
return result;
}
Collection<String> file_names = submission.getFileNames();
for (String file_name : file_names)
{
if (mRequestState.hasUploadedFile(file_name))
{
result.add(file_name);
}
}
return result;
}
boolean hasUploadedFile(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
validateFile(name);
String submission = getSubmission();
if (null == submission)
{
return false;
}
return mRequestState.hasUploadedFile(name);
}
boolean isFileEmpty(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
UploadedFile file = getUploadedFile(name);
return null == file ||
null == file.getFile() ||
0 == file.getFile().length();
}
UploadedFile getUploadedFile(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
validateFile(name);
String submission = getSubmission();
if (null == submission)
{
return null;
}
return mRequestState.getUploadedFile(name);
}
UploadedFile[] getUploadedFiles(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
validateFile(name);
String submission = getSubmission();
if (null == submission)
{
return null;
}
return mRequestState.getUploadedFiles(name);
}
void setOutputValues(String name, String[] values)
{
assert name != null;
assert name.length() > 0;
assert values != null;
mOutputs.put(name, values);
fireOutputValueSet(name, values);
}
void setAutomatedOutputValues(String name, String[] values)
{
mOutputs.putFallback(name, values);
fireAutomatedOutputValueSet(name, values);
}
void clearOutputValue(String name)
{
assert name != null;
assert name.length() > 0;
// clearing the output value instead of removing it, this prevents
// later automated processing to fill it in again
mOutputs.put(name, null);
fireOutputValueCleared(name);
}
void clearAutomatedOutputValue(String name)
{
// clearing the output value instead of removing it, this prevents
// later automated processing to fill it in again
mOutputs.putFallback(name, null);
fireAutomatedOutputValueCleared(name);
}
void addOutputListener(OutputListener listener)
{
if (null == mOutputListeners)
{
mOutputListeners = new ArrayList<OutputListener>();
}
mOutputListeners.add(listener);
}
void removeOutputListener(OutputListener listener)
{
mOutputListeners.remove(listener);
}
void fireOutputValueSet(String name, String[] values)
{
if (null == mOutputListeners)
{
return;
}
for (OutputListener listener : mOutputListeners)
{
listener.outputValueSet(name, values);
}
}
void fireOutputValueCleared(String name)
{
if (null == mOutputListeners)
{
return;
}
for (OutputListener listener : mOutputListeners)
{
listener.outputValueCleared(name);
}
}
void fireAutomatedOutputValueSet(String name, String[] values)
{
if (null == mOutputListeners)
{
return;
}
for (OutputListener listener : mOutputListeners)
{
listener.automatedOutputValueSet(name, values);
}
}
void fireAutomatedOutputValueCleared(String name)
{
if (null == mOutputListeners)
{
return;
}
for (OutputListener listener : mOutputListeners)
{
listener.automatedOutputValueCleared(name);
}
}
void addOutcookieListener(OutcookieListener listener)
{
if (null == mOutcookieListeners)
{
mOutcookieListeners = new ArrayList<OutcookieListener>();
}
mOutcookieListeners.add(listener);
}
void removeOutcookieListener(OutcookieListener listener)
{
mOutcookieListeners.remove(listener);
}
void fireOutcookieSet(Cookie cookie)
{
if (null == mOutcookieListeners)
{
return;
}
for (OutcookieListener listener : mOutcookieListeners)
{
listener.outcookieSet(cookie);
}
}
boolean duringStepBack()
{
return mSteppedBack;
}
void exit(String name)
throws EngineException
{
assert name != null;
assert name.length() > 0;
mElementInfo.validateExitName(name);
throw new ExitTriggeredException(name);
}
/**
* Retrieves the inputs values that a child receives when the parent
* delegates the control flow to it. This is based on the current output
* values of the parent element, combined with the global variables
*/
private Map<String, String[]> collectChildInputValues(ElementInfo targetElement)
{
ElementExecutionState element_state = getElementState();
Map<String, String[]> inputs = null;
inputs = element_state.getTriggerInputs();
if (null == inputs)
{
inputs = element_state.getRequestParameters();
}
if (mElementInfo.hasGlobalVars() &&
targetElement.hasGlobalVars())
{
String[] input_values_array = null;
for (Map.Entry<String, String[]> output_entry : mOutputs.aggregateValues().entrySet())
{
// create automatic data link for global variables
if (mElementInfo.containsGlobalVar(output_entry.getKey()) &&
targetElement.containsGlobalVar(output_entry.getKey()))
{
input_values_array = output_entry.getValue();
inputs.put(output_entry.getKey(), input_values_array);
}
}
}
return inputs;
}
/**
* Retrieves the inputs values that an exit receives when it is
* followed. This is based on the current output values of the
* active element.
*/
private Map<String, String[]> collectExitInputValues(FlowLink flowLink, Set<Map.Entry<String, String[]>> outputEntries, ElementInfo target, boolean snapback)
throws EngineException
{
Map<String, String[]> inputs = new LinkedHashMap<String, String[]>();
// preserve the embedding element's inputs and global vars
if (target.getId().equals(mElementInfo.getId()))
{
ElementContext context = this;
while (context.mRequestState.isEmbedded())
{
context = context.mRequestState.getEmbeddingContext().getElementContext();
ElementExecutionState embedded_element_state = context.getElementState();
for (String input_name : context.mElementInfo.getInputNames())
{
if (embedded_element_state.hasInputValue(input_name))
{
inputs.put(input_name, embedded_element_state.getInputValues(input_name));
}
}
for (String globalvar_name : context.mElementInfo.getGlobalVarNames())
{
if (embedded_element_state.hasInputValue(globalvar_name))
{
inputs.put(globalvar_name, embedded_element_state.getInputValues(globalvar_name));
}
}
}
}
if (mElementInfo.hasSnapbackDataLinks() ||
mElementInfo.hasDataLink(target) ||
(mElementInfo.hasGlobalVars() &&
target.hasGlobalVars()))
{
Map<String, String[]> dest_input_candidates = new LinkedHashMap<String, String[]>();
// if the exit is reflective, preserve the input values that have
// identically named output values
if (target == mElementInfo)
{
Collection<String> input_names = mElementInfo.getInputNames();
Collection<String> output_names = mElementInfo.getOutputNames();
Iterator<String> input_names_it = input_names.iterator();
String input_name = null;
String[] input_values = null;
while (input_names_it.hasNext())
{
input_name = input_names_it.next();
if (output_names.contains(input_name))
{
input_values = getInputValues(input_name);
if (input_values != null)
{
dest_input_candidates.put(input_name, input_values);
}
}
}
}
// add the global default values
GlobalVar globalvar_data = null;
for (Map.Entry<String, GlobalVar> globalvar_entry : mElementInfo.getGlobalVarEntries())
{
globalvar_data = globalvar_entry.getValue();
if (globalvar_data != null &&
globalvar_data.getDefaultValues() != null)
{
dest_input_candidates.put(globalvar_entry.getKey(), globalvar_data.getDefaultValues());
}
}
// add the output values
String[] output_values_array = null;
for (Map.Entry<String, String[]> output_entry : outputEntries)
{
output_values_array = output_entry.getValue();
dest_input_candidates.put(output_entry.getKey(), output_values_array);
}
// process the exit input value candidates
Collection<String> dest_input_names = null;
GlobalVar globalvar_source = null;
GlobalVar globalvar_target = null;
for (String candidate : dest_input_candidates.keySet())
{
if (!mElementInfo.containsDepartureVar(candidate))
{
// create automatic data link for global variables
if (mElementInfo.containsGlobalVar(candidate) &&
target.containsGlobalVar(candidate))
{
globalvar_source = mElementInfo.getGlobalVarInfo(candidate);
globalvar_target = target.getGlobalVarInfo(candidate);
// only create the link if the global vars are in the
// same group scope
if (globalvar_source.getGroupId() == globalvar_target.getGroupId())
{
inputs.put(candidate, dest_input_candidates.get(candidate));
}
}
// translate outputs to inputs through a possible data link
dest_input_names = mElementInfo.getDataLinkInputs(candidate, target, snapback, flowLink);
if (dest_input_names != null)
{
for (String dest_input_name : dest_input_names)
{
inputs.put(dest_input_name, dest_input_candidates.get(candidate));
}
}
}
}
}
return inputs;
}
private static Map<String, String[]> overrideOutputValues(Map<String, String[]> outputValueMap, String[] outputValues)
throws EngineException
{
if (null == outputValues ||
0 == outputValues.length)
{
return outputValueMap;
}
Map<String, String[]> outputs = new LinkedHashMap<String, String[]>(outputValueMap);
// store the output overrides
for (int i = 0; i < outputValues.length; i += 2)
{
outputs.put(outputValues[i], new String[]{outputValues[i+1]});
}
return outputs;
}
private Map<String, String[]> collectExitInputValues(FlowLink flowLink, ElementInfo target, boolean snapback, Map<String, String[]> outputValueMap, String[] outputValues)
throws EngineException
{
// override current output values
Map<String, String[]> overridden_outputs = overrideOutputValues(outputValueMap, outputValues);
// construct the exit input parameters
return collectExitInputValues(flowLink, overridden_outputs.entrySet(), target, snapback);
}
FlowState collectExitParameters(FlowLink flowlink, Map<String, String[]> outputValueMap, String[] outputValues)
{
FlowState state = new FlowState();
ElementExecutionState element_state = getElementState();
// construct the exit input parameters
Map<String, String[]> inputs = collectExitInputValues(flowlink, flowlink.getTarget(), flowlink.isSnapback(), outputValueMap, outputValues);
// Create or preserve the request parameters that were initially send to the child element
// this permits completely seperate processing of any other parameters.
// Maintain a stack of successful parent elements.
if (element_state.inInheritanceStructure() &&
!flowlink.cancelInheritance())
{
state.putParameter(ReservedParameters.CHILDREQUEST, getEncodedChildRequest());
// preserve the trigger list
List<TriggerContext> new_trigger_context = element_state.cloneTriggerList();
new_trigger_context.add(TriggerContext.generateExitTrigger(mElementInfo, flowlink.getExitName(), inputs));
state.putParameter(ReservedParameters.TRIGGERLIST, TriggerListEncoder.encode(new_trigger_context));
}
else
{
// construct the exit input parameters
state.setParameters(inputs);
}
if (!flowlink.cancelContinuations())
{
// Preserve the continuation id if a continuation context is active
// and the exit goes back to the same element
if (mElementInfo == flowlink.getTarget())
{
String continuation_id = ContinuationContext.getActiveContextId();
if (continuation_id != null)
{
state.putParameter(ReservedParameters.CONTID, continuation_id);
}
}
}
return state;
}
private String getContextId()
{
if (mContextId != null)
{
return mContextId;
}
synchronized (mElement)
{
mContextId = mRequestState.buildContextId();
}
return mContextId;
}
FlowState collectSubmissionParameters(String name, String[] parameterValues, Set<Map.Entry<String, String[]>> outputEntries)
{
FlowState state = new FlowState();
state.putParameter(ReservedParameters.SUBMISSION, name);
// add the submission parameters
if (parameterValues != null)
{
String parameter_name = null;
String parameter_value = null;
for (int i = 0; i < parameterValues.length; i += 2)
{
parameter_name = parameterValues[i];
parameter_value = parameterValues[i+1];
state.putParameter(parameter_name, parameter_value);
}
validateParameter(parameter_name);
}
// Create the submission context parameter
Submission submission = mElementInfo.getSubmission(name);
if (null == submission ||
Scope.LOCAL == submission.getScope())
{
String submission_context = getContextId();
String target = getElementInfo().getId();
if (!submission_context.equals(target))
{
StringBuilder submission_context_buffer = new StringBuilder(submission_context);
submission_context_buffer.append("^");
submission_context_buffer.append(target);
submission_context = submission_context_buffer.toString();
}
try
{
state.putParameter(ReservedParameters.SUBMISSIONCONTEXT, Base64.encodeToString(submission_context.getBytes("UTF-8"), false));
}
catch (UnsupportedEncodingException e)
{
// should never happen
}
}
// Preserve the continuation ID if a continuation context is active
if (null == submission ||
!submission.getCancelContinuations())
{
String continuation_id = ContinuationContext.getActiveContextId();
if (continuation_id != null)
{
state.putParameter(ReservedParameters.CONTID, continuation_id);
}
}
ElementExecutionState element_state = getElementState();
// create or preserve the request parameters that were initially send to the child element
// this permits completely seperate processing of any other parameters
if (element_state.inInheritanceStructure())
{
// preserve the original child request
state.putParameter(ReservedParameters.CHILDREQUEST, getEncodedChildRequest());
// preserve the trigger list
state.putParameter(ReservedParameters.TRIGGERLIST, element_state.encodeTriggerList());
}
// preserve the embedding element's inputs and global vars
ElementContext context = this;
while (context.mRequestState.isEmbedded())
{
context = context.mRequestState.getEmbeddingContext().getElementContext();
ElementExecutionState embedded_element_state = context.getElementState();
for (String input_name : context.mElementInfo.getInputNames())
{
if (embedded_element_state.hasInputValue(input_name))
{
state.putSubmissionGlobalInput(input_name, embedded_element_state.getInputValues(input_name, false));
}
}
for (String globalvar_name : context.mElementInfo.getGlobalVarNames())
{
if (embedded_element_state.hasInputValue(globalvar_name))
{
state.putSubmissionGlobalInput(globalvar_name, embedded_element_state.getInputValues(globalvar_name, false));
}
}
}
// preserve the global variables
for (String globalvar_name : mElementInfo.getGlobalVarNames())
{
if (element_state.hasInputValue(globalvar_name))
{
state.putSubmissionGlobalInput(globalvar_name, element_state.getInputValues(globalvar_name, false));
}
}
// activate reflective datalinks for global vars
if (outputEntries != null)
{
for (Map.Entry<String, String[]> output : outputEntries)
{
// global vars
if (mElementInfo.containsGlobalVar(output.getKey()))
{
state.putSubmissionGlobalInput(output.getKey(), output.getValue());
}
}
}
// add this element's inputs
Map<String, String[]> element_inputs = null;
// preserve the input values
for (String input_name : mElementInfo.getInputNames())
{
if (element_state.hasInputValue(input_name))
{
if (null == element_inputs)
{
element_inputs = new LinkedHashMap<String, String[]>();
}
element_inputs.put(input_name, element_state.getInputValues(input_name));
}
}
// merge this element's inputs with its preserved inputs
Map<String, String[]> preserved_inputs = collectPreservedInputs(outputEntries);
String context_id = getContextId();
if (preserved_inputs != null &&
preserved_inputs.size() > 0)
{
if (null == element_inputs)
{
element_inputs = new LinkedHashMap<String, String[]>();
}
element_inputs.putAll(preserved_inputs);
}
state.setSubmissionElementInputs(element_inputs);
// remember this element's context identifier for when the context inputs are extracted
state.setSubmissionContextId(context_id);
return state;
}
Map<String, String[]> collectPreservedInputs(Set<Map.Entry<String, String[]>> outputEntries)
{
Map<String, String[]> element_inputs = null;
// activate reflective datalinks for which outputs point to the inputs
if (outputEntries != null)
{
for (Map.Entry<String, String[]> output : outputEntries)
{
// reflective datalinks
Collection<String> datalink_inputs = mElementInfo.getDataLinkInputs(output.getKey(), mElementInfo, false, null);
if (datalink_inputs != null)
{
for (String input_name : datalink_inputs)
{
if (null == element_inputs)
{
element_inputs = new LinkedHashMap<String, String[]>();
}
element_inputs.put(input_name, output.getValue());
}
}
}
}
return element_inputs;
}
private <BeanType> BeanType retrieveSubmissionBeanNoValidation(Submission submission, Class<BeanType> beanClass, String prefix)
throws EngineException
{
assert submission != null;
assert beanClass != null;
ElementExecutionState element_state = getElementState();
BeanType bean_instance = getNewBeanInstance(beanClass);
try
{
HashMap<String, PropertyDescriptor> bean_properties = BeanUtils.getUppercasedBeanProperties(beanClass);
String[] parameter_values = null;
for (String parameter_name : submission.getParameterNames())
{
if (element_state.hasRequestParameterValue(parameter_name))
{
parameter_values = element_state.getRequestParameterValues(parameter_name);
if (parameter_values != null &&
parameter_values.length > 0)
{
BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean_instance, null);
}
}
}
for (Pattern parameter_regexp : submission.getParameterRegexps())
{
for (String parameter_name : getParameterNames(parameter_regexp.pattern()))
{
if (element_state.hasRequestParameterValue(parameter_name))
{
parameter_values = element_state.getRequestParameterValues(parameter_name);
if (parameter_values != null &&
parameter_values.length > 0)
{
BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean_instance, null);
}
}
}
}
for (String uploadedfile_name : getUploadedFileNames())
{
UploadedFile file = getUploadedFile(uploadedfile_name);
BeanUtils.setUppercasedBeanProperty(uploadedfile_name, file, prefix, bean_properties, bean_instance);
}
}
catch (BeanUtilsException e)
{
throw new EngineException(e);
}
return bean_instance;
}
<BeanType> BeanType getNamedSubmissionBean(String submissionName, String beanName)
throws EngineException
{
assert submissionName != null;
assert submissionName.length() > 0;
assert beanName != null;
assert beanName.length() > 0;
mElementInfo.validateSubmissionName(submissionName);
if (!hasSubmission(submissionName))
{
return null;
}
BeanDeclaration bean = mElementInfo.getSubmission(submissionName).getNamedBean(beanName);
Class<BeanType> bean_class = null;
try
{
bean_class = (Class<BeanType>)Class.forName(bean.getClassname());
}
catch (ClassNotFoundException e)
{
throw new NamedSubmissionBeanClassnameErrorException(mElementInfo.getDeclarationName(), submissionName, beanName, bean.getClassname(), e);
}
return retrieveSubmissionBeanNoValidation(mElementInfo.getSubmission(submissionName), bean_class, bean.getPrefix());
}
<BeanType> BeanType getSubmissionBean(String submissionName, Class<BeanType> beanClass, String prefix)
throws EngineException
{
assert submissionName != null;
assert submissionName.length() > 0;
assert beanClass != null;
mElementInfo.validateSubmissionName(submissionName);
if (!hasSubmission(submissionName))
{
return null;
}
return retrieveSubmissionBeanNoValidation(mElementInfo.getSubmission(submissionName), beanClass, prefix);
}
void fillSubmissionBean(String submissionName, Object bean, String prefix)
throws EngineException
{
assert submissionName != null;
assert submissionName.length() > 0;
if (null == bean)
{
return;
}
mElementInfo.validateSubmissionName(submissionName);
if (!hasSubmission(submissionName))
{
return;
}
Submission submission = mElementInfo.getSubmission(submissionName);
ElementExecutionState element_state = getElementState();
try
{
HashMap<String, PropertyDescriptor> bean_properties = BeanUtils.getUppercasedBeanProperties(bean.getClass());
String[] parameter_values = null;
Object empty_bean = null;
// handle regular parameters
for (String parameter_name : submission.getParameterNames())
{
parameter_values = element_state.getRequestParameterValues(parameter_name);
if (null == empty_bean &&
(null == parameter_values ||
0 == parameter_values[0].length()))
{
try
{
empty_bean = bean.getClass().newInstance();
}
catch (InstantiationException e)
{
throw new EngineException("Unexpected error while invoking the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
}
catch (IllegalAccessException e)
{
throw new EngineException("No permission to invoke the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
}
}
BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean, empty_bean);
}
// handle regexp parameters
for (Pattern parameter_regexp : submission.getParameterRegexps())
{
for (String parameter_name : getParameterNames(parameter_regexp.pattern()))
{
parameter_values = element_state.getRequestParameterValues(parameter_name);
if (null == empty_bean &&
(null == parameter_values ||
0 == parameter_values[0].length()))
{
try
{
empty_bean = bean.getClass().newInstance();
}
catch (InstantiationException e)
{
throw new EngineException("Unexpected error while invoking the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
}
catch (IllegalAccessException e)
{
throw new EngineException("No permission to invoke the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
}
}
BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean, empty_bean);
}
}
// automatically handle uploaded files
for (String uploadedfile_name : getUploadedFileNames())
{
UploadedFile file = getUploadedFile(uploadedfile_name);
BeanUtils.setUppercasedBeanProperty(uploadedfile_name, file, prefix, bean_properties, bean);
}
}
catch (BeanUtilsException e)
{
throw new EngineException(e);
}
}
<BeanType> BeanType getNamedInputBean(String name)
throws EngineException
{
assert name != null;
mElementInfo.validateInbeanName(name);
BeanDeclaration input_bean = mElementInfo.getNamedInbeanInfo(name);
if (null == input_bean)
{
input_bean = mElementInfo.getNamedGlobalBeanInfo(name);
}
Class<BeanType> input_bean_class = null;
try
{
input_bean_class = (Class<BeanType>)Class.forName(input_bean.getClassname());
}
catch (ClassNotFoundException e)
{
throw new NamedInbeanClassnameErrorException(mElementInfo.getDeclarationName(),name, input_bean.getClassname());
}
return getInputBean(input_bean_class, input_bean.getPrefix());
}
<BeanType> BeanType getInputBean(Class<BeanType> beanClass, String prefix)
throws EngineException
{
assert beanClass != null;
ElementExecutionState element_state = getElementState();
BeanType bean_instance = getNewBeanInstance(beanClass);
try
{
HashMap<String, PropertyDescriptor> bean_properties = BeanUtils.getUppercasedBeanProperties(beanClass);
// merge the input and global variable names
Collection<String> input_names = mElementInfo.getInputNames();
Collection<String> globalvar_names = mElementInfo.getGlobalVarNames();
ArrayList<String> merged_names = new ArrayList<String>();
merged_names.addAll(input_names);
merged_names.addAll(globalvar_names);
// process all the possible input names
String[] values = null;
for (String name : merged_names)
{
if (element_state.hasInputValue(name))
{
values = element_state.getInputValues(name);
if (values != null &&
values.length > 0)
{
BeanUtils.setUppercasedBeanProperty(name, values, prefix, bean_properties, bean_instance, null);
}
}
}
}
catch (BeanUtilsException e)
{
throw new EngineException(e);
}
return bean_instance;
}
void setNamedOutputBean(String name, Object bean)
throws EngineException
{
assert name != null;
mElementInfo.validateOutbeanName(name);
BeanDeclaration output_bean = mElementInfo.getNamedOutbeanInfo(name);
if (null == output_bean)
{
output_bean = mElementInfo.getNamedGlobalBeanInfo(name);
}
setOutputBean(bean, output_bean.getPrefix());
}
void setOutputBean(Object bean, String prefix)
throws EngineException
{
if (null == bean) throw new IllegalArgumentException("bean can't be null.");
Map<String, String[]> values = collectOutputBeanValues(bean, prefix, null);
String name;
String[] value;
for (Map.Entry<String, String[]> entry : values.entrySet())
{
name = entry.getKey();
value = entry.getValue();
setOutputValues(name, value);
if (getElementState().inInheritanceStructure() &&
mElementInfo.containsChildTrigger(name))
{
triggerChild(name, value);
}
}
}
Map<String, String[]> collectOutputBeanValues(Object bean, String prefix, Collection<String> included)
throws BeanInstanceValuesErrorException
{
if (null == bean) return Collections.emptyMap();
Map<String, String[]> values = new LinkedHashMap<String, String[]>();
try
{
Constrained constrained = ConstrainedUtils.makeConstrainedInstance(bean);
ConstrainedProperty constrained_property = null;
Set<String> merged_names = new LinkedHashSet<String>();
// handle outputs
Collection<String> output_names = mElementInfo.getOutputNames();
if (null == included)
{
merged_names.addAll(output_names);
}
else
{
for (String name : output_names)
{
if (included.contains(name))
{
merged_names.add(name);
}
}
}
// handle globals
Collection<String> globalvar_names = mElementInfo.getGlobalVarNames();
if (null == included)
{
merged_names.addAll(globalvar_names);
}
else
{
for (String name : globalvar_names)
{
if (included.contains(name))
{
merged_names.add(name);
}
}
}
// create the merged array
String[] merged_names_array = new String[merged_names.size()];
merged_names.toArray(merged_names_array);
// process all the possible output names
Map<String, Object> property_values = BeanUtils.getPropertyValues(bean, merged_names_array, null, prefix);
Object property_value = null;
for (String property_name : property_values.keySet())
{
property_value = property_values.get(property_name);
if (property_value != null)
{
// get the constrained property if that's appropriate
if (constrained != null)
{
if (prefix != null)
{
constrained_property = constrained.getConstrainedProperty(property_name.substring(prefix.length()));
}
else
{
constrained_property = constrained.getConstrainedProperty(property_name);
}
}
values.put(property_name, getOutputObjectValues(property_name, property_value, constrained_property));
}
}
}
catch (BeanUtilsException e)
{
throw new BeanInstanceValuesErrorException(bean, e);
}
return values;
}
private static <BeanType> BeanType getNewBeanInstance(Class<BeanType> beanClass)
throws EngineException
{
BeanType bean_instance;
try
{
bean_instance = beanClass.newInstance();
}
catch (InstantiationException e)
{
throw new EngineException("Can't instantiate a bean with class '"+beanClass.getName()+"'.", e);
}
catch (IllegalAccessException e)
{
throw new EngineException("No permission to instantiate a bean with class '"+beanClass.getName()+"'.", e);
}
return bean_instance;
}
void processEmbeddedElement(Template template, ElementSupport embeddingElement, String elementId, String differentiator, Object data)
throws TemplateException, EngineException
{
// process the embedded elements
if (template.hasFilteredValues(TAG_ELEMENT))
{
List<String[]> element_tags = template.getFilteredValues(TAG_ELEMENT);
for (String[] captured_groups : element_tags)
{
if (null == differentiator)
{
if (elementId.equals(captured_groups[3]))
{
processEmbeddedElement(captured_groups[0], template, embeddingElement, captured_groups[3], null, data);
return;
}
}
else
{
if (elementId.equals(captured_groups[3]))
{
String value_id = captured_groups[1]+":";
String differentiator_value_id = value_id+differentiator;
if (template.hasValueId(differentiator_value_id))
{
processEmbeddedElement(differentiator_value_id, template, embeddingElement, elementId, differentiator, data);
return;
}
else if (template.hasValueId(value_id))
{
processEmbeddedElement(value_id, template, embeddingElement, elementId, differentiator, data);
return;
}
}
}
}
}
throw new EmbeddedElementNotFoundException(elementId);
}
void processEmbeddedElement(String valueId, Template template, ElementSupport embeddingElement, String elementId, String differentiator, Object data)
throws TemplateException, EngineException
{
if (!template.hasValueId(valueId))
{
throw new EmbeddedElementNotFoundException(elementId);
}
ElementExecutionState element_state = getElementState();
ElementInfo embedded_element = null;
RequestState embedded_state = null;
Response embedded_response = null;
// try to obtain the embedded element and throw an exception if
// it couldn't be found
embedded_element = mElementInfo.getSite().resolveId(elementId, mElementInfo);
if (null == embedded_element)
{
throw new ElementIdNotFoundException(elementId);
}
// build the embedded element request parameter by merging
// the current element's request parameters with its input values
// also merge in the state global vars
Map<String, String[]> parameters = new HashMap<String, String[]>(element_state.getRequestParameters());
for (Map.Entry<String, String[]> input_entry : element_state.getInputEntries())
{
if (!parameters.containsKey(input_entry.getKey()))
{
parameters.put(input_entry.getKey(), input_entry.getValue());
}
}
if (mRequestState.getStateGlobalVars() != null)
{
for (Map.Entry<String, String[]> globalvar_entry : mRequestState.getStateGlobalVars().entrySet())
{
parameters.put(globalvar_entry.getKey(), globalvar_entry.getValue());
}
}
// build the request and response objects for the embedded element
// and service the request
EmbeddingContext embedding_context = new EmbeddingContext(this, embeddingElement, template, template.getDefaultValue(valueId), differentiator, data);
embedded_response = mResponse.createEmbeddedResponse(valueId, differentiator);
embedded_state = RequestState.getEmbeddedInstance(embedded_response, embedding_context, parameters, embedded_element);
embedded_state.service();
embedded_response.close();
if (embedding_context.getCancelEmbedding())
{
// handle embedding cancellation by throwing a dedicated exception
// that will bubble up to the first embedding element and print
// out the embedded content
throw new CancelEmbeddingTriggeredException(embedded_response.getEmbeddedContent());
}
else
{
// set the output of the element to the value in the template
template.setValue(valueId, embedded_response.getEmbeddedContent());
}
}
private String getEncodedChildRequest()
{
String child_request = null;
ElementExecutionState element_state = getElementState();
if (element_state.hasRequestParameterValue(ReservedParameters.CHILDREQUEST))
{
child_request = element_state.getRequestParameter(ReservedParameters.CHILDREQUEST);
}
else
{
String element_id = element_state.getInheritanceStack().get(0);
child_request = ChildRequestEncoder.encode(mRequestState.getSite().resolveId(element_id), mRequestState);
}
return child_request;
}
}