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