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