/* * Copyright 2011 Yahoo! Inc * 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. */ package org.commons.jconfig.config; import java.lang.annotation.Annotation; import java.lang.management.ManagementFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import javax.annotation.Nonnull; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.JMException; import javax.management.MBeanRegistrationException; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.modelmbean.InvalidTargetObjectTypeException; import org.apache.log4j.Logger; import org.commons.jconfig.annotations.Config; import org.commons.jconfig.annotations.ConfigGet; import org.commons.jconfig.annotations.ConfigResource; import org.commons.jconfig.annotations.ConfigResourceId; import org.commons.jconfig.annotations.ConfigSet; import org.commons.jconfig.datatype.ByteValue; import org.commons.jconfig.datatype.TimeValue; import org.commons.jconfig.datatype.TypeFormatException; import org.commons.jconfig.datatype.ValueType; import org.commons.jconfig.internal.ConcurrentLRUCache; import org.commons.jconfig.internal.ConfigAdapter; import org.commons.jconfig.internal.ConfigAdapterJson; import org.commons.jconfig.internal.ConfigAdapterProperties; import org.commons.jconfig.internal.ConfigManagerCache; import org.commons.jconfig.internal.ScanClassPath; import org.commons.jconfig.internal.jmx.ConfigManagerJmx; import org.commons.jconfig.internal.jmx.ConfigManagerJvm; import org.commons.jconfig.internal.jmx.JmxUtil; import org.commons.jconfig.internal.jmx.LoadAppConfigsNotification; import com.google.gson.JsonParser; /** * ConfigManager class is used to create a instance of the config class loaded * with the correct values, multiple calls to the ConfigManager for the same * config might return the same instance or a different instance depending if * the config values where changed of not. * * Example code: <code> * App1Config config = ConfigManager.INSTANCE.getConfig(App1Config.class); * String server = config.getAttachmentServerHost(); * </code> * * The ConfigManager job is to expose the Config object on the JMX layer of the * JVM and to return config objects fully loaded with the correct config values, * set by the ConfigLoader or loaded from a config file. * * By default ConfigManager would use the method name without "get|set" part to * load keys from config files. The @ConfigResourceId should be avoided and was * added to provide backwards compatibility with old config files. * * NOTE: ConfigManager is still under development * FIXME fsg 120411 This code has numerous thread-safety problems. */ public enum ConfigManager { INSTANCE; /** Config cache */ private ConfigManagerCache configManagerCache; ConfigManager() { configManagerCache = new ConfigManagerCache(this); } /** * the logger */ private final Logger logger = Logger.getLogger(ConfigManager.class); /** Config can be registered to JMX only once. This set verifies if config is already registered */ private final HashSet<String> registerConfigCache = new HashSet<String>(); /** * cache of fetched config objects. Protect with Atomic Reference. Multiple * readers (getConfig) Multiple writers (LoadAppConfigsNotification and * getConfig). 80000 threads, 200 config instances per thread = 16000000 * million. */ private final ConcurrentLRUCache<String, Object> configObjectsCache = new ConcurrentLRUCache<String, Object>(10000); private final Object waitLoaderLock = new Object(); private volatile boolean isLoaderDone = false; private volatile boolean configManagerInitialized = false; private final Object initLock = new Object(); private volatile int initCount = 0; private Set<Class<?>> annotatedClazzez = null; /** * Generate a set of annotated classes by scanning all path's in classpath */ private void scanAnnotatedClasses() { long start = System.nanoTime(); ScanClassPath<Config> scanClasses = new ScanClassPath<Config>(Config.class, Arrays.asList("org.commons.jconfig.")); annotatedClazzez = scanClasses.scanAnnotatedClasses(); for (Class<?> configClass : annotatedClazzez) { try { // TODO: maybe this is exposed to early. if (!registerConfigCache.contains(configClass.getName())) { JmxUtil.registerConfigObject(this, configClass, getAppName()); registerConfigCache.add(configClass.getName()); } logger.info("Registered config " + configClass.getName() + " to JMX"); } catch (JMException e) { throw new ConfigRuntimeException("ConfigManager JMX fatal exception:", e); } catch (InvalidTargetObjectTypeException e) { throw new ConfigRuntimeException("ConfigManager JMX fatal exception:", e); } } logger.info("ScanClassPath.scanAnnotatedClasses: " + ((System.nanoTime()- start)/1000000) + " ms"); } /** * Load config cache from configurations adapters, and registers * ConfigManager JMX beans. * * @param appName */ private void initialize() { if (configManagerInitialized) { return; } synchronized (initLock) { while (initCount != 0) { if (configManagerInitialized) { return; } try { initLock.wait(500); } catch (InterruptedException e) { throw new ConfigRuntimeException("ConfigManager JMX fatal exception:", e); } } initCount++; } /* Register config manager bean if not registered yet. */ try { ObjectName beanName = new ObjectName(ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName()); if (!ManagementFactory.getPlatformMBeanServer().isRegistered(beanName)) { try { ManagementFactory.getPlatformMBeanServer().registerMBean(new ConfigManagerJmx(getAppName()), beanName); } catch (InstanceAlreadyExistsException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName(), e); } catch (MBeanRegistrationException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName(), e); } catch (NotCompliantMBeanException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName(), e); } } } catch (MalformedObjectNameException e) { throw new ConfigRuntimeException("ConfigManager JMX fatal exception:", e); } try { getInternalConfig(); logger.info("Resize config cache to " + internalConfig.getMaxCacheSize().intValue() + "."); // resize cache to config value configObjectsCache.setMaxSize(internalConfig.getMaxCacheSize().intValue()); if (annotatedClazzez == null) { scanAnnotatedClasses(); } if (internalConfig.getLoadFrom().equals("JMX")) { isLoaderDone = false; long endTime = System.currentTimeMillis() + internalConfig.getConfigLoaderSyncInterval().toMillis(); synchronized (waitLoaderLock) { try { logger.error("Waiting for ConfigLoader to set the values for " + internalConfig.getConfigLoaderSyncInterval().toSeconds() + "s"); while (!isLoaderDone && (endTime > System.currentTimeMillis())) { waitLoaderLock.wait(500); } } catch (InterruptedException e) { Thread.interrupted(); throw new ConfigRuntimeException("Failed to attach to ConfigLoader jvm: ", e); } } if (!isLoaderDone) { throw new ConfigRuntimeException("Failed to load config from ConfigLoader after " + internalConfig.getConfigLoaderSyncInterval()); } } else { for (Class<?> configClass : annotatedClazzez) { ConfigResource anno = configClass.getAnnotation(ConfigResource.class); ConfigAdapter<String> configAdapter = null; if (anno != null) { String uri = anno.name(); if (uri != null) { try { if (uri.toLowerCase().endsWith(".json")) { configAdapter = new ConfigAdapterJson(uri, UTF8, internalConfig); configAdapter.loadValue(configManagerCache); } else if (uri.toLowerCase().endsWith(".properties")) { configAdapter = new ConfigAdapterProperties(uri, UTF8, internalConfig); configAdapter.loadValue(configManagerCache); } } catch (ConfigException e) { /* Catch here because we do not want to fail initialize if one config is bad */ logger.error("Error loading config " + uri + " ", e); } } } } //trigger flipping of cache resetAndFlipCache(); } configManagerInitialized = true; } finally { initCount--; } } private String appName = "ConfigManager_" + System.nanoTime(); private String getAppName() { return appName; } /** * LoadAppConfigsNotification succeeded * */ public void resetAndFlipCache() { configManagerCache.flipCache(); configObjectsCache.clear(); setLoadingDone(); } /** * Inner class that will handle the notifications from the configLoader */ public class ConfigLoaderListener implements NotificationListener { private final ConfigManager manager; ConfigLoaderListener(final ConfigManager manager) { this.manager = manager; } @Override public void handleNotification(final Notification notification, final Object handback) { if (notification instanceof LoadAppConfigsNotification) { LoadAppConfigsNotification acn = (LoadAppConfigsNotification) notification; if (acn.getAppName().equals(manager.getAppName())) { if (acn.getResult() == true) { logger.info("ConfigLoader LoadAppConfigsNotification for " + manager.getAppName() + " returned true"); ConfigManager.INSTANCE.resetAndFlipCache(); } else { logger.error("ConfigLoader LoadAppConfigsNotification for " + manager.getAppName() + " returned false"); } } } } }; /** * Returns a instance of the Config object fully loaded with the correct * values. * * Multiple calls to this methods might return the same or a different * instance of the config object, depending changes to the config values. * * @param <T> * - Config Type * @param classDefinition * - local config Class * @return - a instance of type <T> of the config class * @throws KeyNotFound */ @Deprecated protected <T> T getConfig(final Class<T> classDefinition) throws KeyNotFound { return getConfig(classDefinition, ConfigContext.EMPTY); } /** * Returns an instance of the specified configuration class {@code ClassDefinition}, fully * loaded with all the values specified in the configuration source (file), conditioned by * the settings in the specified {@code context}. * * @param <T> the type of configuration object returned * @param classDefinition the class definition for the desired configuration object * @param context an application-specific collection of configuration settings varying * according to the application environment (farm, colo, host, etc); if the application * has no context adjustments to the configuration, {@code ConfigContext.Empty} may be used. * @return an instance of {@code classDefinition} with the appropriate values loaded * @throws KeyNotFound if there are inconsistencies in the specification of parameters * that prevent loading the returned object with valid values */ @Nonnull public <T> T getConfig(final Class<T> classDefinition, final ConfigContext context) throws KeyNotFound { // initialize on happens ounce initialize(); SortedSet<String> contextSet = configManagerCache.getContextTypes(classDefinition); // return cached entry @SuppressWarnings("unchecked") T config = (T) configObjectsCache.get(getConfigListEntryName(classDefinition, context, contextSet)); if (config != null) { return config; } try { // Validates if class has @Config annotation Config configAnno = getAnnoConfig(classDefinition); configAnno.description(); config = classDefinition.newInstance(); // Initialize Config Object buildConfigObject(config, context, UTF8, configManagerCache); configObjectsCache.put(getConfigListEntryName(classDefinition, context, contextSet), config); return config; } catch (InstantiationException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (IllegalAccessException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } } private ConfigManagerConfig internalConfig = null; /** * Sets the AppName for the current instance of ConfigManager, this is used * By the ConfigLoader to select Application specific modules. By default a * random AppName will be generated for a ConfigManager Instance. This * method also register a JMX MBean with the application name. * * @param appName */ public void setAppName(final String appName) { this.appName = appName; /* Register our MBean in the JVM */ try { ObjectName name = new ObjectName(ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName()); if (ManagementFactory.getPlatformMBeanServer().isRegistered(name)) { ManagementFactory.getPlatformMBeanServer().unregisterMBean(name); } ManagementFactory.getPlatformMBeanServer().registerMBean(new ConfigManagerJmx(appName), name); } catch (InstanceAlreadyExistsException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName(), e); } catch (MBeanRegistrationException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName(), e); } catch (NotCompliantMBeanException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME + getAppName(), e); } catch (MalformedObjectNameException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME, e); } catch (InstanceNotFoundException e) { throw new ConfigRuntimeException("Failed to register JMX bean: " + ConfigManagerJvm.CONFIG_MGR_MBEAN_NAME, e); } if (annotatedClazzez == null) { scanAnnotatedClasses(); } initialize(); } /** * Fetches ConfigManagerConfig from json file. The config file needs to be in * classpath or else config with default values will be initialized. */ private synchronized ConfigManagerConfig getInternalConfig() { if (internalConfig == null) { ConfigManagerConfig config = new ConfigManagerConfig(); ConfigResource anno = ConfigManagerConfig.class.getAnnotation(ConfigResource.class); ConfigAdapter<String> configAdapter = null; ConfigManagerCache localCache = new ConfigManagerCache(this); configAdapter = new ConfigAdapterJson(anno.name(), UTF8, config); try { configAdapter.loadValue(localCache); } catch (ConfigException e) { logger.warn("Fail to load ConfigManager config :" + anno.name(), e); } localCache.flipCache(); buildConfigObject(config, ConfigContext.EMPTY, UTF8, localCache); internalConfig = config; } return internalConfig; } private static Charset UTF8 = Charset.forName("UTF-8"); /** * Populates config instance with given context * * @param config * Config object to be populated by loading the values from * either json or properties file. * @param uri * @param charset * @param localCache * @throws KeyNotFound */ public <T> T buildConfigObject(final T config, final ConfigContext context, final Charset charset, final ConfigManagerCache localCache) throws KeyNotFound { /* * Iterate through all the methods and set values */ Method[] methods = config.getClass().getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(ConfigGet.class)) { ConfigGet configGet = getAnnoConfigGet(method); ValueType valueType = configGet.type(); configGet.defaultValue(); String methodName = "set" + method.getName().substring(3); @SuppressWarnings("rawtypes") Class[] par = new Class[1]; par[0] = valueType.classDefinition(); ConfigSet configSet = getConfigSet(config, methodName, par); /* * Generate fieldList for which we need to perform lookup. Field * can be specified in 3 formats. 1. Member variable name 2. * Complete address for member variable name along with pkg * names 3. Specified separately by annotation @ConfigResourceId * * fieldList = [Timeout, * common.config.App2Config.Timeout] * * Look for each field in configFormat instance for it value. */ List<String> fileIdList = new ArrayList<String>(); try { ConfigResourceId configResId = getAnnoConfigResourceId(config, methodName, par); fileIdList.add(configResId.value()); } catch (KeyNotFound e) { // If missing just use the other 2 below } fileIdList.add(method.getName().substring(3)); fileIdList.add(config.getClass().getName() + "." + method.getName().substring(3)); if(!localCache.isModuleLoaded(config)) { ConfigResource anno = config.getClass().getAnnotation(ConfigResource.class); String uri = config.getClass().getCanonicalName(); if (anno != null) { uri = anno.name(); } logger.warn("Config file for module "+ uri +" was not found"); } String value = null; for (String fileId : fileIdList) { value = localCache.get(config, context, fileId, null); if (value != null) { break; } } loadfromValue(config, configGet, configSet, methodName, valueType, value); } } return config; } /** * Converts the value to appropriate datatype and set it. * * @param config * Config object whose member variable needs to be set * @param methodName * SetMethod which needs to be set * @param valueType * DataType of value * @param value * Actual value which needs to be set * * @throws KeyNotFound */ public <T> void loadfromValue(final Object config, final ConfigGet configGet, final ConfigSet configSet, final String methodName, final ValueType valueType, final T value) throws KeyNotFound { if (configSet.useDefault()) { try { if (value != null) { setKey(config, methodName, valueType, value); if (logger.isTraceEnabled()) { logger.trace("Called " + config.getClass().getName() + "." + methodName + "(" + value + ")"); } } else { setKey(config, methodName, valueType, configGet.defaultValue()); if (logger.isTraceEnabled()) { logger.trace("Called " + config.getClass().getName() + "." + methodName + "(" + configGet.defaultValue() + ") with default"); } } } catch (TypeFormatException e) { // Set to default value in case of exception logger.error("Invalid value, using default.", e); setKey(config, methodName, valueType, configGet.defaultValue()); if (logger.isTraceEnabled()) { logger.trace("Called " + config.getClass().getName() + "." + methodName + "(" + configGet.defaultValue() + ") with default"); } } } else { try { if (value != null) { setKey(config, methodName, valueType, value); if (logger.isTraceEnabled()) { logger.trace("Called " + config.getClass().getName() + "." + methodName + "(" + value + ")"); } } else { throw new KeyNotFound("ConfigManager: key " + methodName.substring(3) + " is required and is currently missing"); } } catch (TypeFormatException e) { throw new KeyNotFound("ConfigManager: key " + methodName.substring(3) + " is required, but unabled to parse value ", e); } } } /** * * @param valueType * @param value * @return */ private Object convertValue(final ValueType valueType, final String value) { if (ValueType.Number == valueType) { return Double.parseDouble(value); } else if (ValueType.Boolean == valueType) { return Boolean.parseBoolean(value); } else if (ValueType.String == valueType) { return value; } else if (ValueType.StringList == valueType) { return Arrays.asList(value.split(":")); } else if (ValueType.TimeList == valueType) { String[] valueArr = value.split(":"); List<TimeValue> listTimeValue = new ArrayList<TimeValue>(); for (String timeValue : valueArr) { listTimeValue.add(TimeValue.parse(timeValue)); } return listTimeValue; } else if (ValueType.Time == valueType) { return TimeValue.parse(value); } else if (ValueType.Bytes == valueType) { return ByteValue.parse(value); } else if (ValueType.Json == valueType) { JsonParser parser = new JsonParser(); return parser.parse(value); } else { throw new ConfigRuntimeException("ConfigManager type " + valueType + " is not supported."); } } /** * * @param <T> * @param config * @param methodName * @param valueType * @param value */ private <T> void setKey(final Object config, final String methodName, final ValueType valueType, final T value) { @SuppressWarnings("rawtypes") Class[] par = new Class[1]; par[0] = valueType.classDefinition(); Method method = getMethodSet(config, methodName, par); try { Object[] params = new Object[1]; if ((value instanceof String) && (ValueType.String != valueType)) { params[0] = convertValue(valueType, (String) value); } else { params[0] = value; } method.invoke(config, params); } catch (TypeFormatException e) { e.setKeyName(methodName.substring(3)); throw e; } catch (IllegalArgumentException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (IllegalAccessException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (InvocationTargetException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } } public <T> boolean containsKey(final Class<T> classDefinition, final String keyName) { return containsMethodGet(getConfig(classDefinition, ConfigContext.EMPTY), keyName); } public <T> Boolean getValueAsBoolean(final Class<T> classDefinition, final String keyName) { return (Boolean) getValueAsObject(getConfig(classDefinition, ConfigContext.EMPTY), keyName); } public <T> Number getValueAsNumber(final Class<T> classDefinition, final String keyName) { return (Number) getValueAsObject(getConfig(classDefinition, ConfigContext.EMPTY), keyName); } public <T> String getValueAsString(final Class<T> classDefinition, final String keyName) { return (String) getValueAsObject(getConfig(classDefinition, ConfigContext.EMPTY), keyName); } /** * * @param <T> * @param config * @param keyName * @return */ private <T> boolean containsMethodGet(final T config, final String keyName) { Method[] methods = config.getClass().getMethods(); for (Method method : methods) { if (method.getName().equals("get" + keyName)) { return true; } } return false; } /** * * @param <T> * @param config * @param keyName * @return */ private <T> Object getValueAsObject(final T config, final String keyName) { try { @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) config.getClass(); Class<?>[] par = null; Method method = clazz.getMethod("get" + keyName, par); return method.invoke(config, new Object[0]); } catch (IllegalArgumentException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (IllegalAccessException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (InvocationTargetException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (SecurityException e) { throw new ConfigRuntimeException("ConfigManager fatal exception:", e); } catch (NoSuchMethodException e) { throw new KeyNotFound("ConfigManager: method get" + keyName + " is required and is currently missing for class " + config.getClass()); } } /** * * @param <T> * @param config * @param methodName * @param parameterTypes * @return */ private <T> ConfigSet getConfigSet(final T config, final String methodName, final Class<?>... parameterTypes) { try { Method method = config.getClass().getMethod(methodName, parameterTypes); if (method.isAnnotationPresent(ConfigSet.class)) { return method.getAnnotation(ConfigSet.class); } else { throw new KeyNotFound("ConfigManager: Method annotation ConfigSet is missing for method " + methodName); } } catch (SecurityException e) { throw new ConfigRuntimeException("ConfigManager: method " + methodToString(methodName, parameterTypes) + " not found in class " + config.getClass(), e); } catch (NoSuchMethodException e) { throw new KeyNotFound("ConfigManager: method " + methodToString(methodName, parameterTypes) + " is required and is currently missing for class " + config.getClass(), e); } } /** * * @param <T> * @param config * @param methodName * @param parameterTypes * @return */ private <T> Method getMethodSet(final T config, final String methodName, final Class<?>... parameterTypes) { try { Method method = config.getClass().getMethod(methodName, parameterTypes); if (method.isAnnotationPresent(ConfigSet.class)) { return method; } else { throw new KeyNotFound("ConfigManager: Method annotation ConfigSet is missing for method " + methodName); } } catch (SecurityException e) { throw new ConfigRuntimeException("ConfigManager: method " + methodToString(methodName, parameterTypes) + " not found in class " + config.getClass(), e); } catch (NoSuchMethodException e) { throw new KeyNotFound("ConfigManager: method " + methodToString(methodName, parameterTypes) + " is required and is currently missing for class " + config.getClass(), e); } } /** * * @param methodName * @param parameterTypes * @return */ private String methodToString(final String methodName, final Class<?>... parameterTypes) { StringBuilder params = new StringBuilder(); boolean first = true; for (@SuppressWarnings("rawtypes") Class clazz : parameterTypes) { if (!first) { params.append(", "); first = false; } params.append(clazz.getName()); } return methodName + "(" + params.toString() + ")"; } /** * * @param method * @return */ private ConfigGet getAnnoConfigGet(final Method method) { Annotation[] annotations = method.getDeclaredAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof ConfigGet) { ConfigGet configGet = (ConfigGet) annotation; return configGet; } } throw new ConfigRuntimeException("ConfigManager: ConfigGet annotation is missing from method " + method); } /** * * @param <T> * @param classDefinition * @return */ private <T> Config getAnnoConfig(final Class<T> classDefinition) { Annotation[] annotations = classDefinition.getDeclaredAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof Config) { Config config = (Config) annotation; return config; } } throw new ConfigRuntimeException("ConfigManager: Class is not annotated with @Config class annotation: " + classDefinition.getName()); } /** * * @param <T> * @param classDefinition * @return */ @SuppressWarnings("unused") private <T> ConfigResource getAnnoConfigResource(final Class<T> classDefinition) { Annotation[] annotations = classDefinition.getDeclaredAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof ConfigResource) { ConfigResource config = (ConfigResource) annotation; return config; } } throw new ConfigRuntimeException( "ConfigManager: Class is not annotated with @ConfigResource class annotation: " + classDefinition.getName()); } /** * * @param config * @param methodName * @param parameterTypes * @return */ private ConfigResourceId getAnnoConfigResourceId(final Object config, final String methodName, final Class<?>... parameterTypes) { Method method = getMethodSet(config, methodName, parameterTypes); if (method.isAnnotationPresent(ConfigResourceId.class) && method.getName().equals(methodName)) { return method.getAnnotation(ConfigResourceId.class); } else { throw new KeyNotFound("ConfigManager: method " + methodName + " is not annotated with @ConfigResourceId"); } } /** * * * @param <T> * @param classDefinition * @param SetsKey * @return */ private <T> String getConfigListEntryName(final Class<T> classDefinition, final ConfigContext context, final SortedSet<String> contextSet) { return classDefinition.getName() + context.getUniqueId(contextSet); } public ConfigManagerCache getCache() { return configManagerCache; } /** * Set to true when config values are loaded to Config Manager cache */ public void setLoadingDone() { isLoaderDone = true; } }