Coverage Report - org.kuali.rice.kew.config.ThinClientResourceLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
ThinClientResourceLoader
0%
0/101
0%
0/36
2.786
ThinClientResourceLoader$1
N/A
N/A
2.786
ThinClientResourceLoader$CustomHttpMethodRetryHandler
0%
0/6
0%
0/4
2.786
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.config;
 18  
 
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.net.SocketException;
 22  
 import java.util.Collections;
 23  
 import java.util.HashMap;
 24  
 import java.util.Iterator;
 25  
 import java.util.Map;
 26  
 import java.util.Properties;
 27  
 
 28  
 import javax.xml.namespace.QName;
 29  
 
 30  
 import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
 31  
 import org.apache.commons.httpclient.HostConfiguration;
 32  
 import org.apache.commons.httpclient.HttpClient;
 33  
 import org.apache.commons.httpclient.HttpMethod;
 34  
 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
 35  
 import org.apache.commons.httpclient.cookie.CookiePolicy;
 36  
 import org.apache.commons.httpclient.params.HttpClientParams;
 37  
 import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
 38  
 import org.apache.commons.httpclient.params.HttpMethodParams;
 39  
 import org.apache.commons.httpclient.params.HttpParams;
 40  
 import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;
 41  
 import org.apache.commons.lang.StringUtils;
 42  
 import org.kuali.rice.core.config.ConfigContext;
 43  
 import org.kuali.rice.core.resourceloader.BaseResourceLoader;
 44  
 import org.kuali.rice.kew.service.WorkflowDocumentActions;
 45  
 import org.kuali.rice.kew.service.WorkflowUtility;
 46  
 import org.kuali.rice.kew.util.KEWConstants;
 47  
 import org.kuali.rice.kim.service.GroupService;
 48  
 import org.kuali.rice.kim.service.IdentityService;
 49  
 import org.kuali.rice.kim.service.KIMServiceLocator;
 50  
 import org.kuali.rice.ksb.messaging.HttpClientHelper;
 51  
 import org.kuali.rice.ksb.messaging.KSBHttpInvokerRequestExecutor;
 52  
 import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
 53  
 
 54  
 
 55  
 /**
 56  
  * Initializes and loads webservice resources for the Embedded plugin.
 57  
  * Currently, the only 2 services which are exposed are the utility service and
 58  
  * the document actions service.
 59  
  *
 60  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 61  
  */
 62  0
 public class ThinClientResourceLoader extends BaseResourceLoader {
 63  0
                 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ThinClientResourceLoader.class);
 64  
 
 65  
             private static final String DEFAULT_MAX_CONNECTIONS = "40";
 66  
             private static final String DEFAULT_CONNECTION_TIMEOUT = "60000";
 67  
             private static final String DEFAULT_CONNECTION_MANAGER_TIMEOUT = "60000";
 68  
             public static final String MAX_CONNECTIONS = "kew." + HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS; // kew.http.connection-manager.max-total
 69  
             public static final String CONNECTION_TIMEOUT = "kew." + HttpConnectionManagerParams.CONNECTION_TIMEOUT; // kew.http.connection.timeout
 70  
             public static final String CONNECTION_MANAGER_TIMEOUT = "kew." + HttpClientParams.CONNECTION_MANAGER_TIMEOUT; // kew.http.connection-manager.timeout
 71  
             public static final String DOCUMENT_ENDPOINT = "workflowdocument.javaservice.endpoint";
 72  
             public static final String SECURE_DOCUMENT_ENDPOINT = "secure.workflowdocument.javaservice.endpoint";
 73  
             public static final String UTILITY_ENDPOINT = "workflowutility.javaservice.endpoint";
 74  
             public static final String SECURE_UTILITY_ENDPOINT = "secure.workflowutility.javaservice.endpoint";
 75  
             public static final String IDENTITY_ENDPOINT = "identity.javaservice.endpoint"; 
 76  
             public static final String SECURE_IDENTITY_ENDPOINT = "secure.identity.javaservice.endpoint"; 
 77  
             public static final String GROUP_ENDPOINT = "group.javaservice.endpoint"; 
 78  
             public static final String SECURE_GROUP_ENDPOINT = "secure.group.javaservice.endpoint"; 
 79  
             
 80  
             
 81  
             private static final String IDLE_CONNECTION_THREAD_INTERVAL_PROPERTY = "ksb.thinClient.idleConnectionThreadInterval";
 82  
             private static final String IDLE_CONNECTION_TIMEOUT_PROPERTY = "ksb.thinClient.idleConnectionTimeout";
 83  
             private static final String DEFAULT_IDLE_CONNECTION_THREAD_INTERVAL = "7500";
 84  
             private static final String DEFAULT_IDLE_CONNECTION_TIMEOUT = "5000";
 85  
             private static final String RETRY_SOCKET_EXCEPTION_PROPERTY = "ksb.thinClient.retrySocketException";
 86  
             
 87  0
             private Map<String, Object> services = Collections.synchronizedMap(new HashMap<String, Object>());
 88  
 
 89  
             private IdleConnectionTimeoutThread ictt;
 90  
             
 91  
         public ThinClientResourceLoader() {
 92  0
                 super(new QName(ConfigContext.getCurrentContextConfig().getServiceNamespace(), "ThinClientResourceLoader"));
 93  0
                 ictt = new IdleConnectionTimeoutThread();
 94  0
         }
 95  
 
 96  
         @Override
 97  
         public void start() throws Exception {
 98  0
                 super.start();
 99  0
                 initializeHttpClientParams();
 100  0
                 runIdleConnectionTimeout();
 101  
                 //springLifecycle.start();
 102  0
         }
 103  
 
 104  
 
 105  
 
 106  
         @Override
 107  
         public void stop() throws Exception {
 108  0
                 super.stop();
 109  0
                 if (ictt != null) {
 110  0
                     ictt.shutdown();
 111  
                 }
 112  
                 //springLifecycle.stop();
 113  0
         }
 114  
 
 115  
         public Object getService(QName serviceQName) {
 116  0
             String serviceName = serviceQName.getLocalPart();
 117  0
                     Object cachedService = services.get(serviceName);
 118  0
                     if (cachedService != null) {
 119  0
                         return cachedService;
 120  
                     }
 121  0
                 if (serviceName.equals(KEWConstants.WORKFLOW_UTILITY_SERVICE)) {
 122  0
                         WorkflowUtility utility = getWorkflowUtility();
 123  0
                         services.put(serviceName, utility);
 124  0
                         return utility;
 125  0
                 } else if (serviceName.equals(KEWConstants.WORKFLOW_DOCUMENT_ACTIONS_SERVICE)) {
 126  0
                         WorkflowDocumentActions documentActions = getWorkflowDocument();
 127  0
                         services.put(serviceName, documentActions);
 128  0
                         return documentActions;
 129  0
                 } else if (serviceName.equals(KIMServiceLocator.KIM_IDENTITY_SERVICE)) {
 130  0
                         IdentityService identityService = getIdentityService();
 131  0
                         services.put(serviceName, identityService);
 132  0
                         return identityService;
 133  0
                 } else if (serviceName.equals(KIMServiceLocator.KIM_GROUP_SERVICE)) {
 134  0
                         GroupService groupService = getGroupService();
 135  0
                         services.put(serviceName, groupService);
 136  0
                         return groupService;
 137  
                 }
 138  0
             return null;
 139  
         }
 140  
 
 141  
         public WorkflowUtility getWorkflowUtility() {
 142  0
             return (WorkflowUtility)getServiceProxy(WorkflowUtility.class, UTILITY_ENDPOINT, SECURE_UTILITY_ENDPOINT);
 143  
         }
 144  
 
 145  
         public WorkflowDocumentActions getWorkflowDocument() {
 146  0
             return (WorkflowDocumentActions)getServiceProxy(WorkflowDocumentActions.class, DOCUMENT_ENDPOINT, SECURE_DOCUMENT_ENDPOINT);
 147  
         }
 148  
         
 149  
         public IdentityService getIdentityService() {
 150  0
             return (IdentityService)getServiceProxy(IdentityService.class, IDENTITY_ENDPOINT, SECURE_IDENTITY_ENDPOINT);
 151  
         }
 152  
 
 153  
         public GroupService getGroupService() {
 154  0
             return (GroupService)getServiceProxy(GroupService.class, GROUP_ENDPOINT, SECURE_GROUP_ENDPOINT);
 155  
         }
 156  
 
 157  
         protected Object getServiceProxy(Class serviceInterface, String endpointParam, String secureEndpointParam) {
 158  0
             HttpInvokerProxyFactoryBean proxyFactory = new HttpInvokerProxyFactoryBean();
 159  0
             String serviceUrl = ConfigContext.getCurrentContextConfig().getProperty(endpointParam);
 160  0
             if (StringUtils.isEmpty(serviceUrl)) {
 161  0
                 throw new IllegalArgumentException("The " + endpointParam + " configuration parameter was not defined but is required.");
 162  
             }
 163  0
             proxyFactory.setServiceUrl(serviceUrl);
 164  0
             proxyFactory.setServiceInterface(serviceInterface);
 165  0
             String secureProp = ConfigContext.getCurrentContextConfig().getProperty(secureEndpointParam);
 166  0
             Boolean secureIt = null;
 167  0
         secureIt = secureProp == null || Boolean.valueOf(secureProp);
 168  0
             KSBHttpInvokerRequestExecutor executor = new KSBHttpInvokerRequestExecutor(getHttpClient());
 169  0
             executor.setSecure(secureIt);
 170  0
             proxyFactory.setHttpInvokerRequestExecutor(executor);
 171  0
             proxyFactory.afterPropertiesSet();
 172  0
             return proxyFactory.getObject();
 173  
         }
 174  
 
 175  
         /*
 176  
          * the below code copied from RemoteResourceServiceLocator
 177  
          */
 178  
 
 179  
         private HttpClientParams httpClientParams;
 180  
 
 181  
         /**
 182  
          * Creates a commons HttpClient for service invocation. Config parameters
 183  
          * that start with http.* are used to configure the client.
 184  
          *
 185  
          * TODO we need to add support for other invocation protocols and
 186  
          * implementations, but for now...
 187  
          */
 188  
         protected HttpClient getHttpClient() {
 189  0
                 return new HttpClient(httpClientParams);
 190  
         }
 191  
 
 192  
         protected void initializeHttpClientParams() {
 193  0
                 httpClientParams = new HttpClientParams();
 194  0
                 configureDefaultHttpClientParams(httpClientParams);
 195  0
                 Properties configProps = ConfigContext.getCurrentContextConfig().getProperties();
 196  0
                 for (Iterator iterator = configProps.keySet().iterator(); iterator.hasNext();) {
 197  0
                         String paramName = (String) iterator.next();
 198  0
                         if (paramName.startsWith("http.")) {
 199  0
                                 HttpClientHelper.setParameter(httpClientParams, paramName, (String) configProps.get(paramName));
 200  
                         }
 201  0
                 }
 202  
 
 203  0
                 String maxConnectionsValue = configProps.getProperty(MAX_CONNECTIONS);
 204  0
                 if (!StringUtils.isEmpty(maxConnectionsValue)) {
 205  0
                     Integer maxConnections = new Integer(maxConnectionsValue);
 206  0
                     Map<HostConfiguration, Integer> maxHostConnectionsMap = new HashMap<HostConfiguration, Integer>();
 207  0
                     maxHostConnectionsMap.put(HostConfiguration.ANY_HOST_CONFIGURATION, maxConnections);
 208  0
                     httpClientParams.setParameter(HttpConnectionManagerParams.MAX_HOST_CONNECTIONS, maxHostConnectionsMap);
 209  0
                     httpClientParams.setIntParameter(HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, maxConnections);
 210  
                 }
 211  
 
 212  0
                 String connectionManagerTimeoutValue = configProps.getProperty(CONNECTION_MANAGER_TIMEOUT);
 213  0
                 if (!StringUtils.isEmpty(connectionManagerTimeoutValue)) {
 214  0
                     httpClientParams.setLongParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, new Long(connectionManagerTimeoutValue));
 215  
                 }
 216  
 
 217  0
                 String connectionTimeoutValue = configProps.getProperty(CONNECTION_TIMEOUT);
 218  0
                 if (!StringUtils.isEmpty(connectionTimeoutValue)) {
 219  0
                     httpClientParams.setIntParameter(HttpConnectionManagerParams.CONNECTION_TIMEOUT, new Integer(connectionTimeoutValue));
 220  
                 }
 221  0
         }
 222  
 
 223  
         protected void configureDefaultHttpClientParams(HttpParams params) {
 224  0
                 params.setParameter(HttpClientParams.CONNECTION_MANAGER_CLASS, MultiThreadedHttpConnectionManager.class);
 225  0
                 params.setParameter(HttpMethodParams.COOKIE_POLICY, CookiePolicy.RFC_2109);
 226  0
                 params.setLongParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, new Long(DEFAULT_CONNECTION_MANAGER_TIMEOUT));
 227  0
                 Map<HostConfiguration, Integer> maxHostConnectionsMap = new HashMap<HostConfiguration, Integer>();
 228  0
                 maxHostConnectionsMap.put(HostConfiguration.ANY_HOST_CONFIGURATION, new Integer(DEFAULT_MAX_CONNECTIONS));
 229  0
                 params.setParameter(HttpConnectionManagerParams.MAX_HOST_CONNECTIONS, maxHostConnectionsMap);
 230  0
                 params.setIntParameter(HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, new Integer(DEFAULT_MAX_CONNECTIONS));
 231  0
                 params.setIntParameter(HttpConnectionManagerParams.CONNECTION_TIMEOUT, new Integer(DEFAULT_CONNECTION_TIMEOUT));
 232  
         
 233  0
                 boolean retrySocketException = new Boolean(ConfigContext.getCurrentContextConfig().getProperty(RETRY_SOCKET_EXCEPTION_PROPERTY));
 234  0
                 if (retrySocketException) {
 235  0
                     LOG.info("Installing custom HTTP retry handler to retry requests in face of SocketExceptions");
 236  0
                     params.setParameter(HttpMethodParams.RETRY_HANDLER, new CustomHttpMethodRetryHandler());
 237  
                 }
 238  0
         }
 239  
         
 240  
                 /**
 241  
          * Idle connection timeout thread added as a part of the fix for ensuring that 
 242  
          * threads that timed out need to be cleaned or and send back to the pool so that 
 243  
          * other clients can use it.
 244  
          *
 245  
          */
 246  
         private void runIdleConnectionTimeout() {
 247  0
             if (ictt != null) {
 248  0
                     String timeoutInterval = ConfigContext.getCurrentContextConfig().getProperty(IDLE_CONNECTION_THREAD_INTERVAL_PROPERTY);
 249  0
                     if (StringUtils.isBlank(timeoutInterval)) {
 250  0
                         timeoutInterval = DEFAULT_IDLE_CONNECTION_THREAD_INTERVAL;
 251  
                     }
 252  0
                     String connectionTimeout = ConfigContext.getCurrentContextConfig().getProperty(IDLE_CONNECTION_TIMEOUT_PROPERTY);
 253  0
                     if (StringUtils.isBlank(connectionTimeout)) {
 254  0
                         connectionTimeout = DEFAULT_IDLE_CONNECTION_TIMEOUT;
 255  
                     }
 256  
                     
 257  0
                     ictt.addConnectionManager(getHttpClient().getHttpConnectionManager());
 258  0
                     ictt.setTimeoutInterval(new Integer(timeoutInterval));
 259  0
                     ictt.setConnectionTimeout(new Integer(connectionTimeout));
 260  
                     //start the thread
 261  0
                     ictt.start();
 262  
             }
 263  0
         }
 264  
         
 265  0
         private static final class CustomHttpMethodRetryHandler extends DefaultHttpMethodRetryHandler {
 266  
 
 267  
             @Override
 268  
             public boolean retryMethod(HttpMethod method, IOException exception, int executionCount) {
 269  0
                 boolean shouldRetry = super.retryMethod(method, exception, executionCount);
 270  0
                 if (!shouldRetry && exception instanceof SocketException) {
 271  0
                     LOG.warn("Retrying request because of SocketException!", exception);
 272  0
                     shouldRetry = true;
 273  
                 }
 274  0
                 return shouldRetry;
 275  
             }
 276  
             
 277  
         }
 278  
 
 279  
 }