1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.ksb.messaging.serviceconnectors;
17
18 import java.io.IOException;
19 import java.net.SocketException;
20 import java.net.SocketTimeoutException;
21 import java.net.URL;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.Properties;
26
27 import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
28 import org.apache.commons.httpclient.HostConfiguration;
29 import org.apache.commons.httpclient.HttpClient;
30 import org.apache.commons.httpclient.HttpMethod;
31 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
32 import org.apache.commons.httpclient.cookie.CookiePolicy;
33 import org.apache.commons.httpclient.params.HttpClientParams;
34 import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
35 import org.apache.commons.httpclient.params.HttpConnectionParams;
36 import org.apache.commons.httpclient.params.HttpMethodParams;
37 import org.apache.commons.httpclient.params.HttpParams;
38 import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;
39 import org.apache.commons.lang.StringUtils;
40 import org.apache.log4j.Logger;
41 import org.kuali.rice.core.api.config.property.ConfigContext;
42 import org.kuali.rice.ksb.api.bus.support.JavaServiceConfiguration;
43 import org.kuali.rice.ksb.messaging.HttpClientHelper;
44 import org.kuali.rice.ksb.messaging.KSBHttpInvokerProxyFactoryBean;
45 import org.kuali.rice.ksb.messaging.KSBHttpInvokerRequestExecutor;
46 import org.kuali.rice.ksb.security.httpinvoker.AuthenticationCommonsHttpInvokerRequestExecutor;
47
48
49
50
51
52
53 public class HttpInvokerConnector extends AbstractServiceConnector {
54
55 private static final Logger LOG = Logger.getLogger(HttpInvokerConnector.class);
56
57 private HttpClientParams httpClientParams;
58
59 private boolean httpClientInitialized = false;
60
61 private static final String IDLE_CONNECTION_THREAD_INTERVAL_PROPERTY = "ksb.thinClient.idleConnectionThreadInterval";
62 private static final String IDLE_CONNECTION_TIMEOUT_PROPERTY = "ksb.thinClient.idleConnectionTimeout";
63 private static final String DEFAULT_IDLE_CONNECTION_THREAD_INTERVAL = "7500";
64 private static final String DEFAULT_IDLE_CONNECTION_TIMEOUT = "5000";
65 private static final String RETRY_SOCKET_EXCEPTION_PROPERTY = "ksb.thinClient.retrySocketException";
66
67 private static IdleConnectionTimeoutThread ictt;
68
69 public HttpInvokerConnector(final JavaServiceConfiguration serviceConfiguration, final URL alternateEndpointUrl) {
70 super(serviceConfiguration, alternateEndpointUrl);
71 initializeHttpClientParams();
72 }
73
74 @Override
75 public JavaServiceConfiguration getServiceConfiguration() {
76 return (JavaServiceConfiguration) super.getServiceConfiguration();
77 }
78
79 public Object getService() {
80 LOG.debug("Getting connector for endpoint " + getActualEndpointUrl());
81 KSBHttpInvokerProxyFactoryBean client = new KSBHttpInvokerProxyFactoryBean();
82 client.setServiceUrl(getActualEndpointUrl().toExternalForm());
83 client.setServiceConfiguration(getServiceConfiguration());
84
85 KSBHttpInvokerRequestExecutor executor;
86
87 if (getCredentialsSource() != null) {
88 executor = new AuthenticationCommonsHttpInvokerRequestExecutor(getHttpClient(), getCredentialsSource(), getServiceConfiguration());
89 } else {
90 executor = new KSBHttpInvokerRequestExecutor(getHttpClient());
91 }
92 executor.setSecure(getServiceConfiguration().getBusSecurity());
93 client.setHttpInvokerRequestExecutor(executor);
94 client.afterPropertiesSet();
95 return getServiceProxyWithFailureMode(client.getObject(), getServiceConfiguration());
96 }
97
98
99
100
101
102
103
104
105 public HttpClient getHttpClient() {
106 return new HttpClient(this.httpClientParams);
107 }
108
109 protected void initializeHttpClientParams() {
110 synchronized (HttpInvokerConnector.class) {
111 if (! this.httpClientInitialized) {
112 this.httpClientParams = new HttpClientParams();
113 configureDefaultHttpClientParams(this.httpClientParams);
114 Properties configProps = ConfigContext.getCurrentContextConfig().getProperties();
115 for (Iterator<Object> iterator = configProps.keySet().iterator(); iterator.hasNext();) {
116 String paramName = (String) iterator.next();
117 if (paramName.startsWith("http.")) {
118 HttpClientHelper.setParameter(this.httpClientParams, paramName, (String) configProps.get(paramName));
119 }
120 }
121 runIdleConnectionTimeout();
122 this.httpClientInitialized = true;
123 }
124 }
125 }
126
127 protected void configureDefaultHttpClientParams(HttpParams params) {
128 params.setParameter(HttpClientParams.CONNECTION_MANAGER_CLASS, MultiThreadedHttpConnectionManager.class);
129 params.setParameter(HttpMethodParams.COOKIE_POLICY, CookiePolicy.RFC_2109);
130 params.setLongParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, 10000);
131 Map<HostConfiguration, Integer> maxHostConnectionsMap = new HashMap<HostConfiguration, Integer>();
132 maxHostConnectionsMap.put(HostConfiguration.ANY_HOST_CONFIGURATION, new Integer(20));
133 params.setParameter(HttpConnectionManagerParams.MAX_HOST_CONNECTIONS, maxHostConnectionsMap);
134 params.setIntParameter(HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 20);
135 params.setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT, 10000);
136 params.setIntParameter(HttpConnectionParams.SO_TIMEOUT, 2*60*1000);
137
138
139 boolean retrySocketException = new Boolean(ConfigContext.getCurrentContextConfig().getProperty(RETRY_SOCKET_EXCEPTION_PROPERTY));
140 if (retrySocketException) {
141 LOG.info("Installing custom HTTP retry handler to retry requests in face of SocketExceptions");
142 params.setParameter(HttpMethodParams.RETRY_HANDLER, new CustomHttpMethodRetryHandler());
143 }
144
145
146 }
147
148
149
150
151
152
153
154
155
156 private void runIdleConnectionTimeout() {
157 if (ictt != null) {
158 String timeoutInterval = ConfigContext.getCurrentContextConfig().getProperty(IDLE_CONNECTION_THREAD_INTERVAL_PROPERTY);
159 if (StringUtils.isBlank(timeoutInterval)) {
160 timeoutInterval = DEFAULT_IDLE_CONNECTION_THREAD_INTERVAL;
161 }
162 String connectionTimeout = ConfigContext.getCurrentContextConfig().getProperty(IDLE_CONNECTION_TIMEOUT_PROPERTY);
163 if (StringUtils.isBlank(connectionTimeout)) {
164 connectionTimeout = DEFAULT_IDLE_CONNECTION_TIMEOUT;
165 }
166
167 ictt.addConnectionManager(getHttpClient().getHttpConnectionManager());
168 ictt.setTimeoutInterval(new Integer(timeoutInterval));
169 ictt.setConnectionTimeout(new Integer(connectionTimeout));
170
171 ictt.start();
172 }
173 }
174
175 public static void shutdownIdleConnectionTimeout() {
176 if (ictt != null) {
177 try {
178 ictt.shutdown();
179 } catch (Exception e) {
180 LOG.error("Failed to shutdown idle connection thread.", e);
181 }
182 }
183 }
184
185 private static final class CustomHttpMethodRetryHandler extends DefaultHttpMethodRetryHandler {
186
187 private static final int MAX_RETRIES = 1;
188
189 public CustomHttpMethodRetryHandler() {
190 super(MAX_RETRIES, true);
191 }
192
193 @Override
194 public boolean retryMethod(HttpMethod method, IOException exception, int executionCount) {
195 boolean shouldRetry = super.retryMethod(method, exception, executionCount);
196 if (!shouldRetry && executionCount < MAX_RETRIES) {
197 if (exception instanceof SocketException) {
198 LOG.warn("Retrying request because of SocketException!", exception);
199 shouldRetry = true;
200 } else if (exception instanceof SocketTimeoutException) {
201 LOG.warn("Retrying request because of SocketTimeoutException!", exception);
202 shouldRetry = true;
203 }
204 }
205 return shouldRetry;
206 }
207
208 }
209
210
211
212 }