diff --git a/core/src/main/java/io/nuun/kernel/core/internal/AliasMap.java b/core/src/main/java/io/nuun/kernel/core/internal/AliasMap.java index c6578fa..8426bcd 100644 --- a/core/src/main/java/io/nuun/kernel/core/internal/AliasMap.java +++ b/core/src/main/java/io/nuun/kernel/core/internal/AliasMap.java @@ -1,73 +1,86 @@ package io.nuun.kernel.core.internal; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; +import io.nuun.kernel.core.KernelException; + +import java.util.*; /** - * - * * @author epo.jemba{@literal @}kametic.com */ -public class AliasMap extends HashMap +public class AliasMap { - private static final long serialVersionUID = 1L; - Map aliases = new HashMap(); + private Map aliases = new HashMap(); + private Map params = new HashMap(); /** - * @param key - * the key to alias. - * @param alias - * the alias to give to the key. - * @return + * @param key the key to alias. + * @param alias the alias to give to the key. + * @return the previous alias corresponding the key */ - public String putAlias(String key, String alias) + public String putAlias(String alias, String key) { - if (super.containsKey(alias)) + if (aliases.containsKey(key)) { - throw new IllegalArgumentException("alias " + alias + " already exists in map."); + throw new IllegalArgumentException("The key \"" + key + "\" to alias is already present in the kernel parameters."); } - return aliases.put(alias, key); + return aliases.put(key, alias); + } + + public String get(String key) + { + List cache = new ArrayList(); + return getWithAlias(key, cache); } - @Override - public String get(Object key) + private String getWithAlias(String key, List cache) { - String keyAlias = aliases.get(key); - if (keyAlias == null) + if (cache.contains(key)) { - return super.get(key); + throw new KernelException("Cycle detected in kernel parameter aliases."); } - else + cache.add(key); + String alias = aliases.get(key); + if (alias == null) { - return super.get(keyAlias); + return params.get(key); + } else + { + return getWithAlias(alias, cache); } } - public boolean containsAllKeys(Collection computedMandatoryParams) + public String put(String key, String value) { - HashSet allKeys = new HashSet(); - allKeys.addAll(keySet()); - allKeys.addAll(aliases.values()); + return params.put(key, value); + } - Collection trans = new HashSet(); - for (String s : computedMandatoryParams) + public Map toMap() + { + Map map = new HashMap(params); + for (Map.Entry entry : aliases.entrySet()) { - String string = aliases.get(s); - if (string != null) + String alias = entry.getKey(); + String paramKey = entry.getValue(); + map.put(alias, get(paramKey)); + } + return Collections.unmodifiableMap(map); + } + + public boolean containsAllKeys(Collection keys) + { + for (String key : keys) + { + if (!containsKey(key)) { - trans.add(string); + return false; } } - - return allKeys.containsAll(trans); + return true; } - @Override - public boolean containsKey(Object key) + public boolean containsKey(String key) { - return aliases.containsKey(key) ? true : super.containsKey(key); + return aliases.containsKey(key) || params.containsKey(key); } } \ No newline at end of file diff --git a/core/src/main/java/io/nuun/kernel/core/internal/KernelCore.java b/core/src/main/java/io/nuun/kernel/core/internal/KernelCore.java index e8c5954..a6fe216 100644 --- a/core/src/main/java/io/nuun/kernel/core/internal/KernelCore.java +++ b/core/src/main/java/io/nuun/kernel/core/internal/KernelCore.java @@ -73,7 +73,7 @@ public final class KernelCore implements Kernel this.name = KERNEL_PREFIX_NAME + kernelIndex.getAndIncrement(); this.logger = LoggerFactory.getLogger(KernelCore.class.getName() + ' ' + name()); this.kernelConfig = kernelConfigurationInternal; - this.requestHandler = new RequestHandler(kernelConfig.kernelParams(), kernelConfig.getClasspathScanMode()); + this.requestHandler = new RequestHandler(kernelConfig.kernelParams().toMap(), kernelConfig.getClasspathScanMode()); this.moduleHandler = new ModuleHandler(kernelConfig); } @@ -85,6 +85,7 @@ public synchronized void init() throw new KernelException("Kernel is already initialized"); } preparePlugins(); + validateMandatoryParams(); fetchPackageRootsFromConfiguration(); extensionManager = new ExtensionManager(pluginRegistry.getPlugins(), Thread.currentThread().getContextClassLoader()); extensionManager.initializing(); @@ -102,12 +103,10 @@ public void preparePlugins() round = new RoundInternal(); DependenciesSpecification dependenciesSpecification = new DependenciesSpecification(facetRegistry); - MandatoryParamsSpecification mandatoryParamsSpecification = new MandatoryParamsSpecification(); for (Plugin plugin : pluginRegistry.getPlugins()) { plugin.provideRound(round); dependenciesSpecification.isSatisfyBy(plugin); - mandatoryParamsSpecification.isSatisfiedBy(plugin, kernelConfig.kernelParams()); addAliasesToKernelParams(plugin); fetchGlobalParametersFrom(plugin); addPackageRootsToRequestHandler(plugin.pluginPackageRoot()); @@ -149,10 +148,10 @@ private void addAliasesToKernelParams(Plugin plugin) { for (Entry entry : plugin.kernelParametersAliases().entrySet()) { - String keyToAlias = entry.getKey(); - String alias = entry.getValue(); + String alias = entry.getKey(); + String keyToAlias = entry.getValue(); logger.info("Adding alias parameter \"{}\" to key \"{}\".", keyToAlias, alias); - kernelConfig.kernelParams().putAlias(keyToAlias, alias); + kernelConfig.kernelParams().putAlias(alias, keyToAlias); } } @@ -211,6 +210,13 @@ private void addPackageRootsToRequestHandler(String pluginPackageRoots) } } + private void validateMandatoryParams() { + MandatoryParamsSpecification mandatoryParamsSpecification = new MandatoryParamsSpecification(); + for (Plugin plugin : pluginRegistry.getPlugins()) { + mandatoryParamsSpecification.isSatisfiedBy(plugin, kernelConfig.kernelParams()); + } + } + private void executeInitializationRounds() { logger.info("Initializing"); @@ -236,7 +242,7 @@ private List callPluginsInitMethod(final List plugins, int round for (Plugin plugin : plugins) { logger.info(" * {} plugin", plugin.name()); - InitContext initContext = new InitContextInternal(kernelConfig.kernelParams(), requestHandler, round, dependencyProvider, plugin.getClass()); + InitContext initContext = new InitContextInternal(kernelConfig.kernelParams().toMap(), requestHandler, round, dependencyProvider, plugin.getClass()); if (plugin.init(initContext) != InitState.INITIALIZED) { nonInitializedPlugins.add(plugin); diff --git a/core/src/main/java/io/nuun/kernel/core/internal/MandatoryParamsSpecification.java b/core/src/main/java/io/nuun/kernel/core/internal/MandatoryParamsSpecification.java index a103d5e..dd7bf98 100644 --- a/core/src/main/java/io/nuun/kernel/core/internal/MandatoryParamsSpecification.java +++ b/core/src/main/java/io/nuun/kernel/core/internal/MandatoryParamsSpecification.java @@ -10,19 +10,23 @@ public class MandatoryParamsSpecification { - public void isSatisfiedBy(Plugin plugin, AliasMap kernelParams) { - Collection kernelParamsRequests = plugin.kernelParamsRequests(); - Collection computedMandatoryParams = new HashSet(); + Collection requestedParams = plugin.kernelParamsRequests(); + Collection mandatoryParams = filterMandatoryParams(requestedParams); + + if (!kernelParams.containsAllKeys(mandatoryParams)) { + throw new KernelException("Plugin " + plugin.name() + " misses parameter/s : " + requestedParams.toString()); + } + } + private Collection filterMandatoryParams(Collection kernelParamsRequests) + { + Collection computedMandatoryParams = new HashSet(); for (KernelParamsRequest kernelParamsRequest : kernelParamsRequests) { if (kernelParamsRequest.requestType == KernelParamsRequestType.MANDATORY) { computedMandatoryParams.add(kernelParamsRequest.keyRequested); } } - - if (!kernelParams.containsAllKeys(computedMandatoryParams)) { - throw new KernelException("Plugin " + plugin.name() + " misses parameter/s : " + kernelParamsRequests.toString()); - } + return computedMandatoryParams; } } diff --git a/core/src/test/java/io/nuun/kernel/core/internal/AliasMapTest.java b/core/src/test/java/io/nuun/kernel/core/internal/AliasMapTest.java new file mode 100644 index 0000000..f456311 --- /dev/null +++ b/core/src/test/java/io/nuun/kernel/core/internal/AliasMapTest.java @@ -0,0 +1,129 @@ +package io.nuun.kernel.core.internal; + +import com.google.common.collect.Lists; +import io.nuun.kernel.core.KernelException; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import static org.assertj.core.data.MapEntry.entry; + +/** + * @author Pierre THIROUIN (pierre.thirouin@ext.inetpsa.com) + */ +public class AliasMapTest +{ + @Test + public void testAccessNormalParam() throws Exception + { + AliasMap aliasMap = new AliasMap(); + aliasMap.put("param", "val1"); + + Assertions.assertThat(aliasMap.get("param")).isEqualTo("val1"); + Assertions.assertThat(aliasMap.containsKey("param")).isTrue(); + } + + @Test + public void testAliasAKernelParam() throws Exception + { + AliasMap aliasMap = new AliasMap(); + aliasMap.put("alias", "val1"); + aliasMap.putAlias("alias", "param"); + + Assertions.assertThat(aliasMap.get("param")).isEqualTo("val1"); + Assertions.assertThat(aliasMap.containsAllKeys(Lists.newArrayList("param", "alias"))).isTrue(); + } + + @Test + public void testCannotAliasAnExistingParameter() throws Exception + { + AliasMap aliasMap = new AliasMap(); + try + { + aliasMap.put("param", "val1"); + aliasMap.putAlias("alias", "param"); + } catch (IllegalArgumentException e) + { + Assertions.assertThat(e).hasMessage("The key \"param\" to alias is already present in the kernel parameters."); + + Assertions.assertThat(aliasMap.containsKey("param")).isTrue(); + } + } + + @Test + public void testContainsKey() throws Exception + { + AliasMap aliasMap = new AliasMap(); + Assertions.assertThat(aliasMap.containsKey("foo")).isFalse(); + Assertions.assertThat(aliasMap.containsAllKeys(Lists.newArrayList("foo", "bar"))).isFalse(); + } + + @Test + public void testComputedMapNotNull() throws Exception + { + Assertions.assertThat(new AliasMap().toMap()).isNotNull(); + } + + @Test + public void testComputedMapContainsParameters() throws Exception + { + final AliasMap aliasMap = new AliasMap(); + aliasMap.put("param1", "val1"); + aliasMap.put("param2", "val2"); + Assertions.assertThat(aliasMap.toMap()).containsExactly(entry("param1", "val1"), entry("param2", "val2")); + } + + @Test + public void testComputedMapContainsAlias() throws Exception + { + final AliasMap aliasMap = new AliasMap(); + aliasMap.put("alias1", "val1"); + aliasMap.put("alias2", "val2"); + aliasMap.putAlias("alias1", "param1"); + aliasMap.putAlias("alias2", "param2"); + Assertions.assertThat(aliasMap.toMap()) + .containsOnly( + entry("param1", "val1"), + entry("param2", "val2"), + entry("alias1", "val1"), + entry("alias2", "val2") + ); + } + + @Test + public void testMultipleAliasIndirection() throws Exception + { + final AliasMap aliasMap = new AliasMap(); + aliasMap.put("alias2", "val1"); + aliasMap.putAlias("alias1", "param1"); + aliasMap.putAlias("alias2", "alias1"); + Assertions.assertThat(aliasMap.toMap()) + .containsOnly( + entry("param1", "val1"), + entry("alias1", "val1"), + entry("alias2", "val1") + ); + } + + @Test + public void testInfiniteIndirection() throws Exception + { + final AliasMap aliasMap = new AliasMap(); + aliasMap.put("alias1", "val1"); + aliasMap.putAlias("alias1", "param1"); + aliasMap.putAlias("param1", "alias1"); + try + { + aliasMap.toMap(); + } catch (KernelException e) + { + Assertions.assertThat(e).hasMessage("Cycle detected in kernel parameter aliases."); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void testComputedMapIsImmutable() throws Exception + { + new AliasMap().toMap().put("foo", "bar"); + Assertions.fail("The map should be immutable"); + } +} diff --git a/core/src/test/java/it/AliasIT.java b/core/src/test/java/it/AliasIT.java index 454ffa0..104eb7f 100644 --- a/core/src/test/java/it/AliasIT.java +++ b/core/src/test/java/it/AliasIT.java @@ -1,8 +1,105 @@ package it; +import io.nuun.kernel.api.Kernel; +import io.nuun.kernel.api.config.KernelConfiguration; +import io.nuun.kernel.api.plugin.InitState; +import io.nuun.kernel.api.plugin.context.InitContext; +import io.nuun.kernel.api.plugin.request.KernelParamsRequest; +import io.nuun.kernel.core.AbstractPlugin; +import io.nuun.kernel.core.NuunCore; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + /** * @author Pierre THIROUIN (pierre.thirouin@ext.inetpsa.com) */ public class AliasIT { + @Test + public void testAliases() throws Exception + { + KernelConfiguration kernelConfig = NuunCore.newKernelConfiguration() + .withoutSpiPluginsLoader() + .param("alias1", "val1") + .param("alias2", "val2") + .addPlugin(AliasProviderPlugin.class) + .addPlugin(AliasUserPlugin.class); + + Kernel kernel = NuunCore.createKernel(kernelConfig); + kernel.init(); + kernel.start(); + + AliasProviderPlugin aliasProvider = (AliasProviderPlugin) kernel.plugins().get("alias-provider"); + Assertions.assertThat(aliasProvider.param1).isEqualTo("val1"); + Assertions.assertThat(aliasProvider.alias1).isEqualTo("val1"); + + AliasUserPlugin aliasUser = (AliasUserPlugin) kernel.plugins().get("alias-user"); + Assertions.assertThat(aliasUser.param2).isEqualTo("val2"); + Assertions.assertThat(aliasUser.alias2).isEqualTo("val2"); + } + + public static class AliasProviderPlugin extends AbstractPlugin + { + String param1; + String alias1; + + @Override + public String name() + { + return "alias-provider"; + } + + @Override + public Collection kernelParamsRequests() + { + return kernelParamsRequestBuilder().mandatory("param1").build(); + } + + @Override + public Map kernelParametersAliases() + { + Map aliases = new HashMap(); + aliases.put("alias1", "param1"); + aliases.put("alias2", "param2"); + return aliases; + } + + @Override + public InitState init(InitContext initContext) + { + param1 = initContext.kernelParam("param1"); + alias1 = initContext.kernelParam("alias1"); + return InitState.INITIALIZED; + } + } + + public static class AliasUserPlugin extends AbstractPlugin + { + String param2; + String alias2; + + @Override + public String name() + { + return "alias-user"; + } + + @Override + public Collection kernelParamsRequests() + { + return kernelParamsRequestBuilder().mandatory("param2").build(); + } + + @Override + public InitState init(InitContext initContext) + { + param2 = initContext.kernelParam("param2"); + alias2 = initContext.kernelParam("alias2"); + return InitState.INITIALIZED; + } + } }