AnsweredAssumed Answered

Java Service Task and HttpClient

Question asked by martinfranz on Nov 2, 2014
Latest reply on Nov 10, 2014 by jbarrez
Hi all,

I try to write a custom java service task which uses one http client to make one call per service task to an external custom api.

PoolingHttpClientConnectionManager > CloseableHTTPClient > Java Service task (async) > 100 * HttpPost / HttpGet

The problem with this is: When I execute more than about 20 of these service tasks at the same time (Timer Event) activiti "crashes". I don't see any errors but i am not able to login again. The activiti explorer is like "freezed". The loading indicator turns red and never stops spinning.
From that point i need to delete the database and install activiti again.

I stack with this problem for many days know and I don't have any idea what could be the problem.

What I have tested so far:
Started 100 threads in a small unit test using a pooled connection manager in order to call the target api 100 times concurrently - no problems on client an server side. Where the client was my mac osx java runtime. No tomcat around.


Maybe you have any ideas? Thanks a lot :-)

This is my setup for the service task:

activiti-context.xml

// relevant parts

     <!– Application Context Provider –>
     <bean id="applicationContextProvider"
        class="com.sw.activiti.spring.context.ApplicationContextProvider">
     </bean>

          <!– A service provider which holdes the base url to the external api –>
     <bean id="fm3RestServiceProvider"
        class="com.sw.activiti.service.tasks.rest.provider.FM3RestServiceProviderImpl">
           <property name="serviceUrl" value="http://myurl/api/" />
     </bean>

       <!– A custom pooled http client usded in the java service task –>
       <bean id="pooledHttpClient" class="com.sw.activiti.service.http.client.PooledHttpClient">
         <property name="maxTotal" value="1000" />
         <property name="defaultMaxPerRoute" value="50" />
       </bean>


com.sw.activiti.service.http.client.PooledHttpClient

public class PooledHttpClient implements InitializingBean {

   private int maxTotal;
   
   private int defaultMaxPerRoute;
   
   
   
   public int getMaxTotal() {
      return maxTotal;
   }

   public void setMaxTotal(int maxTotal) {
      this.maxTotal = maxTotal;
   }

   public int getDefaultMaxPerRoute() {
      return defaultMaxPerRoute;
   }

   public void setDefaultMaxPerRoute(int defaultMaxPerRoute) {
      this.defaultMaxPerRoute = defaultMaxPerRoute;
   }


   private PoolingHttpClientConnectionManager connectionManager;
   
   private CloseableHttpClient httpclient;
   
   
   public PoolingHttpClientConnectionManager getConnectionManager() {
      return connectionManager;
   }

   public CloseableHttpClient getHttpclient() {
      return httpclient;
   }


   @Override
   public void afterPropertiesSet() throws Exception {
      
      this.connectionManager = new PoolingHttpClientConnectionManager();
      this.connectionManager.setMaxTotal(this.maxTotal);
      this.connectionManager.setDefaultMaxPerRoute(this.defaultMaxPerRoute);
      
      this.httpclient = HttpClients.custom().setConnectionManager(this.connectionManager).build();

   }

}


The java service task - ThreadSaveFM3RestServiceTask

public class ThreadSaveFM3RestServiceTask implements JavaDelegate {

    protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
   
    private Expression api_key;

    private Expression rest_url;
   
    private Expression json_string;

    private Expression http_method;
   
   @Override
   public void execute(DelegateExecution execution) throws Exception {

      String final_api_key = (String)api_key.getValue(execution);
      String final_rest_url = (String)rest_url.getValue(execution);
      String final_json_string = (String)json_string.getValue(execution);
      String final_method = (String)http_method.getValue(execution);

      ApplicationContext ctx = ApplicationContextProvider.getApplicationContext();
      
                ServiceProvider fm3RestServiceProvider = (FM3RestServiceProviderImpl) ctx.getBean("fm3RestServiceProvider");
      PooledHttpClient pooledHttpClient = (PooledHttpClient) ctx.getBean("pooledHttpClient");
      
      try { 
          
            // prepare the request
            HttpEntityEnclosingRequestBase request = null;
            
            if(final_method.equals("POST"))
            {
               request = new HttpPost(fm3RestServiceProvider.getServiceUrl() + final_rest_url);
            }
               
            if(final_method.equals("PUT"))
            {
               request = new HttpPut(fm3RestServiceProvider.getServiceUrl() + final_rest_url);
            }
            
            // prepare headers
            request.addHeader("Accept", "application/json");
            request.addHeader("Content-Type", "application/json; charset=utf-8");
            request.addHeader("Authorization", "ApiKey " + final_api_key);
                            
            // prepare the body      
            StringEntity entity = new StringEntity(final_json_string, "UTF-8");   
            request.setEntity(entity);
            
            CloseableHttpResponse response = pooledHttpClient.getHttpclient().execute(request);
            
            if (response.getStatusLine().getStatusCode() != 200)
            {
               LOGGER.debug("FM3RestServiceTask request status: " + response.getStatusLine().getStatusCode());            
            }
              
            try { 

               HttpEntity responseEntity = response.getEntity(); 
                  
            } finally { 

               LOGGER.debug("FM3RestServiceTask close response");
               response.close(); 
               request.releaseConnection();
            }
         
         
      } catch (ClientProtocolException e)
      {
         LOGGER.debug("FM3RestServiceTask ClientProtocolException: " + e.getMessage());
         
      } catch (IOException e)
      {
         LOGGER.debug("FM3RestServiceTask IOException: " + e.getMessage());

      } finally
      {
         LOGGER.debug("FM3RestServiceTask done");
      }
      
   }
}


The usage of the service task in a process definition

    <serviceTask id="servicetask1" name="FM3 Rest Service Task" activiti:async="true" activiti:class="com.sw.activiti.service.tasks.rest.ThreadSaveFM3RestServiceTask">
      <extensionElements>
        <activiti:field name="rest_url">
          <activiti:expression>Termine/setTerminStatus/${terminID}</activiti:expression>
        </activiti:field>
        <activiti:field name="api_key">
          <activiti:string><![CDATA[myapikey]]></activiti:string>
        </activiti:field>
        <activiti:field name="json_string">
          <activiti:string><![CDATA[{"terminStatus3":70,"terminStatus3Info":"Ressource ueberfaellig"}]]></activiti:string>
        </activiti:field>
        <activiti:field name="http_method">
          <activiti:string><![CDATA[POST]]></activiti:string>
        </activiti:field>
      </extensionElements>
    </serviceTask>

Outcomes