// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. /** *
Provides classes and interfaces for implementing an {@link com.yahoo.jdisc.application.Application * Application}.
* *In every jDISC process there is exactly one Application instance, it is created during jDISC startup, and it is * destroyed during jDISC shutdown. The Application uses the {@link com.yahoo.jdisc.application.ContainerBuilder * ContainerBuilder} interface to load OSGi {@link org.osgi.framework.Bundle Bundles}, install Guice {@link * com.google.inject.Module Modules}, create and start {@link com.yahoo.jdisc.service.ServerProvider ServerProviders}, * inject a {@link com.yahoo.jdisc.application.BindingSetSelector BindingSetSelector}, and configure {@link * com.yahoo.jdisc.application.BindingRepository BindingSets} with {@link com.yahoo.jdisc.handler.RequestHandler * RequestHandlers} and {@link com.yahoo.jdisc.service.ClientProvider ClientProviders}. Once the ContainerBuilder is * appropriately configured, it is passed to the local {@link com.yahoo.jdisc.application.ContainerActivator} to perform * an atomic switch from current to new {@link com.yahoo.jdisc.Container Container}.
*@Inject MyApplication(ContainerActivator activator) { ContainerBuilder builder = activator.newContainerBuilder(); builder.guiceModules().install(new MyBindings()); Bundle bundle = builder.osgiBundles().install("file:$VESPA_HOME/lib/jars/jdisc_http.jar"); builder.serverProviders().install(bundle, "com.yahoo.disc.service.http.HttpServer"); builder.serverBindings().bind("http://localhost/admin/*", new MyAdminHandler()); builder.serverBindings().bind("http://localhost/*", new MyRequestHandler()); activator.activateContainer(builder); }* *
Because the {@link com.yahoo.jdisc.Request Request} owns a reference to the Container that was active on Request- * construction, jDISC is able to guarantee that no component is shut down as long as there are pending Requests that * can reach them. When activating a new Container, the previous Container is returned as a {@link * com.yahoo.jdisc.application.DeactivatedContainer DeactivatedContainer} instance - an API that can be used by the * Application to asynchronously wait for Container termination in order to completely shut down components that are no * longer required. This activation pattern is used both for Application startup, runtime reconfigurations, as well as * for Application shutdown. It allows all jDISC Application to continously serve Requests during reconfiguration, * causing no down time other than what the Application itself explicitly enforces.
*void reconfigureApplication() { (...) reconfiguredContainerBuilder.handlers().install(myRetainedClients); reconfiguredContainerBuilder.servers().install(myRetainedServers); myExpiredServers.close(); DeactivatedContainer deactivatedContainer = containerActivator.activateContainer(reconfiguredContainerBuilder); deactivatedContainer.notifyTermination(new Runnable() { void run() { myExpiredClients.destroy(); myExpiredServers.destroy(); } }); }* *
At the heart of jDISC is an OSGi framework. An Application is always packaged as an OSGi bundle. The OSGi * technology itself is a set of specifications that define a dynamic component system for Java. These specifications * enable a development model where applications are (dynamically) composed of many different (reusable) components. The * OSGi specifications enable components to hide their implementations from other components while communicating through * common interfaces (in our case, defined by jDISC's core API) or services (which are objects that are explicitly * shared between components). Initially this framework is used to load and bootstrap the application from an OSGi * bundle specified on deployment, but because it is exposed through the ContainerBuilder interface, an Application * itself can load other bundles as required.
* *The OSGi integration in jDISC adds the following manifest instructions:
*One of the benefits of using OSGi is that it provides Classloader isolation, meaning that one bundle can not * inadvertently affect the inernals of another. jDISC leverages this to isolate the different implementations of * RequestHandlers, ServerProviders, and jDISC's core internals.
* *The OSGi manifest instruction "X-JDisc-Application" tells jDISC the name of the Application class to inject from
* the loaded bundle during startup. To this end, it is necessary for the named Application to offer an
* injection-enabled constructor (annotated with the Inject
keyword). At a minimum, an Application
* typically needs to have the ContainerActivator injected and saved to a member variable. Because of jDISC's additional
* OSGi manifest instruction "X-JDisc-Preinstall-Bundle", an Application bundle can be built with compile-time
* dependencies on other OSGi bundles (using the "provided" scope in maven) without having to repack those dependency
* into the application itself. Unless incompatible API changes are made to 3rd party jDISC components, it should be
* possible to upgrade dependencies without having to recompile and redeploy the Application.
jDISC allows a single binary to execute any application without having to change the command line parameters. * Instead of * modifying the parameters of the single application binary, changing the application is achieved by setting a single * environment variable. The planned method of deployment is therefore to 1) install the application's OSGi bundle, * 2) set the necessary "jdisc.application" environment variable, and 3) restart the package.
*$ install myapp_jar $ set jdisc.application="myapp.jar" $ restart jdisc* *
It is the responsibility of the Application itself to create, configure * and activate a Container instance. Although jDISC offers an API that allows for- and manages the change of an active * Container instance, making the necessary calls to do so is also considered Application logic. When jDISC receives an * external signal to shut down, it instructs the running Application to initiate a graceful shutdown, and waits for it * to terminate. Any in-flight Requests should complete, and all services will close.
* *Because jDISC runs as a Daemon it has the opportunity to run code with root privileges, and it can be configured * to provide these privileges to an application's initialization code. However, 1) deployment-time configuration must * explicitly enable this capability (by setting the environment variable "jdisc.privileged" to "true"), and 2) the * application bundle must explicitly declare that it requires privileges (by including the manifest header * "X-JDisc-Privileged-Activator" with the value "true"). If privileges are required but unavailable, deployment of the * application will fail. Code that requires privileges will never be run WITHOUT privileges, and code that does not * explicitly request privileges will never be run WITH privileges. Finally, the code snippet that is run with * privileges is separate from the Application class to avoid unintentionally passing privileges to third-party * code.
* * @see com.yahoo.jdisc * @see com.yahoo.jdisc.handler * @see com.yahoo.jdisc.service */ package com.yahoo.jdisc.application;