/*
* 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.continuation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.ContinuationState;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SequenceType;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.SynapseException;
import org.apache.synapse.core.axis2.ProxyService;
import org.apache.synapse.mediators.MediatorFaultHandler;
import org.apache.synapse.mediators.base.SequenceMediator;
import org.apache.synapse.rest.API;
import org.apache.synapse.rest.RESTConstants;
import org.apache.synapse.rest.Resource;
import java.util.Set;
import java.util.Stack;
/**
* This is the utility class which manages ContinuationState Stack.
* <p/>
* All operations for the stack done by mediators are done through this manager class in order to
* easily control the operations from a central place.
*
*/
public class ContinuationStackManager {
private static Log log = LogFactory.getLog(ContinuationStackManager.class);
public static final String SKIP_CONTINUATION_STATE = "SKIP_CONTINUATION_STATE";
/**
* Add new SeqContinuationState to the stack.
* This should be done when branching to a new Sequence
*
* @param synCtx Message Context
* @param seqName Name of the branching sequence
* @param seqType Sequence Type
*/
public static void addSeqContinuationState(MessageContext synCtx, String seqName,
SequenceType seqType) {
if (synCtx.isContinuationEnabled() && !SequenceType.ANON.equals(seqType)) {
//ignore Anonymous type sequences
synCtx.pushContinuationState(new SeqContinuationState(seqType, seqName));
}
}
/**
* Check whether sequence continuation state addition need to be skipped
*
* @param synCtx message context
* @return whether sequence continuation state addition need to be skipped
*/
public static boolean isSkipSeqContinuationStateAddition(MessageContext synCtx) {
Boolean isSkipContinuationState = (Boolean) synCtx.getProperty(SKIP_CONTINUATION_STATE);
if (isSkipContinuationState != null && isSkipContinuationState) {
Set keySet = synCtx.getPropertyKeySet();
if (keySet != null) {
keySet.remove(SKIP_CONTINUATION_STATE);
}
return true;
}
return false;
}
/**
* Remove top SeqContinuationState from the stack.
* This should be done when returning from a Sequence branch.
*
* @param synCtx Message Context
*/
public static void removeSeqContinuationState(MessageContext synCtx, SequenceType seqType) {
if (synCtx.isContinuationEnabled() && !synCtx.getContinuationStateStack().isEmpty()) {
if (!SequenceType.ANON.equals(seqType)) {
ContinuationStackManager.popContinuationStateStack(synCtx);
} else {
removeReliantContinuationState(synCtx);
}
}
}
/**
* Update SeqContinuationState with the current mediator position in the sequence.
* SeqContinuationState should be updated when branching to a new flow
* using a FlowContinuableMediator
*
* @param synCtx Message Context
*/
public static void updateSeqContinuationState(MessageContext synCtx, int position) {
if (synCtx.isContinuationEnabled()) {
ContinuationState seqContState = ContinuationStackManager.peakContinuationStateStack(synCtx);
if (seqContState != null) {
seqContState.getLeafChild().setPosition(position);
} else {
// Ideally we should not get here.
log.warn("Continuation Stack is empty. Probably due to a configuration issue");
}
}
}
/**
* Add a ReliantContinuationState to the top SeqContinuationState in the stack.
* This should be done when branching to a sub branch using FlowContinuableMediators
* except Sequence Mediator
*
* @param synCtx Message Context
* @param subBranch Sub branch id
*/
public static void addReliantContinuationState(MessageContext synCtx, int subBranch,
int position) {
if (synCtx.isContinuationEnabled()) {
ContinuationState seqContState = ContinuationStackManager.peakContinuationStateStack(synCtx);
if (seqContState != null) {
seqContState.getLeafChild().setPosition(position);
seqContState.addLeafChild(new ReliantContinuationState(subBranch));
} else {
// Ideally we should not get here.
log.warn("Continuation Stack is empty. Probably due to a configuration issue");
}
}
}
/**
* Remove a ReliantContinuationState from the top SeqContinuationState in the stack.
* This should be done when returning back from a sub branch of a FlowContinuableMediator.
*
* @param synCtx MessageContext
*/
public static void removeReliantContinuationState(MessageContext synCtx) {
if (synCtx.isContinuationEnabled()) {
ContinuationState seqContState = ContinuationStackManager.peakContinuationStateStack(synCtx);
if (seqContState != null) {
seqContState.removeLeafChild();
} else {
// Ideally we should not get here.
log.warn("Continuation Stack is empty. Probably due to a configuration issue");
}
}
}
/**
* Get a clone of a SeqContinuationState
*
* @param oriSeqContinuationState original SeqContinuationState
* @return cloned SeqContinuationState
*/
public static SeqContinuationState getClonedSeqContinuationState(
SeqContinuationState oriSeqContinuationState) {
SeqContinuationState clone =
new SeqContinuationState(oriSeqContinuationState.getSeqType(),
oriSeqContinuationState.getSeqName());
clone.setPosition(oriSeqContinuationState.getPosition());
if (oriSeqContinuationState.hasChild()) {
clone.setChildContState(getClonedReliantContState(
oriSeqContinuationState.getChildContState()));
}
return clone;
}
/*
* Get a clone of the ReliantContinuationState
*/
private static ReliantContinuationState getClonedReliantContState(
org.apache.synapse.ContinuationState continuationState) {
ReliantContinuationState oriConstState =
(ReliantContinuationState) continuationState;
ReliantContinuationState clone =
new ReliantContinuationState(oriConstState.getSubBranch());
clone.setPosition(oriConstState.getPosition());
if (oriConstState.hasChild()) {
clone.setChildContState(
getClonedReliantContState(oriConstState.getChildContState()));
}
return clone;
}
/**
* Remove all ContinuationStates from ContinuationState Stack
* @param synCtx MessageContext
*/
public static void clearStack(MessageContext synCtx) {
Stack<ContinuationState> continuationStack = synCtx.getContinuationStateStack();
if (synCtx.isContinuationEnabled()) {
synchronized (continuationStack){
continuationStack.clear();
}
}
}
/**
* Peek from Continuation Stack
* @return ContinuationState
*/
public static ContinuationState peakContinuationStateStack(MessageContext synCtx){
Stack<ContinuationState> continuationStack = synCtx.getContinuationStateStack();
synchronized (continuationStack) {
if (!continuationStack.isEmpty()) {
return continuationStack.peek();
} else {
return null;
}
}
}
/**
* Pop from Continuation Stack
*/
public static void popContinuationStateStack(MessageContext synCtx){
Stack<ContinuationState> continuationStack = synCtx.getContinuationStateStack();
synchronized (continuationStack) {
if (!continuationStack.isEmpty()) {
continuationStack.pop();
}
}
}
/**
* Retrieve the sequence from Continuation state which message should be injected to.
*
* @param seqContState SeqContinuationState which contain the sequence information
* @param synCtx message context
* @return sequence which message should be injected to
*/
public static SequenceMediator retrieveSequence(MessageContext synCtx,
SeqContinuationState seqContState) {
SequenceMediator sequence = null;
switch (seqContState.getSeqType()) {
case NAMED: {
sequence = (SequenceMediator) synCtx.getSequence(seqContState.getSeqName());
if (sequence == null) {
// This can happen only if someone delete the sequence while running
handleException("Sequence : " + seqContState.getSeqName() + " not found");
}
break;
}
case PROXY_INSEQ: {
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
ProxyService proxyService = synCtx.getConfiguration().getProxyService(proxyName);
if (proxyService != null) {
sequence = proxyService.getTargetInLineInSequence();
} else {
handleException("Proxy Service :" + proxyName + " not found");
}
break;
}
case API_INSEQ: {
String apiName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API);
String resourceName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_RESOURCE);
API api = synCtx.getEnvironment().getSynapseConfiguration().getAPI(apiName);
if (api != null) {
Resource resource = api.getResource(resourceName);
if (resource != null) {
sequence = resource.getInSequence();
} else {
handleException("Resource : " + resourceName + " not found");
}
} else {
handleException("REST API : " + apiName + " not found");
}
break;
}
case PROXY_OUTSEQ: {
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
ProxyService proxyService = synCtx.getConfiguration().getProxyService(proxyName);
if (proxyService != null) {
sequence = proxyService.getTargetInLineOutSequence();
} else {
handleException("Proxy Service :" + proxyName + " not found");
}
break;
}
case API_OUTSEQ: {
String apiName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API);
String resourceName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_RESOURCE);
API api = synCtx.getEnvironment().getSynapseConfiguration().getAPI(apiName);
if (api != null) {
Resource resource = api.getResource(resourceName);
if (resource != null) {
sequence = resource.getOutSequence();
} else {
handleException("Resource : " + resourceName + " not found");
}
} else {
handleException("REST API : " + apiName + " not found");
}
break;
}
case PROXY_FAULTSEQ: {
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
ProxyService proxyService = synCtx.getConfiguration().getProxyService(proxyName);
if (proxyService != null) {
sequence = proxyService.getTargetInLineFaultSequence();
} else {
handleException("Proxy Service :" + proxyName + " not found");
}
break;
}
case API_FAULTSEQ: {
String apiName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API);
String resourceName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_RESOURCE);
API api = synCtx.getEnvironment().getSynapseConfiguration().getAPI(apiName);
if (api != null) {
Resource resource = api.getResource(resourceName);
if (resource != null) {
sequence = resource.getFaultSequence();
} else {
handleException("Resource : " + resourceName + " not found");
}
} else {
handleException("REST API : " + apiName + " not found");
}
break;
}
}
return sequence;
}
/**
* Push fault handler for the received continuation call response.
*
* @param seqContState SeqContinuationState which contain the sequence information
* @param synCtx message context
*/
public static void pushFaultHandler(MessageContext synCtx, SeqContinuationState seqContState) {
switch (seqContState.getSeqType()) {
case NAMED: {
pushRootFaultHandlerForSequence(synCtx);
break;
}
case PROXY_INSEQ: {
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
ProxyService proxyService = synCtx.getConfiguration().getProxyService(proxyName);
if (proxyService != null) {
proxyService.registerFaultHandler(synCtx);
} else {
handleException("Proxy Service :" + proxyName + " not found");
}
break;
}
case API_INSEQ: {
String apiName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API);
String resourceName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_RESOURCE);
API api = synCtx.getEnvironment().getSynapseConfiguration().getAPI(apiName);
if (api != null) {
Resource resource = api.getResource(resourceName);
if (resource != null) {
resource.registerFaultHandler(synCtx);
} else {
handleException("Resource : " + resourceName + " not found");
}
} else {
handleException("REST API : " + apiName + " not found");
}
break;
}
case PROXY_OUTSEQ: {
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
ProxyService proxyService = synCtx.getConfiguration().getProxyService(proxyName);
if (proxyService != null) {
proxyService.registerFaultHandler(synCtx);
} else {
handleException("Proxy Service :" + proxyName + " not found");
}
break;
}
case API_OUTSEQ: {
String apiName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API);
String resourceName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_RESOURCE);
API api = synCtx.getEnvironment().getSynapseConfiguration().getAPI(apiName);
if (api != null) {
Resource resource = api.getResource(resourceName);
if (resource != null) {
resource.registerFaultHandler(synCtx);
} else {
handleException("Resource : " + resourceName + " not found");
}
} else {
handleException("REST API : " + apiName + " not found");
}
break;
}
}
}
/**
* Find the correct root fault handler for named sequences.
*
* If the message is initiated from a proxy, we need to assign the proxy fault sequence.
* If the message is initiated from a API Resource, we need to assign the resource fault sequence.
*
* @param synCtx message context
*/
private static void pushRootFaultHandlerForSequence(MessageContext synCtx) {
// For Proxy services
String proxyName = (String) synCtx.getProperty(SynapseConstants.PROXY_SERVICE);
if (proxyName != null && !"".equals(proxyName)) {
ProxyService proxyService = synCtx.getConfiguration().getProxyService(proxyName);
if (proxyService != null) {
proxyService.registerFaultHandler(synCtx);
} else {
handleException("Proxy service : " + proxyName + " not found");
}
return;
}
// For APIs
String apiName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API);
if (apiName != null && !"".equals(apiName)) {
API api = synCtx.getEnvironment().getSynapseConfiguration().getAPI(apiName);
if (api != null) {
String resourceName = (String) synCtx.getProperty(RESTConstants.SYNAPSE_RESOURCE);
Resource resource = api.getResource(resourceName);
if (resource != null) {
resource.registerFaultHandler(synCtx);
} else {
handleException("Resource : " + resourceName + " not found");
}
} else {
handleException("REST API : " + apiName + " not found");
}
return;
}
//For main sequence/MessageInjector etc, push the default fault handler
synCtx.pushFaultHandler(new MediatorFaultHandler(synCtx.getFaultSequence()));
}
private static void handleException(String msg) {
log.error(msg);
throw new SynapseException(msg);
}
}