package com.redhat.lightblue.migrator.facade; import java.util.HashMap; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.redhat.lightblue.migrator.facade.ServiceFacade.FacadeOperation; /** * <p> * Provides means to access timeout configuration defined in a * {@link Properties} object.</p> * * <p> * Define bean level timeout example:</p> * <pre> * com.redhat.lightblue.migrator.facade.timeout.CountryDAO=2000 * </pre> * * <p> * Define method level timeout example (takes precedence over bean level * timeout):</p> * <pre> * com.redhat.lightblue.migrator.facade.timeout.CountryDAO.getCountries=5000 * </pre> * * <p> * Zero or less means no timeout.</p> * * @author mpatercz * */ public class TimeoutConfiguration { private static final Logger log = LoggerFactory.getLogger(TimeoutConfiguration.class); public enum Type { timeout, slowwarning; } public static final String CONFIG_PREFIX = "com.redhat.lightblue.migrator.facade."; private final long defaultTimeoutMS, defaultSlowwarnMS; private String beanName; private Properties properties; private boolean interruptOnTimeout = true; private HashMap<String, Long> methodTimeouts = new HashMap<>(); /** * * @param defaultTimeoutMS Use this timeout if nothing matches in the * properties * @param defaultSlowwarnMS Use this slow warn time if nothing matches in * the properties * @param beanName bean name to use, e.g. CountryDAO * @param properties properties read from a file with timeout settings. Can * be null. */ public TimeoutConfiguration(long defaultTimeoutMS, long defaultSlowwarnMS, String beanName, Properties properties) { this.defaultTimeoutMS = defaultTimeoutMS; this.defaultSlowwarnMS = defaultSlowwarnMS; this.beanName = beanName; if (properties != null) { this.properties = properties; } else { this.properties = new Properties(); } if (properties != null) { // interruptOnTimeout key does not include bean name - it is global interruptOnTimeout = Boolean.parseBoolean(properties.getProperty(TimeoutConfiguration.CONFIG_PREFIX+".timeout.interruptOnTimeout", "true")); } log.info("Initialized TimeoutConfiguration for {}, interruptOnTimeout={}", beanName, interruptOnTimeout); } /** * * @param defaultTimeoutMS Use this timeout if nothing matches in the * properties. For defaultSlowwarnMS, it will use defaultTimeoutMS. * (so it doesn't log both timeout and slowwarn unless source is slow) * @param beanName bean name to use, e.g. CountryDAO * @param properties properties read from a file with timeout settings. Can * be null. */ public TimeoutConfiguration(long defaultTimeoutMS, String beanName, Properties properties) { this(defaultTimeoutMS, defaultTimeoutMS, beanName, properties); } /** * Return timeout or slowwarning value. First checks if timeout was * configured for that method explicitly. If not, looks for timeout defined * for operation type (read/write). If not, looks for a timeout defined for * entire bean. If that is not set, takes a default global timeout. * * @param methodName to lookup timeout configuration by name * @param op to lookup timeout configuration by operation * @param type to lookup timeout configuration by type * @return */ public long getMS(String methodName, FacadeOperation op, Type type) { String cacheKey = type + "-" + methodName; if (methodTimeouts.containsKey(cacheKey)) { return methodTimeouts.get(cacheKey); } String configurationKeyPrefix = CONFIG_PREFIX+type+"."+beanName; String timeoutPropValue = properties.getProperty(configurationKeyPrefix + "." + methodName); if (timeoutPropValue == null && op != null) { if (log.isDebugEnabled()) { log.debug("{} config not found for method {}, trying default for {} operations for this bean", type, methodName, op); } timeoutPropValue = properties.getProperty(configurationKeyPrefix + "." + op); } if (timeoutPropValue == null) { if (log.isDebugEnabled()) { log.debug("{} config not found for method {}, trying default for this bean", type, methodName); } timeoutPropValue = properties.getProperty(configurationKeyPrefix); } Long timeout; if (timeoutPropValue == null) { if (log.isDebugEnabled()) { log.debug("{} config not found for bean {} using global timeout", type, beanName); } switch (type) { case timeout: { timeout = defaultTimeoutMS; break; } case slowwarning: { timeout = defaultSlowwarnMS; break; } default: throw new IllegalArgumentException("Type " + type + " not known!"); } } else { timeout = Long.parseLong(timeoutPropValue); } if (log.isDebugEnabled()) { log.debug("Setting {} for {}.{} to {}ms", type, beanName, methodName, timeout); } methodTimeouts.put(cacheKey, timeout); return timeout; } /** * See ${link * {@link TimeoutConfiguration#getMS(String, FacadeOperation, Type)} * * @param methodName * @param op * @return */ public long getTimeoutMS(String methodName, FacadeOperation op) { return getMS(methodName, op, Type.timeout); } /** * See ${link * {@link TimeoutConfiguration#getMS(String, FacadeOperation, Type)} * * @param methodName * @param op * @return */ public long getSlowWarningMS(String methodName, FacadeOperation op) { return getMS(methodName, op, Type.slowwarning); } public boolean isInterruptOnTimeout() { return interruptOnTimeout; } public void setInterruptOnTimeout(boolean interruptOnTimeout) { this.interruptOnTimeout = interruptOnTimeout; } }