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: */package org.apache.cxf.systest.clustering;
019:
020: import java.net.ConnectException;
021: import java.util.ArrayList;
022: import java.util.List;
023: import java.util.logging.Logger;
024:
025: import org.apache.cxf.Bus;
026: import org.apache.cxf.BusFactory;
027: import org.apache.cxf.bus.spring.SpringBusFactory;
028: import org.apache.cxf.clustering.FailoverTargetSelector;
029: import org.apache.cxf.clustering.RandomStrategy;
030: import org.apache.cxf.clustering.SequentialStrategy;
031: import org.apache.cxf.endpoint.ConduitSelector;
032: import org.apache.cxf.endpoint.Endpoint;
033: import org.apache.cxf.frontend.ClientProxy;
034: import org.apache.cxf.greeter_control.ClusteredGreeterService;
035: import org.apache.cxf.greeter_control.Control;
036: import org.apache.cxf.greeter_control.ControlService;
037: import org.apache.cxf.greeter_control.Greeter;
038: import org.apache.cxf.greeter_control.PingMeFault;
039: import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
040: import org.apache.cxf.ws.addressing.MAPAggregator;
041: import org.apache.cxf.ws.addressing.soap.MAPCodec;
042:
043: import org.junit.After;
044: import org.junit.Before;
045: import org.junit.BeforeClass;
046: import org.junit.Test;
047:
048: /**
049: * Tests failover within a static cluster.
050: */
051: public class FailoverTest extends AbstractBusClientServerTestBase {
052:
053: protected static final String REPLICA_A = "http://localhost:9051/SoapContext/ReplicatedPortA";
054: protected static final String REPLICA_B = "http://localhost:9052/SoapContext/ReplicatedPortB";
055: protected static final String REPLICA_C = "http://localhost:9053/SoapContext/ReplicatedPortC";
056: protected static final String REPLICA_D = "http://localhost:9054/SoapContext/ReplicatedPortD";
057: private static final Logger LOG = Logger
058: .getLogger(FailoverTest.class.getName());
059: private static final String FAILOVER_CONFIG = "org/apache/cxf/systest/clustering/failover.xml";
060:
061: private Bus bus;
062: private Control control;
063: private Greeter greeter;
064: private List<String> targets;
065: private MAPAggregator mapAggregator;
066: private MAPCodec mapCodec;
067:
068: @BeforeClass
069: public static void startServers() throws Exception {
070: assertTrue("server did not launch correctly",
071: launchServer(Server.class));
072: }
073:
074: @Before
075: public void setUp() {
076: targets = new ArrayList<String>();
077: SpringBusFactory bf = new SpringBusFactory();
078: bus = bf.createBus(FAILOVER_CONFIG);
079: BusFactory.setDefaultBus(bus);
080: }
081:
082: @After
083: public void tearDown() {
084: if (null != control) {
085: for (String address : targets) {
086: assertTrue("Failed to stop greeter", control
087: .stopGreeter(address));
088: }
089: }
090: targets = null;
091: if (bus != null) {
092: bus.shutdown(true);
093: }
094: }
095:
096: @Test
097: public void testNoFailoverAcrossBindings() throws Exception {
098: startTarget(REPLICA_D);
099: setupGreeter();
100:
101: try {
102: greeter.greetMe("fred");
103: fail("expected exception");
104: } catch (Exception e) {
105: verifyCurrentEndpoint(REPLICA_A);
106: }
107: }
108:
109: @Test
110: public void testRevertExceptionOnUnsucessfulFailover()
111: throws Exception {
112: startTarget(REPLICA_B);
113: startTarget(REPLICA_C);
114: setupGreeter();
115: stopTarget(REPLICA_C);
116: stopTarget(REPLICA_B);
117:
118: try {
119: greeter.greetMe("fred");
120: fail("expected exception");
121: } catch (Exception e) {
122: Throwable cause = e;
123: while (cause.getCause() != null) {
124: cause = cause.getCause();
125: }
126: // failover attempt bails after retried invocations on
127: // started & stopped replicas B & C fail with HTTP 404
128: // indicated by a thrown IOException("Not found"),
129: // in which case we should revert back to the original
130: // java.net.ConnectionException on the unavailable
131: // replica A
132: //
133: assertTrue(
134: "should revert to original exception when no failover",
135: cause instanceof ConnectException);
136:
137: // similarly the current endpoint referenced by the client
138: // should also revert back to the origianl replica A
139: //
140: verifyCurrentEndpoint(REPLICA_A);
141: }
142: }
143:
144: @Test
145: public void testInitialFailoverOnPrimaryReplicaUnavailable()
146: throws Exception {
147: startTarget(REPLICA_C);
148: setupGreeter();
149: String response = null;
150:
151: response = greeter.greetMe("fred");
152: assertNotNull("expected non-null response", response);
153: assertTrue("response from unexpected target: " + response,
154: response.endsWith(REPLICA_C));
155: verifyCurrentEndpoint(REPLICA_C);
156:
157: response = greeter.greetMe("joe");
158: assertNotNull("expected non-null response", response);
159: assertTrue("response from unexpected target: " + response,
160: response.endsWith(REPLICA_C));
161: }
162:
163: @Test
164: public void testNoFailoverOnApplicationFault() throws Exception {
165: startTarget(REPLICA_C);
166: setupGreeter();
167:
168: greeter.pingMe();
169: verifyCurrentEndpoint(REPLICA_C);
170:
171: startTarget(REPLICA_B);
172:
173: try {
174: greeter.pingMe();
175: } catch (PingMeFault pmf) {
176: verifyCurrentEndpoint(REPLICA_C);
177: }
178: }
179:
180: @Test
181: public void testFailoverOnCurrentReplicaDeath() throws Exception {
182: startTarget(REPLICA_C);
183: setupGreeter();
184: String response = null;
185:
186: response = greeter.greetMe("fred");
187: assertNotNull("expected non-null response", response);
188: assertTrue("response from unexpected target: " + response,
189: response.endsWith(REPLICA_C));
190: verifyCurrentEndpoint(REPLICA_C);
191:
192: startTarget(REPLICA_B);
193: stopTarget(REPLICA_C);
194:
195: response = greeter.greetMe("joe");
196: assertNotNull("expected non-null response", response);
197: assertTrue("response from unexpected target: " + response,
198: response.endsWith(REPLICA_B));
199: verifyCurrentEndpoint(REPLICA_B);
200: }
201:
202: @Test
203: public void testNoFailbackWhileCurrentReplicaLive()
204: throws Exception {
205: startTarget(REPLICA_C);
206: setupGreeter();
207: String response = null;
208:
209: response = greeter.greetMe("fred");
210: assertNotNull("expected non-null response", response);
211: assertTrue("response from unexpected target: " + response,
212: response.endsWith(REPLICA_C));
213: verifyCurrentEndpoint(REPLICA_C);
214:
215: startTarget(REPLICA_A);
216: response = greeter.greetMe("joe");
217: assertNotNull("expected non-null response", response);
218: assertTrue("response from unexpected target: " + response,
219: response.endsWith(REPLICA_C));
220: verifyCurrentEndpoint(REPLICA_C);
221:
222: startTarget(REPLICA_B);
223: response = greeter.greetMe("bob");
224: assertNotNull("expected non-null response", response);
225: assertTrue("response from unexpected target: " + response,
226: response.endsWith(REPLICA_C));
227: verifyCurrentEndpoint(REPLICA_C);
228:
229: stopTarget(REPLICA_B);
230: response = greeter.greetMe("john");
231: assertNotNull("expected non-null response", response);
232: assertTrue("response from unexpected target: " + response,
233: response.endsWith(REPLICA_C));
234: verifyCurrentEndpoint(REPLICA_C);
235:
236: stopTarget(REPLICA_A);
237: response = greeter.greetMe("mike");
238: assertNotNull("expected non-null response", response);
239: assertTrue("response from unexpected target: " + response,
240: response.endsWith(REPLICA_C));
241: verifyCurrentEndpoint(REPLICA_C);
242: }
243:
244: @Test
245: public void testEndpointSpecificInterceptorsDoNotPersistAcrossFailover()
246: throws Exception {
247: startTarget(REPLICA_A);
248: setupGreeter();
249: String response = null;
250:
251: enableWSAForCurrentEndpoint();
252:
253: response = greeter.greetMe("fred");
254: assertNotNull("expected non-null response", response);
255: assertTrue("response from unexpected target: " + response,
256: response.endsWith(REPLICA_A));
257: assertTrue("response expected to include WS-A messageID",
258: response.indexOf("message: urn:uuid") != -1);
259: verifyCurrentEndpoint(REPLICA_A);
260: assertTrue("expected WSA enabled for current endpoint",
261: isWSAEnabledForCurrentEndpoint());
262:
263: stopTarget(REPLICA_A);
264: startTarget(REPLICA_C);
265:
266: response = greeter.greetMe("mike");
267: assertNotNull("expected non-null response", response);
268: assertTrue("response from unexpected target: " + response,
269: response.endsWith(REPLICA_C));
270: assertTrue("response not expected to include WS-A messageID",
271: response.indexOf("message: urn:uuid") == -1);
272: verifyCurrentEndpoint(REPLICA_C);
273: assertFalse("unexpected WSA enabled for current endpoint",
274: isWSAEnabledForCurrentEndpoint());
275: }
276:
277: @Test
278: public void testDefaultSequentialStrategy() throws Exception {
279: strategyTest(REPLICA_B, REPLICA_C, REPLICA_A, false);
280: }
281:
282: @Test
283: public void testExplicitSequentialStrategy() throws Exception {
284: strategyTest(REPLICA_A, REPLICA_C, REPLICA_B, false);
285: }
286:
287: @Test
288: public void testRandomStrategy() throws Exception {
289: strategyTest(REPLICA_A, REPLICA_B, REPLICA_C, true);
290: }
291:
292: private void strategyTest(String activeReplica1,
293: String activeReplica2, String inactiveReplica,
294: boolean expectRandom) {
295: startTarget(activeReplica1);
296: startTarget(activeReplica2);
297: boolean randomized = false;
298: String prevEndpoint = null;
299: for (int i = 0; i < 20; i++) {
300: Greeter g = REPLICA_A.equals(inactiveReplica) ? new ClusteredGreeterService()
301: .getReplicatedPortA()
302: : REPLICA_B.equals(inactiveReplica) ? new ClusteredGreeterService()
303: .getReplicatedPortB()
304: : new ClusteredGreeterService()
305: .getReplicatedPortC();
306: verifyStrategy(g, expectRandom ? RandomStrategy.class
307: : SequentialStrategy.class);
308: String response = g.greetMe("fred");
309: assertNotNull("expected non-null response", response);
310: String currEndpoint = getCurrentEndpoint(g);
311: if (!(prevEndpoint == null || currEndpoint
312: .equals(prevEndpoint))) {
313: randomized = true;
314: }
315: prevEndpoint = currEndpoint;
316: }
317: stopTarget(activeReplica1);
318: stopTarget(activeReplica2);
319: assertEquals(
320: "unexpected random/sequential distribution of failovers",
321: expectRandom, randomized);
322: }
323:
324: private void startTarget(String address) {
325: ControlService cs = new ControlService();
326: control = cs.getControlPort();
327:
328: LOG.info("starting replicated target: " + address);
329: assertTrue("Failed to start greeter", control
330: .startGreeter(address));
331: targets.add(address);
332: }
333:
334: private void stopTarget(String address) {
335: if (control != null && targets.contains(address)) {
336: LOG.info("starting replicated target: " + address);
337: assertTrue("Failed to start greeter", control
338: .stopGreeter(address));
339: targets.remove(address);
340: }
341: }
342:
343: private void verifyCurrentEndpoint(String replica) {
344: assertEquals("unexpected current endpoint", replica,
345: getCurrentEndpoint(greeter));
346: }
347:
348: private String getCurrentEndpoint(Object proxy) {
349: return ClientProxy.getClient(proxy).getEndpoint()
350: .getEndpointInfo().getAddress();
351: }
352:
353: private void setupGreeter() {
354: ClusteredGreeterService cs = new ClusteredGreeterService();
355: // REVISIT: why doesn't the generic (i.e. non-Port-specific)
356: // Service.getPort() load the <jaxws:client> configuration?
357: greeter = cs.getReplicatedPortA();
358: assertTrue(
359: "unexpected conduit slector",
360: ClientProxy.getClient(greeter).getConduitSelector() instanceof FailoverTargetSelector);
361: }
362:
363: private void verifyStrategy(Object proxy, Class clz) {
364: ConduitSelector conduitSelector = ClientProxy.getClient(proxy)
365: .getConduitSelector();
366: if (conduitSelector instanceof FailoverTargetSelector) {
367: Object strategy = ((FailoverTargetSelector) conduitSelector)
368: .getStrategy();
369: assertTrue("unexpected strategy", clz.isInstance(strategy));
370: } else {
371: fail("unexpected conduit selector: " + conduitSelector);
372: }
373: }
374:
375: protected void enableWSAForCurrentEndpoint() {
376: Endpoint provider = ClientProxy.getClient(greeter)
377: .getEndpoint();
378: mapAggregator = new MAPAggregator();
379: mapCodec = new MAPCodec();
380: provider.getInInterceptors().add(mapAggregator);
381: provider.getInInterceptors().add(mapCodec);
382:
383: provider.getOutInterceptors().add(mapAggregator);
384: provider.getOutInterceptors().add(mapCodec);
385:
386: provider.getInFaultInterceptors().add(mapAggregator);
387: provider.getInFaultInterceptors().add(mapCodec);
388:
389: provider.getOutFaultInterceptors().add(mapAggregator);
390: provider.getOutFaultInterceptors().add(mapCodec);
391: }
392:
393: protected boolean isWSAEnabledForCurrentEndpoint() {
394: Endpoint provider = ClientProxy.getClient(greeter)
395: .getEndpoint();
396: boolean enabledIn = provider.getInInterceptors().contains(
397: mapAggregator)
398: && provider.getInInterceptors().contains(mapCodec)
399: && provider.getInFaultInterceptors().contains(
400: mapAggregator)
401: && provider.getInFaultInterceptors().contains(mapCodec);
402: boolean enabledOut = provider.getOutInterceptors().contains(
403: mapAggregator)
404: && provider.getOutInterceptors().contains(mapCodec)
405: && provider.getOutFaultInterceptors().contains(
406: mapAggregator)
407: && provider.getOutFaultInterceptors()
408: .contains(mapCodec);
409: return enabledIn && enabledOut;
410: }
411: }
|