/*
* 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.Mediator;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.MessageContext;
import org.apache.synapse.core.SynapseEnvironment;
import org.apache.synapse.mediators.AbstractListMediator;
import org.apache.synapse.mediators.MediatorFaultHandler;
import org.apache.synapse.statistics.StatisticsUtils;
import org.apache.synapse.statistics.StatisticsStack;
import org.apache.synapse.statistics.impl.SequenceStatisticsStack;
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 {
/** The name of the this sequence */
private String name = null;
/** The local registry key which is used to pick a sequence definition*/
private String 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;
/** To decide to whether statistics should have collected or not */
private int statisticsState = SynapseConstants.STATISTICS_UNSET;
/**
* 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) {
boolean statsOn = SynapseConstants.STATISTICS_ON == statisticsState;
boolean traceOn = isTraceOn(synCtx);
boolean traceOrDebugOn = isTraceOrDebugOn(traceOn);
if (traceOrDebugOn) {
traceOrDebug(traceOn, "Start : Sequence "
+ (name == null ? (key == null ? "<anonymous" : "key=<" + key) : "<" + name) + ">");
if (traceOn && trace.isTraceEnabled()) {
trace.trace("Message : " + synCtx.getEnvelope());
}
}
if (key == null) {
// The onError sequence for handling errors which may occur during the
// mediation through this sequence
Mediator errorHandlerMediator = null;
// Setting Required property to collect the sequence statistics
if (statsOn) {
StatisticsStack sequenceStack = (StatisticsStack)
synCtx.getProperty(SynapseConstants.SEQUENCE_STATS);
if (sequenceStack == null) {
sequenceStack = new SequenceStatisticsStack();
synCtx.setProperty(SynapseConstants.SEQUENCE_STATS, sequenceStack);
}
String seqName = (name == null ? SynapseConstants.ANONYMOUS_SEQUENCE : name);
boolean isFault = synCtx.getEnvelope().getBody().hasFault();
sequenceStack.put(seqName, System.currentTimeMillis(),
!synCtx.isResponse(), statsOn, isFault);
}
try {
// push the errorHandler sequence into the current message as the fault handler
if (errorHandler != null) {
errorHandlerMediator = synCtx.getSequence(errorHandler);
if (errorHandlerMediator != null) {
if (traceOrDebugOn) {
traceOrDebug(traceOn, "Setting the onError handler : " +
errorHandler + " for the sequence : " + name);
}
synCtx.pushFaultHandler(
new MediatorFaultHandler(errorHandlerMediator));
} else {
auditWarn("onError handler : " + errorHandler + " for sequence : " +
name + " cannot be found", synCtx);
}
}
boolean result = super.mediate(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();
}
}
}
if (traceOrDebugOn) {
if (traceOn && trace.isTraceEnabled()) {
trace.trace("Message : " + synCtx.getEnvelope());
}
traceOrDebug(traceOn,
"End : Sequence <" + (name == null ? "anonymous" : name) + ">");
}
return result;
} finally {
//If this sequence is finished it's task normally
if (statsOn) {
StatisticsUtils.processSequenceStatistics(synCtx);
}
//If this sequence is a IN or OUT sequence of a proxy service
StatisticsUtils.processProxyServiceStatistics(synCtx);
}
} else {
Mediator m = synCtx.getSequence(key);
if (m == null) {
handleException("Sequence named " + key + " cannot be found", synCtx);
} else {
if (traceOrDebugOn) {
traceOrDebug(traceOn, "Executing with key " + key);
}
boolean result = m.mediate(synCtx);
if (traceOrDebugOn) {
traceOrDebug(traceOn, "End : Sequence key=<" + key + ">");
}
return result;
}
}
return false;
}
/**
* This method will ensure that each and every sequence wil only be initialized atmost once
* @param se - enviorenment to be initialized
*/
public synchronized void init(SynapseEnvironment se) {
if (!initialized) {
super.init(se);
initialized = true;
}
}
public synchronized void destroy() {
if (initialized) {
super.destroy();
initialized = false;
}
}
/**
* 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 String 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(String 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;
}
/**
* To check whether statistics should have collected or not
*
* @return Returns the int value that indicate statistics is enabled or not.
*/
public int getStatisticsState() {
return statisticsState;
}
/**
* To set the statistics enable variable value
*
* @param statisticsState To indicate statistics collecting state
*/
public void setStatisticsState(int statisticsState) {
this.statisticsState = statisticsState;
}
/**
* 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;
}
}