001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.synapse.endpoints;
021:
022: import org.apache.axis2.clustering.ClusterManager;
023: import org.apache.axis2.context.ConfigurationContext;
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.apache.synapse.FaultHandler;
027: import org.apache.synapse.MessageContext;
028: import org.apache.synapse.SynapseConstants;
029: import org.apache.synapse.core.axis2.Axis2MessageContext;
030:
031: import java.util.List;
032:
033: /**
034: * FailoverEndpoint can have multiple child endpoints. It will always try to send messages to current
035: * endpoint. If the current endpoint is failing, it gets another active endpoint from the list and
036: * make it the current endpoint. Then the message is sent to the current endpoint and if it fails, above
037: * procedure repeats until there are no active endpoints. If all endpoints are failing and parent
038: * endpoint is available, this will delegate the problem to the parent endpoint. If parent endpoint
039: * is not available it will pop the next FaultHandler and delegate the problem to that.
040: */
041: public class FailoverEndpoint implements Endpoint {
042:
043: private static final Log log = LogFactory
044: .getLog(FailoverEndpoint.class);
045:
046: /**
047: * Name of the endpoint. Used for named endpoints which can be referred using the key attribute
048: * of indirect endpoints.
049: */
050: private String name = null;
051: /**
052: * List of child endpoints. Failover sending is done among these. Any object implementing the
053: * Endpoint interface can be a child.
054: */
055: private List endpoints = null;
056:
057: /**
058: * Endpoint for which currently sending the SOAP traffic.
059: */
060: private Endpoint currentEndpoint = null;
061:
062: /**
063: * Parent endpoint of this endpoint if this used inside another endpoint. Possible parents are
064: * LoadbalanceEndpoint, SALoadbalanceEndpoint and FailoverEndpoint objects. But use of
065: * SALoadbalanceEndpoint as the parent is the logical scenario.
066: */
067: private Endpoint parentEndpoint = null;
068:
069: /**
070: * The endpoint context , place holder for keep any runtime states related to the endpoint
071: */
072: private final EndpointContext endpointContext = new EndpointContext();
073:
074: public void send(MessageContext synMessageContext) {
075:
076: if (log.isDebugEnabled()) {
077: log.debug("Start : Failover Endpoint");
078: }
079:
080: boolean isClusteringEnable = false;
081: // get Axis2 MessageContext and ConfigurationContext
082: org.apache.axis2.context.MessageContext axisMC = ((Axis2MessageContext) synMessageContext)
083: .getAxis2MessageContext();
084: ConfigurationContext cc = axisMC.getConfigurationContext();
085:
086: //The check for clustering environment
087:
088: ClusterManager clusterManager = cc.getAxisConfiguration()
089: .getClusterManager();
090: if (clusterManager != null
091: && clusterManager.getContextManager() != null) {
092: isClusteringEnable = true;
093: }
094:
095: String endPointName = this .getName();
096: if (endPointName == null) {
097:
098: if (log.isDebugEnabled() && isClusteringEnable) {
099: log
100: .warn("In a clustering environment , the endpoint name should be specified"
101: + "even for anonymous endpoints. Otherwise , the clustering would not be "
102: + "functioned correctly if there are more than one anonymous endpoints. ");
103: }
104: endPointName = SynapseConstants.ANONYMOUS_ENDPOINT;
105: }
106:
107: if (isClusteringEnable) {
108: // if this is a cluster environment , then set configuration context to endpoint context
109: if (endpointContext.getConfigurationContext() == null) {
110: endpointContext.setConfigurationContext(cc);
111: endpointContext.setContextID(endPointName);
112: }
113: }
114:
115: // We have to build the envelop if we are supporting failover.
116: // Failover should sent the original message multiple times if failures occur. So we have to
117: // access the envelop multiple times.
118: synMessageContext.getEnvelope().build();
119:
120: if (currentEndpoint.isActive(synMessageContext)) {
121: currentEndpoint.send(synMessageContext);
122: } else {
123:
124: Endpoint liveEndpoint = null;
125: boolean foundEndpoint = false;
126: for (int i = 0; i < endpoints.size(); i++) {
127: liveEndpoint = (Endpoint) endpoints.get(i);
128: if (liveEndpoint.isActive(synMessageContext)) {
129: foundEndpoint = true;
130: currentEndpoint = liveEndpoint;
131: currentEndpoint.send(synMessageContext);
132: break;
133: }
134: }
135:
136: if (!foundEndpoint) {
137: // there are no active child endpoints. so mark this endpoint as failed.
138: setActive(false, synMessageContext);
139:
140: if (parentEndpoint != null) {
141: parentEndpoint.onChildEndpointFail(this ,
142: synMessageContext);
143: } else {
144: Object o = synMessageContext.getFaultStack().pop();
145: if (o != null) {
146: ((FaultHandler) o)
147: .handleFault(synMessageContext);
148: }
149: }
150: }
151: }
152: }
153:
154: public String getName() {
155: return this .name;
156: }
157:
158: public void setName(String name) {
159: this .name = name.trim();
160: }
161:
162: /**
163: * If this endpoint is in inactive state, checks if all immediate child endpoints are still
164: * failed. If so returns false. If at least one child endpoint is in active state, sets this
165: * endpoint's state to active and returns true.
166: *
167: * @param synMessageContext MessageContext of the current message. This is not used here.
168: * @return true if active. false otherwise.
169: */
170: public boolean isActive(MessageContext synMessageContext) {
171: boolean active = endpointContext.isActive();
172: if (!active) {
173: for (int i = 0; i < endpoints.size(); i++) {
174: Endpoint endpoint = (Endpoint) endpoints.get(i);
175: if (endpoint.isActive(synMessageContext)) {
176: endpointContext.setActive(true);
177:
178: // don't break the loop though we found one active endpoint. calling isActive()
179: // on all child endpoints will update their active state. so this is a good
180: // time to do that.
181: }
182: }
183: }
184:
185: return active;
186: }
187:
188: public void setActive(boolean active,
189: MessageContext synMessageContext) {
190: // setting a volatile boolean value is thread safe.
191: this .endpointContext.setActive(active);
192: }
193:
194: public List getEndpoints() {
195: return endpoints;
196: }
197:
198: public void setEndpoints(List endpoints) {
199: this .endpoints = endpoints;
200: if (endpoints.size() > 0) {
201: currentEndpoint = (Endpoint) endpoints.get(0);
202: }
203: }
204:
205: public void onChildEndpointFail(Endpoint endpoint,
206: MessageContext synMessageContext) {
207: send(synMessageContext);
208: }
209:
210: public void setParentEndpoint(Endpoint parentEndpoint) {
211: this.parentEndpoint = parentEndpoint;
212: }
213: }
|