/* * Copyright 2008-2014 the original author or authors. * * 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.kaleidofoundry.core.cache; import static org.kaleidofoundry.core.cache.AbstractCacheManager.LOGGER; import static org.kaleidofoundry.core.cache.CacheManagerContextBuilder.FileStoreUri; import static org.kaleidofoundry.core.cache.CacheManagerContextBuilder.ProviderCode; import static org.kaleidofoundry.core.i18n.InternalBundleHelper.CacheMessageBundle; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashSet; import java.util.Map.Entry; import java.util.Set; import org.kaleidofoundry.core.context.AbstractProviderService; import org.kaleidofoundry.core.context.ProviderException; import org.kaleidofoundry.core.context.RuntimeContext; import org.kaleidofoundry.core.i18n.InternalBundleEnum; import org.kaleidofoundry.core.lang.annotation.NotNull; import org.kaleidofoundry.core.lang.annotation.ThreadSafe; import org.kaleidofoundry.core.plugin.Declare; import org.kaleidofoundry.core.plugin.PluginFactory; import org.kaleidofoundry.core.plugin.model.Plugin; import org.kaleidofoundry.core.util.Registry; import org.kaleidofoundry.core.util.StringHelper; /** * Registry & provider of {@link CacheManager} instances. * Please see {@link CacheManagerFactory} javadoc for more details. * * @author jraduget * @see CacheManagerFactory */ @ThreadSafe public class CacheManagerProvider extends AbstractProviderService<CacheManager> { // default cache provider code to use static String DEFAULT_CACHE_PROVIDER = CacheProvidersEnum.local.name(); // set of reserved cache name static Set<String> RESERVED_CACHE_NAME = Collections.synchronizedSet(new HashSet<String>());; // main configuration registry instance (shared between providers instances) private static final Registry<String, CacheManager> REGISTRY = new Registry<String, CacheManager>(); // does default init have occurred private static boolean INIT_LOADED = false; /** * load the default cache provider to use * * @param cacheProviderCode * @see CacheProvidersEnum */ public static synchronized void init(final String cacheProviderCode) { if (!INIT_LOADED) { Plugin<CacheManager> defaultCachePlugin = null; boolean userCacheImplNotFound = false; if (cacheProviderCode != null && !"".equals(cacheProviderCode.trim())) { defaultCachePlugin = findCacheManagerByPluginCode(CacheConstants.CacheManagerPluginName + "." + cacheProviderCode); if (defaultCachePlugin == null) { userCacheImplNotFound = true; } } DEFAULT_CACHE_PROVIDER = defaultCachePlugin != null ? defaultCachePlugin.getName() : CacheConstants.CacheManagerPluginName + "." + CacheProvidersEnum.local.name(); if (userCacheImplNotFound && StringHelper.isEmpty(cacheProviderCode)) { LOGGER.warn(CacheMessageBundle.getMessage("cacheprovider.notfound", cacheProviderCode)); } LOGGER.info(CacheMessageBundle.getMessage("cacheprovider.customize")); LOGGER.info(CacheMessageBundle.getMessage("cacheprovider.default", DEFAULT_CACHE_PROVIDER)); INIT_LOADED = true; } } /** * @return code of the default cache provider (that have been set) */ public static String getDefaultCacheProvider() { return DEFAULT_CACHE_PROVIDER; } /** * @param genericClass */ public CacheManagerProvider(final Class<CacheManager> genericClass) { super(genericClass); } /** * @return default cache manager provider (java system env. will be used, see class javadoc header) * @throws ProviderException encapsulate class implementation constructor call error (like {@link NoSuchMethodException}, * {@link InstantiationException}, {@link IllegalAccessException}, {@link InvocationTargetException}) */ @NotNull public CacheManager provides() throws ProviderException { return provides(DEFAULT_CACHE_PROVIDER); } /** * @param providerCodeCode * @return cache manager using specific providerCodeCode * @throws ProviderException encapsulate class implementation constructor call error (like {@link NoSuchMethodException}, * {@link InstantiationException}, {@link IllegalAccessException}, {@link InvocationTargetException}) */ @NotNull public CacheManager provides(@NotNull final String providerCodeCode) throws ProviderException { return provides(providerCodeCode, null, new RuntimeContext<CacheManager>(providerCodeCode, CacheManager.class)); } /** * @param context * @return cache manager if context specify a specific provider, it will use it, otherwise default cache manager * provider (java system env. will be used, see class java-doc header) * @throws ProviderException encapsulate class implementation constructor call error (like {@link NoSuchMethodException}, * {@link InstantiationException}, {@link IllegalAccessException}, {@link InvocationTargetException}) */ @NotNull @Override public CacheManager _provides(final RuntimeContext<CacheManager> context) throws ProviderException { final String providerCode = context.getString(ProviderCode, DEFAULT_CACHE_PROVIDER); final String providerConfiguration = context.getString(FileStoreUri); return provides(providerCode, providerConfiguration, context); } /** * @param providerCode * @param configuration * @return cache manager instance * @throws CacheConfigurationException cache configuration resource exception * @throws ProviderException encapsulate class implementation constructor call error (like {@link NoSuchMethodException}, * {@link InstantiationException}, {@link IllegalAccessException}, {@link InvocationTargetException}) */ @NotNull public CacheManager provides(@NotNull final String providerCode, final String configuration) throws ProviderException { return provides(providerCode, configuration, new RuntimeContext<CacheManager>(providerCode, CacheManager.class)); } /** * @param providerCode * @param configuration * @param context * @return cache manager instance * @throws ProviderException encapsulate class implementation constructor call error (like {@link NoSuchMethodException}, * {@link InstantiationException}, {@link IllegalAccessException}, {@link InvocationTargetException}) */ @NotNull public CacheManager provides(@NotNull final String providerCode, final String configuration, @NotNull final RuntimeContext<CacheManager> context) throws ProviderException { RuntimeContext<CacheManager> newNamedContext = context; if (StringHelper.isEmpty(context.getName())) { newNamedContext = new RuntimeContext<CacheManager>(providerCode, CacheManager.class); RuntimeContext.copyFrom(context, newNamedContext); } CacheManager cacheManager = getRegistry().get(newNamedContext.getName()); if (cacheManager == null) { cacheManager = create(providerCode, configuration, newNamedContext); getRegistry().put(newNamedContext.getName(), cacheManager); } return cacheManager; } /** * @return cache manager registry. each instance provided will be registered here */ @Override public Registry<String, CacheManager> getRegistry() { return REGISTRY; } /** * @return set of some reserved cache name<br/> * You can add your own as you need */ public static Set<String> getReservedCacheName() { return RESERVED_CACHE_NAME; } /** * @param providerCode * @param configuration * @param context * @return new cache manager instance * @throws ProviderException encapsulate class implementation constructor call error (like {@link NoSuchMethodException}, * {@link InstantiationException}, {@link IllegalAccessException}, {@link InvocationTargetException}) */ protected static synchronized CacheManager create(@NotNull final String providerCode, final String configuration, @NotNull final RuntimeContext<CacheManager> context) throws ProviderException { // only for optimization reasons if (providerCode.equalsIgnoreCase(CacheProvidersEnum.local.name())) { return new LocalCacheManagerImpl(configuration, context); } // dynamic lookup into register plugin final Set<Plugin<CacheManager>> pluginImpls = PluginFactory.getImplementationRegistry().findByInterface(CacheManager.class); // scan each @Declare file store implementation, to get one which handle the given implementation code for (final Plugin<CacheManager> pi : pluginImpls) { final Class<? extends CacheManager> impl = pi.getAnnotatedClass(); try { final Declare declarePlugin = impl.getAnnotation(Declare.class); if (declarePlugin.value().endsWith(providerCode)) { final Constructor<? extends CacheManager> constructor = impl.getConstructor(String.class, RuntimeContext.class); return constructor.newInstance(configuration, context); } } catch (final NoSuchMethodException e) { throw new ProviderException("context.provider.error.NoSuchConstructorException", impl.getName(), "String configuration, RuntimeContext<CacheManager> context"); } catch (final InstantiationException e) { throw new ProviderException("context.provider.error.InstantiationException", impl.getName(), e.getMessage()); } catch (final IllegalAccessException e) { throw new ProviderException("context.provider.error.IllegalAccessException", impl.getName(), "String configuration, RuntimeContext<CacheManager> context"); } catch (final InvocationTargetException e) { if (e.getCause() instanceof CacheConfigurationException) { throw (CacheConfigurationException) e.getCause(); } else { throw new ProviderException("context.provider.error.InvocationTargetException", e.getCause(), impl.getName(), "String configuration, RuntimeContext<CacheManager> context", e.getCause().getClass().getName(), e.getMessage()); } } } throw new ProviderException(new CacheException("cacheprovider.illegal", providerCode)); } /* * find cache manager by plugin code */ @SuppressWarnings("unchecked") private static Plugin<CacheManager> findCacheManagerByPluginCode(@NotNull final String code) { final Plugin<?> plugin = PluginFactory.getImplementationRegistry().get(code); try { return (Plugin<CacheManager>) plugin; } catch (final ClassCastException cce) { throw new CacheException("cacheprovider.classcasterror", code, plugin.getClass().getTypeParameters()[0].getClass().getName(), CacheManager.class.getName()); } } /** * Free all cache managers instances */ public void destroyAll() { if (INIT_LOADED) { for (Entry<String, CacheManager> entry : REGISTRY.entrySet()) { try { if (InternalBundleEnum.isInternalBundle(entry.getValue().getName())) { entry.getValue().destroyAll(); } } catch (Throwable th) { LOGGER.info(CacheMessageBundle.getMessage("cachemanager.destroyall.error", entry.getKey()), th); } } } INIT_LOADED = false; DEFAULT_CACHE_PROVIDER = CacheProvidersEnum.local.name(); } }