From 72231250ed81e10d66bfe70701e64fa5fe50f712 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Wed, 15 Jun 2016 23:09:44 +0200 Subject: Publish --- container-disc/.gitignore | 2 + container-disc/OWNERS | 1 + container-disc/pom.xml | 230 +++++++++++++++ .../com/yahoo/container/FilterConfigProvider.java | 77 +++++ .../container/config/jersey/package-info.java | 5 + .../container/jdisc/CertificateStoreProvider.java | 34 +++ .../container/jdisc/ConfiguredApplication.java | 328 +++++++++++++++++++++ .../container/jdisc/ContainerDiscApplication.java | 51 ++++ .../container/jdisc/ContainerPortsOverride.java | 19 ++ .../container/jdisc/ContainerThreadFactory.java | 33 +++ .../container/jdisc/DisableOsgiFramework.java | 66 +++++ .../container/jdisc/FilterBindingsProvider.java | 61 ++++ .../java/com/yahoo/container/jdisc/FilterUtil.java | 109 +++++++ .../main/java/com/yahoo/container/jdisc/Main.java | 56 ++++ .../container/jdisc/RestrictedBundleContext.java | 190 ++++++++++++ .../yahoo/container/jdisc/SecretStoreProvider.java | 34 +++ .../jdisc/SslKeyStoreFactoryProvider.java | 41 +++ .../container/jdisc/component/Deconstructor.java | 56 ++++ .../yahoo/container/jdisc/config/package-info.java | 5 + .../container/jdisc/metric/DisableGuiceMetric.java | 31 ++ .../jdisc/metric/ForwardingMetricConsumer.java | 58 ++++ .../jdisc/metric/MetricConsumerProvider.java | 55 ++++ .../metric/MetricConsumerProviderProvider.java | 36 +++ .../container/jdisc/metric/MetricProvider.java | 39 +++ .../container/jdisc/metric/MetricUpdater.java | 66 +++++ .../yahoo/container/jdisc/metric/package-info.java | 4 + .../metric/state/StateMetricConsumerFactory.java | 23 ++ .../container/jdisc/metric/state/package-info.java | 5 + .../jdisc/osgi/ContainerBundleActivator.java | 31 ++ .../jdisc/osgi/JacksonJaxrsResolverHook.java | 74 +++++ .../usability/BindingsOverviewHandler.java | 197 +++++++++++++ .../yahoo/container/usability/package-info.java | 5 + .../metrics/yamasconsumer/cloud/package-info.java | 5 + .../container.jdisc.config.http-server.def | 45 +++ .../resources/configdefinitions/jdisc-bindings.def | 6 + .../configdefinitions/jersey-connection.def | 7 + .../resources/configdefinitions/jersey-init.def | 8 + .../configdefinitions/jersey-web-app-pool.def | 8 + .../configdefinitions/metric-defaults.def | 4 + .../resources/configdefinitions/port-overrides.def | 6 + .../resources/configdefinitions/score-board.def | 9 + container-disc/src/main/ssl/jdisc_container.keydb | 11 + .../jdisc/ContainerThreadFactoryTest.java | 46 +++ .../container/jdisc/DisableOsgiFrameworkTest.java | 46 +++ .../jdisc/FilterBindingsProviderTest.java | 202 +++++++++++++ .../jdisc/component/DeconstructorTest.java | 68 +++++ .../jdisc/metric/ForwardingMetricConsumerTest.java | 45 +++ .../jdisc/metric/MetricConsumerFactories.java | 35 +++ .../jdisc/metric/MetricConsumerProviderTest.java | 45 +++ .../jdisc/metric/MetricConsumerProviders.java | 51 ++++ .../container/jdisc/metric/MetricProviderTest.java | 71 +++++ .../container/jdisc/metric/MetricProviders.java | 19 ++ .../container/jdisc/metric/MetricUpdaterTest.java | 35 +++ container-disc/src/test/resources/ssl/keystore.jks | Bin 0 -> 1283 bytes .../src/test/resources/ssl/truststore.jks | Bin 0 -> 876 bytes 55 files changed, 2794 insertions(+) create mode 100644 container-disc/.gitignore create mode 100644 container-disc/OWNERS create mode 100644 container-disc/pom.xml create mode 100644 container-disc/src/main/java/com/yahoo/container/FilterConfigProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/config/jersey/package-info.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/CertificateStoreProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/ContainerDiscApplication.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/ContainerPortsOverride.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/ContainerThreadFactory.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/DisableOsgiFramework.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/FilterBindingsProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/FilterUtil.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/Main.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/RestrictedBundleContext.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/SecretStoreProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/SslKeyStoreFactoryProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/component/Deconstructor.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/config/package-info.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/DisableGuiceMetric.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumer.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricProvider.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/MetricUpdater.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/package-info.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/StateMetricConsumerFactory.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/metric/state/package-info.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/osgi/ContainerBundleActivator.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/osgi/JacksonJaxrsResolverHook.java create mode 100644 container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java create mode 100644 container-disc/src/main/java/com/yahoo/container/usability/package-info.java create mode 100644 container-disc/src/main/java/com/yahoo/jdisc/metrics/yamasconsumer/cloud/package-info.java create mode 100644 container-disc/src/main/resources/configdefinitions/container.jdisc.config.http-server.def create mode 100644 container-disc/src/main/resources/configdefinitions/jdisc-bindings.def create mode 100644 container-disc/src/main/resources/configdefinitions/jersey-connection.def create mode 100644 container-disc/src/main/resources/configdefinitions/jersey-init.def create mode 100644 container-disc/src/main/resources/configdefinitions/jersey-web-app-pool.def create mode 100644 container-disc/src/main/resources/configdefinitions/metric-defaults.def create mode 100644 container-disc/src/main/resources/configdefinitions/port-overrides.def create mode 100644 container-disc/src/main/resources/configdefinitions/score-board.def create mode 100644 container-disc/src/main/ssl/jdisc_container.keydb create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/ContainerThreadFactoryTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/DisableOsgiFrameworkTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/FilterBindingsProviderTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/component/DeconstructorTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/ForwardingMetricConsumerTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerFactories.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviderTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricConsumerProviders.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviderTest.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricProviders.java create mode 100644 container-disc/src/test/java/com/yahoo/container/jdisc/metric/MetricUpdaterTest.java create mode 100644 container-disc/src/test/resources/ssl/keystore.jks create mode 100644 container-disc/src/test/resources/ssl/truststore.jks (limited to 'container-disc') 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 @@ + + + + 4.0.0 + + com.yahoo.vespa + parent + 6-SNAPSHOT + ../parent/pom.xml + + container-disc + 6-SNAPSHOT + container-plugin + + + junit + junit + test + + + org.mockito + mockito-core + test + + + com.yahoo.vespa + config-lib + ${project.version} + provided + + + com.yahoo.vespa + provided-dependencies + ${project.version} + provided + + + org.json + json + + + com.yahoo.vespa + component + ${project.version} + provided + + + javax.servlet + javax.servlet-api + provided + + + org.eclipse.jetty + jetty-servlet + provided + + + com.yahoo.vespa + container-core + ${project.version} + + + com.yahoo.vespa + config + + + + + com.yahoo.vespa + container-messagebus + ${project.version} + + + com.yahoo.vespa + config + + + + + com.yahoo.vespa + messagebus-disc + ${project.version} + + + com.yahoo.vespa + config + + + + + com.yahoo.vespa + vespajlib + ${project.version} + + + com.yahoo.vespa + vespalog + ${project.version} + + + + com.yahoo.vespa + simplemetrics + ${project.version} + provided + + + com.yahoo.vespa + config-bundle + ${project.version} + provided + + + com.yahoo.vespa + searchlib + ${project.version} + + + com.yahoo.vespa + config + + + + + com.yahoo.vespa + configdefinitions + ${project.version} + provided + + + + + + + com.yahoo.vespa + config-class-plugin + ${project.version} + + + + config-gen + + + + + + com.yahoo.vespa + bundle-plugin + true + + com.yahoo.container.jdisc.osgi.ContainerBundleActivator + com.yahoo.container.jdisc.ConfiguredApplication + + 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, + + 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, + 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 + + + + + + + + + coverage + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + validate + + exec + + + + + rm + ${project.build.directory} + + -f + generated-sources/vespa-configgen-plugin/com/yahoo/container/jdisc/JdiscBindingsConfig.java + + + + + + + + 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 { + + private static class MapFilterConfig implements FilterConfig { + + private final Map initParameters; + private final String filterName; + private final String filterClass; + + MapFilterConfig(Map 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 getInitParameterNames() { + return initParameters.keySet(); + } + } + + private final FilterConfig filterConfig; + + public FilterConfigProvider(HttpFilterConfig vespaConfig) { + filterConfig = new MapFilterConfig(toMap(vespaConfig), vespaConfig.filterName(), vespaConfig.filterClass()); + } + + private static Map toMap(HttpFilterConfig vespaConfig) { + Map 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 { + + 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 startedClients = newWeakIdentitySet(); + + private static final Set 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 getConfig(Class 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 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 currentServers = Container.get().getServerProviderRegistry().allComponents(); + HashSet 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 requestHandlerRegistry, + JdiscBindingsConfig discBindingsConfig) { + for (Map.Entry 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 bindings, List uriPatterns, + RequestHandler target) { + for (String uri : uriPatterns) { + bindings.bind(uri, target); + } + } + + private static Set newWeakIdentitySet() { + Map 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 Einar M R Rosenvinge + */ +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 Simon Thoresen Hult + */ +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() { + + @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 installBundle(String bundleLocation) throws BundleException { + throw newException(); + } + + @Override + public void startBundles(List 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 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 Oyvind Bakksjo + */ +public class FilterBindingsProvider implements Provider { + final BindingRepository requestFilters = new BindingRepository<>(); + final BindingRepository responseFilters = new BindingRepository<>(); + + public FilterBindingsProvider( + final ComponentId componentId, + final ServerConfig config, + final FilterChainRepository filterChainRepository, + final ComponentRegistry 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 toFilterSpecs(List inFilters) { + List 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 Oyvind Bakksjo + */ +class FilterUtil { + private static final ComponentId SEARCH_SERVER_COMPONENT_ID = ComponentId.fromString("SearchServer"); + + private final BindingRepository requestFilters; + private final BindingRepository responseFilters; + + private FilterUtil( + final BindingRepository requestFilters, + final BindingRepository responseFilters) { + this.requestFilters = requestFilters; + this.responseFilters = responseFilters; + } + + private void configureFilters( + final List 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 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 legacyRequestFilters, + final List filtersConfig, + final FilterChainRepository filterChainRepository, + final BindingRepository requestFilters, + final BindingRepository 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 Einar M R Rosenvinge + */ +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 Einar M R Rosenvinge + * @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 stringDictionary) { + if (wrapped == null) { + return null; + } + return wrapped.registerService(strings, o, stringDictionary); + } + + @Override + public ServiceRegistration registerService(String localHostname, Object o, Dictionary stringDictionary) { + if (wrapped == null) { + return null; + } + return wrapped.registerService(localHostname, o, stringDictionary); + } + + @Override + public ServiceRegistration registerService(Class sClass, S s, Dictionary 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 ServiceReference getServiceReference(Class sClass) { + if (wrapped == null) { + return null; + } + return wrapped.getServiceReference(sClass); + } + + @Override + public Collection> getServiceReferences(Class sClass, String localHostname) throws InvalidSyntaxException { + if (wrapped == null) { + return Collections.>emptyList(); + } + return wrapped.getServiceReferences(sClass, localHostname); + } + + @Override + public S getService(ServiceReference 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 { + + 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 { + + 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 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; + +/** + *

If more than one {@link MetricConsumerFactory} is registered in a container, calls to Metric need to be + * forwarded to all the underlying MetricConsumers. That is the responsibility of this class. Instances of this + * class is created by the {@link MetricConsumerProvider} in those cases.

+ * + * @author Simon Thoresen Hult + */ +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 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; + + +/** + *

The purpose of this class it to be the only provider for the MetricConsumer interface in a component + * graph. This component is automatically registered in the graph by the config server. Configuring a different + * MetricConsumer is done by registering one or more {@link MetricConsumerFactory} in the services-file.

+ * + *

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 Metric will be reconfigured. Any component that depends + * directly on MetricConsumer will also be reconfigured.

+ * + * @author Simon Thoresen Hult + */ +public class MetricConsumerProvider { + + private final MetricConsumerFactory[] factories; + + @Inject + public MetricConsumerProvider(ComponentRegistry 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 { + + private final MetricConsumerProvider provided; + + @Inject + public MetricConsumerProviderProvider(ComponentRegistry 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; + +/** + *

This class implements a {@link Provider} component of Metric. 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 Metric.

+ * + * @author Simon Thoresen Hult + */ +public final class MetricProvider implements Provider { + + private final Metric metric; + + public MetricProvider(MetricConsumerProvider provider) { + metric = new com.yahoo.jdisc.application.MetricProvider(new com.google.inject.Provider() { + + @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 Simon Thoresen Hult + */ +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 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 bundleRevisions) { + return new JacksonJaxrsResolverHook(); + } + } + + @Override + public void filterResolvable(Collection bundleRevisions) {} + + @Override + public void filterSingletonCollisions(BundleCapability bundleCapability, Collection bundleCapabilities) {} + + @Override + public void filterMatches(BundleRequirement bundleRequirement, Collection 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 bundleCapabilities, Predicate symbolicNamePredicate) { + for (Iterator 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 handlersById) { + JSONArray ret = new JSONArray(); + + for (Map.Entry 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 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 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 @@ + + + + + + + + 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 Simon Thoresen Hult + */ +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 Oyvind Bakksjo + */ +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 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 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(), + new ComponentRegistry()); + + // Set up the provider that we aim to test. + final FilterBindingsProvider provider = new FilterBindingsProvider( + new ComponentId("foo"), + new ServerConfig(configBuilder), + filterChainRepository, + new ComponentRegistry()); + + // 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 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 Matcher> containsFilters( + final T... requiredInstances) { + return new TypeSafeMatcher>() { + private final Set requiredFilterSet = new HashSet<>(Arrays.asList(requiredInstances)); + + @Override + protected boolean matchesSafely(final BindingRepository actualInstances) { + final Set notFoundFilterSet = new HashSet<>(requiredFilterSet); + for (final Map.Entry 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 { + 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 Simon Thoresen Hult + */ +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.>any())).thenReturn(fooCtx); + + MetricConsumer barConsumer = Mockito.mock(MetricConsumer.class); + Metric.Context barCtx = Mockito.mock(Metric.Context.class); + Mockito.when(barConsumer.createContext(Mockito.>any())).thenReturn(barCtx); + + MetricConsumer fwdConsumer = new ForwardingMetricConsumer(new MetricConsumer[] { fooConsumer, barConsumer }); + + Map 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 Simon Thoresen Hult + */ +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 Simon Thoresen Hult + */ +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 Simon Thoresen Hult + */ +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 newComponentRegistry(MetricConsumerFactory... factories) { + ComponentRegistry 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 Simon Thoresen Hult + */ +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 { + + final Provider provider; + + MetricTask(Provider 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 Simon Thoresen Hult + */ +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 Binary files /dev/null and b/container-disc/src/test/resources/ssl/keystore.jks 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 Binary files /dev/null and b/container-disc/src/test/resources/ssl/truststore.jks differ -- cgit v1.2.3