aboutsummaryrefslogtreecommitdiffstats
path: root/container-disc/src/main/java
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/src/main/java
Publish
Diffstat (limited to 'container-disc/src/main/java')
-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
30 files changed, 1794 insertions, 0 deletions
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