/**
* Copyright (C) 2013 Kametic <epo.jemba@kametic.com>
*
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3, 29 June 2007;
* or any later version
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl-3.0.txt
*
* 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.nuunframework.kernel;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections.iterators.ArrayIterator;
import org.nuunframework.kernel.commons.graph.Graph;
import org.nuunframework.kernel.context.Context;
import org.nuunframework.kernel.context.InitContext;
import org.nuunframework.kernel.context.InitContextInternal;
import org.nuunframework.kernel.internal.InternalKernelGuiceModule;
import org.nuunframework.kernel.plugin.InitState;
import org.nuunframework.kernel.plugin.Plugin;
import org.nuunframework.kernel.plugin.RoundEnvironementInternal;
import org.nuunframework.kernel.plugin.provider.DependencyInjectionProvider;
import org.nuunframework.kernel.plugin.request.BindingRequest;
import org.nuunframework.kernel.plugin.request.ClasspathScanRequest;
import org.nuunframework.kernel.plugin.request.KernelParamsRequest;
import org.nuunframework.kernel.plugin.request.KernelParamsRequestType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Stage;
import com.google.inject.util.Modules;
/**
* @author Epo Jemba
*/
public final class Kernel
{
public static final String NUUN_ROOT_PACKAGE = "nuun.root.package";
public static final String NUUN_NUM_CP_PATH = "nuun.num.classpath.path";
public static final String NUUN_CP_PATH_PREFIX = "nuun.classpath.path.prefix-";
public static final String NUUN_CP_STRATEGY_NAME = "nuun.classpath.strategy.name";
public static final String NUUN_CP_STRATEGY_ADD = "nuun.classpath.strategy.additional";
public static final String KERNEL_PREFIX_NAME = "Kernel-";
private final int MAXIMAL_ROUND_NUMBER = 50;
private final Logger logger;
private static ConcurrentHashMap<String, Kernel> kernels = new ConcurrentHashMap<String, Kernel>();
private final String name;
private final String NUUN_PROPERTIES_PREFIX = "nuun-";
private ServiceLoader<Plugin> pluginLoader;
private boolean spiPluginEnabled = true;
private Map<String, Plugin> plugins = Collections.synchronizedMap(new HashMap<String, Plugin>()); //
private Map<String, Plugin> pluginsToAdd = Collections.synchronizedMap(new HashMap<String, Plugin>()); //
private final InitContextInternal initContext;
private Injector mainInjector;
private final AliasMap kernelParamsAndAlias;
private boolean started = false;
private boolean initialized = false;
private Context context;
private Collection<DependencyInjectionProvider> dependencyInjectionProviders;
private Object containerContext;
private ArrayList<Plugin> orderedPlugins;
private Collection<DependencyInjectionProvider> globalDependencyInjectionProviders;
private List<Iterator<Plugin>> pluginIterators;
private List<Plugin> fetchedPlugins;
private Set<URL> globalAdditionalClasspath;
private RoundEnvironementInternal roundEnv;
private Kernel(String... keyValues)
{
name = KERNEL_PREFIX_NAME+kernels.size();
logger = LoggerFactory.getLogger( Kernel.class.getPackage().getName() +'.' + name());
kernelParamsAndAlias = new AliasMap();
@SuppressWarnings("unchecked")
Iterator<String> it = new ArrayIterator(keyValues);
while (it.hasNext())
{
String key = it.next();
String value = "";
if (it.hasNext())
value = it.next();
logger.info("Adding {} = {} as param to kernel", key, value);
kernelParamsAndAlias.put(key, value);
}
this.initContext = new InitContextInternal(NUUN_PROPERTIES_PREFIX, kernelParamsAndAlias);
}
public String name()
{
return name;
}
public boolean isStarted()
{
return started;
}
public boolean isInitialized()
{
return initialized;
}
/**
*
*
*/
public synchronized void init()
{
if (!initialized)
{
fetchPlugins();
computeAliases();
initRoundEnvironment();
checkPlugins();
fetchGlobalParametersFromPlugins();
initPlugins();
initialized = true;
}
else
{
throw new KernelException("Kernel is already initialized");
}
}
private void initRoundEnvironment()
{
// we initialize plugins
roundEnv = new RoundEnvironementInternal();
for (Plugin plugin : fetchedPlugins)
{
// we pass the roundEnvironment
plugin.provideRoundEnvironment(roundEnv);
}
}
private void fetchGlobalParametersFromPlugins()
{
globalDependencyInjectionProviders = new HashSet<DependencyInjectionProvider>();
globalAdditionalClasspath = Sets.newHashSet();
// Constants from plugin outside rounds
// We pass the container context object for plugin
for (Plugin plugin : plugins.values())
{
plugin.provideContainerContext(containerContext);
String name = plugin.name();
logger.info("Get additional classpath to scan from Plugin {}.", name);
Set<URL> computeAdditionalClasspathScan = plugin.computeAdditionalClasspathScan();
if (computeAdditionalClasspathScan != null && computeAdditionalClasspathScan.size() > 0)
{
logger.info("Adding from Plugin {} start.", name);
for (URL url : computeAdditionalClasspathScan)
{
if (url != null)
{
globalAdditionalClasspath.add(url);
logger.debug(url.toExternalForm());
}
}
logger.info("Adding from Plugin {} end. {} elements.", name, "" + computeAdditionalClasspathScan.size());
}
// Convert dependency manager classes to instances //
DependencyInjectionProvider iocProvider = plugin.dependencyInjectionProvider();
if (iocProvider != null)
{
globalDependencyInjectionProviders.add(iocProvider);
}
}
}
@SuppressWarnings("unchecked")
private void fetchPlugins()
{
// plugin from kernel call api
Iterator<Plugin> iterator1 = pluginsToAdd.values().iterator();
// TODO add unit and test integration test for this
if (spiPluginEnabled)
{
pluginLoader = ServiceLoader.load(Plugin.class, Thread.currentThread().getContextClassLoader());
// plugin from service loader
Iterator<Plugin> iterator2 = pluginLoader.iterator();
pluginIterators = Arrays.asList(iterator2, iterator1);
}
else
{
pluginIterators = Arrays.asList(iterator1);
}
fetchedPlugins = new LinkedList<Plugin>();
for (Iterator<Plugin> iterator : pluginIterators)
{
Plugin plugin;
while (iterator.hasNext())
{
plugin = iterator.next();
fetchedPlugins.add(plugin);
}
}
}
private void computeAliases()
{
// Compute alias
for (Plugin plugin : fetchedPlugins)
{
Map<String, String> pluginKernelParametersAliases = plugin.kernelParametersAliases();
for (Entry<String, String> entry : pluginKernelParametersAliases.entrySet())
{
// entry.getValue() is the alias to give
// entry.getKey() is the key to alias
logger.info("Adding alias parameter \"{}\" to key \"{}\"." , entry.getKey() , entry.getValue());
kernelParamsAndAlias.putAlias(entry.getKey() , entry.getValue());
}
}
//
if (kernelParamsAndAlias.containsKey(NUUN_ROOT_PACKAGE))
{
String tmp = kernelParamsAndAlias.get(NUUN_ROOT_PACKAGE);
fillPackagesRoot(tmp);
}
}
// String getKernelParam(String key)
// {
//
// }
/**
*
*/
private void checkPlugins()
{
logger.info("Plugins initialisation ");
plugins.clear();
List<Class<? extends Plugin>> pluginClasses = new ArrayList<Class<? extends Plugin>>();
for (Plugin plugin : fetchedPlugins) {
String pluginName = plugin.name();
logger.info("checking Plugin {}.", pluginName);
if (!Strings.isNullOrEmpty(pluginName))
{
Object ok = plugins.put(pluginName, plugin);
if (ok == null)
{
// Check for required param
// ========================
Collection<KernelParamsRequest> kernelParamsRequests = plugin.kernelParamsRequests();
Collection<String> computedMandatoryParams = new HashSet<String>();
for (KernelParamsRequest kernelParamsRequest : kernelParamsRequests)
{
if (kernelParamsRequest.requestType == KernelParamsRequestType.MANDATORY)
{
computedMandatoryParams.add(kernelParamsRequest.keyRequested);
}
}
if (kernelParamsAndAlias.containsAllKeys(computedMandatoryParams))
// if (kernelParams.keySet().containsAll(computedMandatoryParams))
{
pluginClasses.add(plugin.getClass());
}
else
{
logger.error("plugin {} miss parameter/s : {}", pluginName, kernelParamsRequests.toString());
throw new KernelException("plugin " + pluginName + " miss parameter/s : " + kernelParamsRequests.toString());
}
}
else
{
logger.error("Can not have 2 Plugin {} of the same type {}. please fix this before the kernel can start.", pluginName, plugin.getClass()
.getName());
throw new KernelException(
"Can not have 2 Plugin %s of the same type %s. please fix this before the kernel can start.", pluginName, plugin.getClass()
.getName());
}
}
else
{
logger.warn("Plugin {} has no correct name it won't be installed.", plugin.getClass());
throw new KernelException("Plugin %s has no correct name it won't be installed.", pluginName);
}
}
// Check for required and dependent plugins
for (Plugin plugin : plugins.values())
{
{
Collection<Class<? extends Plugin>> pluginDependenciesRequired = plugin.requiredPlugins();
if (pluginDependenciesRequired != null && !pluginDependenciesRequired.isEmpty() && !pluginClasses.containsAll(pluginDependenciesRequired))
{
logger.error("plugin {} misses the following plugin/s as dependency/ies {}", plugin.name(), pluginDependenciesRequired.toString());
throw new KernelException("plugin %s misses the following plugin/s as dependency/ies %s", plugin.name(), pluginDependenciesRequired.toString());
}
}
{
Collection<Class<? extends Plugin>> dependentPlugin = plugin.dependentPlugins();
if (dependentPlugin != null && !dependentPlugin.isEmpty() && !pluginClasses.containsAll(dependentPlugin))
{
logger.error("plugin {} misses the following plugin/s as dependee/s {}", plugin.name(), dependentPlugin.toString());
throw new KernelException("plugin %s misses the following plugin/s as dependee/s %s", plugin.name(), dependentPlugin.toString());
}
}
}
}
public synchronized void preStart()
{
}
public synchronized void start()
{
if (initialized)
{
// All bindings will be computed
InternalKernelGuiceModule internalKernelGuiceModule = new InternalKernelGuiceModule(initContext);
InternalKernelGuiceModule internalKernelGuiceModuleOverriding = new InternalKernelGuiceModule(initContext).overriding();
Module mainFinalModule = Modules.override(internalKernelGuiceModule).with(internalKernelGuiceModuleOverriding);
mainInjector = Guice.createInjector( Stage.PRODUCTION , mainFinalModule);
// Here we can pass the mainInjector to the non guice modules
context = mainInjector.getInstance(Context.class);
// 1) inject plugins via injector
// 2) inject context via injector
// 2) start them
for (Plugin plugin : orderedPlugins)
{
mainInjector.injectMembers(plugin);
plugin.start(context);
}
started = true;
}
else
{
throw new KernelException("Kernel is not initialized.");
}
}
public Injector getMainInjector()
{
return mainInjector;
}
public void stop()
{
// 1) stop plugins
//
for (Plugin plugin : plugins.values())
{
plugin.stop();
}
}
/**
*
*/
@SuppressWarnings("unchecked")
private void initPlugins()
{
// We reset the resettable element of initcontext
this.initContext.reset();
// Get plugins requests
// ====================
Collection<Plugin> globalPlugins = plugins.values(); // first round all plugins
// We sort them
ArrayList<Plugin> unOrderedPlugins = new ArrayList<Plugin>(globalPlugins);
logger.info("unordered plugins: (" + unOrderedPlugins.size() + ") " + unOrderedPlugins);
orderedPlugins = sortPlugins(unOrderedPlugins);
logger.info("ordered plugins: (" + orderedPlugins.size() + ") " + orderedPlugins);
Map<String,InitState> states = new HashMap<String, InitState>();
ArrayList<Plugin> roundOrderedPlugins = new ArrayList<Plugin>(orderedPlugins);
do
{ // ROUND ITERATIONS
// we update the number of initialization round.
this.initContext.roundNumber(roundEnv.roundNumber());
logger.info("ROUND " + roundEnv.roundNumber() + " of the kernel initialisation.");
for (Plugin plugin : roundOrderedPlugins)
{
// Configure properties prefixes
String pluginPropertiesPrefix = plugin.pluginPropertiesPrefix();
if (!Strings.isNullOrEmpty(pluginPropertiesPrefix))
{
this.initContext.addPropertiesPrefix(pluginPropertiesPrefix);
}
// Configure package root
String pluginPackageRoot = plugin.pluginPackageRoot();
fillPackagesRoot(pluginPackageRoot);
Collection<ClasspathScanRequest> classpathScanRequests = plugin.classpathScanRequests();
if (classpathScanRequests != null && classpathScanRequests.size() > 0)
{
for (ClasspathScanRequest request : classpathScanRequests)
{
switch (request.requestType)
{
case ANNOTATION_TYPE:
this.initContext.addAnnotationTypesToScan((Class<? extends Annotation>) request.objectRequested);
break;
case ANNOTATION_REGEX_MATCH:
this.initContext.addAnnotationRegexesToScan((String) request.objectRequested);
break;
// case META_ANNOTATION_TYPE:
// this.initContext.addAnnotationTypesToScan((Class<? extends Annotation>)
// request.objectRequested);
// break;
// case META_ANNOTATION_REGEX_MATCH:
// this.initContext.addAnnotationRegexesToScan((String) request.objectRequested);
// break;
case SUBTYPE_OF_BY_CLASS:
this.initContext.addParentTypeClassToScan((Class<?>) request.objectRequested);
break;
case SUBTYPE_OF_BY_TYPE_DEEP:
this.initContext.addAncestorTypeClassToScan((Class<?>) request.objectRequested);
break;
case SUBTYPE_OF_BY_REGEX_MATCH:
this.initContext.addParentTypeRegexesToScan((String) request.objectRequested);
break;
case RESOURCES_REGEX_MATCH:
this.initContext.addResourcesRegexToScan((String) request.objectRequested);
// this.initContext.addTypeClassToScan((Class<?>) request.objectRequested);
break;
case TYPE_OF_BY_REGEX_MATCH:
this.initContext.addTypeRegexesToScan((String) request.objectRequested);
break;
case VIA_SPECIFICATION: // pas encore pluggé
this.initContext.addSpecificationToScan(request.specification);
break;
default:
logger.warn("{} is not a ClasspathScanRequestType a o_O", request.requestType);
break;
}
}
}
Collection<BindingRequest> bindingRequests = plugin.bindingRequests();
if (bindingRequests != null && bindingRequests.size() > 0)
{
for (BindingRequest request : bindingRequests)
{
switch (request.requestType)
{
case ANNOTATION_TYPE:
this.initContext.addAnnotationTypesToBind((Class<? extends Annotation>) request.requestedObject, request.requestedScope);
break;
case ANNOTATION_REGEX_MATCH:
this.initContext.addAnnotationRegexesToBind((String) request.requestedObject, request.requestedScope);
break;
case META_ANNOTATION_TYPE:
this.initContext.addMetaAnnotationTypesToBind((Class<? extends Annotation>) request.requestedObject, request.requestedScope);
break;
case META_ANNOTATION_REGEX_MATCH:
this.initContext.addMetaAnnotationRegexesToBind((String) request.requestedObject, request.requestedScope);
break;
case SUBTYPE_OF_BY_CLASS:
this.initContext.addParentTypeClassToBind((Class<?>) request.requestedObject, request.requestedScope);
break;
case SUBTYPE_OF_BY_TYPE_DEEP:
this.initContext.addAncestorTypeClassToBind((Class<?>) request.requestedObject, request.requestedScope);
break;
case SUBTYPE_OF_BY_REGEX_MATCH:
this.initContext.addTypeRegexesToBind((String) request.requestedObject, request.requestedScope);
break;
case VIA_SPECIFICATION:
this.initContext.addSpecificationToBind(request.specification, request.requestedScope);
break;
default:
logger.warn("{} is not a BindingRequestType o_O !", request.requestType);
break;
}
}
}
} // end plugin request
for (URL url : globalAdditionalClasspath)
{
initContext.addClasspathToScan(url);
}
// We launch classpath scan and store results of requests
this.initContext.executeRequests();
// INITIALISATION
for (Plugin plugin : roundOrderedPlugins)
{
InitContext actualInitContext = this.initContext;
// TODO : we compute dependencies only in round 0 for first version , no other plugin will be given
Collection<Class<? extends Plugin>> requiredPluginsClasses = plugin.requiredPlugins();
Collection<Class<? extends Plugin>> dependentPluginsClasses = plugin.dependentPlugins();
if (roundEnv.roundNumber() == 0 && (requiredPluginsClasses != null && !requiredPluginsClasses.isEmpty()) || (dependentPluginsClasses != null && !dependentPluginsClasses.isEmpty()) )
{
Collection<Plugin> requiredPlugins = filterPlugins(globalPlugins, requiredPluginsClasses);
Collection<Plugin> dependentPlugins = filterPlugins(globalPlugins, dependentPluginsClasses);
actualInitContext = proxyfy(initContext, requiredPlugins,dependentPlugins);
}
String name = plugin.name();
logger.info("initializing Plugin {}.", name);
InitState state = plugin.init(actualInitContext);
states.put(name, state);
}
// After been initialized plugin can give they module
// Configure module //
ArrayList<Plugin> nextRoundOrderedPlugins = new ArrayList<Plugin>();
for (Plugin plugin : roundOrderedPlugins)
{
String name = plugin.name();
InitState state = states.get(name);
if ( state == InitState.INITIALIZED )
{
// Main // =====================================================================
Object pluginDependencyInjectionDef = plugin.dependencyInjectionDef();
if (pluginDependencyInjectionDef != null)
{
if (pluginDependencyInjectionDef instanceof com.google.inject.Module)
{
this.initContext.addChildModule(com.google.inject.Module.class.cast(pluginDependencyInjectionDef));
}
else
{
addModuleViaProvider(name, pluginDependencyInjectionDef);
}
} //
// Overrinding definition
Object dependencyInjectionOverridingDef = plugin.dependencyInjectionOverridingDef();
if (dependencyInjectionOverridingDef != null)
{
if (dependencyInjectionOverridingDef instanceof com.google.inject.Module)
{
this.initContext.addChildOverridingModule(com.google.inject.Module.class.cast(dependencyInjectionOverridingDef));
}
else
{
addModuleViaProvider(name, dependencyInjectionOverridingDef);
}
}
}
else
{ // the plugin is not initialized we add it for a new round
logger.info("Plugin " + name + " is not initialized. We set it for a new round");
nextRoundOrderedPlugins.add(plugin);
}
}
roundOrderedPlugins = nextRoundOrderedPlugins;
// increment round number
roundEnv.incrementRoundNumber();
}
while( ! roundOrderedPlugins .isEmpty() && roundEnv.roundNumber() < MAXIMAL_ROUND_NUMBER );
}
private void fillPackagesRoot(String pluginPackageRoot)
{
if (!Strings.isNullOrEmpty(pluginPackageRoot))
{
String[] packages = null;
packages = pluginPackageRoot.split(",");
for (String pack : packages) {
logger.info("Adding {} as package root", pack);
this.initContext.addPackageRoot(pack.trim());
}
}
}
private void addModuleViaProvider(String name, Object pluginDependencyInjectionDef)
{
DependencyInjectionProvider provider = findDependencyInjectionProvider(pluginDependencyInjectionDef);
if (provider != null)
{
this.initContext.addChildModule(provider.convert(pluginDependencyInjectionDef));
}
else
{
logger.error("Kernel did not recognize module {} of plugin {}", pluginDependencyInjectionDef, name);
throw new KernelException(
"Kernel did not recognize module %s of plugin %s. Please provide a DependencyInjectionProvider.",
pluginDependencyInjectionDef.toString(), name);
}
}
private DependencyInjectionProvider findDependencyInjectionProvider(Object pluginDependencyInjectionDef)
{
DependencyInjectionProvider provider = null;
providerLoop: for (DependencyInjectionProvider providerIt : globalDependencyInjectionProviders)
{
if (providerIt.canHandle(pluginDependencyInjectionDef.getClass()))
{
provider = providerIt;
break providerLoop;
}
}
return provider;
}
private ArrayList<Plugin> sortPlugins(ArrayList<Plugin> unsortedPlugins)
{
Graph graph = new Graph(unsortedPlugins.size());
ArrayList<Plugin> sorted = new ArrayList<Plugin>();
Map<Integer, Plugin> idxPlug = new HashMap<Integer, Plugin>();
Map<Character, Plugin> charPlug = new HashMap<Character, Plugin>();
Map<Plugin, Integer> plugIdx = new HashMap<Plugin, Integer>();
Map<Class<? extends Plugin>, Integer> classPlugIdx = new HashMap<Class<? extends Plugin>, Integer>();
// Add vertices
for (short i = 0; i < unsortedPlugins.size(); i++)
{
char c = (char) i;
Plugin unsortedPlugin = unsortedPlugins.get(i);
Integer pluginIndex = graph.addVertex(c);
charPlug.put(c, unsortedPlugin);
idxPlug.put(pluginIndex, unsortedPlugin);
plugIdx.put(unsortedPlugin, pluginIndex);
classPlugIdx.put(unsortedPlugin.getClass(), pluginIndex);
}
// add edges
for (Entry<Integer, Plugin> entry : idxPlug.entrySet())
{
Plugin source = entry.getValue();
// based on required plugins
for (Class<? extends Plugin> dependencyClass : source.requiredPlugins())
{
int start = classPlugIdx.get(dependencyClass);
int end = plugIdx.get(source);
graph.addEdge(start, end);
}
// based on dependent plugins
for (Class<? extends Plugin> dependencyClass : source.dependentPlugins())
{
int start = plugIdx.get(source); // we inversed
int end = classPlugIdx.get(dependencyClass);
graph.addEdge(start, end);
}
}
// launch the algo
char[] topologicalSort = graph.topologicalSort();
if (topologicalSort != null)
{
for (Character c : topologicalSort)
{
sorted.add(charPlug.get(c));
}
}
else
{
throw new KernelException("Error when sorting plugins : either a Cycle in dependencies or another cause.");
}
return sorted;
}
private Collection<Plugin> filterPlugins(Collection<Plugin> collection, Collection<Class<? extends Plugin>> pluginDependenciesRequired)
{
Set<Plugin> filteredSet = new HashSet<Plugin>();
for (Plugin plugin : collection)
{
if (pluginDependenciesRequired.contains(plugin.getClass()))
{
filteredSet.add(plugin);
}
}
return filteredSet;
}
private InitContext proxyfy(final InitContext initContext, final Collection<Plugin> requiredPlugins , final Collection<Plugin> dependentPlugins)
{
return (InitContext) Proxy.newProxyInstance( //
initContext.getClass().getClassLoader(), //
new Class[] {
InitContext.class
}, //
new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if (method.getName().equals("pluginsRequired"))
{
return requiredPlugins;
}
else if (method.getName().equals("dependentPlugins"))
{
return dependentPlugins;
}
else
{
return method.invoke(initContext, args);
}
}
});
}
void createDependencyInjectionProvidersMap(Collection<Class<?>> dependencyInjectionProvidersClasses)
{
dependencyInjectionProviders = new HashSet<DependencyInjectionProvider>();
for (Class<?> dependencyInjectionProviderClass : dependencyInjectionProvidersClasses)
{
DependencyInjectionProvider injectionDependencyProvider = newInstance(dependencyInjectionProviderClass);
if (injectionDependencyProvider != null)
{
dependencyInjectionProviders.add(injectionDependencyProvider);
}
else
{
throw new KernelException("DependencyInjectionProvider %s can not be instanciated", (Object) dependencyInjectionProviderClass);
}
}
}
/**
* You have only one chance to get the current kernel.
*
* @param keyValues
* @return
*/
public synchronized static KernelBuilderWithPluginAndContext createKernel(String... keyValues)
{
return new KernelBuilderImpl(keyValues);
}
public static interface KernelBuilder
{
Kernel build();
}
public static interface KernelBuilderWithPluginAndContext extends KernelBuilderWithContainerContext, KernelBuilderWithPlugins
{
}
public static interface KernelBuilderWithContainerContext extends KernelBuilder
{
KernelBuilderWithPlugins withContainerContext(Object containerContext);
}
public static interface KernelBuilderWithPlugins extends KernelBuilder
{
KernelBuilderWithPluginAndContext withPlugins(Class<? extends Plugin>... klass);
KernelBuilderWithPluginAndContext withPlugins(Plugin... plugins);
KernelBuilderWithPluginAndContext withoutSpiPluginsLoader();
}
private static class KernelBuilderImpl implements KernelBuilderWithPluginAndContext
{
private final Kernel kernel;
/**
*
*/
public KernelBuilderImpl(String... keyValues)
{
kernel = new Kernel(keyValues);
}
@Override
public Kernel build()
{
if (!kernels.contains(kernel.name))
{
kernels.put(kernel.name, kernel);
return kernel;
}
else
{
throw new KernelException(String.format("A kernel named %s already exists" , kernel.name ));
}
}
@Override
public KernelBuilderWithPlugins withContainerContext(Object containerContext)
{
kernel.addContainerContext(containerContext);
return (KernelBuilderWithPlugins) this;
}
public KernelBuilderWithPluginAndContext withPlugins(java.lang.Class<? extends Plugin>... klass)
{
kernel.addPlugins(klass);
return this;
}
public KernelBuilderWithPluginAndContext withPlugins(Plugin... plugin)
{
kernel.addPlugins(plugin);
return this;
}
@Override
public KernelBuilderWithPluginAndContext withoutSpiPluginsLoader()
{
kernel.spiPluginDisabled();
return this;
}
}
@SuppressWarnings("unchecked")
private <T> T newInstance(Class<?> klass)
{
T instance = null;
try
{
instance = (T) klass.newInstance();
}
catch (InstantiationException e)
{
logger.error("Error when instantiating class " + klass, e);
}
catch (IllegalAccessException e)
{
logger.error("Error when instantiating class " + klass, e);
}
return instance;
}
void addContainerContext(Object containerContext)
{
this.containerContext = containerContext;
}
void spiPluginEnabled()
{
this.spiPluginEnabled = true;
}
void spiPluginDisabled()
{
this.spiPluginEnabled = false;
}
/**
* @param klass
*/
void addPlugins(Class<? extends Plugin>... klass)
{
for (Class<? extends Plugin> class1 : klass)
{
Plugin plugin = newInstance(class1);
if (plugin == null)
{
throw new KernelException("Plugin %s can not be instanciated", (Object) klass);
}
else
{
addPlugin(plugin);
}
}
}
void addPlugins(Plugin... plugins)
{
for (Plugin plugin : plugins)
{
addPlugin(plugin);
}
}
void addPlugin(Plugin plugin)
{
if (!this.started)
{
pluginsToAdd.put(plugin.name(), plugin);
}
else
{
throw new KernelException("Plugin %s can not be added. Kernel already is started", plugin.name());
}
}
static class AliasMap extends HashMap<String, String>
{
private static final long serialVersionUID = 1L;
Map<String, String> aliases = new HashMap<String, String>();
/**
*
*
* @param key the key to alias.
* @param alias the alias to give to the key.
* @return
*/
public String putAlias(String key, String alias)
{
if (super.containsKey(alias))
throw new IllegalArgumentException("alias "+alias+" already exists in map.");
return aliases.put(alias, key);
}
@Override
public String get(Object key)
{
String keyAlias = aliases.get(key);
if (keyAlias == null)
{
return super.get(key);
}
else
{
return super.get(keyAlias);
}
}
public boolean containsAllKeys(Collection<String> computedMandatoryParams)
{
HashSet<String> allKeys = new HashSet<String>();
allKeys.addAll( this.keySet() );
allKeys.addAll(aliases.values());
Collection<String> trans = new HashSet<String>();
for (String s : computedMandatoryParams)
{
String string = aliases.get(s);
if (string != null)
{
trans.add(string);
}
}
return allKeys.containsAll(trans);
}
@Override
public boolean containsKey(Object key)
{
return aliases.containsKey(key) ? true : super.containsKey(key);
}
}
}