1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 package org.apache.synapse.endpoints; 21 22 import org.apache.axis2.clustering.ClusterManager; 23 import org.apache.axis2.context.ConfigurationContext; 24 import org.apache.commons.logging.Log; 25 import org.apache.commons.logging.LogFactory; 26 import org.apache.synapse.FaultHandler; 27 import org.apache.synapse.MessageContext; 28 import org.apache.synapse.SynapseConstants; 29 import org.apache.synapse.core.axis2.Axis2MessageContext; 30 31 import java.util.List; 32 33 /** 34 * FailoverEndpoint can have multiple child endpoints. It will always try to send messages to 35 * current endpoint. If the current endpoint is failing, it gets another active endpoint from the 36 * list and make it the current endpoint. Then the message is sent to the current endpoint and if 37 * it fails, above procedure repeats until there are no active endpoints. If all endpoints are 38 * failing and parent endpoint is available, this will delegate the problem to the parent endpoint. 39 * If parent endpoint is not available it will pop the next FaultHandler and delegate the problem 40 * to that. 41 */ 42 public class FailoverEndpoint implements Endpoint { 43 44 private static final Log log = LogFactory.getLog(FailoverEndpoint.class); 45 46 /** 47 * Name of the endpoint. Used for named endpoints which can be referred using the key attribute 48 * of indirect endpoints. 49 */ 50 private String name = null; 51 /** 52 * List of child endpoints. Failover sending is done among these. Any object implementing the 53 * Endpoint interface can be a child. 54 */ 55 private List<Endpoint> endpoints = null; 56 57 /** 58 * Endpoint for which currently sending the SOAP traffic. 59 */ 60 private Endpoint currentEndpoint = null; 61 62 /** 63 * Parent endpoint of this endpoint if this used inside another endpoint. Possible parents are 64 * LoadbalanceEndpoint, SALoadbalanceEndpoint and FailoverEndpoint objects. But use of 65 * SALoadbalanceEndpoint as the parent is the logical scenario. 66 */ 67 private Endpoint parentEndpoint = null; 68 69 /** 70 * The endpoint context , place holder for keep any runtime states related to the endpoint 71 */ 72 private final EndpointContext endpointContext = new EndpointContext(); 73 74 public void send(MessageContext synMessageContext) { 75 76 if (log.isDebugEnabled()) { 77 log.debug("Start : Failover Endpoint"); 78 } 79 80 boolean isClusteringEnable = false; 81 // get Axis2 MessageContext and ConfigurationContext 82 org.apache.axis2.context.MessageContext axisMC = 83 ((Axis2MessageContext) synMessageContext).getAxis2MessageContext(); 84 ConfigurationContext cc = axisMC.getConfigurationContext(); 85 86 //The check for clustering environment 87 88 ClusterManager clusterManager = cc.getAxisConfiguration().getClusterManager(); 89 if (clusterManager != null && 90 clusterManager.getContextManager() != null) { 91 isClusteringEnable = true; 92 } 93 94 String endPointName = this.getName(); 95 if (endPointName == null) { 96 97 if (log.isDebugEnabled() && isClusteringEnable) { 98 log.warn("In a clustering environment , the endpoint name should be specified" + 99 "even for anonymous endpoints. Otherwise , the clustering would not be " + 100 "functioned correctly if there are more than one anonymous endpoints. "); 101 } 102 endPointName = SynapseConstants.ANONYMOUS_ENDPOINT; 103 } 104 105 if (isClusteringEnable) { 106 // if this is a cluster environment , then set configuration context to endpoint context 107 if (endpointContext.getConfigurationContext() == null) { 108 endpointContext.setConfigurationContext(cc); 109 endpointContext.setContextID(endPointName); 110 } 111 } 112 113 // We have to build the envelop if we are supporting failover. 114 // Failover should sent the original message multiple times if failures occur. So we have to 115 // access the envelop multiple times. 116 synMessageContext.getEnvelope().build(); 117 118 if (currentEndpoint.isActive(synMessageContext)) { 119 currentEndpoint.send(synMessageContext); 120 } else { 121 122 boolean foundEndpoint = false; 123 for (Endpoint endpoint : endpoints) { 124 if (endpoint.isActive(synMessageContext)) { 125 foundEndpoint = true; 126 currentEndpoint = endpoint; 127 currentEndpoint.send(synMessageContext); 128 break; 129 } 130 } 131 132 if (!foundEndpoint) { 133 // there are no active child endpoints. so mark this endpoint as failed. 134 setActive(false, synMessageContext); 135 136 if (parentEndpoint != null) { 137 parentEndpoint.onChildEndpointFail(this, synMessageContext); 138 } else { 139 Object o = synMessageContext.getFaultStack().pop(); 140 if (o != null) { 141 ((FaultHandler) o).handleFault(synMessageContext); 142 } 143 } 144 } 145 } 146 } 147 148 public String getName() { 149 return this.name; 150 } 151 152 public void setName(String name) { 153 this.name = name.trim(); 154 } 155 156 /** 157 * If this endpoint is in inactive state, checks if all immediate child endpoints are still 158 * failed. If so returns false. If at least one child endpoint is in active state, sets this 159 * endpoint's state to active and returns true. 160 * 161 * @param synMessageContext MessageContext of the current message. This is not used here. 162 * @return true if active. false otherwise. 163 */ 164 public boolean isActive(MessageContext synMessageContext) { 165 boolean active = endpointContext.isActive(); 166 if (!active) { 167 for (Endpoint endpoint : endpoints) { 168 if (endpoint.isActive(synMessageContext)) { 169 active = true; 170 endpointContext.setActive(true); 171 172 // don't break the loop though we found one active endpoint. calling isActive() 173 // on all child endpoints will update their active state. so this is a good 174 // time to do that. 175 } 176 } 177 } 178 179 if (log.isDebugEnabled()) { 180 log.debug("Endpoint '" + name + "' is in state ' " + active + " '"); 181 } 182 183 return active; 184 } 185 186 public void setActive(boolean active, MessageContext synMessageContext) { 187 // setting a volatile boolean value is thread safe. 188 this.endpointContext.setActive(active); 189 } 190 191 public List<Endpoint> getEndpoints() { 192 return endpoints; 193 } 194 195 public void setEndpoints(List<Endpoint> endpoints) { 196 this.endpoints = endpoints; 197 if (endpoints.size() > 0) { 198 currentEndpoint = endpoints.get(0); 199 } 200 } 201 202 public void onChildEndpointFail(Endpoint endpoint, MessageContext synMessageContext) { 203 send(synMessageContext); 204 } 205 206 public void setParentEndpoint(Endpoint parentEndpoint) { 207 this.parentEndpoint = parentEndpoint; 208 } 209 }