/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.synapse.mediators.base;
import org.apache.synapse.ContinuationState;
import org.apache.synapse.Mediator;
import org.apache.synapse.MessageContext;
import org.apache.synapse.Nameable;
import org.apache.synapse.SequenceType;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.SynapseLog;
import org.apache.synapse.aspects.AspectConfiguration;
import org.apache.synapse.aspects.flow.statistics.StatisticIdentityGenerator;
import org.apache.synapse.aspects.flow.statistics.collectors.CloseEventCollector;
import org.apache.synapse.aspects.flow.statistics.collectors.OpenEventCollector;
import org.apache.synapse.aspects.flow.statistics.collectors.RuntimeStatisticCollector;
import org.apache.synapse.aspects.flow.statistics.data.artifact.ArtifactHolder;
import org.apache.synapse.transport.customlogsetter.CustomLogSetter;
import org.apache.synapse.aspects.ComponentType;
import org.apache.synapse.continuation.ContinuationStackManager;
import org.apache.synapse.continuation.SeqContinuationState;
import org.apache.synapse.core.SynapseEnvironment;
import org.apache.synapse.mediators.AbstractListMediator;
import org.apache.synapse.mediators.FlowContinuableMediator;
import org.apache.synapse.mediators.MediatorFaultHandler;
import org.apache.synapse.mediators.Value;
import java.util.Stack;
/**
* The Sequence mediator either refers to a named Sequence mediator instance
* or is a *Named* list/sequence of other (child) Mediators
* <p/>
* If this instance defines a sequence mediator, then the name is required, and
* an errorHandler sequence name optional. If this instance refers to another (defined)
* sequence mediator, the errorHandler will not have a meaning, and if an error in
* encountered in the referred sequence, its errorHandler would execute.
*/
public class SequenceMediator extends AbstractListMediator implements Nameable,
FlowContinuableMediator {
/** The name of the this sequence */
private String name = null;
/** The local registry key which is used to pick a sequence definition*/
private Value key = null;
/** The name of the error handler which is used to handle error during the mediation */
private String errorHandler = null;
/** is this definition dynamic */
private boolean dynamic = false;
/** flag to ensure that each and every sequence is initialized and destroyed atmost once */
private boolean initialized = false;
/** the registry key to load this definition if dynamic */
private String registryKey = null;
/** The name of the file where this sequence is defined */
private String fileName;
/** type of the sequence*/
private SequenceType sequenceType = SequenceType.NAMED;
/** Reference to the synapse environment */
private SynapseEnvironment synapseEnv;
/** Name of the car file which the sequence deployed from */
private String artifactContainerName;
/**
* Whether the sequence is edited through the management console or not
*/
private boolean isEdited;
/**
* If this mediator refers to another named Sequence, execute that. Else
* execute the list of mediators (children) contained within this. If a referenced
* named sequence mediator instance cannot be found at runtime, an exception is
* thrown. This may occur due to invalid configuration of an erroneous runtime
* change of the synapse configuration. It is the responsibility of the
* SynapseConfiguration builder to ensure that dead references are not present.
*
* @param synCtx the synapse message
* @return as per standard mediator result
*/
public boolean mediate(MessageContext synCtx) {
if (synCtx.getEnvironment().isDebuggerEnabled()) {
if (super.divertMediationRoute(synCtx)) {
return true;
}
}
SynapseLog synLog = getLog(synCtx);
if (sequenceType == SequenceType.NAMED) {
CustomLogSetter.getInstance().setLogAppender(artifactContainerName);
}
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Start : Sequence "
+ (name == null ? (key == null ? "<anonymous" : "key=<" + key) : "<"
+ name) + ">");
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + synCtx.getEnvelope());
}
}
if (key == null) {
// The onError sequence for handling errors which may occur during the
// mediation through this sequence
Mediator errorHandlerMediator = null;
Integer statisticReportingIndex = null;
if (RuntimeStatisticCollector.isStatisticsEnabled()) {
statisticReportingIndex = reportOpenStatistics(synCtx, false);
}
// Setting Required property to reportForComponent the sequence aspects
try {
// push the errorHandler sequence into the current message as the fault handler
if (errorHandler != null) {
errorHandlerMediator = synCtx.getSequence(errorHandler);
if (errorHandlerMediator != null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Setting the onError handler : " +
errorHandler + " for the sequence : " + name);
}
synCtx.pushFaultHandler(
new MediatorFaultHandler(errorHandlerMediator));
} else {
synLog.auditWarn("onError handler : " + errorHandler + " for sequence : " +
name + " cannot be found");
}
}
// Add a new SeqContinuationState as we branched to new Sequence.
boolean skipAddition = ContinuationStackManager.isSkipSeqContinuationStateAddition(synCtx);
if (!skipAddition) {
if (dynamic && registryKey != null) {
ContinuationStackManager.addSeqContinuationState(synCtx, registryKey, sequenceType);
} else {
ContinuationStackManager.addSeqContinuationState(synCtx, name, sequenceType);
}
}
boolean result = super.mediate(synCtx);
if (result && !skipAddition) {
// if flow completed remove the previously added SeqContinuationState
ContinuationStackManager.removeSeqContinuationState(synCtx, sequenceType);
}
// if we pushed an error handler, pop it from the fault stack
// before we exit normally without an exception
if (errorHandlerMediator != null) {
Stack faultStack = synCtx.getFaultStack();
if (faultStack != null && !faultStack.isEmpty()) {
Object o = faultStack.peek();
if (o instanceof MediatorFaultHandler &&
errorHandlerMediator.equals(
((MediatorFaultHandler) o).getFaultMediator())) {
faultStack.pop();
}
}
}
if (synLog.isTraceOrDebugEnabled()) {
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + synCtx.getEnvelope());
}
synLog.traceOrDebug(
"End : Sequence <" + (name == null ? "anonymous" : name) + ">");
}
return result;
} finally {
// End Statistics
if (RuntimeStatisticCollector.isStatisticsEnabled()) {
reportCloseStatistics(synCtx, statisticReportingIndex);
}
}
} else {
String sequenceKey = key.evaluateValue(synCtx);
//Mediator m = synCtx.getSequence(key);
Mediator m = synCtx.getSequence(sequenceKey);
if (m == null) {
handleException("Sequence named " + key + " cannot be found", synCtx);
} else {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Executing with key " + key);
}
// Update the SeqContinuationState position with the mediator position of
// sequence mediator in the sequence
ContinuationStackManager.updateSeqContinuationState(synCtx, getMediatorPosition());
boolean result = m.mediate(synCtx);
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("End : Sequence key=<" + key + ">");
}
return result;
}
}
return false;
}
public boolean mediate(MessageContext synCtx, ContinuationState continuationState) {
SynapseLog synLog = getLog(synCtx);
if (sequenceType == SequenceType.NAMED) {
CustomLogSetter.getInstance().setLogAppender(artifactContainerName);
}
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Mediating using the SeqContinuationState type : " +
((SeqContinuationState) continuationState).getSeqType() +
" name : " +
((SeqContinuationState) continuationState).getSeqName());
}
Mediator errorHandlerMediator = null;
// push the errorHandler sequence into the current message as the fault handler
if (errorHandler != null) {
errorHandlerMediator = synCtx.getSequence(errorHandler);
if (errorHandlerMediator != null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Setting the onError handler : " +
errorHandler + " for the sequence : " + name);
}
synCtx.pushFaultHandler(
new MediatorFaultHandler(errorHandlerMediator));
} else {
synLog.auditWarn("onError handler : " + errorHandler + " for sequence : " +
name + " cannot be found");
}
}
boolean result;
if (!continuationState.hasChild()) {
result = super.mediate(synCtx, continuationState.getPosition() + 1);
} else {
// if children exists first mediate from them starting from grandchild.
do {
FlowContinuableMediator mediator =
(FlowContinuableMediator) getChild(continuationState.getPosition());
result = mediator.mediate(synCtx,
continuationState.getChildContState());
if (RuntimeStatisticCollector.isStatisticsEnabled()) {
((Mediator) mediator).reportCloseStatistics(synCtx, null);
}
if (result) {
// if flow completed remove leaf child
continuationState.removeLeafChild();
}
} while (result && continuationState.hasChild());
if (result) {
// after mediating from children, mediate from current SeqContinuationState
result = super.mediate(synCtx, continuationState.getPosition() + 1);
}
}
if (result) {
// if flow completed, remove top ContinuationState from stack
ContinuationStackManager.popContinuationStateStack(synCtx);
}
// if we pushed an error handler, pop it from the fault stack
// before we exit normally without an exception
if (errorHandlerMediator != null) {
Stack faultStack = synCtx.getFaultStack();
if (faultStack != null && !faultStack.isEmpty()) {
Object o = faultStack.peek();
if (o instanceof MediatorFaultHandler &&
errorHandlerMediator.equals(
((MediatorFaultHandler) o).getFaultMediator())) {
faultStack.pop();
}
}
}
return result;
}
/**
* This method will ensure that each and every sequence wil only be initialized at most once
* @param se - environment to be initialized
*/
@Override
public synchronized void init(SynapseEnvironment se) {
if (!initialized) {
synapseEnv = se;
super.init(se);
initialized = true;
if (!isDynamic()) {
// mark as available, if this is marked previously as unavailable in the environment
se.clearUnavailabilityOfArtifact(name);
}
if (key != null) {
if (key.getKeyValue() != null) {
SequenceMediator sequenceMediator =
(SequenceMediator) se.getSynapseConfiguration().
getSequence(key.getKeyValue());
if (sequenceMediator == null || sequenceMediator.isDynamic()) {
// undefined or dynamic sequences are treated as unavailable
// in the environment.
// At the time of their initialization, these will be marked as available.
se.addUnavailableArtifactRef(key.getKeyValue());
}
} else {
// sequences referred by key-expressions are treated as unavailable at initialization
se.addUnavailableArtifactRef(key.getExpression().toString());
}
}
}
}
@Override
public synchronized void destroy() {
if (initialized) {
super.destroy();
initialized = false;
if (key != null) {
// Clearing unavailable sequence references added by this sequence
if (key.getKeyValue() != null) {
SequenceMediator sequenceMediator =
(SequenceMediator) synapseEnv.getSynapseConfiguration().
getSequence(key.getKeyValue());
if (sequenceMediator == null || sequenceMediator.isDynamic()) {
synapseEnv.removeUnavailableArtifactRef(key.getKeyValue());
}
} else {
synapseEnv.removeUnavailableArtifactRef(key.getExpression().toString());
}
}
}
}
/**
* To get the name of the sequence
* @return the name of the sequence
*/
public String getName() {
return name;
}
/**
* setting the name of the sequence
* @param name the name of the this sequence
*/
public void setName(String name) {
this.name = name;
}
/**
* To get the key which is used to pick the sequence definition from the local registry
* @return return the key which is used to pick the sequence definition from the local registry
*/
public Value getKey() {
return key;
}
/**
* To set the local registry key in order to pick the sequence definition
* @param key the local registry key
*/
public void setKey(Value key) {
this.key = key;
}
/**
*
* @return Returns the errorhandler sequence name
*/
public String getErrorHandler() {
return errorHandler;
}
/**
* @param errorHandler to used handle error will appear during the
* mediation through this sequence
*/
public void setErrorHandler(String errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Is this a dynamic sequence?
* @return true if dynamic
*/
public boolean isDynamic() {
return dynamic;
}
/**
* Mark this as a dynamic sequence
* @param dynamic true if this is a dynamic sequence
*/
public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
/**
* Return the registry key used to load this sequence dynamically
* @return registry key
*/
public String getRegistryKey() {
return registryKey;
}
/**
* To get the registry key used to load this sequence dynamically
* @param registryKey returns the registry key which point to this sequence
*/
public void setRegistryKey(String registryKey) {
this.registryKey = registryKey;
}
public String getAuditId() {
return getName();
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public boolean isInitialized() {
return initialized;
}
public void setArtifactContainerName (String name) {
artifactContainerName = name;
}
public String getArtifactContainerName () {
return artifactContainerName;
}
public boolean isEdited() {
return isEdited;
}
public void setIsEdited(boolean isEdited) {
this.isEdited = isEdited;
}
@Override
public boolean isContentAware() {
return false;
}
public void setSequenceType(SequenceType sequenceType) {
this.sequenceType = sequenceType;
}
public String getSequenceNameForStatistics() {
if (this.name != null) {
return this.name;
} else {
if (this.sequenceType != SequenceType.ANON) {
return this.sequenceType.toString();
} else {
return SynapseConstants.ANONYMOUS_SEQUENCE;
}
}
}
@Override
public Integer reportOpenStatistics(MessageContext messageContext, boolean isContentAltering) {
if (key == null) {
return OpenEventCollector.reportEntryEvent(messageContext, getSequenceNameForStatistics(),
getAspectConfiguration(), ComponentType.SEQUENCE);
}
return null;
}
@Override
public void reportCloseStatistics(MessageContext messageContext, Integer currentIndex) {
if (key == null) {
CloseEventCollector
.tryEndFlow(messageContext, getSequenceNameForStatistics(), ComponentType.SEQUENCE,
currentIndex, isContentAltering());
}
}
@Override
public void setComponentStatisticsId(ArtifactHolder holder) {
String sequenceId = null;
// if (sequenceType != SequenceType.ANON) {
if (getAspectConfiguration() == null) {
configure(new AspectConfiguration(name));
}
if (getKey()!= null) {
sequenceId = StatisticIdentityGenerator
.getIdReferencingComponent(getKey().getKeyValue(), ComponentType.SEQUENCE, holder);
} else {
sequenceId = StatisticIdentityGenerator
.getIdForFlowContinuableMediator(getSequenceNameForStatistics(), ComponentType.SEQUENCE, holder);
}
getAspectConfiguration().setUniqueId(sequenceId);
// }
setStatisticIdForMediators(holder);
if (sequenceType != SequenceType.ANON) {
StatisticIdentityGenerator.reportingFlowContinuableEndEvent(sequenceId, ComponentType.SEQUENCE, holder);
}
}
}