/*
* 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.endpoints;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.AxisFault;
import org.apache.axis2.databinding.types.soapencoding.Integer;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.aspects.ComponentType;
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.core.axis2.Axis2MessageContext;
import org.apache.synapse.transport.passthru.PassThroughConstants;
import org.apache.synapse.transport.passthru.Pipe;
import java.util.HashMap;
import java.util.Map;
/**
* FailoverEndpoint can have multiple child endpoints. It will always try to send messages to
* current endpoint. If the current endpoint is failing, it gets another active endpoint from the
* list and make it the current endpoint. Then the message is sent to the current endpoint and if
* it fails, above procedure repeats until there are no active endpoints. If all endpoints are
* failing and parent endpoint is available, this will delegate the problem to the parent endpoint.
* If parent endpoint is not available it will pop the next FaultHandler and delegate the problem
* to that.
*/
public class FailoverEndpoint extends AbstractEndpoint {
/** Endpoint for which is currently used */
private Endpoint currentEndpoint = null;
/** The fail-over mode supported by this endpoint. By default we do dynamic fail-over */
private boolean dynamic = true;
public void send(MessageContext synCtx) {
if (RuntimeStatisticCollector.isStatisticsEnabled()) {
java.lang.Integer currentIndex = null;
boolean retry = (synCtx.getProperty(SynapseConstants.LAST_ENDPOINT) != null);
if ((getDefinition() != null) && !retry) {
currentIndex = OpenEventCollector.reportChildEntryEvent(synCtx, getReportingName(),
ComponentType.ENDPOINT, getDefinition().getAspectConfiguration(), true);
}
try {
sendMessage(synCtx);
} finally {
if (currentIndex != null) {
CloseEventCollector.closeEntryEvent(synCtx, getReportingName(),
ComponentType.MEDIATOR, currentIndex, false);
}
}
} else {
sendMessage(synCtx);
}
}
private void sendMessage(MessageContext synCtx) {
logSetter();
if (log.isDebugEnabled()) {
log.debug("Failover Endpoint : " + getName());
}
if (getContext().isState(EndpointContext.ST_OFF)) {
informFailure(synCtx, SynapseConstants.ENDPOINT_FO_NONE_READY,
"Failover endpoint : " + getName() != null ? getName() : SynapseConstants.ANONYMOUS_ENDPOINT + " - is inactive");
return;
}
boolean isARetry = false;
Map<String,Integer>mEndpointLog = null;
if (synCtx.getProperty(SynapseConstants.LAST_ENDPOINT) == null) {
if (log.isDebugEnabled()) {
log.debug(this + " Building the SoapEnvelope");
}
// If not yet a retry, we have to build the envelope since we need to support failover
synCtx.getEnvelope().build();
//If the endpoint failed during the sending, we need to keep the original envelope and reuse that for other endpoints
if (Boolean.TRUE.equals(((Axis2MessageContext) synCtx).getAxis2MessageContext().getProperty(
PassThroughConstants.MESSAGE_BUILDER_INVOKED))) {
synCtx.setProperty(SynapseConstants.LB_FO_ENDPOINT_ORIGINAL_MESSAGE, synCtx.getEnvelope());
}
mEndpointLog = new HashMap<String,Integer>();
synCtx.setProperty(SynapseConstants.ENDPOINT_LOG, mEndpointLog);
} else {
isARetry = true;
mEndpointLog = (Map<String,Integer>)synCtx.getProperty(SynapseConstants.ENDPOINT_LOG);
}
if (getChildren().isEmpty()) {
informFailure(synCtx, SynapseConstants.ENDPOINT_FO_NONE_READY,
"FailoverLoadbalance endpoint : " + getName() + " - no child endpoints");
return;
}
// evaluate the endpoint properties
evaluateProperties(synCtx);
if (dynamic) {
// Dynamic fail-over mode - Switch to a backup endpoint when an error occurs
// in the primary endpoint. But switch back to the primary as soon as it becomes
// active again.
boolean foundEndpoint = false;
for (Endpoint endpoint : getChildren()) {
if (endpoint.readyToSend()) {
foundEndpoint = true;
if (isARetry && metricsMBean != null) {
metricsMBean.reportSendingFault(SynapseConstants.ENDPOINT_FO_FAIL_OVER);
}
synCtx.pushFaultHandler(this);
if(endpoint instanceof AbstractEndpoint){
org.apache.axis2.context.MessageContext axisMC = ((Axis2MessageContext) synCtx).getAxis2MessageContext();
Pipe pipe = (Pipe) axisMC.getProperty(PassThroughConstants.PASS_THROUGH_PIPE);
if(pipe != null){
pipe.forceSetSerializationRest();
}
//allow the message to be content aware if the given message comes via PT
if (axisMC.getProperty(PassThroughConstants.PASS_THROUGH_PIPE) != null) {
((AbstractEndpoint) endpoint).setContentAware(true);
((AbstractEndpoint) endpoint).setForceBuildMC(true);
if(endpoint instanceof TemplateEndpoint && ((TemplateEndpoint)endpoint).getRealEndpoint() != null){
if(((TemplateEndpoint)endpoint).getRealEndpoint() instanceof AbstractEndpoint){
((AbstractEndpoint)((TemplateEndpoint)endpoint).getRealEndpoint()).setContentAware(true);
((AbstractEndpoint)((TemplateEndpoint)endpoint).getRealEndpoint()).setForceBuildMC(true);
}
}
}
}
if(endpoint.getName() != null){
mEndpointLog.put(endpoint.getName(), null);
}
endpoint.send(synCtx);
break;
}
}
if (!foundEndpoint) {
String msg = "Failover endpoint : " +
(getName() != null ? getName() : SynapseConstants.ANONYMOUS_ENDPOINT) +
" - no ready child endpoints";
log.warn(msg);
informFailure(synCtx, SynapseConstants.ENDPOINT_FO_NONE_READY, msg);
}
} else {
// Static fail-over mode - Switch to a backup endpoint when an error occurs
// in the primary endpoint. Keep sending messages to the backup endpoint until
// an error occurs in that endpoint.
if (currentEndpoint == null) {
currentEndpoint = getChildren().get(0);
}
if (currentEndpoint.readyToSend()) {
if (isARetry && metricsMBean != null) {
metricsMBean.reportSendingFault(SynapseConstants.ENDPOINT_FO_FAIL_OVER);
}
synCtx.pushFaultHandler(this);
currentEndpoint.send(synCtx);
} else {
boolean foundEndpoint = false;
for (Endpoint endpoint : getChildren()) {
if (endpoint.readyToSend()) {
foundEndpoint = true;
currentEndpoint = endpoint;
if (isARetry && metricsMBean != null) {
metricsMBean.reportSendingFault(SynapseConstants.ENDPOINT_FO_FAIL_OVER);
}
synCtx.pushFaultHandler(this);
currentEndpoint.send(synCtx);
break;
}
}
if (!foundEndpoint) {
String msg = "Failover endpoint : " +
(getName() != null ? getName() : SynapseConstants.ANONYMOUS_ENDPOINT) +
" - no ready child endpoints";
log.warn(msg);
informFailure(synCtx, SynapseConstants.ENDPOINT_FO_NONE_READY, msg);
}
}
}
}
public void onChildEndpointFail(Endpoint endpoint, MessageContext synMessageContext) {
//If there is a failure in child endpoint, restore the original message envelope from the message context
if (synMessageContext.getProperty(SynapseConstants.LB_FO_ENDPOINT_ORIGINAL_MESSAGE) != null) {
try {
synMessageContext.setEnvelope(
(SOAPEnvelope) synMessageContext.getProperty(SynapseConstants.LB_FO_ENDPOINT_ORIGINAL_MESSAGE));
} catch (AxisFault ex) {
log.error("Couldn't restore the original message to the failover endpoint", ex);
}
}
logOnChildEndpointFail(endpoint, synMessageContext);
if (((AbstractEndpoint)endpoint).isRetry(synMessageContext)) {
if (log.isDebugEnabled()) {
log.debug(this + " Retry Attempt for Request with [Message ID : " +
synMessageContext.getMessageID() + "], [To : " +
synMessageContext.getTo() + "]");
}
send(synMessageContext);
} else {
String msg = "Failover endpoint : " +
(getName() != null ? getName() : SynapseConstants.ANONYMOUS_ENDPOINT) +
" - one of the child endpoints encounterd a non-retry error, " +
"not sending message to another endpoint";
log.warn(msg);
informFailure(synMessageContext, SynapseConstants.ENDPOINT_FO_NONE_READY, msg);
}
}
public boolean readyToSend() {
if (getContext().isState(EndpointContext.ST_OFF)) {
return false;
}
for (Endpoint endpoint : getChildren()) {
if (endpoint.readyToSend()) {
currentEndpoint = endpoint;
return true;
}
}
return false;
}
public boolean isDynamic() {
return dynamic;
}
public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
}