CaravanHttpThreadPoolConfig.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2014 wcm.io
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package io.wcm.caravan.io.http.impl;

import java.util.Map;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Configures thread pool options for the transport layer.
 * The configuration is mapped to archaius configuration internally.
 */
@Component(immediate = true, metatype = true,
label = "wcm.io Caravan Resilient Http Thread Pool Configuration",
description = " Configures thread pool options for the transport layer.",
configurationFactory = true, policy = ConfigurationPolicy.REQUIRE)
@Property(name = "webconsole.configurationFactory.nameHint", value = "{threadPoolName}: {hystrixThreadpoolCoresize}")
public class CaravanHttpThreadPoolConfig {

  private static final Logger log = LoggerFactory.getLogger(CaravanHttpThreadPoolConfig.class);

  /**
   * Thread Pool Name
   */
  @Property(label = "Thread Pool Name", description = "Internal thread pool name.")
  public static final String THREAD_POOL_NAME_PROPERTY = "threadPoolName";

  /**
   * Threadpool size
   */
  @Property(label = "Threadpool size",
      description = "Hystrix: Maximum number of HystrixCommands that can execute concurrently.")
  public static final String HYSTRIX_THREADPOOL_CORESIZE_PROPERTY = "hystrixThreadpoolCoresize";
  static final int HYSTRIX_THREADPOOL_CORESIZE_DEFAULT = 10;

  /**
   * Max. Threadpool Queue Size
   */
  @Property(label = "Max. Threadpool Queue Size",
      description = "Hystrix: Maximum queue size at which rejections will occur. Note: This property only applies at initialization time.")
  public static final String HYSTRIX_THREADPOOL_MAXQUEUESIZE_PROPERTY = "hystrixThreadpoolMaxqueuesize";
  static final int HYSTRIX_THREADPOOL_MAXQUEUESIZE_DEFAULT = 4096;

  /**
   * Dynamic Threadpool Queue Size
   */
  @Property(label = "Dynamic Threadpool Queue Size",
      description = "Hystrix: Artificial maximum queue size at which rejections will occur even if hystrixThreadpoolDefaultMaxqueuesize has not been reached.")
  public static final String HYSTRIX_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD_PROPERTY = "hystrixThreadpoolQueuesizerejectionthreshold";
  static final int HYSTRIX_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD_DEFAULT = 4096;

  private static final String HYSTRIX_THREADPOOL_PREFIX = "hystrix.threadpool.";
  private static final String HYSTRIX_PARAM_THREADPOOL_CORESIZE = ".coreSize";
  private static final String HYSTRIX_PARAM_THREADPOOL_MAXQUEUESIZE = ".maxQueueSize";
  private static final String HYSTRIX_PARAM_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD = ".queueSizeRejectionThreshold";

  @Activate
  protected void activate(Map<String, Object> config) {
    String threadPoolName = getThreadPoolName(config);
    if (validateConfig(threadPoolName, config)) {
      setArchiausProperties(threadPoolName, config);
    }
  }

  @Deactivate
  protected void deactivate(Map<String, Object> config) {
    // clear configuration by writing empty properties
    String threadPoolName = getThreadPoolName(config);
    clearArchiausProperties(threadPoolName);
  }

  private String getThreadPoolName(Map<String, Object> config) {
    return PropertiesUtil.toString(config.get(THREAD_POOL_NAME_PROPERTY), null);
  }

  /**
   * Validates configuration
   * @param threadPoolName Thread pool name
   * @param config OSGi config
   */
  private boolean validateConfig(String threadPoolName, Map<String, Object> config) {
    if (StringUtils.isBlank(threadPoolName)) {
      log.warn("Invalid http thread pool configuration without thread pool name, ignoring.", threadPoolName);
      return false;
    }
    return true;
  }

  /**
   * Writes OSGi configuration to archaius configuration.
   * @param threadPoolName Thread pool name
   * @param config OSGi config
   */
  private void setArchiausProperties(String threadPoolName, Map<String, Object> config) {
    Configuration archaiusConfig = ArchaiusConfig.getConfiguration();
    // thread pool size
    archaiusConfig.setProperty(HYSTRIX_THREADPOOL_PREFIX + threadPoolName + HYSTRIX_PARAM_THREADPOOL_CORESIZE,
        PropertiesUtil.toInteger(config.get(HYSTRIX_THREADPOOL_CORESIZE_PROPERTY), HYSTRIX_THREADPOOL_CORESIZE_DEFAULT));
    // maximum thread queue size
    archaiusConfig.setProperty(HYSTRIX_THREADPOOL_PREFIX + threadPoolName + HYSTRIX_PARAM_THREADPOOL_MAXQUEUESIZE,
        PropertiesUtil.toInteger(config.get(HYSTRIX_THREADPOOL_MAXQUEUESIZE_PROPERTY), HYSTRIX_THREADPOOL_MAXQUEUESIZE_DEFAULT));
    // dynamic thread queue size
    archaiusConfig.setProperty(HYSTRIX_THREADPOOL_PREFIX + threadPoolName + HYSTRIX_PARAM_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD,
        PropertiesUtil.toInteger(config.get(HYSTRIX_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD_PROPERTY), HYSTRIX_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD_DEFAULT));
  }

  /**
   * Removes OSGi configuration from archaius configuration.
   * @param threadPoolName Thread pool name
   */
  private void clearArchiausProperties(String threadPoolName) {
    Configuration archaiusConfig = ArchaiusConfig.getConfiguration();
    archaiusConfig.clearProperty(HYSTRIX_THREADPOOL_PREFIX + threadPoolName + HYSTRIX_PARAM_THREADPOOL_CORESIZE);
    archaiusConfig.clearProperty(HYSTRIX_THREADPOOL_PREFIX + threadPoolName + HYSTRIX_PARAM_THREADPOOL_MAXQUEUESIZE);
    archaiusConfig.clearProperty(HYSTRIX_THREADPOOL_PREFIX + threadPoolName + HYSTRIX_PARAM_THREADPOOL_QUEUESIZEREJECTIONTHRESHOLD);
  }

}