summaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/container/core
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /container-core/src/main/java/com/yahoo/container/core
Publish
Diffstat (limited to 'container-core/src/main/java/com/yahoo/container/core')
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java14
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java186
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java201
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java132
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/document/package-info.java5
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/http/package-info.java5
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/package-info.java5
-rw-r--r--container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java26
8 files changed, 574 insertions, 0 deletions
diff --git a/container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java b/container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java
new file mode 100644
index 00000000000..d9ff342c107
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/BundleLoaderProperties.java
@@ -0,0 +1,14 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core;
+
+/**
+ * @author gjoranv
+ * @since 5.46
+ */
+public interface BundleLoaderProperties {
+ // TODO: This should be removed. The prefix is used to separate the bundles in BundlesConfig
+ // into those that are transferred with filedistribution and those that are preinstalled
+ // on disk. Instead, the model should have put them in two different configs. I.e. create a new
+ // config 'preinstalled-bundles.def'.
+ public static final String DISK_BUNDLE_PREFIX = "file:";
+}
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java b/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java
new file mode 100644
index 00000000000..682abcc53ac
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java
@@ -0,0 +1,186 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config;
+
+import com.yahoo.collections.PredicateSplit;
+import com.yahoo.config.FileReference;
+import com.yahoo.container.Container;
+import com.yahoo.filedistribution.fileacquirer.FileAcquirer;
+import com.yahoo.osgi.Osgi;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleRevision;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import static com.yahoo.collections.PredicateSplit.partition;
+import static com.yahoo.container.core.BundleLoaderProperties.DISK_BUNDLE_PREFIX;
+
+/**
+ * Manages the set of installed 3rd-party component bundles.
+ *
+ * @author tonytv
+ */
+public class BundleLoader {
+
+ private final List<Bundle> initialBundles;
+
+ private final Map<FileReference, List<Bundle>> reference2Bundles = new LinkedHashMap<>();
+
+ private final Logger log = Logger.getLogger(BundleLoader.class.getName());
+ private final Osgi osgi;
+
+ public BundleLoader(Osgi osgi) {
+ this.osgi = osgi;
+ initialBundles = Arrays.asList(osgi.getBundles());
+ }
+
+ private List<Bundle> obtainBundles(FileReference reference, FileAcquirer fileAcquirer)
+ throws InterruptedException {
+ File file = fileAcquirer.waitFor(reference, 15, TimeUnit.MINUTES);
+ return osgi.install(file.getAbsolutePath());
+ }
+
+ /**
+ * @return the number of bundles installed by this call.
+ */
+ private int install(List<FileReference> references) {
+ Set<FileReference> bundlesToInstall = new HashSet<>(references);
+ bundlesToInstall.removeAll(reference2Bundles.keySet());
+
+ PredicateSplit<FileReference> bundlesToInstall_isDisk = partition(bundlesToInstall, BundleLoader::isDiskBundle);
+ installBundlesFromDisk(bundlesToInstall_isDisk.trueValues);
+ installBundlesFromFileDistribution(bundlesToInstall_isDisk.falseValues);
+
+ startBundles();
+ return bundlesToInstall.size();
+ }
+
+ private static boolean isDiskBundle(FileReference fileReference) {
+ return fileReference.value().startsWith(DISK_BUNDLE_PREFIX);
+ }
+
+ private void installBundlesFromDisk(List<FileReference> bundlesToInstall) {
+ for (FileReference reference : bundlesToInstall) {
+ try {
+ installBundleFromDisk(reference);
+ }
+ catch(Exception e) {
+ throw new RuntimeException("Could not install bundle '" + reference + "'", e);
+ }
+ }
+ }
+
+ private void installBundlesFromFileDistribution(List<FileReference> bundlesToInstall) {
+ if (!bundlesToInstall.isEmpty()) {
+ FileAcquirer fileAcquirer = Container.get().getFileAcquirer();
+ boolean hasFileDistribution = (fileAcquirer != null);
+ if (hasFileDistribution) {
+ installWithFileDistribution(bundlesToInstall, fileAcquirer);
+ } else {
+ log.warning("Can't retrieve bundles since file distribution is disabled.");
+ }
+ }
+ }
+
+ private void installBundleFromDisk(FileReference reference) {
+ assert(reference.value().startsWith(DISK_BUNDLE_PREFIX));
+ String referenceFileName = reference.value().substring(DISK_BUNDLE_PREFIX.length());
+ log.info("Installing bundle from disk with reference '" + reference.value() + "'");
+
+ File file = new File(referenceFileName);
+ if (!file.exists()) {
+ throw new IllegalArgumentException("Reference '" + reference.value() + "' not found on disk.");
+ }
+
+ List<Bundle> bundles = osgi.install(file.getAbsolutePath());
+ reference2Bundles.put(reference, bundles);
+ }
+
+ private void installWithFileDistribution(List<FileReference> bundlesToInstall, FileAcquirer fileAcquirer) {
+ for (FileReference reference : bundlesToInstall) {
+ try {
+ log.info("Installing bundle with reference '" + reference.value() + "'");
+ List<Bundle> bundles = obtainBundles(reference, fileAcquirer);
+ reference2Bundles.put(reference, bundles);
+ }
+ catch(Exception e) {
+ throw new RuntimeException("Could not install bundle '" + reference + "'", e);
+ }
+ }
+ }
+
+ //all bundles must have been started first to ensure correct package resolution.
+ private void startBundles() {
+ for (List<Bundle> bundles : reference2Bundles.values()) {
+ for (Bundle bundle : bundles) {
+ try {
+ if (!isFragment(bundle))
+ bundle.start();
+ } catch(Exception e) {
+ throw new RuntimeException("Could not start bundle '" + bundle.getSymbolicName() + "'", e);
+ }
+ }
+ }
+ }
+
+ // The OSGi APIs are just getting worse...
+ private boolean isFragment(Bundle bundle) {
+ BundleRevision bundleRevision = bundle.adapt(BundleRevision.class);
+ if (bundleRevision == null)
+ throw new NullPointerException("Null bundle revision means that bundle has probably been uninstalled: " +
+ bundle.getSymbolicName() + ":" + bundle.getVersion());
+ return (bundleRevision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0;
+ }
+
+ /**
+ * Returns the number of uninstalled bundles
+ */
+ private int retainOnly(List<FileReference> newReferences) {
+ Set<Bundle> bundlesToRemove = new HashSet<>(Arrays.asList(osgi.getBundles()));
+
+ for (FileReference fileReferenceToKeep: newReferences) {
+ if (reference2Bundles.containsKey(fileReferenceToKeep))
+ bundlesToRemove.removeAll(reference2Bundles.get(fileReferenceToKeep));
+ }
+
+ bundlesToRemove.removeAll(initialBundles);
+ for (Bundle bundle : bundlesToRemove) {
+ log.info("Removing bundle '" + bundle.toString() + "'");
+ osgi.uninstall(bundle);
+ }
+
+ Set<FileReference> fileReferencesToRemove = new HashSet<>(reference2Bundles.keySet());
+ fileReferencesToRemove.removeAll(newReferences);
+
+ for (FileReference fileReferenceToRemove : fileReferencesToRemove) {
+ reference2Bundles.remove(fileReferenceToRemove);
+ }
+ return bundlesToRemove.size();
+ }
+
+ public synchronized int use(List<FileReference> bundles) {
+ int removedBundles = retainOnly(bundles);
+ int installedBundles = install(bundles);
+ startBundles();
+
+ log.info(removedBundles + " bundles were removed, and " + installedBundles + " bundles were installed.");
+ log.info(installedBundlesMessage());
+ return removedBundles + installedBundles;
+ }
+
+ private String installedBundlesMessage() {
+ StringBuilder sb = new StringBuilder("Installed bundles: {" );
+ for (Bundle b : osgi.getBundles())
+ sb.append("[" + b.getBundleId() + "]" + b.getSymbolicName() + ", ");
+ sb.setLength(sb.length() - 2);
+ sb.append("}");
+ return sb.toString();
+ }
+}
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java
new file mode 100644
index 00000000000..0f56934c5bc
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java
@@ -0,0 +1,201 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.config.FileReference;
+import com.yahoo.container.core.DiagnosticsConfig;
+import com.yahoo.container.di.ComponentDeconstructor;
+import com.yahoo.container.di.Container;
+import com.yahoo.container.di.componentgraph.core.ComponentGraph;
+import com.yahoo.container.di.componentgraph.core.DotGraph;
+import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.container.di.osgi.OsgiUtil;
+import com.yahoo.container.handler.observability.OverviewHandler;
+import com.yahoo.container.logging.AccessLog;
+import com.yahoo.container.logging.AccessLogInterface;
+import com.yahoo.container.protect.FreezeDetector;
+import com.yahoo.jdisc.application.OsgiFramework;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.jdisc.service.ClientProvider;
+import com.yahoo.jdisc.service.ServerProvider;
+import com.yahoo.language.Linguistics;
+import com.yahoo.language.simple.SimpleLinguistics;
+import com.yahoo.log.LogLevel;
+import com.yahoo.osgi.OsgiImpl;
+import com.yahoo.statistics.Statistics;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleWiring;
+import scala.collection.immutable.Set;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+import static com.yahoo.collections.CollectionUtil.first;
+import static com.yahoo.container.util.Util.quote;
+
+
+/**
+ * For internal use only.
+ *
+ * @author tonytv
+ * @author gjoranv
+ */
+//TODO: rename
+public class HandlersConfigurerDi {
+
+ private static final Logger log = Logger.getLogger(HandlersConfigurerDi.class.getName());
+
+ public static class RegistriesHack {
+
+ @Inject
+ public RegistriesHack(com.yahoo.container.Container vespaContainer,
+ ComponentRegistry<AbstractComponent> allComponents,
+ ComponentRegistry<RequestHandler> requestHandlerRegistry,
+ ComponentRegistry<ClientProvider> clientProviderRegistry,
+ ComponentRegistry<ServerProvider> serverProviderRegistry) {
+ log.log(LogLevel.DEBUG, "RegistriesHack.init " + System.identityHashCode(this));
+
+ vespaContainer.setComponentRegistry(allComponents);
+ vespaContainer.setRequestHandlerRegistry(requestHandlerRegistry);
+ vespaContainer.setClientProviderRegistry(clientProviderRegistry);
+ vespaContainer.setServerProviderRegistry(serverProviderRegistry);
+ }
+
+ }
+
+ private final com.yahoo.container.Container vespaContainer;
+ private final OsgiWrapper osgiWrapper;
+ private final Container container;
+
+ private volatile ComponentGraph currentGraph = new ComponentGraph(0);
+
+ public HandlersConfigurerDi(SubscriberFactory subscriberFactory,
+ com.yahoo.container.Container vespaContainer,
+ String configId,
+ ComponentDeconstructor deconstructor,
+ Injector discInjector,
+ OsgiFramework osgiFramework) {
+
+ this.vespaContainer = vespaContainer;
+ osgiWrapper = new OsgiWrapper(osgiFramework, vespaContainer.getBundleLoader());
+
+ container = new Container(
+ subscriberFactory,
+ configId,
+ deconstructor,
+ osgiWrapper
+ );
+ try {
+ runOnceAndEnsureRegistryHackRun(discInjector);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while setting up handlers for the first time.");
+ }
+ }
+
+ private static class OsgiWrapper extends OsgiImpl implements com.yahoo.container.di.Osgi {
+
+ private final OsgiFramework osgiFramework;
+ private final BundleLoader bundleLoader;
+
+ public OsgiWrapper(OsgiFramework osgiFramework, BundleLoader bundleLoader) {
+ super(osgiFramework);
+ this.osgiFramework = osgiFramework;
+ this.bundleLoader = bundleLoader;
+ }
+
+
+ @Override
+ public BundleClasses getBundleClasses(ComponentSpecification bundleSpec, Set<String> packagesToScan) {
+ //Not written in an OO way since FelixFramework resides in JDisc core which for now is pure java,
+ //and to load from classpath one needs classes from scalalib.
+
+ //Temporary hack: Using class name since ClassLoaderOsgiFramework is not available at compile time in this bundle.
+ if (osgiFramework.getClass().getName().equals("com.yahoo.application.container.impl.ClassLoaderOsgiFramework")) {
+ Bundle syntheticClassPathBundle = first(osgiFramework.bundles());
+ ClassLoader classLoader = syntheticClassPathBundle.adapt(BundleWiring.class).getClassLoader();
+
+ return new BundleClasses(
+ syntheticClassPathBundle,
+ OsgiUtil.getClassEntriesForBundleUsingProjectClassPathMappings(classLoader, bundleSpec, packagesToScan));
+ } else {
+ Bundle bundle = getBundle(bundleSpec);
+ if (bundle == null)
+ throw new RuntimeException("No bundle matching " + quote(bundleSpec));
+
+ return new BundleClasses(bundle, OsgiUtil.getClassEntriesInBundleClassPath(bundle, packagesToScan));
+ }
+ }
+
+ @Override
+ public void useBundles(Collection<FileReference> bundles) {
+ log.info("Installing bundles from the latest application");
+
+ List<FileReference> fileReferences = new ArrayList<>(bundles);
+ int bundlesRemovedOrInstalled = bundleLoader.use(fileReferences);
+
+ if (bundlesRemovedOrInstalled > 0) {
+ refreshPackages();
+ }
+ }
+ }
+
+ public void runOnceAndEnsureRegistryHackRun(Injector discInjector) throws InterruptedException {
+ currentGraph = container.runOnce(currentGraph, createFallbackInjector(vespaContainer, discInjector));
+
+ RegistriesHack registriesHack = currentGraph.getInstance(RegistriesHack.class);
+ assert (registriesHack != null);
+ injectDotGraph();
+ }
+
+ @SuppressWarnings("deprecation")
+ private Injector createFallbackInjector(final com.yahoo.container.Container vespaContainer, Injector discInjector) {
+ return discInjector.createChildInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(com.yahoo.container.Container.class).toInstance(vespaContainer);
+ bind(com.yahoo.statistics.Statistics.class).toInstance(Statistics.nullImplementation);
+ bind(Linguistics.class).toInstance(new SimpleLinguistics());
+ bind(FreezeDetector.class).toInstance(new FreezeDetector(new DiagnosticsConfig(new DiagnosticsConfig.Builder().disabled(true))));
+ bind(AccessLog.class).toInstance(new AccessLog(new ComponentRegistry<>()));
+ bind(Executor.class).toInstance(Executors.newCachedThreadPool(ThreadFactoryFactory.getThreadFactory("HandlersConfigurerDI")));
+
+ if (vespaContainer.getFileAcquirer() != null)
+ bind(com.yahoo.filedistribution.fileacquirer.FileAcquirer.class).toInstance(vespaContainer.getFileAcquirer());
+ }
+ });
+ }
+
+ private void injectDotGraph() {
+ try {
+ OverviewHandler overviewHandler = currentGraph.getInstance(OverviewHandler.class);
+ overviewHandler.setDotGraph(DotGraph.generate(currentGraph));
+ } catch (Exception e) {
+ log.fine("No overview handler");
+ }
+
+ }
+
+ public void reloadConfig(long generation) {
+ container.reloadConfig(generation);
+ }
+
+ public <T> T getComponent(Class<T> componentClass) {
+ return currentGraph.getInstance(componentClass);
+ }
+
+ public void shutdown(ComponentDeconstructor deconstructor) {
+ container.shutdown(currentGraph, deconstructor);
+ }
+
+}
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java b/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java
new file mode 100644
index 00000000000..b01b800a462
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/config/testutil/HandlersConfigurerTestWrapper.java
@@ -0,0 +1,132 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config.testutil;
+
+import com.google.inject.Guice;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.config.subscription.ConfigSourceSet;
+import com.yahoo.container.Container;
+import com.yahoo.container.di.CloudSubscriberFactory;
+import com.yahoo.container.di.ComponentDeconstructor;
+import com.yahoo.container.core.config.HandlersConfigurerDi;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.osgi.MockOsgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Class for testing HandlersConfigurer.
+ * Not for public use.
+ *
+ * If possible, please avoid using this class and HandlersConfigurer in your tests
+ * @author tonytv
+ * @author gjoranv
+ *
+*/
+public class HandlersConfigurerTestWrapper {
+ private ConfigSourceSet configSources =
+ new ConfigSourceSet(this.getClass().getSimpleName() + ": " + new Random().nextLong());
+ private HandlersConfigurerDi configurer;
+
+ // TODO: Remove once tests use ConfigSet rather than dir:
+ private final static String testFiles[] = {
+ "components.cfg",
+ "handlers.cfg",
+ "bundles.cfg",
+ "string.cfg",
+ "int.cfg",
+ "renderers.cfg",
+ "diagnostics.cfg",
+ "qr-templates.cfg",
+ "documentmanager.cfg",
+ "schemamapping.cfg",
+ "chains.cfg",
+ "container-mbus.cfg",
+ "container-mbus.cfg",
+ "specialtokens.cfg",
+ "documentdb-info.cfg",
+ "qr-search.cfg",
+ "query-profiles.cfg"
+ };
+ private final Set<File> createdFiles = new LinkedHashSet<>();
+ private int lastGeneration = 0;
+ private final Container container;
+
+ private void createFiles(String configId) {
+ if (configId.startsWith("dir:")) {
+ try {
+ System.setProperty("config.id", configId);
+ String dirName = configId.substring(4);
+ for (String file : testFiles) {
+ createIfNotExists(dirName, file);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // TODO: Remove once tests use ConfigSet rather than dir:
+ private void createIfNotExists(String dir, String file) throws IOException {
+ final File f = new File(dir + "/" + file);
+ if (f.createNewFile()) {
+ createdFiles.add(f);
+ }
+ }
+
+ public HandlersConfigurerTestWrapper(String configId) {
+ this(Container.get(), configId);
+ }
+
+ public HandlersConfigurerTestWrapper(Container container, String configId) {
+ createFiles(configId);
+ MockOsgi mockOsgi = new MockOsgi();
+ container.setOsgi(mockOsgi);
+ ComponentDeconstructor testDeconstructor = getTestDeconstructor();
+ configurer = new HandlersConfigurerDi(
+ new CloudSubscriberFactory(configSources),
+ container,
+ configId,
+ testDeconstructor,
+ Guice.createInjector(),
+ mockOsgi);
+ this.container = container;
+ }
+
+ private ComponentDeconstructor getTestDeconstructor() {
+ return new ComponentDeconstructor() {
+ @Override
+ public void deconstruct(Object component) {
+ if (component instanceof AbstractComponent) {
+ AbstractComponent abstractComponent = (AbstractComponent) component;
+ if (abstractComponent.isDeconstructable())
+ ((AbstractComponent) component).deconstruct();
+ }
+ }};
+ }
+
+ public void reloadConfig() {
+ configurer.reloadConfig(++lastGeneration);
+ try {
+ configurer.runOnceAndEnsureRegistryHackRun(Guice.createInjector());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void shutdown() {
+ // TODO: Remove once tests use ConfigSet rather than dir:
+ for (File f : createdFiles) {
+ f.delete();
+ }
+ }
+
+ public ComponentRegistry<RequestHandler> getRequestHandlerRegistry() {
+ return container.getRequestHandlerRegistry();
+ }
+
+}
diff --git a/container-core/src/main/java/com/yahoo/container/core/document/package-info.java b/container-core/src/main/java/com/yahoo/container/core/document/package-info.java
new file mode 100644
index 00000000000..ffb130493ed
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/document/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.container.core.document;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-core/src/main/java/com/yahoo/container/core/http/package-info.java b/container-core/src/main/java/com/yahoo/container/core/http/package-info.java
new file mode 100644
index 00000000000..2db74625480
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/http/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.container.core.http;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-core/src/main/java/com/yahoo/container/core/package-info.java b/container-core/src/main/java/com/yahoo/container/core/package-info.java
new file mode 100644
index 00000000000..c9b43a8be66
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.container.core;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java b/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java
new file mode 100644
index 00000000000..7e19df254c1
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/core/slobrok/SlobrokConfigurator.java
@@ -0,0 +1,26 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.slobrok;
+
+import com.yahoo.cloud.config.SlobroksConfig;
+import com.yahoo.cloud.config.SlobroksConfig.Slobrok;
+import com.yahoo.container.Container;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Configures which slobrok nodes the container should register with.
+ * @author tonytv
+ */
+public class SlobrokConfigurator {
+ public SlobrokConfigurator(SlobroksConfig config) {
+ Container.get().getRpcAdaptor().registerInSlobrok(
+ connectionSpecs(config.slobrok()));
+ }
+
+ private static List<String> connectionSpecs(List<Slobrok> slobroks) {
+ return slobroks.stream().
+ map(Slobrok::connectionspec).
+ collect(Collectors.toList());
+ }
+}