aboutsummaryrefslogtreecommitdiffstats
path: root/container-disc
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-disc
Publish
Diffstat (limited to 'container-disc')
-rw-r--r--container-disc/.gitignore2
-rw-r--r--container-disc/OWNERS1
-rw-r--r--container-disc/pom.xml230
-rw-r--r--container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java77
-rw-r--r--container-disc/src/main/java/com/yahoo/container/config/jersey/package-info.java5
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/CertificateStoreProvider.java34
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java328
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ContainerDiscApplication.java51
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ContainerPortsOverride.java19
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ContainerThreadFactory.java33
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java66
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/FilterBindingsProvider.java61
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/FilterUtil.java109
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/Main.java56
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/RestrictedBundleContext.java190
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/SecretStoreProvider.java34
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/SslKeyStoreFactoryProvider.java41
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java56
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/config/package-info.java5
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java31
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java58
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java55
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java36
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java39
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java66
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/package-info.java4
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java23
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/package-info.java5
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/osgi/ContainerBundleActivator.java31
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/osgi/JacksonJaxrsResolverHook.java74
-rw-r--r--container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java197
-rw-r--r--container-disc/src/main/java/com/yahoo/container/usability/package-info.java5
-rw-r--r--container-disc/src/main/java/com/yahoo/jdisc/metrics/yamasconsumer/cloud/package-info.java5
-rw-r--r--container-disc/src/main/resources/configdefinitions/container.jdisc.config.http-server.def45
-rw-r--r--container-disc/src/main/resources/configdefinitions/jdisc-bindings.def6
-rw-r--r--container-disc/src/main/resources/configdefinitions/jersey-connection.def7
-rw-r--r--container-disc/src/main/resources/configdefinitions/jersey-init.def8
-rw-r--r--container-disc/src/main/resources/configdefinitions/jersey-web-app-pool.def8
-rw-r--r--container-disc/src/main/resources/configdefinitions/metric-defaults.def4
-rw-r--r--container-disc/src/main/resources/configdefinitions/port-overrides.def6
-rw-r--r--container-disc/src/main/resources/configdefinitions/score-board.def9
-rw-r--r--container-disc/src/main/ssl/jdisc_container.keydb11
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java46
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/DisableOsgiFrameworkTest.java46
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/FilterBindingsProviderTest.java202
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/component/DeconstructorTest.java68
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java45
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java35
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java45
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java51
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java71
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java19
-rw-r--r--container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricUpdaterTest.java35
-rw-r--r--container-disc/src/test/resources/ssl/keystore.jksbin0 -> 1283 bytes
-rw-r--r--container-disc/src/test/resources/ssl/truststore.jksbin0 -> 876 bytes
55 files changed, 2794 insertions, 0 deletions
diff --git a/container-disc/.gitignore b/container-disc/.gitignore
new file mode 100644
index 00000000000..3cc25b51fc4
--- /dev/null
+++ b/container-disc/.gitignore
@@ -0,0 +1,2 @@
+/pom.xml.build
+/target
diff --git a/container-disc/OWNERS b/container-disc/OWNERS
new file mode 100644
index 00000000000..3b2ba1ede81
--- /dev/null
+++ b/container-disc/OWNERS
@@ -0,0 +1 @@
+gjoranv
diff --git a/container-disc/pom.xml b/container-disc/pom.xml
new file mode 100644
index 00000000000..f320eb371bb
--- /dev/null
+++ b/container-disc/pom.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0"?>
+<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>6-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>container-disc</artifactId>
+ <version>6-SNAPSHOT</version>
+ <packaging>container-plugin</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-lib</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>provided-dependencies</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>component</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-core</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-messagebus</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>messagebus-disc</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespajlib</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespalog</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- WARNING: These are only here to make bundlification work -->
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>simplemetrics</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-bundle</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>searchlib</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>configdefinitions</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- end WARNING -->
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-class-plugin</artifactId>
+ <version>${project.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>config-gen</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <Bundle-Activator>com.yahoo.container.jdisc.osgi.ContainerBundleActivator</Bundle-Activator>
+ <discApplicationClass>com.yahoo.container.jdisc.ConfiguredApplication</discApplicationClass>
+ <discPreInstallBundle>
+ config-bundle-jar-with-dependencies.jar,
+ configdefinitions-jar-with-dependencies.jar,
+ container-jersey2-jar-with-dependencies.jar,
+ container-search-and-docproc-jar-with-dependencies.jar,
+ docprocs-jar-with-dependencies.jar,
+ jdisc_http_service-jar-with-dependencies.jar,
+ persistence-jar-with-dependencies.jar,
+ vespaclient-container-plugin-jar-with-dependencies.jar,
+ simplemetrics-jar-with-dependencies.jar,
+ defaults-jar-with-dependencies.jar,
+ component-jar-with-dependencies.jar,
+ <!-- jersey2 -->
+ aopalliance-repackaged-2.3.0-b05.jar,
+ hk2-api-2.3.0-b05.jar,
+ hk2-locator-2.3.0-b05.jar,
+ hk2-utils-2.3.0-b05.jar,
+ jackson-annotations-${jackson2.version}.jar,
+ jackson-core-${jackson2.version}.jar,
+ jackson-databind-${jackson2.version}.jar,
+ jackson-datatype-jdk8-${jackson2.version}.jar,
+ jackson-jaxrs-base-${jackson2.version}.jar,
+ jackson-jaxrs-json-provider-${jackson2.version}.jar,
+ jackson-module-jaxb-annotations-${jackson2.version}.jar,
+ javassist-3.18.1-GA.jar,
+ javax.ws.rs-api-${javax.ws.rs-api.version}.jar,
+ jersey-client-${jersey2.version}.jar,
+ jersey-common-${jersey2.version}.jar,
+ jersey-container-servlet-${jersey2.version}.jar,
+ jersey-container-servlet-core-${jersey2.version}.jar,
+ jersey-guava-${jersey2.version}.jar,
+ jersey-media-json-jackson-${jersey2.version}.jar,
+ jersey-media-multipart-${jersey2.version}.jar,
+ mimepull-1.9.3.jar, <!-- needed by media-multipart -->
+ jersey-server-${jersey2.version}.jar,
+ jersey-proxy-client-${jersey2.version}.jar,
+ osgi-resource-locator-1.0.1.jar,
+ validation-api-1.1.0.Final.jar
+ <!-- jersey2 end -->
+ </discPreInstallBundle>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>coverage</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <phase>validate</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <executable>rm</executable>
+ <workingDirectory>${project.build.directory}</workingDirectory>
+ <arguments>
+ <argument>-f</argument>
+ <argument>generated-sources/vespa-configgen-plugin/com/yahoo/container/jdisc/JdiscBindingsConfig.java</argument>
+ </arguments>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java b/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java
new file mode 100644
index 00000000000..0b27d83630b
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java
@@ -0,0 +1,77 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container;
+
+import com.yahoo.container.core.http.HttpFilterConfig;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.http.filter.FilterConfig;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author tonytv
+ */
+public final class FilterConfigProvider implements Provider<FilterConfig> {
+
+ private static class MapFilterConfig implements FilterConfig {
+
+ private final Map<String, String> initParameters;
+ private final String filterName;
+ private final String filterClass;
+
+ MapFilterConfig(Map<String, String> initParameters, String filterName, String filterClass) {
+ this.initParameters = initParameters;
+ this.filterName = filterName;
+ this.filterClass = filterClass;
+ }
+
+ @Override
+ public String getFilterName() {
+ return filterName;
+ }
+
+ @Override
+ public String getFilterClass() {
+ return filterClass;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return initParameters.get(name);
+ }
+
+ @Override
+ public boolean getBooleanInitParameter(String name, boolean defaultValue) {
+ return initParameters.containsKey(name) ?
+ Boolean.parseBoolean(initParameters.get(name)) :
+ defaultValue;
+ }
+
+ @Override
+ public Collection<String> getInitParameterNames() {
+ return initParameters.keySet();
+ }
+ }
+
+ private final FilterConfig filterConfig;
+
+ public FilterConfigProvider(HttpFilterConfig vespaConfig) {
+ filterConfig = new MapFilterConfig(toMap(vespaConfig), vespaConfig.filterName(), vespaConfig.filterClass());
+ }
+
+ private static Map<String, String> toMap(HttpFilterConfig vespaConfig) {
+ Map<String, String> parameters = new HashMap<>();
+ for (HttpFilterConfig.Param param : vespaConfig.param())
+ parameters.put(param.name(), param.value());
+ return parameters;
+ }
+
+ @Override
+ public FilterConfig get() {
+ return filterConfig;
+ }
+
+ @Override
+ public void deconstruct() {}
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/config/jersey/package-info.java b/container-disc/src/main/java/com/yahoo/container/config/jersey/package-info.java
new file mode 100644
index 00000000000..ab8b41b3a18
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/config/jersey/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.config.jersey;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/CertificateStoreProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/CertificateStoreProvider.java
new file mode 100644
index 00000000000..251fbc83d2e
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/CertificateStoreProvider.java
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.http.CertificateStore;
+
+/**
+ * An certificate store provider which provides a factory which throws exception on
+ * invocation - as no certificate store is currently provided by default.
+ * The purpose of this is to provide a certificate store for injection in the case where
+ * no certificate store component is provided.
+ *
+ * @author bratseth
+ */
+public class CertificateStoreProvider implements Provider<CertificateStore> {
+
+ private static final ThrowingCertificateStore instance = new ThrowingCertificateStore();
+
+ @Override
+ public CertificateStore get() { return instance; }
+
+ @Override
+ public void deconstruct() { }
+
+ private static final class ThrowingCertificateStore implements CertificateStore {
+
+ @Override
+ public String getCertificate(String key, long ttl, long retry) {
+ throw new UnsupportedOperationException("A certificate store is not available");
+ }
+
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
new file mode 100644
index 00000000000..62fdb36cbad
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
@@ -0,0 +1,328 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.google.common.collect.MapMaker;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.concurrent.DaemonThreadFactory;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.config.subscription.ConfigInterruptedException;
+import com.yahoo.container.Container;
+import com.yahoo.container.QrConfig;
+import com.yahoo.container.core.ChainsConfig;
+import com.yahoo.container.core.config.HandlersConfigurerDi;
+import com.yahoo.container.di.config.Subscriber;
+import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.container.http.filter.FilterChainRepository;
+import com.yahoo.container.jdisc.component.Deconstructor;
+import com.yahoo.container.jdisc.config.PortOverridesConfig;
+import com.yahoo.container.jdisc.metric.DisableGuiceMetric;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.application.Application;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.application.ContainerActivator;
+import com.yahoo.jdisc.application.ContainerBuilder;
+import com.yahoo.jdisc.application.GuiceRepository;
+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.jrt.ListenFailedException;
+import com.yahoo.log.LogSetup;
+import com.yahoo.osgi.OsgiImpl;
+import com.yahoo.vespa.config.ConfigKey;
+import com.yahoo.yolean.Exceptions;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.yahoo.collections.CollectionUtil.first;
+
+/**
+ * @author tonytv
+ */
+public final class ConfiguredApplication implements Application {
+
+ public static final class ApplicationContext {
+
+ final JdiscBindingsConfig discBindingsConfig;
+
+ public ApplicationContext(com.yahoo.container.jdisc.JdiscBindingsConfig discBindingsConfig) {
+ this.discBindingsConfig = discBindingsConfig;
+ }
+ }
+
+ private static final Logger log = Logger.getLogger(ConfiguredApplication.class.getName());
+ private static final Set<ClientProvider> startedClients = newWeakIdentitySet();
+
+ private static final Set<ServerProvider> startedServers = Collections.newSetFromMap(new IdentityHashMap<>());
+ private final SubscriberFactory subscriberFactory;
+ private final ContainerActivator activator;
+ private final String configId;
+ private final ContainerDiscApplication applicationWithLegacySetup;
+ private final OsgiFramework osgiFramework;
+ private final com.yahoo.jdisc.Timer timerSingleton;
+ //TODO: FilterChainRepository should instead always be set up in the model.
+ private final FilterChainRepository defaultFilterChainRepository =
+ new FilterChainRepository(new ChainsConfig(new ChainsConfig.Builder()),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>());
+ private final ContainerPortsOverride defaultContainerPortsOverride = new ContainerPortsOverride(
+ new PortOverridesConfig(new PortOverridesConfig.Builder()));
+ private final OsgiFramework restrictedOsgiFramework;
+ private volatile int applicationSerialNo = 0;
+ private HandlersConfigurerDi configurer;
+ private ScheduledThreadPoolExecutor shutdownDeadlineExecutor;
+ private Thread reconfigurerThread;
+
+ static {
+ LogSetup.initVespaLogging("Container");
+ }
+
+ /**
+ * Do not delete this method even if its empty.
+ * Calling this methods forces this class to be loaded,
+ * which runs the static block.
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static void ensureVespaLoggingInitialized() {
+
+ }
+
+ @Inject
+ public ConfiguredApplication(ContainerActivator activator,
+ OsgiFramework osgiFramework,
+ com.yahoo.jdisc.Timer timer,
+ SubscriberFactory subscriberFactory) throws ListenFailedException {
+ this.activator = activator;
+ this.osgiFramework = osgiFramework;
+ this.timerSingleton = timer;
+ this.subscriberFactory = subscriberFactory;
+ this.configId = System.getProperty("config.id");
+ this.restrictedOsgiFramework = new DisableOsgiFramework(new RestrictedBundleContext(osgiFramework.bundleContext()));
+ Container.get().setOsgi(new OsgiImpl(osgiFramework));
+
+ applicationWithLegacySetup = new ContainerDiscApplication(configId);
+ }
+
+ @Override
+ public void start() {
+ try {
+ ContainerDiscApplication.hackToInitializeServer(getConfig(QrConfig.class));
+
+ ContainerBuilder builder = createBuilderWithGuiceBindings();
+ configureComponents(builder.guiceModules().activate());
+
+ intitializeAndActivateContainer(builder);
+ startReconfigurerThread();
+ } catch (Exception e) {
+ com.yahoo.protect.Process.logAndDie("Failed starting container:", e);
+ }
+ }
+
+ private <T extends ConfigInstance> T getConfig(Class<T> configClass) {
+ Subscriber subscriber = subscriberFactory.getSubscriber(
+ Collections.singleton(new ConfigKey<>(configClass, configId)));
+ try {
+ subscriber.waitNextGeneration();
+ return configClass.cast(first(subscriber.config().values()));
+ } finally {
+ subscriber.close();
+ }
+ }
+
+ private void intitializeAndActivateContainer(ContainerBuilder builder) {
+ addHandlerBindings(builder, Container.get().getRequestHandlerRegistry(),
+ configurer.getComponent(ApplicationContext.class).discBindingsConfig);
+ installServerProviders(builder);
+
+ activator.activateContainer(builder); // TODO: .notifyTermination(.. decompose previous component graph ..)
+
+ startClients();
+ startAndStopServers();
+
+ log.info("Switching to the latest deployed set of configurations and components. " +
+ "Application switch number: " + (applicationSerialNo++));
+ }
+
+ private ContainerBuilder createBuilderWithGuiceBindings() {
+ ContainerBuilder builder = activator.newContainerBuilder();
+ setupGuiceBindings(builder.guiceModules());
+ return builder;
+ }
+
+ private void startReconfigurerThread() {
+ reconfigurerThread = new Thread(() -> {
+ while (!Thread.interrupted()) {
+ try {
+ ContainerBuilder builder = createBuilderWithGuiceBindings();
+ configurer.runOnceAndEnsureRegistryHackRun(builder.guiceModules().activate());
+ intitializeAndActivateContainer(builder);
+ } catch (ConfigInterruptedException | InterruptedException e) {
+ break;
+ } catch (Exception e) {
+ log.log(Level.SEVERE,
+ "Reconfiguration failed, your application package must be fixed, unless this is a " +
+ "JNI reload issue: " + Exceptions.toMessageString(e), e);
+ }
+ }
+ log.fine("Shutting down HandlersConfigurerDi");
+ });
+ reconfigurerThread.start();
+ }
+
+ private static void installServerProviders(ContainerBuilder builder) {
+ List<ServerProvider> serverProviders = Container.get().getServerProviderRegistry().allComponents();
+ for (ServerProvider server : serverProviders) {
+ builder.serverProviders().install(server);
+ }
+ }
+
+ private static void startClients() {
+ for (ClientProvider client : Container.get().getClientProviderRegistry().allComponents()) {
+ if (!startedClients.contains(client)) {
+ client.start();
+ startedClients.add(client);
+ }
+ }
+ }
+
+ private static void startAndStopServers() {
+ List<ServerProvider> currentServers = Container.get().getServerProviderRegistry().allComponents();
+ HashSet<ServerProvider> serversToClose = new HashSet<>(startedServers);
+ serversToClose.removeAll(currentServers);
+ for (ServerProvider server : serversToClose) {
+ closeServer(server);
+ }
+ for (ServerProvider server : currentServers) {
+ if (!startedServers.contains(server)) {
+ server.start();
+ startedServers.add(server);
+ }
+ }
+ }
+
+ private static void closeServer(ServerProvider server) {
+ server.close();
+ startedServers.remove(server);
+ }
+
+ private void configureComponents(Injector discInjector) {
+ configurer = new HandlersConfigurerDi(
+ subscriberFactory,
+ Container.get(),
+ configId,
+ new Deconstructor(true),
+ discInjector,
+ osgiFramework);
+ }
+
+ private void setupGuiceBindings(GuiceRepository modules) {
+ modules.install(new AbstractModule() {
+
+ @Override
+ protected void configure() {
+ bind(Metric.class).to(DisableGuiceMetric.class);
+ bind(OsgiFramework.class).toInstance(restrictedOsgiFramework);
+ bind(com.yahoo.jdisc.Timer.class).toInstance(timerSingleton);
+ bind(FilterChainRepository.class).toInstance(defaultFilterChainRepository);
+ bind(ContainerPortsOverride.class).toInstance(defaultContainerPortsOverride);
+ }
+ });
+ modules.install(applicationWithLegacySetup.getMbusBindings());
+ }
+
+ @Override
+ public void stop() {
+ startShutdownDeadlineExecutor();
+ shutdownReconfigurerThread();
+
+ configurer.shutdown(new Deconstructor(false));
+
+ for (ServerProvider server : Container.get().getServerProviderRegistry().allComponents()) {
+ if (startedServers.contains(server)) {
+ closeServer(server);
+ }
+ }
+ Container.get().shutdown();
+ }
+
+ private void shutdownReconfigurerThread() {
+ reconfigurerThread.interrupt();
+ try {
+ //Workaround for component constructors masking InterruptedException.
+ while (reconfigurerThread.isAlive()) {
+ reconfigurerThread.interrupt();
+ long millis = 200;
+ reconfigurerThread.join(millis);
+ }
+ } catch (InterruptedException e) {
+ log.info("Interrupted while joining on HandlersConfigurer reconfigure thread.");
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override
+ public void destroy() {
+ if (shutdownDeadlineExecutor != null) { //stop() is not called when exception happens during start
+ shutdownDeadlineExecutor.shutdownNow();
+ }
+ }
+
+ // Workaround for ApplicationLoader.stop not being able to shutdown
+ private void startShutdownDeadlineExecutor() {
+ shutdownDeadlineExecutor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("Shutdown deadline timer"));
+ shutdownDeadlineExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+ long delayMillis = 50 * 1000;
+ shutdownDeadlineExecutor.schedule(new Runnable() {
+
+ @Override
+ public void run() {
+ com.yahoo.protect.Process.logAndDie(
+ "Timed out waiting for application shutdown. Please check that all your request handlers " +
+ "drain their request content channels.");
+ }
+ }, delayMillis, TimeUnit.MILLISECONDS);
+ }
+
+ private static void addHandlerBindings(ContainerBuilder builder,
+ ComponentRegistry<RequestHandler> requestHandlerRegistry,
+ JdiscBindingsConfig discBindingsConfig) {
+ for (Map.Entry<String, JdiscBindingsConfig.Handlers> handlerEntry : discBindingsConfig.handlers().entrySet()) {
+ String id = handlerEntry.getKey();
+ JdiscBindingsConfig.Handlers handlerConfig = handlerEntry.getValue();
+
+ RequestHandler handler = requestHandlerRegistry.getComponent(id);
+ if (handler == null) {
+ throw new RuntimeException("Binding configured for non-jdisc request handler " + id);
+ }
+ bindUri(builder.serverBindings(), handlerConfig.serverBindings(), handler);
+ bindUri(builder.clientBindings(), handlerConfig.clientBindings(), handler);
+ }
+ }
+
+ private static void bindUri(BindingRepository<RequestHandler> bindings, List<String> uriPatterns,
+ RequestHandler target) {
+ for (String uri : uriPatterns) {
+ bindings.bind(uri, target);
+ }
+ }
+
+ private static <T> Set<T> newWeakIdentitySet() {
+ Map<T, Boolean> weakIdentityHashMap = new MapMaker().weakKeys().makeMap();
+ return Collections.newSetFromMap(weakIdentityHashMap);
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerDiscApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerDiscApplication.java
new file mode 100644
index 00000000000..0311eedd726
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerDiscApplication.java
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.yahoo.container.QrConfig;
+import com.yahoo.container.Server;
+import com.yahoo.container.jdisc.messagebus.SessionCache;
+import com.yahoo.jrt.ListenFailedException;
+import com.yahoo.log.LogLevel;
+
+import java.util.logging.Logger;
+
+
+/**
+ * The application which sets up the jDisc container
+ *
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class ContainerDiscApplication {
+
+ private static final Logger log = Logger.getLogger(ContainerDiscApplication.class.getName());
+
+ private SessionCache sessionCache;
+
+
+ @Inject
+ public ContainerDiscApplication(String configId) throws ListenFailedException {
+ sessionCache = new SessionCache(configId);
+ }
+
+ AbstractModule getMbusBindings() {
+ return new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(SessionCache.class).toInstance(sessionCache);
+ }
+ };
+ }
+
+ public static void hackToInitializeServer(QrConfig config) {
+ try {
+ Server.get().initialize(config);
+ } catch (Exception e) {
+ log.log(LogLevel.ERROR, "Caught exception when initializing server. Exiting.", e);
+ Runtime.getRuntime().halt(1);
+ }
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerPortsOverride.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerPortsOverride.java
new file mode 100644
index 00000000000..421a268587e
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerPortsOverride.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.container.jdisc.config.PortOverridesConfig;
+
+/**
+ * Holds PortsOverrideConfig for the current container.
+ * HttpServerConfigProvider can't depend on PortsOverrideConfig directly,
+ * since it's config producer is not under the container in the model.
+ * @author tonytv
+ */
+public final class ContainerPortsOverride extends AbstractComponent {
+ public final PortOverridesConfig config;
+
+ public ContainerPortsOverride(PortOverridesConfig portOverridesConfigConfig) {
+ this.config = portOverridesConfigConfig;
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerThreadFactory.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerThreadFactory.java
new file mode 100644
index 00000000000..4b4403bc15b
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ContainerThreadFactory.java
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.container.jdisc.metric.MetricConsumerProvider;
+import com.yahoo.jdisc.application.ContainerThread;
+import com.yahoo.jdisc.application.MetricConsumer;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class ContainerThreadFactory implements ThreadFactory {
+
+ private final ThreadFactory delegate;
+
+ public ContainerThreadFactory(MetricConsumerProvider metricConsumerProvider) {
+ metricConsumerProvider.getClass(); // throws NullPointerException
+ delegate = new ContainerThread.Factory(new com.google.inject.Provider<MetricConsumer>() {
+
+ @Override
+ public MetricConsumer get() {
+ return metricConsumerProvider.newInstance();
+ }
+ });
+ }
+
+ @Override
+ public Thread newThread(Runnable target) {
+ return delegate.newThread(target);
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java b/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java
new file mode 100644
index 00000000000..6ed8e721e34
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java
@@ -0,0 +1,66 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.jdisc.application.OsgiFramework;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+import java.util.List;
+
+/**
+ * @author tonytv
+ */
+public final class DisableOsgiFramework implements OsgiFramework {
+ private final RestrictedBundleContext restrictedBundleContext;
+
+ public DisableOsgiFramework() {
+ this.restrictedBundleContext = null;
+ }
+
+ public DisableOsgiFramework(RestrictedBundleContext restrictedBundleContext) {
+ this.restrictedBundleContext = restrictedBundleContext;
+ }
+
+ @Override
+ public List<Bundle> installBundle(String bundleLocation) throws BundleException {
+ throw newException();
+ }
+
+ @Override
+ public void startBundles(List<Bundle> bundles, boolean privileged) throws BundleException {
+ throw newException();
+ }
+
+ @Override
+ public void refreshPackages() {
+ throw newException();
+ }
+
+ @Override
+ public BundleContext bundleContext() {
+ if (restrictedBundleContext == null) {
+ throw newException();
+ }
+ return restrictedBundleContext;
+ }
+
+ @Override
+ public List<Bundle> bundles() {
+ throw newException();
+ }
+
+ @Override
+ public void start() throws BundleException {
+ throw newException();
+ }
+
+ @Override
+ public void stop() throws BundleException {
+ throw newException();
+ }
+
+ private RuntimeException newException() {
+ return new UnsupportedOperationException("The OSGi framework is not available to components.");
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/FilterBindingsProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/FilterBindingsProvider.java
new file mode 100644
index 00000000000..838593ab28a
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/FilterBindingsProvider.java
@@ -0,0 +1,61 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.http.filter.FilterChainRepository;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.http.ServerConfig;
+import com.yahoo.jdisc.http.filter.RequestFilter;
+import com.yahoo.jdisc.http.filter.ResponseFilter;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+import com.yahoo.jdisc.http.server.FilterBindings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides filter bindings based on vespa config.
+ *
+ * @author <a href="mailto:bakksjo@yahoo-inc.com">Oyvind Bakksjo</a>
+ */
+public class FilterBindingsProvider implements Provider<FilterBindings> {
+ final BindingRepository<RequestFilter> requestFilters = new BindingRepository<>();
+ final BindingRepository<ResponseFilter> responseFilters = new BindingRepository<>();
+
+ public FilterBindingsProvider(
+ final ComponentId componentId,
+ final ServerConfig config,
+ final FilterChainRepository filterChainRepository,
+ final ComponentRegistry<SecurityRequestFilter> legacyRequestFilters) {
+ final ComponentId serverId = componentId.getNamespace();
+ try {
+ FilterUtil.setupFilters(
+ componentId,
+ legacyRequestFilters,
+ toFilterSpecs(config.filter()),
+ filterChainRepository,
+ requestFilters,
+ responseFilters);
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid config for http server " + serverId, e);
+ }
+ }
+
+ private List<FilterUtil.FilterSpec> toFilterSpecs(List<ServerConfig.Filter> inFilters) {
+ List<FilterUtil.FilterSpec> outFilters = new ArrayList<>();
+ for (ServerConfig.Filter inFilter : inFilters) {
+ outFilters.add(new FilterUtil.FilterSpec(inFilter.id(), inFilter.binding()));
+ }
+ return outFilters;
+ }
+
+ @Override
+ public FilterBindings get() {
+ return new FilterBindings(requestFilters, responseFilters);
+ }
+
+ @Override
+ public void deconstruct() {}
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/FilterUtil.java b/container-disc/src/main/java/com/yahoo/container/jdisc/FilterUtil.java
new file mode 100644
index 00000000000..7254d438ac0
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/FilterUtil.java
@@ -0,0 +1,109 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.http.filter.FilterChainRepository;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.application.UriPattern;
+import com.yahoo.jdisc.http.filter.RequestFilter;
+import com.yahoo.jdisc.http.filter.ResponseFilter;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilterChain;
+
+import java.util.List;
+
+/**
+ * Helper class to set up filter binding repositories based on config.
+ *
+ * @author <a href="mailto:bakksjo@yahoo-inc.com">Oyvind Bakksjo</a>
+ */
+class FilterUtil {
+ private static final ComponentId SEARCH_SERVER_COMPONENT_ID = ComponentId.fromString("SearchServer");
+
+ private final BindingRepository<RequestFilter> requestFilters;
+ private final BindingRepository<ResponseFilter> responseFilters;
+
+ private FilterUtil(
+ final BindingRepository<RequestFilter> requestFilters,
+ final BindingRepository<ResponseFilter> responseFilters) {
+ this.requestFilters = requestFilters;
+ this.responseFilters = responseFilters;
+ }
+
+ private void configureFilters(
+ final List<FilterSpec> filtersConfig,
+ final FilterChainRepository filterChainRepository) {
+ for (final FilterSpec filterConfig : filtersConfig) {
+ final Object filter = filterChainRepository.getFilter(ComponentSpecification.fromString(filterConfig.getId()));
+ if (filter == null) {
+ throw new RuntimeException("No http filter with id " + filterConfig.getId());
+ }
+ addFilter(filter, filterConfig.getBinding());
+ }
+ }
+
+ private void addFilter(final Object filter, final String binding) {
+ if (filter instanceof RequestFilter && filter instanceof ResponseFilter) {
+ throw new RuntimeException("The filter " + filter.getClass().getName() + " is unsupported since it's both a RequestFilter and a ResponseFilter.");
+ } else if (filter instanceof RequestFilter) {
+ requestFilters.put(new UriPattern(binding), (RequestFilter) filter);
+ } else if (filter instanceof ResponseFilter) {
+ responseFilters.put(new UriPattern(binding), (ResponseFilter) filter);
+ } else {
+ throw new RuntimeException("Unknown filter type " + filter.getClass().getName());
+ }
+ }
+
+ //TVT: remove
+ private void configureLegacyFilters(
+ final ComponentId id,
+ final ComponentRegistry<SecurityRequestFilter> legacyRequestFilters) {
+ final ComponentId serverName = id.getNamespace();
+ if (SEARCH_SERVER_COMPONENT_ID.equals(serverName) && !legacyRequestFilters.allComponents().isEmpty()) {
+ requestFilters.bind(
+ "http://*/*",
+ SecurityRequestFilterChain.newInstance(legacyRequestFilters.allComponents()));
+ }
+ }
+
+ /**
+ * Populates binding repositories with filters based on config.
+ *
+ * @param requestFilters output argument that will be mutated
+ * @param responseFilters output argument that will be mutated
+ */
+ public static void setupFilters(
+ final ComponentId componentId,
+ final ComponentRegistry<SecurityRequestFilter> legacyRequestFilters,
+ final List<FilterSpec> filtersConfig,
+ final FilterChainRepository filterChainRepository,
+ final BindingRepository<RequestFilter> requestFilters,
+ final BindingRepository<ResponseFilter> responseFilters) {
+ final FilterUtil filterUtil = new FilterUtil(requestFilters, responseFilters);
+
+ // TODO: remove
+ filterUtil.configureLegacyFilters(componentId, legacyRequestFilters);
+
+ filterUtil.configureFilters(filtersConfig, filterChainRepository);
+ }
+
+ public static class FilterSpec {
+ private final String id;
+ private final String binding;
+
+ public FilterSpec(String id, String binding) {
+ this.id = id;
+ this.binding = binding;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getBinding() {
+ return binding;
+ }
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/Main.java b/container-disc/src/main/java/com/yahoo/container/jdisc/Main.java
new file mode 100644
index 00000000000..d4322e4980b
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/Main.java
@@ -0,0 +1,56 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
+import com.yahoo.jdisc.test.TestDriver;
+import org.apache.commons.daemon.Daemon;
+import org.apache.commons.daemon.DaemonContext;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class Main implements Daemon {
+
+ public static void main(String[] args) {
+ startContainer(System.getProperty("config.id"));
+ }
+
+ public static TestDriver startContainer(final String configId) {
+ System.setProperty("config.id", configId);
+
+ @SuppressWarnings("unused")
+ TestDriver driver = TestDriver.newInjectedApplicationInstance(ConfiguredApplication.class,
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(String.class).annotatedWith(Names.named("configId")).toInstance(configId);
+
+ }
+ });
+
+ return driver;
+ }
+
+ @Override
+ public void init(DaemonContext context) throws Exception {
+ //nada
+ }
+
+ @Override
+ public void start() throws Exception {
+ main(new String[0]);
+ }
+
+ @Override
+ public void stop() throws Exception {
+ //TODO: Implement this method:
+
+ }
+
+ @Override
+ public void destroy() {
+ //nada
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/RestrictedBundleContext.java b/container-disc/src/main/java/com/yahoo/container/jdisc/RestrictedBundleContext.java
new file mode 100644
index 00000000000..8fad9a48376
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/RestrictedBundleContext.java
@@ -0,0 +1,190 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import org.osgi.framework.*;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @since 5.22.0
+ */
+public class RestrictedBundleContext implements BundleContext {
+ private final BundleContext wrapped;
+
+ public RestrictedBundleContext(BundleContext wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public ServiceRegistration<?> registerService(String[] strings, Object o, Dictionary<String, ?> stringDictionary) {
+ if (wrapped == null) {
+ return null;
+ }
+ return wrapped.registerService(strings, o, stringDictionary);
+ }
+
+ @Override
+ public ServiceRegistration<?> registerService(String localHostname, Object o, Dictionary<String, ?> stringDictionary) {
+ if (wrapped == null) {
+ return null;
+ }
+ return wrapped.registerService(localHostname, o, stringDictionary);
+ }
+
+ @Override
+ public <S> ServiceRegistration<S> registerService(Class<S> sClass, S s, Dictionary<String, ?> stringDictionary) {
+ if (wrapped == null) {
+ return null;
+ }
+ return wrapped.registerService(sClass, s, stringDictionary);
+ }
+
+ @Override
+ public ServiceReference<?>[] getServiceReferences(String localHostname, String localHostname2) throws InvalidSyntaxException {
+ if (wrapped == null) {
+ return new ServiceReference<?>[0];
+ }
+ return wrapped.getServiceReferences(localHostname, localHostname2);
+ }
+
+ @Override
+ public ServiceReference<?>[] getAllServiceReferences(String localHostname, String localHostname2) throws InvalidSyntaxException {
+ if (wrapped == null) {
+ return new ServiceReference<?>[0];
+ }
+ return wrapped.getAllServiceReferences(localHostname, localHostname2);
+ }
+
+ @Override
+ public ServiceReference<?> getServiceReference(String localHostname) {
+ if (wrapped == null) {
+ return null;
+ }
+ return wrapped.getServiceReference(localHostname);
+ }
+
+ @Override
+ public <S> ServiceReference<S> getServiceReference(Class<S> sClass) {
+ if (wrapped == null) {
+ return null;
+ }
+ return wrapped.getServiceReference(sClass);
+ }
+
+ @Override
+ public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> sClass, String localHostname) throws InvalidSyntaxException {
+ if (wrapped == null) {
+ return Collections.<ServiceReference<S>>emptyList();
+ }
+ return wrapped.getServiceReferences(sClass, localHostname);
+ }
+
+ @Override
+ public <S> S getService(ServiceReference<S> sServiceReference) {
+ if (wrapped == null) {
+ return null;
+ }
+ return wrapped.getService(sServiceReference);
+ }
+
+ @Override
+ public boolean ungetService(ServiceReference<?> serviceReference) {
+ if (wrapped == null) {
+ return false;
+ }
+ return wrapped.ungetService(serviceReference);
+ }
+
+
+ //---------------------
+
+
+ @Override
+ public String getProperty(String localHostname) {
+ throw newException();
+ }
+
+ @Override
+ public Bundle getBundle() {
+ throw newException();
+ }
+
+ @Override
+ public Bundle installBundle(String localHostname, InputStream inputStream) throws BundleException {
+ throw newException();
+ }
+
+ @Override
+ public Bundle installBundle(String localHostname) throws BundleException {
+ throw newException();
+ }
+
+ @Override
+ public Bundle getBundle(long l) {
+ throw newException();
+ }
+
+ @Override
+ public Bundle[] getBundles() {
+ throw newException();
+ }
+
+ @Override
+ public void addServiceListener(ServiceListener serviceListener, String localHostname) throws InvalidSyntaxException {
+ throw newException();
+ }
+
+ @Override
+ public void addServiceListener(ServiceListener serviceListener) {
+ throw newException();
+ }
+
+ @Override
+ public void removeServiceListener(ServiceListener serviceListener) {
+ throw newException();
+ }
+
+ @Override
+ public void addBundleListener(BundleListener bundleListener) {
+ throw newException();
+ }
+
+ @Override
+ public void removeBundleListener(BundleListener bundleListener) {
+ throw newException();
+ }
+
+ @Override
+ public void addFrameworkListener(FrameworkListener frameworkListener) {
+ throw newException();
+ }
+
+ @Override
+ public void removeFrameworkListener(FrameworkListener frameworkListener) {
+ throw newException();
+ }
+
+ @Override
+ public File getDataFile(String localHostname) {
+ throw newException();
+ }
+
+ @Override
+ public Filter createFilter(String localHostname) throws InvalidSyntaxException {
+ throw newException();
+ }
+
+ @Override
+ public Bundle getBundle(String localHostname) {
+ throw newException();
+ }
+
+ private RuntimeException newException() {
+ return new UnsupportedOperationException("This BundleContext operation is not available to components.");
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/SecretStoreProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/SecretStoreProvider.java
new file mode 100644
index 00000000000..e229e049bfe
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/SecretStoreProvider.java
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.http.SecretStore;
+
+/**
+ * An secret store provider which provides a factory which throws exception on
+ * invocation - as no secret store is currently provided by default.
+ * The purpose of this is to provide a secret store for injection in the case where
+ * no secret store component is provided.
+ *
+ * @author bratseth
+ */
+public class SecretStoreProvider implements Provider<SecretStore> {
+
+ private static final ThrowingSecretStore instance = new ThrowingSecretStore();
+
+ @Override
+ public SecretStore get() { return instance; }
+
+ @Override
+ public void deconstruct() { }
+
+ private static final class ThrowingSecretStore implements SecretStore {
+
+ @Override
+ public String getSecret(String key) {
+ throw new UnsupportedOperationException("A secret store is not available");
+ }
+
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/SslKeyStoreFactoryProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/SslKeyStoreFactoryProvider.java
new file mode 100644
index 00000000000..a92f95e4416
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/SslKeyStoreFactoryProvider.java
@@ -0,0 +1,41 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.http.ssl.ReaderForPath;
+import com.yahoo.jdisc.http.ssl.SslKeyStore;
+import com.yahoo.jdisc.http.ssl.SslKeyStoreFactory;
+
+/**
+ * An SSL key store provider which provides a factory which throws exception on
+ * invocation - as no SSL key store is currently provided by default.
+ * The purpose of this is to provide a ssl store factory for injection in the case where
+ * no secret store component is provided.
+ *
+ * @author bratseth
+ */
+public class SslKeyStoreFactoryProvider implements Provider<SslKeyStoreFactory> {
+
+ private static final ThrowingSslKeyStoreFactory instance = new ThrowingSslKeyStoreFactory();
+
+ @Override
+ public SslKeyStoreFactory get() { return instance; }
+
+ @Override
+ public void deconstruct() { }
+
+ private static final class ThrowingSslKeyStoreFactory implements SslKeyStoreFactory {
+
+ @Override
+ public SslKeyStore createKeyStore(ReaderForPath certificateFile, ReaderForPath keyFile) {
+ throw new UnsupportedOperationException("A SSL key store factory component is not available");
+ }
+
+ @Override
+ public SslKeyStore createTrustStore(ReaderForPath certificateFile) {
+ throw new UnsupportedOperationException("A SSL key store factory component is not available");
+ }
+
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java b/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java
new file mode 100644
index 00000000000..c5796ffd290
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java
@@ -0,0 +1,56 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.component;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.concurrent.DaemonThreadFactory;
+import com.yahoo.concurrent.ThreadFactoryFactory;
+import com.yahoo.container.di.ComponentDeconstructor;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.SharedResource;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+* @author tonyv
+* @author gv
+*/
+public class Deconstructor implements ComponentDeconstructor {
+ private final ScheduledExecutorService executor =
+ Executors.newScheduledThreadPool(1, ThreadFactoryFactory.getDaemonThreadFactory("deconstructor"));
+
+ private final int delay;
+
+ public Deconstructor(boolean delayDeconstruction) {
+ delay = delayDeconstruction ? 60 : 0;
+ }
+
+
+ @Override
+ public void deconstruct(Object component) {
+ if (component instanceof AbstractComponent) {
+ AbstractComponent abstractComponent = (AbstractComponent) component;
+ if (abstractComponent.isDeconstructable())
+ executor.schedule(
+ new DestructComponentTask(abstractComponent), delay, TimeUnit.SECONDS);
+ } else if (component instanceof Provider) {
+ ((Provider)component).deconstruct();
+ } else if (component instanceof SharedResource) {
+ // No need to delay release, as jdisc does ref-counting
+ ((SharedResource)component).release();
+ }
+ }
+
+ private static class DestructComponentTask implements Runnable {
+ private final AbstractComponent component;
+
+ DestructComponentTask(AbstractComponent component) {
+ this.component = component;
+ }
+
+ public void run() {
+ component.deconstruct();
+ }
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/config/package-info.java b/container-disc/src/main/java/com/yahoo/container/jdisc/config/package-info.java
new file mode 100644
index 00000000000..004a9030a4b
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/config/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.jdisc.config;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java
new file mode 100644
index 00000000000..09cad401bc1
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.jdisc.Metric;
+
+import java.util.Map;
+
+/**
+ * @author tonytv
+ */
+public class DisableGuiceMetric implements Metric {
+
+ @Override
+ public void set(String s, Number number, Context context) {
+ throw newException();
+ }
+
+ @Override
+ public void add(String s, Number number, Context context) {
+ throw newException();
+ }
+
+ @Override
+ public Context createContext(Map<String, ?> stringMap) {
+ throw newException();
+ }
+
+ private static RuntimeException newException() {
+ return new UnsupportedOperationException("The Metric framework is only available to components.");
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java
new file mode 100644
index 00000000000..87f2e27a471
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java
@@ -0,0 +1,58 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.application.MetricConsumer;
+
+import java.util.Map;
+
+/**
+ * <p>If more than one {@link MetricConsumerFactory} is registered in a container, calls to <tt>Metric</tt> need to be
+ * forwarded to all the underlying <tt>MetricConsumers</tt>. That is the responsibility of this class. Instances of this
+ * class is created by the {@link MetricConsumerProvider} in those cases.</p>
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public final class ForwardingMetricConsumer implements MetricConsumer {
+
+ private final MetricConsumer[] consumers;
+
+ public ForwardingMetricConsumer(MetricConsumer[] consumers) {
+ this.consumers = consumers;
+ }
+
+ @Override
+ public void set(String key, Number val, Metric.Context ctx) {
+ ForwardingContext fwd = (ForwardingContext)ctx;
+ for (int i = 0; i < consumers.length; ++i) {
+ consumers[i].set(key, val, fwd != null ? fwd.contexts[i] : null);
+ }
+ }
+
+ @Override
+ public void add(String key, Number val, Metric.Context ctx) {
+ ForwardingContext fwd = (ForwardingContext)ctx;
+ for (int i = 0; i < consumers.length; ++i) {
+ consumers[i].add(key, val, fwd != null ? fwd.contexts[i] : null);
+ }
+ }
+
+ @Override
+ public Metric.Context createContext(Map<String, ?> properties) {
+ Metric.Context[] contexts = new Metric.Context[consumers.length];
+ for (int i = 0; i < consumers.length; ++i) {
+ contexts[i] = consumers[i].createContext(properties);
+ }
+ return new ForwardingContext(contexts);
+ }
+
+ private static class ForwardingContext implements Metric.Context {
+
+ final Metric.Context[] contexts;
+
+ ForwardingContext(Metric.Context[] contexts) {
+ this.contexts = contexts;
+ }
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
new file mode 100644
index 00000000000..9cb2a768528
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java
@@ -0,0 +1,55 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.google.inject.Inject;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.container.jdisc.metric.state.StateMetricConsumerFactory;
+import com.yahoo.container.jdisc.state.StateMonitor;
+import com.yahoo.jdisc.application.MetricConsumer;
+import com.yahoo.metrics.MetricsPresentationConfig;
+
+
+/**
+ * <p>The purpose of this class it to be the only provider for the <tt>MetricConsumer</tt> interface in a component
+ * graph. This component is automatically registered in the graph by the config server. Configuring a different
+ * <tt>MetricConsumer</tt> is done by registering one or more {@link MetricConsumerFactory} in the services-file.</p>
+ *
+ * <p>Because this class depends on the {@link ComponentRegistry} of {@link MetricConsumerFactory}, any added or removed
+ * {@link MetricConsumerFactory} will cause this component to be reconfigured. Because {@link MetricProvider} depends on
+ * this class, which means any component that uses <tt>Metric</tt> will be reconfigured. Any component that depends
+ * directly on <tt>MetricConsumer</tt> will also be reconfigured.</p>
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class MetricConsumerProvider {
+
+ private final MetricConsumerFactory[] factories;
+
+ @Inject
+ public MetricConsumerProvider(ComponentRegistry<MetricConsumerFactory> factoryRegistry,
+ MetricsPresentationConfig presentationConfig,
+ StateMonitor stateMonitor) {
+ MetricConsumerFactory[] factories;
+ if (factoryRegistry.getComponentCount() == 0 || ! presentationConfig.slidingwindow()) {
+ factories = new MetricConsumerFactory[1];
+ factories[0] = new StateMetricConsumerFactory(stateMonitor);
+ } else {
+ factories = new MetricConsumerFactory[factoryRegistry.getComponentCount()];
+ factoryRegistry.allComponents().toArray(factories);
+ }
+ this.factories = factories;
+ }
+
+ public MetricConsumer newInstance() {
+ if (factories.length == 1) {
+ return factories[0].newInstance();
+ }
+ MetricConsumer[] consumers = new MetricConsumer[factories.length];
+ for (int i = 0; i < factories.length; ++i) {
+ consumers[i] = factories[i].newInstance();
+ }
+ return new ForwardingMetricConsumer(consumers);
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java
new file mode 100644
index 00000000000..930773e8546
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.google.inject.Inject;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.container.jdisc.state.StateMonitor;
+import com.yahoo.metrics.MetricsPresentationConfig;
+
+/**
+ * A dependency injection-provider which providers the default metrics provider
+ * if no provider is set up explicitly in the application package.
+ * The purpose of this is to be a fallback if nothing is set up explicitly.
+ *
+ * @author bratseth
+ */
+public class MetricConsumerProviderProvider implements Provider<MetricConsumerProvider> {
+
+ private final MetricConsumerProvider provided;
+
+ @Inject
+ public MetricConsumerProviderProvider(ComponentRegistry<MetricConsumerFactory> factoryRegistry,
+ MetricsPresentationConfig presentationConfig,
+ StateMonitor stateMonitor) {
+ provided = new MetricConsumerProvider(factoryRegistry, presentationConfig, stateMonitor);
+ }
+
+ @Override
+ public MetricConsumerProvider get() { return provided; }
+
+ @Override
+ public void deconstruct() {
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java
new file mode 100644
index 00000000000..7fdf9821bb1
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.application.MetricConsumer;
+
+/**
+ * <p>This class implements a {@link Provider} component of <tt>Metric</tt>. Because this class depends on {@link
+ * MetricConsumerProvider}, any change to the consumer configuration will trigger reconfiguration of this component,
+ * which in turn triggers reconfiguration of any component that depends on <tt>Metric</tt>.</p>
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public final class MetricProvider implements Provider<Metric> {
+
+ private final Metric metric;
+
+ public MetricProvider(MetricConsumerProvider provider) {
+ metric = new com.yahoo.jdisc.application.MetricProvider(new com.google.inject.Provider<MetricConsumer>() {
+
+ @Override
+ public MetricConsumer get() {
+ return provider.newInstance();
+ }
+ }).get();
+ }
+
+ @Override
+ public Metric get() {
+ return metric;
+ }
+
+ @Override
+ public void deconstruct() {
+
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java
new file mode 100644
index 00000000000..ab6e0b04085
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java
@@ -0,0 +1,66 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.jdisc.Metric;
+
+/**
+ * Uses a timer to emit metrics
+ *
+ * @author vegardh
+ * @since 5.17
+ *
+ */
+public class MetricUpdater extends AbstractComponent {
+
+ private static final String FREE_MEMORY_BYTES = "freeMemoryBytes";
+ private static final String USED_MEMORY_BYTES = "usedMemoryBytes";
+ private static final String TOTAL_MEMORY_BYTES = "totalMemoryBytes";
+ private static final String MANHATTAN_FREE_MEMORY_BYTES = "mem.heap.free";
+ private static final String MANHATTAN_USED_MEMORY_BYTES = "mem.heap.used";
+ private static final String MANHATTAN_TOTAL_MEMORY_BYTES = "mem.heap.total";
+
+ private final Metric metric;
+ private final Timer timer = new Timer();
+ long freeMemory = -1;
+ long totalMemory = -1;
+
+ @Inject
+ public MetricUpdater(Metric metric) {
+ this(metric, 10*1000);
+ }
+
+ public MetricUpdater(Metric metric, long delayMillis) {
+ this.metric = metric;
+ timer.schedule(new UpdaterTask(), delayMillis, delayMillis);
+ }
+
+ @Override
+ public void deconstruct() {
+ if (timer!=null) timer.cancel();
+ }
+
+ // For testing
+ long getFreeMemory() { return freeMemory; }
+ long getTotalMemory() { return totalMemory; }
+
+ private class UpdaterTask extends TimerTask {
+ @Override
+ public void run() {
+ freeMemory = Runtime.getRuntime().freeMemory();
+ totalMemory = Runtime.getRuntime().totalMemory();
+ long usedMemory = totalMemory - freeMemory;
+ metric.set(FREE_MEMORY_BYTES, freeMemory, null);
+ metric.set(USED_MEMORY_BYTES, usedMemory, null);
+ metric.set(TOTAL_MEMORY_BYTES, totalMemory, null);
+ metric.set(MANHATTAN_FREE_MEMORY_BYTES, freeMemory, null);
+ metric.set(MANHATTAN_USED_MEMORY_BYTES, usedMemory, null);
+ metric.set(MANHATTAN_TOTAL_MEMORY_BYTES, totalMemory, null);
+ }
+ }
+}
+
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/package-info.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/package-info.java
new file mode 100644
index 00000000000..512f283058b
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/package-info.java
@@ -0,0 +1,4 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@com.yahoo.osgi.annotation.ExportPackage
+package com.yahoo.container.jdisc.metric;
+
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
new file mode 100644
index 00000000000..ae4c7130c76
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric.state;
+
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.container.jdisc.state.StateMonitor;
+import com.yahoo.jdisc.application.MetricConsumer;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class StateMetricConsumerFactory implements MetricConsumerFactory {
+
+ private final StateMonitor stateMonitor;
+
+ public StateMetricConsumerFactory(final StateMonitor stateMonitor) {
+ this.stateMonitor = stateMonitor;
+ }
+
+ @Override
+ public MetricConsumer newInstance() {
+ return stateMonitor.newMetricConsumer();
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/package-info.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/package-info.java
new file mode 100644
index 00000000000..2731740abd5
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/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.jdisc.metric.state;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/osgi/ContainerBundleActivator.java b/container-disc/src/main/java/com/yahoo/container/jdisc/osgi/ContainerBundleActivator.java
new file mode 100644
index 00000000000..17e64426b88
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/osgi/ContainerBundleActivator.java
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.osgi;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+
+import java.util.Hashtable;
+
+/**
+ * @author tonytv
+ */
+public class ContainerBundleActivator implements BundleActivator {
+
+ private ServiceRegistration<ResolverHookFactory> resolverHookFactoryServiceRegistration;
+
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ resolverHookFactoryServiceRegistration = bundleContext.registerService(
+ ResolverHookFactory.class,
+ new JacksonJaxrsResolverHook.Factory(),
+ new Hashtable<>());
+ }
+
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ resolverHookFactoryServiceRegistration.unregister();
+ }
+
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/osgi/JacksonJaxrsResolverHook.java b/container-disc/src/main/java/com/yahoo/container/jdisc/osgi/JacksonJaxrsResolverHook.java
new file mode 100644
index 00000000000..8096a9f399f
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/osgi/JacksonJaxrsResolverHook.java
@@ -0,0 +1,74 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.osgi;
+
+import com.yahoo.log.LogLevel;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Predicate;
+import java.util.logging.Logger;
+
+/**
+ * Makes jackson1 jaxrs integration use jersey1 and jackson2 jaxrs integration use jersey2
+ * @author tonytv
+ */
+public class JacksonJaxrsResolverHook implements ResolverHook {
+ private static Logger log = Logger.getLogger(JacksonJaxrsResolverHook.class.getName());
+
+ public static class Factory implements ResolverHookFactory {
+ @Override
+ public ResolverHook begin(Collection<BundleRevision> bundleRevisions) {
+ return new JacksonJaxrsResolverHook();
+ }
+ }
+
+ @Override
+ public void filterResolvable(Collection<BundleRevision> bundleRevisions) {}
+
+ @Override
+ public void filterSingletonCollisions(BundleCapability bundleCapability, Collection<BundleCapability> bundleCapabilities) {}
+
+ @Override
+ public void filterMatches(BundleRequirement bundleRequirement, Collection<BundleCapability> bundleCapabilities) {
+ Bundle bundle = bundleRequirement.getRevision().getBundle();
+ String symbolicName = bundle.getSymbolicName();
+
+ log.log(LogLevel.DEBUG, "Filtering matches for " + symbolicName);
+
+ if (symbolicName.startsWith("com.fasterxml.jackson.jaxrs"))
+ removeBundlesMatching(bundleCapabilities, JacksonJaxrsResolverHook::isJaxRs1Bundle);
+ else if (symbolicName.equals("jackson-jaxrs") && bundle.getVersion().getMajor() == 1) {
+ removeBundlesMatching(bundleCapabilities, JacksonJaxrsResolverHook::isJaxRs2Bundle);
+ }
+ }
+
+ private static boolean isJaxRs1Bundle(String bundleSymbolicName) {
+ return bundleSymbolicName.startsWith("com.sun.jersey.");
+ }
+
+ private static boolean isJaxRs2Bundle(String bundleSymbolicName) {
+ return bundleSymbolicName.startsWith("org.glassfish.jersey.") ||
+ bundleSymbolicName.equals("javax.ws.rs-api");
+ }
+
+ private void removeBundlesMatching(Collection<BundleCapability> bundleCapabilities, Predicate<String> symbolicNamePredicate) {
+ for (Iterator<BundleCapability> i = bundleCapabilities.iterator(); i.hasNext(); ) {
+ BundleCapability bundleCapability = i.next();
+ String symbolicName = bundleCapability.getRevision().getSymbolicName();
+
+ if (symbolicNamePredicate.test(symbolicName)) {
+ log.log(LogLevel.DEBUG, "- Removing bundle " + symbolicName);
+ i.remove();
+ }
+ }
+ }
+
+ @Override
+ public void end() {}
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java b/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java
new file mode 100644
index 00000000000..8f4435a511a
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java
@@ -0,0 +1,197 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.usability;
+
+import com.google.inject.Inject;
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.Container;
+import com.yahoo.container.jdisc.JdiscBindingsConfig;
+import com.yahoo.jdisc.handler.AbstractRequestHandler;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.FastContentWriter;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.jdisc.handler.ResponseDispatch;
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.http.HttpRequest;
+import com.yahoo.jdisc.http.HttpRequest.Method;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author gjoranv
+ * @since 5.18
+ */
+public class BindingsOverviewHandler extends AbstractRequestHandler {
+
+ private final JdiscBindingsConfig bindingsConfig;
+
+ @Inject
+ public BindingsOverviewHandler(JdiscBindingsConfig bindingsConfig) {
+ this.bindingsConfig = bindingsConfig;
+ }
+
+ @Override
+ public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
+ JSONObject json;
+ int statusToReturn;
+
+ if (request instanceof HttpRequest && ((HttpRequest) request).getMethod() != Method.GET) {
+ json = errorMessageInJson();
+ statusToReturn = com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED;
+ } else {
+ json = new StatusResponse(bindingsConfig).render();
+ statusToReturn = com.yahoo.jdisc.Response.Status.OK;
+ }
+
+ FastContentWriter writer = new FastContentWriter(new ResponseDispatch() {
+ @Override
+ protected com.yahoo.jdisc.Response newResponse() {
+ com.yahoo.jdisc.Response response = new com.yahoo.jdisc.Response(statusToReturn);
+ response.headers().add("Content-Type", Arrays.asList(new String[]{"application/json"}));
+ return response;
+ }
+ }.connect(handler));
+
+ try {
+ writer.write(json.toString());
+ } finally {
+ writer.close();
+ }
+
+ return new IgnoredContent();
+ }
+
+ private JSONObject errorMessageInJson() {
+ JSONObject error = new JSONObject();
+ try {
+ error.put("error", "This API, "
+ + this.getClass().getSimpleName()
+ + ", only supports HTTP GET."
+ + " You are probably looking for another API/path.");
+ } catch (org.json.JSONException e) {
+ // just ignore it
+ }
+ return error;
+ }
+
+ static JSONArray renderRequestHandlers(JdiscBindingsConfig bindingsConfig,
+ Map<ComponentId, ? extends RequestHandler> handlersById) {
+ JSONArray ret = new JSONArray();
+
+ for (Map.Entry<ComponentId, ? extends RequestHandler> handlerEntry : handlersById.entrySet()) {
+ String id = handlerEntry.getKey().stringValue();
+ RequestHandler handler = handlerEntry.getValue();
+
+ JSONObject handlerJson = renderComponent(handler, handlerEntry.getKey());
+ addBindings(bindingsConfig, id, handlerJson);
+ ret.put(handlerJson);
+ }
+ return ret;
+ }
+
+ private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, JSONObject handlerJson) {
+ List<String> serverBindings = new ArrayList<>();
+
+ JdiscBindingsConfig.Handlers handlerConfig = bindingsConfig.handlers(id);
+ if (handlerConfig != null) {
+ serverBindings = handlerConfig.serverBindings();
+ }
+ putJson(handlerJson, "serverBindings", renderBindings(serverBindings));
+ }
+
+ private static JSONArray renderBindings(List<String> bindings) {
+ JSONArray ret = new JSONArray();
+
+ for (String binding : bindings)
+ ret.put(binding);
+
+ return ret;
+ }
+
+ private static JSONObject renderComponent(Object component, ComponentId id) {
+ JSONObject jc = new JSONObject();
+ putJson(jc, "id", id.stringValue());
+ addBundleInfo(jc, component);
+ return jc;
+ }
+
+ private static void addBundleInfo(JSONObject jsonObject, Object component) {
+ BundleInfo bundleInfo = bundleInfo(component);
+ putJson(jsonObject, "class", bundleInfo.className);
+ putJson(jsonObject, "bundle", bundleInfo.bundleName);
+
+ }
+
+ private static BundleInfo bundleInfo(Object component) {
+ try {
+ Bundle bundle = FrameworkUtil.getBundle(component.getClass());
+
+ String bundleName = bundle != null ?
+ bundle.getSymbolicName() + ":" + bundle.getVersion() :
+ "From classpath";
+ return new BundleInfo(component.getClass().getName(), bundleName);
+ } catch (Exception | NoClassDefFoundError e) {
+ return new BundleInfo("Unavailable, reconfiguration in progress.", "");
+ }
+ }
+
+ private static void putJson(JSONObject json, String key, Object value) {
+ try {
+ json.put(key, value);
+ } catch (JSONException e) {
+ // The original JSONException lacks key-value info.
+ throw new RuntimeException("Trying to add invalid JSON object with key '" + key + "' and value '" + value + "' - " + e.getMessage(), e);
+ }
+ }
+
+ static final class BundleInfo {
+ public final String className;
+ public final String bundleName;
+ BundleInfo(String className, String bundleName) {
+ this.className = className;
+ this.bundleName = bundleName;
+ }
+ }
+
+ static final class StatusResponse {
+
+ private final JdiscBindingsConfig bindingsConfig;
+
+ StatusResponse(JdiscBindingsConfig bindingsConfig) {
+ this.bindingsConfig = bindingsConfig;
+ }
+
+ public JSONObject render() {
+ JSONObject root = new JSONObject();
+
+ putJson(root, "handlers",
+ renderRequestHandlers(bindingsConfig, Container.get().getRequestHandlerRegistry().allComponentsById()));
+
+ return root;
+ }
+
+ }
+
+ private class IgnoredContent implements ContentChannel {
+ @Override
+ public void write(ByteBuffer buf, CompletionHandler handler) {
+ handler.completed();
+ }
+
+ @Override
+ public void close(CompletionHandler handler) {
+ handler.completed();
+ }
+ }
+}
diff --git a/container-disc/src/main/java/com/yahoo/container/usability/package-info.java b/container-disc/src/main/java/com/yahoo/container/usability/package-info.java
new file mode 100644
index 00000000000..d5abbfccddd
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/usability/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.usability;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-disc/src/main/java/com/yahoo/jdisc/metrics/yamasconsumer/cloud/package-info.java b/container-disc/src/main/java/com/yahoo/jdisc/metrics/yamasconsumer/cloud/package-info.java
new file mode 100644
index 00000000000..9c3976cd972
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/jdisc/metrics/yamasconsumer/cloud/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.jdisc.metrics.yamasconsumer.cloud;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/container-disc/src/main/resources/configdefinitions/container.jdisc.config.http-server.def b/container-disc/src/main/resources/configdefinitions/container.jdisc.config.http-server.def
new file mode 100644
index 00000000000..283f8c160a8
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/container.jdisc.config.http-server.def
@@ -0,0 +1,45 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=container.jdisc.config
+
+serverName string default="default"
+tcpKeepAliveEnabled bool default=false
+httpKeepAliveEnabled bool default=true
+optimizedForPipeline bool default=false
+removeRawPostBodyForWwwUrlEncodedPost bool default=false
+responseChunkingEnabled bool default=true
+tcpNoDelayEnabled bool default=true
+listenPort int
+maxChunkSize int default=65536
+maxHeaderSize int default=65536
+maxInitialLineLength int default=65536
+maxUnchunkedResponseSize int default=10362880
+numWorkerThreads int default=16
+receiveBufferSize int default=-1
+responseCompressionLevel int default=6
+sendBufferSize int default=-1
+tcpListenBacklogLength int default=0
+connectTimeout double default=10
+idleConnectionTimeout double default=180
+soLinger double default=-1
+perRequestMetricsEnabled bool default=false
+metricUpdateInterval double default=1
+ssl.enabled bool default=false
+ssl.keyStoreType string default="JKS"
+ssl.keyStorePath string default="default"
+ssl.trustStorePath string default="default"
+ssl.keyDBKey string default=""
+ssl.algorithm string default="SunX509"
+ssl.protocol string default="TLS"
+
+filter[].id string
+filter[].binding string
+
+#copy pasted from new http-server.def(namespace=jdisc.http.server)
+accessLog.bufferSize int default = 10240
+accessLog.flushTimeout double default = 5 # seconds
+accessLog.fileName string default = ""
+accessLog.rotatable bool default = false
+accessLog.directory string default = "logs/jdisc_core"
+accessLog.prefix string default = "access_log."
+accessLog.suffix string default = ""
+accessLog.fileDateFormat string default = "yyyy-MM-dd"
diff --git a/container-disc/src/main/resources/configdefinitions/jdisc-bindings.def b/container-disc/src/main/resources/configdefinitions/jdisc-bindings.def
new file mode 100644
index 00000000000..d3790d616b4
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/jdisc-bindings.def
@@ -0,0 +1,6 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+version=1
+namespace=container.jdisc
+
+handlers{}.serverBindings[] string
+handlers{}.clientBindings[] string
diff --git a/container-disc/src/main/resources/configdefinitions/jersey-connection.def b/container-disc/src/main/resources/configdefinitions/jersey-connection.def
new file mode 100644
index 00000000000..2827f4ce680
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/jersey-connection.def
@@ -0,0 +1,7 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+## Do NOT move this file to the container-jersey module. If system bundles
+## like config-model import packages from container-jersey, new class
+## loaders for these bundles will be created after reconfig.
+namespace=container.config.jersey
+
+requestTimeout long default=-1
diff --git a/container-disc/src/main/resources/configdefinitions/jersey-init.def b/container-disc/src/main/resources/configdefinitions/jersey-init.def
new file mode 100644
index 00000000000..fb4801d3919
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/jersey-init.def
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+## Do NOT move this file to the container-jersey module. If system bundles
+## like config-model import packages from container-jersey, new class
+## loaders for these bundles will be created after reconfig.
+namespace=container.config.jersey
+
+# Controlled by the config framework, do not set this from services.xml!
+jerseyMapping string
diff --git a/container-disc/src/main/resources/configdefinitions/jersey-web-app-pool.def b/container-disc/src/main/resources/configdefinitions/jersey-web-app-pool.def
new file mode 100644
index 00000000000..5710aeb4d3b
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/jersey-web-app-pool.def
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+## Do NOT move this file to the container-jersey module. If system bundles
+## like config-model import packages from container-jersey, new class
+## loaders for these bundles will be created after reconfig.
+namespace=container.config.jersey
+
+maxActiveWebApp int default=4
+maxIdleWebApp int default=4
diff --git a/container-disc/src/main/resources/configdefinitions/metric-defaults.def b/container-disc/src/main/resources/configdefinitions/metric-defaults.def
new file mode 100644
index 00000000000..7cd43b329d3
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/metric-defaults.def
@@ -0,0 +1,4 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=container.jdisc.config
+
+factory enum { STATE_MONITOR, YAMAS_SCOREBOARD } default = STATE_MONITOR
diff --git a/container-disc/src/main/resources/configdefinitions/port-overrides.def b/container-disc/src/main/resources/configdefinitions/port-overrides.def
new file mode 100644
index 00000000000..0f62c6268c8
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/port-overrides.def
@@ -0,0 +1,6 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+version=1
+namespace=container.jdisc.config
+
+server[].id string
+server[].port int
diff --git a/container-disc/src/main/resources/configdefinitions/score-board.def b/container-disc/src/main/resources/configdefinitions/score-board.def
new file mode 100644
index 00000000000..723b8a70cd9
--- /dev/null
+++ b/container-disc/src/main/resources/configdefinitions/score-board.def
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+namespace=jdisc.metrics.yamasconsumer.cloud
+
+applicationName string default="JDisc"
+step int default=10 # in seconds
+flushTime int default=10 # in seconds
+histogram[].metric string default=""
+histogram[].flags int default=0
+histogram[].buckets[] double
diff --git a/container-disc/src/main/ssl/jdisc_container.keydb b/container-disc/src/main/ssl/jdisc_container.keydb
new file mode 100644
index 00000000000..23479a83442
--- /dev/null
+++ b/container-disc/src/main/ssl/jdisc_container.keydb
@@ -0,0 +1,11 @@
+<keydb>
+ <keygroup name="jdisc_container" id="0">
+ <keyname name="jdisc_key" usage="all" type="transient">
+ <key version="0"
+ value = "2eMsYNKWBOWpsah8d57B65xvmFVZGiPAGv3pbI1mlZU-" current = "true"
+ timestamp = "20130320234320"
+ expiry = "20160319234320">
+ </key>
+ </keyname>
+ </keygroup>
+</keydb>
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java
new file mode 100644
index 00000000000..408eb813eb3
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java
@@ -0,0 +1,46 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.container.jdisc.metric.MetricConsumerProvider;
+import com.yahoo.jdisc.application.ContainerThread;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.concurrent.ThreadFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class ContainerThreadFactoryTest {
+
+ @Test
+ public void requireThatMetricConsumerProviderCanNotBeNull() {
+ try {
+ new ContainerThreadFactory(null);
+ fail();
+ } catch (NullPointerException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatThreadsCreatedAreJDiscContainerThreads() {
+ assertEquals(ContainerThread.class,
+ new ContainerThreadFactory(Mockito.mock(MetricConsumerProvider.class))
+ .newThread(Mockito.mock(Runnable.class))
+ .getClass());
+ }
+
+ @Test
+ public void requireThatThreadFactoryCallsProvider() {
+ MetricConsumerProvider provider = Mockito.mock(MetricConsumerProvider.class);
+ ThreadFactory factory = new ContainerThreadFactory(provider);
+ factory.newThread(Mockito.mock(Runnable.class));
+ Mockito.verify(provider, Mockito.times(1)).newInstance();
+ factory.newThread(Mockito.mock(Runnable.class));
+ Mockito.verify(provider, Mockito.times(2)).newInstance();
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/DisableOsgiFrameworkTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/DisableOsgiFrameworkTest.java
new file mode 100644
index 00000000000..7b05b3cbd65
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/DisableOsgiFrameworkTest.java
@@ -0,0 +1,46 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import org.junit.Test;
+import org.osgi.framework.BundleException;
+
+/**
+ * @author lulf
+ * @since 5.1
+ */
+public class DisableOsgiFrameworkTest {
+ @Test(expected = RuntimeException.class)
+ public void require_that_installBundle_throws_exception() throws BundleException {
+ new DisableOsgiFramework().installBundle("foo");
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_that_startBundles_throws_exception() throws BundleException {
+ new DisableOsgiFramework().startBundles(null, true);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_that_bundleContext_throws_exception() throws BundleException {
+ new DisableOsgiFramework().bundleContext();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_that_refreshPackages_throws_exception() {
+ new DisableOsgiFramework().refreshPackages();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_that_bundles_throws_exception() {
+ new DisableOsgiFramework().bundles();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_that_start_throws_exception() throws BundleException {
+ new DisableOsgiFramework().start();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void require_that_stop_throws_exception() throws BundleException {
+ new DisableOsgiFramework().stop();
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/FilterBindingsProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/FilterBindingsProviderTest.java
new file mode 100644
index 00000000000..061d357310f
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/FilterBindingsProviderTest.java
@@ -0,0 +1,202 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.core.ChainsConfig;
+import com.yahoo.container.http.filter.FilterChainRepository;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.application.UriPattern;
+import com.yahoo.jdisc.http.ServerConfig;
+import com.yahoo.jdisc.http.filter.RequestFilter;
+import com.yahoo.jdisc.http.filter.ResponseFilter;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+import com.yahoo.jdisc.http.filter.SecurityResponseFilter;
+import com.yahoo.jdisc.http.server.FilterBindings;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+/**
+ * @author <a href="mailto:bakksjo@yahoo-inc.com">Oyvind Bakksjo</a>
+ */
+public class FilterBindingsProviderTest {
+ final ServerConfig.Builder configBuilder = new ServerConfig.Builder();
+
+ @Test
+ public void requireThatEmptyInputGivesEmptyOutput() {
+ final FilterChainRepository filterChainRepository = new FilterChainRepository(
+ new ChainsConfig(new ChainsConfig.Builder()),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>());
+
+ final FilterBindingsProvider provider = new FilterBindingsProvider(
+ new ComponentId("foo"),
+ new ServerConfig(configBuilder),
+ filterChainRepository,
+ new ComponentRegistry<>());
+
+ final FilterBindings filterBindings = provider.get();
+
+ assertThat(filterBindings, is(not(nullValue())));
+ assertThat(filterBindings.getRequestFilters().iterator().hasNext(), is(false));
+ assertThat(filterBindings.getResponseFilters().iterator().hasNext(), is(false));
+ }
+
+ @Test
+ public void requireThatCorrectlyConfiguredFiltersAreIncluded() {
+ final String requestFilter1Id = "requestFilter1";
+ final String requestFilter2Id = "requestFilter2";
+ final String requestFilter3Id = "requestFilter3";
+ final String responseFilter1Id = "responseFilter1";
+ final String responseFilter2Id = "responseFilter2";
+ final String responseFilter3Id = "responseFilter3";
+
+ // Set up config.
+ configBuilder.filter(new ServerConfig.Filter.Builder().id(requestFilter1Id).binding("http://*/a"));
+ configBuilder.filter(new ServerConfig.Filter.Builder().id(requestFilter2Id).binding("http://*/b"));
+ configBuilder.filter(new ServerConfig.Filter.Builder().id(responseFilter1Id).binding("http://*/c"));
+ configBuilder.filter(new ServerConfig.Filter.Builder().id(responseFilter3Id).binding("http://*/d"));
+
+ // Set up registry.
+ final ComponentRegistry<RequestFilter> availableRequestFilters = new ComponentRegistry<>();
+ final RequestFilter requestFilter1Instance = mock(RequestFilter.class);
+ final RequestFilter requestFilter2Instance = mock(RequestFilter.class);
+ final RequestFilter requestFilter3Instance = mock(RequestFilter.class);
+ availableRequestFilters.register(ComponentId.fromString(requestFilter1Id), requestFilter1Instance);
+ availableRequestFilters.register(ComponentId.fromString(requestFilter2Id), requestFilter2Instance);
+ availableRequestFilters.register(ComponentId.fromString(requestFilter3Id), requestFilter3Instance);
+ final ComponentRegistry<ResponseFilter> availableResponseFilters = new ComponentRegistry<>();
+ final ResponseFilter responseFilter1Instance = mock(ResponseFilter.class);
+ final ResponseFilter responseFilter2Instance = mock(ResponseFilter.class);
+ final ResponseFilter responseFilter3Instance = mock(ResponseFilter.class);
+ availableResponseFilters.register(ComponentId.fromString(responseFilter1Id), responseFilter1Instance);
+ availableResponseFilters.register(ComponentId.fromString(responseFilter2Id), responseFilter2Instance);
+ availableResponseFilters.register(ComponentId.fromString(responseFilter3Id), responseFilter3Instance);
+ final FilterChainRepository filterChainRepository = new FilterChainRepository(
+ new ChainsConfig(new ChainsConfig.Builder()),
+ availableRequestFilters,
+ availableResponseFilters,
+ new ComponentRegistry<SecurityRequestFilter>(),
+ new ComponentRegistry<SecurityResponseFilter>());
+
+ // Set up the provider that we aim to test.
+ final FilterBindingsProvider provider = new FilterBindingsProvider(
+ new ComponentId("foo"),
+ new ServerConfig(configBuilder),
+ filterChainRepository,
+ new ComponentRegistry<SecurityRequestFilter>());
+
+ // Execute.
+ final FilterBindings filterBindings = provider.get();
+
+ // Verify.
+ assertThat(filterBindings, is(not(nullValue())));
+ assertThat(
+ filterBindings.getRequestFilters(),
+ containsFilters(requestFilter1Instance, requestFilter2Instance));
+ assertThat(
+ filterBindings.getRequestFilters(),
+ not(containsFilters(requestFilter3Instance)));
+ assertThat(
+ filterBindings.getResponseFilters(),
+ containsFilters(responseFilter1Instance, responseFilter3Instance));
+ assertThat(
+ filterBindings.getResponseFilters(),
+ not(containsFilters(responseFilter2Instance)));
+ }
+
+ private interface DualRoleFilter extends RequestFilter, ResponseFilter {}
+
+ @Test
+ public void requireThatInstanceCanNotBeBothRequestAndResponseFilter() {
+ final String filterId = "filter";
+
+ // Set up config.
+ configBuilder.filter(new ServerConfig.Filter.Builder().id(filterId).binding("http://*/*"));
+
+ // Set up registry.
+ final DualRoleFilter filterInstance = mock(DualRoleFilter.class);
+ final ComponentRegistry<RequestFilter> availableRequestFilters = new ComponentRegistry<>();
+ availableRequestFilters.register(ComponentId.fromString(filterId), filterInstance);
+ final FilterChainRepository filterChainRepository = new FilterChainRepository(
+ new ChainsConfig(new ChainsConfig.Builder()),
+ availableRequestFilters,
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>());
+
+ try {
+ new FilterBindingsProvider(
+ new ComponentId("foo"),
+ new ServerConfig(configBuilder),
+ filterChainRepository,
+ new ComponentRegistry<>());
+ fail("Dual-role filter should not be accepted");
+ } catch (RuntimeException e) {
+ assertThat(e.getMessage(), containsString("Invalid config"));
+ }
+ }
+
+ @Test
+ public void requireThatConfigWithUnknownReferenceFails() {
+ // Set up config.
+ configBuilder.filter(new ServerConfig.Filter.Builder().id("someFilter").binding("http://*/*"));
+
+ // Set up registry.
+ final FilterChainRepository filterChainRepository = new FilterChainRepository(
+ new ChainsConfig(new ChainsConfig.Builder()),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>(),
+ new ComponentRegistry<>());
+
+ try {
+ new FilterBindingsProvider(
+ new ComponentId("foo"),
+ new ServerConfig(configBuilder),
+ filterChainRepository,
+ new ComponentRegistry<>());
+ fail("Config with unknown filter reference should not be accepted");
+ } catch (RuntimeException e) {
+ assertThat(e.getMessage(), containsString("Invalid config"));
+ }
+ }
+
+ private static <T> Matcher<? super BindingRepository<T>> containsFilters(
+ final T... requiredInstances) {
+ return new TypeSafeMatcher<BindingRepository<T>>() {
+ private final Set<T> requiredFilterSet = new HashSet<>(Arrays.asList(requiredInstances));
+
+ @Override
+ protected boolean matchesSafely(final BindingRepository<T> actualInstances) {
+ final Set<T> notFoundFilterSet = new HashSet<>(requiredFilterSet);
+ for (final Map.Entry<UriPattern, T> actualEntry : actualInstances) {
+ notFoundFilterSet.remove(actualEntry.getValue());
+ }
+ return notFoundFilterSet.isEmpty();
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText("BindingRepository containing " + requiredFilterSet);
+ }
+ };
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/component/DeconstructorTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/component/DeconstructorTest.java
new file mode 100644
index 00000000000..b6bc1359406
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/component/DeconstructorTest.java
@@ -0,0 +1,68 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.component;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.ResourceReference;
+import com.yahoo.jdisc.SharedResource;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author gjoranv
+ */
+public class DeconstructorTest {
+ public static Deconstructor deconstructor;
+
+ @Before
+ public void init() {
+ deconstructor = new Deconstructor(false);
+ }
+
+ @Test
+ public void require_abstract_component_destructed() throws InterruptedException {
+ TestAbstractComponent abstractComponent = new TestAbstractComponent();
+ // Done by executor, so it takes some time even with a 0 delay.
+ deconstructor.deconstruct(abstractComponent);
+ int cnt = 0;
+ while (! abstractComponent.destructed && (cnt++ < 10)) {
+ Thread.sleep(10);
+ }
+ assertTrue(abstractComponent.destructed);
+ }
+
+ @Test
+ public void require_provider_destructed() {
+ TestProvider provider = new TestProvider();
+ deconstructor.deconstruct(provider);
+ assertTrue(provider.destructed);
+ }
+
+ @Test
+ public void require_shared_resource_released() {
+ TestSharedResource sharedResource = new TestSharedResource();
+ deconstructor.deconstruct(sharedResource);
+ assertTrue(sharedResource.released);
+ }
+
+ private static class TestAbstractComponent extends AbstractComponent {
+ boolean destructed = false;
+ @Override public void deconstruct() { destructed = true; }
+ }
+
+ private static class TestProvider implements Provider<Void> {
+ boolean destructed = false;
+
+ @Override public Void get() { return null; }
+ @Override public void deconstruct() { destructed = true; }
+ }
+
+ private static class TestSharedResource implements SharedResource {
+ boolean released = false;
+
+ @Override public ResourceReference refer() { return null; }
+ @Override public void release() { released = true; }
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java
new file mode 100644
index 00000000000..4a2714541be
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java
@@ -0,0 +1,45 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.application.MetricConsumer;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class ForwardingMetricConsumerTest {
+
+ @Test
+ public void requireThatAllMethodsAreForwarded() {
+ MetricConsumer fooConsumer = Mockito.mock(MetricConsumer.class);
+ Metric.Context fooCtx = Mockito.mock(Metric.Context.class);
+ Mockito.when(fooConsumer.createContext(Mockito.<Map<String, ?>>any())).thenReturn(fooCtx);
+
+ MetricConsumer barConsumer = Mockito.mock(MetricConsumer.class);
+ Metric.Context barCtx = Mockito.mock(Metric.Context.class);
+ Mockito.when(barConsumer.createContext(Mockito.<Map<String, ?>>any())).thenReturn(barCtx);
+
+ MetricConsumer fwdConsumer = new ForwardingMetricConsumer(new MetricConsumer[] { fooConsumer, barConsumer });
+
+ Map<String, ?> properties = new HashMap<>();
+ Metric.Context ctx = fwdConsumer.createContext(properties);
+ assertNotNull(ctx);
+ Mockito.verify(fooConsumer, Mockito.times(1)).createContext(properties);
+ Mockito.verify(barConsumer, Mockito.times(1)).createContext(properties);
+
+ fwdConsumer.add("a", 69, ctx);
+ Mockito.verify(fooConsumer, Mockito.times(1)).add("a", 69, fooCtx);
+ Mockito.verify(barConsumer, Mockito.times(1)).add("a", 69, barCtx);
+
+ fwdConsumer.set("b", 96, ctx);
+ Mockito.verify(fooConsumer, Mockito.times(1)).set("b", 96, fooCtx);
+ Mockito.verify(barConsumer, Mockito.times(1)).set("b", 96, barCtx);
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java
new file mode 100644
index 00000000000..b1173ba2bca
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java
@@ -0,0 +1,35 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.jdisc.application.MetricConsumer;
+import org.mockito.Mockito;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+class MetricConsumerFactories {
+
+ public static MetricConsumerFactory newSingleton(final MetricConsumer consumer) {
+ return new MetricConsumerFactory() {
+
+ @Override
+ public MetricConsumer newInstance() {
+ return consumer;
+ }
+ };
+ }
+
+ public static MetricConsumerFactory newCounter(final AtomicInteger counter) {
+ return new MetricConsumerFactory() {
+
+ @Override
+ public MetricConsumer newInstance() {
+ counter.incrementAndGet();
+ return Mockito.mock(MetricConsumer.class);
+ }
+ };
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
new file mode 100644
index 00000000000..2cbd6a839ed
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java
@@ -0,0 +1,45 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.jdisc.application.MetricConsumer;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class MetricConsumerProviderTest {
+
+ @Test
+ public void requireThatSingleConsumerIsNotDelegated() {
+ MetricConsumer consumer = Mockito.mock(MetricConsumer.class);
+ MetricConsumerProvider provider = MetricConsumerProviders.newSingletonFactories(consumer);
+ assertSame(consumer, provider.newInstance());
+ }
+
+ @Test
+ public void requireThatMultipleConsumersAreDelegated() {
+ MetricConsumer foo = Mockito.mock(MetricConsumer.class);
+ MetricConsumer bar = Mockito.mock(MetricConsumer.class);
+ MetricConsumerProvider provider = MetricConsumerProviders.newSingletonFactories(foo, bar);
+ MetricConsumer consumer = provider.newInstance();
+ assertNotSame(foo, consumer);
+ assertNotSame(bar, consumer);
+ consumer.add("foo", 6, null);
+ Mockito.verify(foo, Mockito.times(1)).add("foo", 6, null);
+ Mockito.verify(bar, Mockito.times(1)).add("foo", 6, null);
+ }
+
+ @Test
+ public void requireThatDefaultConsumerFactoryIsStateMetric() {
+ MetricConsumer consumer = MetricConsumerProviders.newDefaultInstance().newInstance();
+ assertEquals("StateMetricConsumer", consumer.getClass().getSimpleName());
+ }
+
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
new file mode 100644
index 00000000000..d094f43519f
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.provider.ComponentRegistry;
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.container.jdisc.config.HealthMonitorConfig;
+import com.yahoo.metrics.MetricsPresentationConfig;
+import com.yahoo.container.jdisc.state.StateMonitor;
+import com.yahoo.jdisc.application.MetricConsumer;
+import com.yahoo.jdisc.core.SystemTimer;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+class MetricConsumerProviders {
+
+ public static MetricConsumerProvider newSingletonFactories(MetricConsumer... consumers) {
+ MetricConsumerFactory[] factories = new MetricConsumerFactory[consumers.length];
+ for (int i = 0; i < consumers.length; ++i) {
+ factories[i] = MetricConsumerFactories.newSingleton(consumers[i]);
+ }
+ return newInstance(factories);
+ }
+
+ public static MetricConsumerProvider newInstance(MetricConsumerFactory... factories) {
+ return new MetricConsumerProvider(newComponentRegistry(factories),
+ new MetricsPresentationConfig(new MetricsPresentationConfig.Builder()),
+ newStateMonitor());
+ }
+
+ public static MetricConsumerProvider newDefaultInstance() {
+ return new MetricConsumerProvider(newComponentRegistry(),
+ new MetricsPresentationConfig(new MetricsPresentationConfig.Builder()),
+ newStateMonitor());
+ }
+
+ public static ComponentRegistry<MetricConsumerFactory> newComponentRegistry(MetricConsumerFactory... factories) {
+ ComponentRegistry<MetricConsumerFactory> registry = new ComponentRegistry<>();
+ for (MetricConsumerFactory factory : factories) {
+ registry.register(new ComponentId(String.valueOf(factory.hashCode())), factory);
+ }
+ return registry;
+ }
+
+ public static StateMonitor newStateMonitor() {
+ return new StateMonitor(new HealthMonitorConfig(new HealthMonitorConfig.Builder()),
+ new SystemTimer());
+ }
+
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java
new file mode 100644
index 00000000000..a5cb1730089
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java
@@ -0,0 +1,71 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.container.di.componentgraph.Provider;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.application.MetricConsumer;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class MetricProviderTest {
+
+ @Test
+ public void requireThatMetricProviderDelegatesToConsumerFactory() {
+ MetricConsumer consumer = Mockito.mock(MetricConsumer.class);
+ MetricProvider provider = MetricProviders.newInstance(consumer);
+
+ Metric metric = provider.get();
+ assertNotNull(metric);
+
+ Metric.Context fooCtx = Mockito.mock(Metric.Context.class);
+ metric.add("foo", 6, fooCtx);
+ metric.set("foo", 9, fooCtx);
+ Mockito.verify(consumer, Mockito.times(1)).add("foo", 6, fooCtx);
+ Mockito.verify(consumer, Mockito.times(1)).set("foo", 9, fooCtx);
+ }
+
+ @Test
+ public void requireThatThreadLocalConsumersAreProvided() throws Exception {
+ AtomicInteger cnt = new AtomicInteger(0);
+ final MetricProvider metricProvider = MetricProviders.newInstance(MetricConsumerFactories.newCounter(cnt));
+ assertEquals(0, cnt.get());
+ metricProvider.get().add("foo", 6, null); // need to call on Metric to instantiate MetricConsumer
+ assertEquals(1, cnt.get());
+ metricProvider.get().add("bar", 9, null);
+ assertEquals(1, cnt.get());
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ assertTrue(executor.submit(new MetricTask(metricProvider)).get(60, TimeUnit.SECONDS));
+ assertEquals(2, cnt.get());
+ assertTrue(executor.submit(new MetricTask(metricProvider)).get(60, TimeUnit.SECONDS));
+ assertEquals(2, cnt.get());
+ }
+
+ private static class MetricTask implements Callable<Boolean> {
+
+ final Provider<Metric> provider;
+
+ MetricTask(Provider<Metric> provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public Boolean call() throws Exception {
+ provider.get().add("foo", 69, null);
+ return true;
+ }
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java
new file mode 100644
index 00000000000..620fb613f47
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.container.jdisc.MetricConsumerFactory;
+import com.yahoo.jdisc.application.MetricConsumer;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+class MetricProviders {
+
+ public static MetricProvider newInstance(MetricConsumer... consumers) {
+ return new MetricProvider(MetricConsumerProviders.newSingletonFactories(consumers));
+ }
+
+ public static MetricProvider newInstance(MetricConsumerFactory... factories) {
+ return new MetricProvider(MetricConsumerProviders.newInstance(factories));
+ }
+}
diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricUpdaterTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricUpdaterTest.java
new file mode 100644
index 00000000000..1067b572644
--- /dev/null
+++ b/container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricUpdaterTest.java
@@ -0,0 +1,35 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.metric;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.yahoo.jdisc.Metric;
+import com.yahoo.jdisc.application.MetricConsumer;
+
+public class MetricUpdaterTest {
+
+ @Test
+ public void testFreeMemory() throws InterruptedException {
+ MetricConsumer consumer = Mockito.mock(MetricConsumer.class);
+ MetricProvider provider = MetricProviders.newInstance(consumer);
+
+ Metric metric = provider.get();
+ MetricUpdater updater = new MetricUpdater(metric, 10);
+ long start = System.currentTimeMillis();
+ boolean updated = false;
+ while (System.currentTimeMillis() - start < 60000 && !updated) {
+ Thread.sleep(10);
+ if (memoryMetricsUpdated(updater)) {
+ updated = true;
+ }
+ }
+ assertTrue(memoryMetricsUpdated(updater));
+ }
+
+ private boolean memoryMetricsUpdated(MetricUpdater updater) {
+ return updater.getFreeMemory()>0 && updater.getTotalMemory()>0;
+ }
+}
diff --git a/container-disc/src/test/resources/ssl/keystore.jks b/container-disc/src/test/resources/ssl/keystore.jks
new file mode 100644
index 00000000000..fd49a1e15ee
--- /dev/null
+++ b/container-disc/src/test/resources/ssl/keystore.jks
Binary files differ
diff --git a/container-disc/src/test/resources/ssl/truststore.jks b/container-disc/src/test/resources/ssl/truststore.jks
new file mode 100644
index 00000000000..db3d8703eb9
--- /dev/null
+++ b/container-disc/src/test/resources/ssl/truststore.jks
Binary files differ