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

com.opentable.server.OTApplication Maven / Gradle / Ivy

Go to download

Wiring for basic Jetty core servlet regardless of MVC or JaxRS choices. Also wires logging, metrics, etc

There is a newer version: 6.0.4
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.opentable.server;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

import com.google.common.base.Preconditions;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.StandardEnvironment;

/**
 * OpenTable specific Spring Boot style application runner.
 * Sets up logging and other OT specific customizations.
 *
 * Note that this API only accepts {@code Class} arguments
 * rather than generic {@code Object} -- this is a style choice,
 * we prefer to write explicit classes rather than relying on instances.
 * This can change down the road if needed.
 */
public final class OTApplication {

    private static final String OPT_IN_TO_NEW_BEHAVIOR = "ot.spring.logging";
    public static final String ORG_SPRINGFRAMEWORK_BOOT_LOGGING_LOGGING_SYSTEM = "org.springframework.boot.logging.LoggingSystem";
    public static final String LOGGING_CONFIG = "logging.config";
    public static final String LEGACY_LOGGING_CONFIG = "logback.configurationFile";

    private OTApplication() { }

    /**
     * If you are running a command line app (perhaps a scheduled or run once task), this
     * allows you to run using no WebApplication setup. We still recommend you import
     * otj-server-core and add the @NonWebSetup annotation in place of @MVCServer or @ReactiveServer
     * to import otj-jackson, otj-metrics, and other such otj goodness.
     * @param applicationClass your application class
     * @param args args, if any. May be new String[0]
     * @return The context
     */
    public static ConfigurableApplicationContext commandline(Class applicationClass,
                                                             String[] args) {
        return run(applicationClass, args, new HashMap<>(),
                springApplicationBuilder -> springApplicationBuilder.web(WebApplicationType.NONE));
    }

    /**
     * Construct and run a {@link SpringApplication} with the default settings for
     * {code otj-} OpenTable Spring Boot based applications.
     * @param applicationClass the main class to boot.  Should be a Spring configuration class.
     * @param args the {@code main()}-style application arguments
     * @return the configured application context
     */
    public static ConfigurableApplicationContext run(Class applicationClass, String... args) {
        return run(applicationClass, args, b -> {});
    }

    /**
     * Construct and run a {@link SpringApplication} with custom settings for
     * {code otj-} OpenTable Spring Boot based applications.
     * @param applicationClass the main class to boot.  Should be a Spring configuration class.
     * @param args the {@code main()}-style application arguments
     * @param customize a hook to configure the application before running
     * @return the configured application context
     */
    public static ConfigurableApplicationContext run(Class applicationClass, String[] args, Consumer customize) {
        /*
         * Truth table of scenarios
         *
         * 1. New Server, New otpl-common-config
         * Goal: We'd want this to switch over, conditionally to the new system.
         * Answer: The code below, plus otpl-common-config adding the OPT_IN_TO_NEW_BEHAVIOR and the LOGGING_CONFIG. otpl-common-config never sets
         * the logging system directly.
         *
         * 2. New Server, old otpl-common-config
         * Goal: We'd want this to have the old behavior, eg turn off spring logging
         * Answer: The code below will continue to execute old path unless user manually sets OPT_IN_TO_NEW_BEHAVIOR and the LOGGING_CONFIG.
         * This also covers java8/9/10 which we won't update otpl-common-config for.
         *
         * 3. Old Server, New otpl-common-confiig
         * Goal: We'd want this to "work". Either option is acceptable
         * Answer: The old otj-server ignores these switches, so assuming it's safe to set LOGGING_CONFIG but set none, we are fine
         *
         * 4. Old Server, Old otpl-common-config
         * Goal: By definition old Spring system
         * Answer: By definition this works
         *
         * 5. Consider these running locally, without otpl-common-config
         * Answer: Unless they manually sets OPT_IN_TO_NEW_BEHAVIOR and the LOGGING_CONFIG, the old behavior applies
         *
         * In addition, scenario 1 has an escape hatch, the user can manually set this and opt out.
         */
        final boolean enableSpringLogging = Boolean.parseBoolean(System.getProperty(OPT_IN_TO_NEW_BEHAVIOR));
        final boolean springLoggingConfigFileExists = StringUtils.isNotBlank(System.getProperty(LEGACY_LOGGING_CONFIG));
        if (springLoggingConfigFileExists) {
            if (enableSpringLogging) {
                // Copy "logback.configurationFile" property to the "logging.config" and clear old one, to avoid warning
                // "Ignoring 'logback.configurationFile' system property. Please use 'logging.config' instead."
                System.setProperty(LOGGING_CONFIG, System.getProperty(LEGACY_LOGGING_CONFIG));
                System.clearProperty(LEGACY_LOGGING_CONFIG);
            } else {
                // Disable spring system logging subsystem initialization
                // and clear "logging.config" property
                System.setProperty(ORG_SPRINGFRAMEWORK_BOOT_LOGGING_LOGGING_SYSTEM, "none");
                System.clearProperty(LOGGING_CONFIG);
            }
        }

        final SpringApplicationBuilder builder = new SpringApplicationBuilder(applicationClass);
        builder.main(applicationClass);
        customize.accept(builder);
        return builder.run(args);
    }

    /**
     * Construct and run a {@link SpringApplication} with custom settings for
     * {code otj-} OpenTable Spring Boot based applications, accepting property overrides.
     * @param applicationClass the main class to boot.  Should be a Spring configuration class.
     * @param args the {@code main()}-style application arguments
     * @param properties a map of properties to override the standard environment-resolved properties
     * @param customize a hook to configure the application after property overrides, but before running
     * @return the configured application context
     */
    public static ConfigurableApplicationContext run(
            Class applicationClass,
            String[] args,
            Map properties,
            Consumer customize) {
        final Consumer overrideProperties = builder ->
                builder.environment(
                        new StandardEnvironment() {
                            @Override
                            protected void customizePropertySources(MutablePropertySources propertySources) {
                                super.customizePropertySources(propertySources);
                                propertySources.addFirst(
                                        new MapPropertySource(
                                                "OTApplication.run$properties",
                                                properties
                                        )
                                );
                            }
                        }
                );
        return run(applicationClass, args, overrideProperties.andThen(customize));
    }

    /**
     * Construct and run a {@link SpringApplication} with custom settings for
     * {code otj-} OpenTable Spring Boot based applications, accepting property overrides.
     * @param applicationClass the main class to boot.  Should be a Spring configuration class.
     * @param args the {@code main()}-style application arguments
     * @param properties a map of properties to override the standard environment-resolved properties
     * @return the configured application context
     */
    public static ConfigurableApplicationContext run(
            Class applicationClass,
            String[] args,
            Map properties) {
        return run(applicationClass, args, properties, b -> {});
    }

    /**
     * @param ctx Spring application context
     * @return URI Return the base URI for a given application context.  Mostly useful in tests.
     */
    public static URI getBaseUri(ConfigurableApplicationContext ctx) {
        return getBaseUri(ctx, "default");
    }

    /**
     * @param ctx Spring application context
     * @param connector connector name. Currently only default is accepted.
     * @return URI Return the base URI for a given application context.  Mostly useful in tests.
     */
    public static URI getBaseUri(ConfigurableApplicationContext ctx, String connector) {
        Preconditions.checkState("default".equals(connector), "TODO: implement non-default connectors");
        return URI.create("http://127.0.0.1:" + ctx.getBean(HttpServerInfo.class).getPort());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy