All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yammer.dropwizard.config.Environment Maven / Gradle / Ivy

package com.yammer.dropwizard.config;

import com.google.common.collect.*;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.core.reflection.AnnotatedMethod;
import com.sun.jersey.core.reflection.MethodList;
import com.sun.jersey.core.spi.scanning.PackageNamesScanner;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import com.yammer.dropwizard.jersey.DropwizardResourceConfig;
import com.yammer.dropwizard.jetty.JettyManaged;
import com.yammer.dropwizard.jetty.NonblockingServletHolder;
import com.yammer.dropwizard.json.ObjectMapperFactory;
import com.yammer.dropwizard.lifecycle.ExecutorServiceManager;
import com.yammer.dropwizard.lifecycle.Managed;
import com.yammer.dropwizard.lifecycle.ServerLifecycleListener;
import com.yammer.dropwizard.tasks.GarbageCollectionTask;
import com.yammer.dropwizard.tasks.Task;
import com.yammer.dropwizard.validation.Validator;
import com.yammer.metrics.core.HealthCheck;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import javax.ws.rs.ext.Provider;
import java.util.EventListener;
import java.util.concurrent.*;

import static com.google.common.base.Preconditions.checkNotNull;

// TODO: 10/12/11  -- test Environment
/*
    REVIEW: 11/12/11  -- Probably better to invert this code.
    Instead of letting it collect intermediate results and then exposing those via package-private
    getters, it might be better to pass this a ServletContextHandler, etc., and have it modify
    those directly. That's easier to test.
*/

/**
 * A Dropwizard service's environment.
 */
public class Environment extends AbstractLifeCycle {
    private static final Logger LOGGER = LoggerFactory.getLogger(Environment.class);

    private final String name;
    private final Configuration configuration;
    private final DropwizardResourceConfig config;
    private final ImmutableSet.Builder healthChecks;
    private final ImmutableMap.Builder servlets;
    private final ImmutableMultimap.Builder filters;
    private final ImmutableSet.Builder servletListeners;
    private final ImmutableSet.Builder tasks;
    private final ImmutableSet.Builder protectedTargets;
    private Resource baseResource;
    private final AggregateLifeCycle lifeCycle;
    private final ObjectMapperFactory objectMapperFactory;
    private SessionHandler sessionHandler;
    private ServletContainer jerseyServletContainer;
    private Validator validator;

    private ServerLifecycleListener serverListener;

    /**
     * Creates a new environment.
     *
     * @param name                the name of the service
     * @param configuration       the service's {@link Configuration}
     * @param objectMapperFactory the {@link ObjectMapperFactory} for the service
     */
    public Environment(String name,
                       Configuration configuration,
                       ObjectMapperFactory objectMapperFactory,
                       Validator validator) {
        this.name = name;
        this.configuration = configuration;
        this.objectMapperFactory = objectMapperFactory;
        this.validator = validator;
        this.config = new DropwizardResourceConfig(false) {
            @Override
            public void validate() {
                super.validate();
                logResources();
                logProviders();
                logHealthChecks();
                logManagedObjects();
                logEndpoints();
                logTasks();
            }
        };
        this.healthChecks = ImmutableSet.builder();
        this.servlets = ImmutableMap.builder();
        this.filters = ImmutableMultimap.builder();
        this.servletListeners = ImmutableSet.builder();
        this.tasks = ImmutableSet.builder();
        this.baseResource = Resource.newClassPathResource(".");
        this.protectedTargets = ImmutableSet.builder();
        this.lifeCycle = new AggregateLifeCycle();
        this.jerseyServletContainer = new ServletContainer(config);
        addTask(new GarbageCollectionTask());
    }

    @Override
    protected void doStart() throws Exception {
        lifeCycle.start();
    }

    @Override
    protected void doStop() throws Exception {
        lifeCycle.stop();
    }

    /**
     * Adds the given object as a Jersey singleton resource.
     *
     * @param resource a Jersey singleton resource
     */
    public void addResource(Object resource) {
        config.getSingletons().add(checkNotNull(resource));
    }

    /**
     * Scans the packages and sub-packages of the given {@link Class} objects for resources and
     * providers.
     *
     * @param classes the classes whose packages to scan
     */
    public void scanPackagesForResourcesAndProviders(Class... classes) {
        checkNotNull(classes);
        final String[] names = new String[classes.length];
        for (int i = 0; i < classes.length; i++) {
            names[i] = classes[i].getPackage().getName();
        }
        config.init(new PackageNamesScanner(names));
    }

    /**
     * Adds the given class as a Jersey resource. 

N.B.: This class must either have a * no-args constructor or use Jersey's built-in dependency injection. * * @param klass a Jersey resource class */ public void addResource(Class klass) { config.getClasses().add(checkNotNull(klass)); } /** * Adds the given object as a Jersey provider. * * @param provider a Jersey provider */ public void addProvider(Object provider) { config.getSingletons().add(checkNotNull(provider)); } /** * Adds the given class as a Jersey provider.

N.B.: This class must either have a * no-args constructor or use Jersey's built-in dependency injection. * * @param klass a Jersey provider class */ public void addProvider(Class klass) { config.getClasses().add(checkNotNull(klass)); } /** * Adds the given health check to the set of health checks exposed on the admin port. * * @param healthCheck a health check */ public void addHealthCheck(HealthCheck healthCheck) { healthChecks.add(checkNotNull(healthCheck)); } /** * Adds the given {@link Managed} instance to the set of objects managed by the server's * lifecycle. When the server starts, {@code managed} will be started. When the server stops, * {@code managed} will be stopped. * * @param managed a managed object */ public void manage(Managed managed) { lifeCycle.addBean(new JettyManaged(checkNotNull(managed))); } /** * Adds the given Jetty {@link LifeCycle} instances to the server's lifecycle. * * @param managed a Jetty-managed object */ public void manage(LifeCycle managed) { lifeCycle.addBean(checkNotNull(managed)); } /** * Add a servlet instance. * * @param servlet the servlet instance * @param urlPattern the URL pattern for requests that should be handled by {@code servlet} * @return a {@link ServletBuilder} instance allowing for further configuration */ public ServletBuilder addServlet(Servlet servlet, String urlPattern) { final ServletHolder holder = new NonblockingServletHolder(checkNotNull(servlet)); final ServletBuilder servletConfig = new ServletBuilder(holder, servlets); servletConfig.addUrlPattern(checkNotNull(urlPattern)); return servletConfig; } /** * Add a servlet class. * * @param klass the servlet class * @param urlPattern the URL pattern for requests that should be handled by instances of {@code * klass} * @return a {@link ServletBuilder} instance allowing for further configuration */ public ServletBuilder addServlet(Class klass, String urlPattern) { final ServletHolder holder = new ServletHolder(checkNotNull(klass)); final ServletBuilder servletConfig = new ServletBuilder(holder, servlets); servletConfig.addUrlPattern(checkNotNull(urlPattern)); return servletConfig; } /** * Add a filter instance. * * @param filter the filter instance * @param urlPattern the URL pattern for requests that should be handled by {@code filter} * @return a {@link FilterBuilder} instance allowing for further configuration */ public FilterBuilder addFilter(Filter filter, String urlPattern) { final FilterHolder holder = new FilterHolder(checkNotNull(filter)); final FilterBuilder filterConfig = new FilterBuilder(holder, filters); filterConfig.addUrlPattern(checkNotNull(urlPattern)); return filterConfig; } /** * Add a filter class. * * @param klass the filter class * @param urlPattern the URL pattern for requests that should be handled by instances of {@code * klass} * @return a {@link FilterBuilder} instance allowing for further configuration */ public FilterBuilder addFilter(Class klass, String urlPattern) { final FilterHolder holder = new FilterHolder(checkNotNull(klass)); final FilterBuilder filterConfig = new FilterBuilder(holder, filters); filterConfig.addUrlPattern(checkNotNull(urlPattern)); return filterConfig; } /** * Add one or more servlet event listeners. * * @param listeners one or more listener instances that implement {@link * javax.servlet.ServletContextListener}, {@link javax.servlet.ServletContextAttributeListener}, * {@link javax.servlet.ServletRequestListener} or {@link * javax.servlet.ServletRequestAttributeListener} */ public void addServletListeners(EventListener... listeners) { this.servletListeners.add(listeners); } /** * Adds a {@link Task} instance. * * @param task a {@link Task} */ public void addTask(Task task) { tasks.add(checkNotNull(task)); } /** * Adds a protected Target (ie a target that 404s) * * @param target a protected target */ public void addProtectedTarget(String target) { protectedTargets.add(checkNotNull(target)); } public void setBaseResource(Resource baseResource) { this.baseResource = baseResource; } public void setSessionHandler(SessionHandler sessionHandler) { this.sessionHandler = sessionHandler; } /** * Enables the Jersey feature with the given name. * * @param name the name of the feature to be enabled * @see ResourceConfig */ public void enableJerseyFeature(String name) { config.getFeatures().put(checkNotNull(name), Boolean.TRUE); } /** * Disables the Jersey feature with the given name. * * @param name the name of the feature to be disabled * @see ResourceConfig */ public void disableJerseyFeature(String name) { config.getFeatures().put(checkNotNull(name), Boolean.FALSE); } /** * Sets the given Jersey property. * * @param name the name of the Jersey property * @param value the value of the Jersey property * @see ResourceConfig */ public void setJerseyProperty(String name, @Nullable Object value) { config.getProperties().put(checkNotNull(name), value); } /** * Gets the given Jersey property. * * @param name the name of the Jersey property * @see ResourceConfig */ @SuppressWarnings("unchecked") public T getJerseyProperty(String name) { return (T) config.getProperties().get(name); } /** * Creates a new {@link ExecutorService} instance with the given parameters whose lifecycle is * managed by the service. * * @param nameFormat a {@link String#format(String, Object...)}-compatible format String, * to which a unique integer (0, 1, etc.) will be supplied as the single * parameter. * @param corePoolSize the number of threads to keep in the pool, even if they are idle. * @param maximumPoolSize the maximum number of threads to allow in the pool. * @param keepAliveTime when the number of threads is greater than the core, this is the * maximum time that excess idle threads will wait for new tasks before * terminating. * @param unit the time unit for the keepAliveTime argument. * @return a new {@link ExecutorService} instance */ public ExecutorService managedExecutorService(String nameFormat, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) { final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(nameFormat) .build(); final ExecutorService executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingQueue(), threadFactory); manage(new ExecutorServiceManager(executor, 5, TimeUnit.SECONDS, nameFormat)); return executor; } /** * Creates a new {@link ScheduledExecutorService} instance with the given parameters whose * lifecycle is managed by the service. * * @param nameFormat a {@link String#format(String, Object...)}-compatible format String, to * which a unique integer (0, 1, etc.) will be supplied as the single * parameter. * @param corePoolSize the number of threads to keep in the pool, even if they are idle. * @return a new {@link ScheduledExecutorService} instance */ public ScheduledExecutorService managedScheduledExecutorService(String nameFormat, int corePoolSize) { final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(nameFormat) .build(); final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); manage(new ExecutorServiceManager(executor, 5, TimeUnit.SECONDS, nameFormat)); return executor; } public Validator getValidator() { return validator; } public void setValidator(Validator validator) { this.validator = checkNotNull(validator); } /* * Internal Accessors */ ImmutableSet getHealthChecks() { return healthChecks.build(); } ImmutableMap getServlets() { return servlets.build(); } ImmutableMultimap getFilters() { return filters.build(); } ImmutableSet getTasks() { return tasks.build(); } ImmutableSet getProtectedTargets() { return protectedTargets.build(); } Resource getBaseResource() { return baseResource; } ImmutableSet getServletListeners() { return servletListeners.build(); } private void logManagedObjects() { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (Object bean : lifeCycle.getBeans()) { builder.add(bean.toString()); } LOGGER.debug("managed objects = {}", builder.build()); } private void logHealthChecks() { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (HealthCheck healthCheck : healthChecks.build()) { final String canonicalName = healthCheck.getClass().getCanonicalName(); if (canonicalName == null) { builder.add(String.format("%s(\"%s\")", HealthCheck.class.getCanonicalName(), healthCheck.getName())); } else { builder.add(canonicalName); } } LOGGER.debug("health checks = {}", builder.build()); } private void logResources() { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (Class klass : config.getClasses()) { if (klass.isAnnotationPresent(Path.class)) { builder.add(klass.getCanonicalName()); } } for (Object o : config.getSingletons()) { if (o.getClass().isAnnotationPresent(Path.class)) { builder.add(o.getClass().getCanonicalName()); } } LOGGER.debug("resources = {}", builder.build()); } private void logProviders() { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (Class klass : config.getClasses()) { if (klass.isAnnotationPresent(Provider.class)) { builder.add(klass.getCanonicalName()); } } for (Object o : config.getSingletons()) { if (o.getClass().isAnnotationPresent(Provider.class)) { builder.add(o.getClass().getCanonicalName()); } } LOGGER.debug("providers = {}", builder.build()); } private void logEndpoints() { final StringBuilder stringBuilder = new StringBuilder(1024).append("\n\n"); final ImmutableList.Builder> builder = ImmutableList.builder(); for (Object o : config.getSingletons()) { if (o.getClass().isAnnotationPresent(Path.class)) { builder.add(o.getClass()); } } for (Class klass : config.getClasses()) { if (klass.isAnnotationPresent(Path.class)) { builder.add(klass); } } for (Class klass : builder.build()) { final String path = klass.getAnnotation(Path.class).value(); final ImmutableList.Builder endpoints = ImmutableList.builder(); for (AnnotatedMethod method : annotatedMethods(klass)) { String rootPath = configuration.getHttpConfiguration().getRootPath(); if (rootPath.endsWith("/*")) { rootPath = rootPath.substring(0, rootPath.length() - 2); } final StringBuilder pathBuilder = new StringBuilder() .append(rootPath) .append(path); if (method.isAnnotationPresent(Path.class)) { final String methodPath = method.getAnnotation(Path.class).value(); if (!methodPath.startsWith("/") && !path.endsWith("/")) { pathBuilder.append('/'); } pathBuilder.append(methodPath); } for (HttpMethod verb : method.getMetaMethodAnnotations(HttpMethod.class)) { endpoints.add(String.format(" %-7s %s (%s)", verb.value(), pathBuilder.toString(), klass.getCanonicalName())); } } for (String line : Ordering.natural().sortedCopy(endpoints.build())) { stringBuilder.append(line).append('\n'); } } LOGGER.info(stringBuilder.toString()); } private void logTasks() { final StringBuilder stringBuilder = new StringBuilder(1024).append("\n\n"); for (Task task : tasks.build()) { stringBuilder.append(String.format(" %-7s /tasks/%s (%s)\n", "POST", task.getName(), task.getClass().getCanonicalName())); } LOGGER.info("tasks = {}", stringBuilder.toString()); } private MethodList annotatedMethods(Class resource) { return new MethodList(resource, true).hasMetaAnnotation(HttpMethod.class); } public SessionHandler getSessionHandler() { return sessionHandler; } public ObjectMapperFactory getObjectMapperFactory() { return objectMapperFactory; } public ResourceConfig getJerseyResourceConfig() { return config; } public ServletContainer getJerseyServletContainer() { return jerseyServletContainer; } public void setJerseyServletContainer(ServletContainer jerseyServletContainer) { this.jerseyServletContainer = jerseyServletContainer; } public String getName() { return name; } public ServerLifecycleListener getServerListener() { return serverListener; } public void setServerLifecycleListener(ServerLifecycleListener listener) { this.serverListener = listener; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy