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