1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.ksb.messaging;
17
18 import org.apache.commons.httpclient.ConnectTimeoutException;
19 import org.apache.commons.httpclient.ConnectionPoolTimeoutException;
20 import org.apache.commons.httpclient.NoHttpResponseException;
21 import org.apache.log4j.Logger;
22 import org.kuali.rice.core.api.util.ClassLoaderUtils;
23 import org.kuali.rice.core.api.util.ContextClassLoaderProxy;
24 import org.kuali.rice.core.api.util.reflect.BaseTargetedInvocationHandler;
25 import org.kuali.rice.ksb.api.KsbApiServiceLocator;
26 import org.kuali.rice.ksb.api.bus.Endpoint;
27 import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
28
29 import java.io.InterruptedIOException;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Proxy;
32 import java.net.ConnectException;
33 import java.net.NoRouteToHostException;
34 import java.net.UnknownHostException;
35 import java.util.ArrayList;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39
40
41
42 public class BusClientFailureProxy extends BaseTargetedInvocationHandler {
43
44 private static final Logger LOG = Logger.getLogger(BusClientFailureProxy.class);
45
46 private final Object failoverLock = new Object();
47
48 private ServiceConfiguration serviceConfiguration;
49
50
51 private static List<Class<?>> serviceRemovalExceptions = new ArrayList<Class<?>>();
52 private static List<Integer> serviceRemovalResponseCodes = new ArrayList<Integer>();
53
54 static {
55 serviceRemovalExceptions.add(NoHttpResponseException.class);
56 serviceRemovalExceptions.add(InterruptedIOException.class);
57 serviceRemovalExceptions.add(UnknownHostException.class);
58 serviceRemovalExceptions.add(NoRouteToHostException.class);
59 serviceRemovalExceptions.add(ConnectTimeoutException.class);
60 serviceRemovalExceptions.add(ConnectionPoolTimeoutException.class);
61 serviceRemovalExceptions.add(ConnectException.class);
62 }
63
64 static {
65 serviceRemovalResponseCodes.add(new Integer(404));
66 serviceRemovalResponseCodes.add(new Integer(503));
67 }
68
69 private BusClientFailureProxy(Object target, ServiceConfiguration serviceConfiguration) {
70 super(target);
71 this.serviceConfiguration = serviceConfiguration;
72 }
73
74 public static Object wrap(Object target, ServiceConfiguration serviceConfiguration) {
75 return Proxy.newProxyInstance(ClassLoaderUtils.getDefaultClassLoader(), ContextClassLoaderProxy.getInterfacesToProxy(target), new BusClientFailureProxy(target, serviceConfiguration));
76 }
77
78 protected Object invokeInternal(Object proxyObject, Method method, Object[] params) throws Throwable {
79 Set<ServiceConfiguration> servicesTried = null;
80
81 do {
82 try {
83 return method.invoke(getTarget(), params);
84 } catch (Throwable throwable) {
85 if (isServiceRemovalException(throwable)) {
86 synchronized (failoverLock) {
87 LOG.error("Exception caught accessing remote service " + this.serviceConfiguration.getServiceName(), throwable);
88 if (servicesTried == null) {
89 servicesTried = new HashSet<ServiceConfiguration>();
90 servicesTried.add(serviceConfiguration);
91 }
92 Object failoverService = null;
93 List<Endpoint> endpoints = KsbApiServiceLocator.getServiceBus().getEndpoints(serviceConfiguration.getServiceName());
94 for (Endpoint endpoint : endpoints) {
95 if (!servicesTried.contains(endpoint.getServiceConfiguration())) {
96 failoverService = endpoint.getService();
97 servicesTried.add(endpoint.getServiceConfiguration());
98 }
99 }
100 if (failoverService != null) {
101 LOG.info("Refetched replacement service for service " + this.serviceConfiguration.getServiceName());
102
103 setTarget(failoverService);
104 } else {
105 LOG.error("Didn't find replacement service throwing exception");
106 throw throwable;
107 }
108 }
109 } else {
110 throw throwable;
111 }
112 }
113 } while (true);
114 }
115
116 private static boolean isServiceRemovalException(Throwable throwable) {
117 LOG.info("Checking for Service Removal Exception: " + throwable.getClass().getName());
118 if (serviceRemovalExceptions.contains(throwable.getClass())) {
119 LOG.info("Found a Service Removal Exception: " + throwable.getClass().getName());
120 return true;
121 } else if (throwable instanceof HttpException) {
122 HttpException httpException = (HttpException)throwable;
123 if (serviceRemovalResponseCodes.contains(httpException.getResponseCode())) {
124 LOG.info("Found a Service Removal Exception because of a " + httpException.getResponseCode() + " " + throwable.getClass().getName());
125 return true;
126 }
127 }
128 if (throwable.getCause() != null) {
129 LOG.info("Unwrapping Throwable cause to check for service removal exception from: " + throwable.getClass().getName());
130 return isServiceRemovalException(throwable.getCause());
131 }
132 return false;
133 }
134
135 }