/* * 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.context; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.kaleidofoundry.core.config.Configuration; import org.kaleidofoundry.core.config.ConfigurationAdapter; import org.kaleidofoundry.core.config.ConfigurationChangeEvent; import org.kaleidofoundry.core.config.ConfigurationFactory; import org.kaleidofoundry.core.config.ConfigurationListener; import org.kaleidofoundry.core.lang.annotation.ThreadSafe; import org.kaleidofoundry.core.plugin.PluginHelper; import org.kaleidofoundry.core.plugin.model.Plugin; import org.kaleidofoundry.core.util.Registry; /** * Base implementation for {@link ProviderService} <br/> * The dynamic runtime contexts are registered here, in order to trigger configuration changes<br/> * * @author jraduget * @param <T> */ @ThreadSafe public abstract class AbstractProviderService<T> implements ProviderService<T> { /** Keep generic type (type erasure once compiled) */ protected final Class<T> genericClassInterface; /** runtime context instances */ protected final List<RuntimeContext<T>> dynamicsRegisterContext; /** created configurations listeners instances by configuration */ protected final Map<String, ConfigurationListener> configurationsListeners; protected final Plugin<?> currentPlugin; protected final boolean keepInstanceInRegistry; /** * @param genericClassInterface */ public AbstractProviderService(final Class<T> genericClassInterface) { this.genericClassInterface = genericClassInterface; this.dynamicsRegisterContext = Collections.synchronizedList(new ArrayList<RuntimeContext<T>>()); this.configurationsListeners = new ConcurrentHashMap<String, ConfigurationListener>(); this.currentPlugin = PluginHelper.getInterfacePlugin(genericClassInterface); // do we keep instances in registry Provider providerAnnot = this.genericClassInterface.getAnnotation(Provider.class); this.keepInstanceInRegistry = providerAnnot != null && providerAnnot.scope() == Scope.singleton; registerConfigurationsListeners(); } /** * @return if {@link Provider} is configured has {@link Provider#singletons()} , this method handle the access to the registry */ protected abstract Registry<String, T> getRegistry(); /** * @param context * @return new T instance, build from context informations * @throws ProviderException */ protected abstract T _provides(RuntimeContext<T> context) throws ProviderException; /* * (non-Javadoc) * @see org.kaleidofoundry.core.context.Provider#provides(org.kaleidofoundry.core.context.Context, java.lang.String, java.lang.Class) */ @Override public final T provides(final Context context, final String defaultName, final Class<T> genericClassInterface) throws ProviderException { return provides(RuntimeContext.createFrom(context, defaultName, genericClassInterface)); } /* * (non-Javadoc) * @see org.kaleidofoundry.core.context.ProviderService#provides(org.kaleidofoundry.core.context.RuntimeContext) */ @Override public final T provides(final RuntimeContext<T> context) throws ProviderException { T instance = keepInstanceInRegistry(context) ? getRegistry().get(context.getName()) : null; if (instance == null) { registerDynamicContext(context); instance = _provides(context); if (keepInstanceInRegistry(context)) { getRegistry().put(context.getName(), instance); } } return instance; } /** * must we keep the instance in the registry as unique instance ? * * @param context * @return */ protected boolean keepInstanceInRegistry(RuntimeContext<T> context) { return keepInstanceInRegistry && context.getScope() == Scope.singleton; } /** * register for listening configuration changes (multiples), for all registered configuration */ synchronized void registerConfigurationsListeners() { // for each declared configuration for (final Configuration configuration : ConfigurationFactory.getRegistry().values()) { if (configuration.isUpdateable()) { // clear existing listeners cleanupConfigurationsListeners(configuration); // create configuration change listener that trigger changes to registered runtime context final ConfigurationListener configurationListener = new ConfigurationAdapter() { @Override public void propertiesChanges(final LinkedHashSet<ConfigurationChangeEvent> events) { boolean fireChanges = false; // if one event is bound to current plugin (property name start by the plugin code) -> fire changes to runtime contexts for (final ConfigurationChangeEvent evt : events) { if (evt != null && evt.getPropertyName().startsWith(currentPlugin.getName())) { fireChanges = true; break; } } if (fireChanges) { for (final RuntimeContext<T> rc : dynamicsRegisterContext) { if (rc.isDynamics()) { rc.triggerConfigurationChangeEvents(events); } } } } }; // add and register the new configuration listener configuration.addConfigurationListener(configurationListener); configurationsListeners.put(configuration.getName(), configurationListener); } } } /** * cleanup configuration listeners that have been created by current provider instance * * @param configuration */ synchronized void cleanupConfigurationsListeners(final Configuration configuration) { if (configuration != null) { final ConfigurationListener listener = configurationsListeners.get(configuration.getName()); if (listener != null) { configuration.removeConfigurationListener(listener); } } } /** * registered given runtime context (if it is dynamics) * * @param context */ protected void registerDynamicContext(final RuntimeContext<T> context) { if (context.isDynamics()) { dynamicsRegisterContext.add(context); } } /* * (non-Javadoc) * @see java.lang.Object#finalize() */ @Override protected void finalize() throws Throwable { dynamicsRegisterContext.clear(); super.finalize(); } }