com.sixt.service.framework.AbstractService.java Source code

Java tutorial

Introduction

Here is the source code for com.sixt.service.framework.AbstractService.java

Source

/**
 * Copyright 2016-2017 Sixt GmbH & Co. Autovermietung KG
 * 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.sixt.service.framework;

import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.protobuf.Message;
import com.sixt.service.framework.annotation.RpcHandler;
import com.sixt.service.framework.configuration.ConfigurationManager;
import com.sixt.service.framework.configuration.ConfigurationProvider;
import com.sixt.service.framework.configuration.LogLevelChangeCallback;
import com.sixt.service.framework.database.SchemaMigrator;
import com.sixt.service.framework.health.HealthCheck;
import com.sixt.service.framework.health.HealthCheckContributor;
import com.sixt.service.framework.health.HealthCheckManager;
import com.sixt.service.framework.injection.*;
import com.sixt.service.framework.jetty.JettyComposer;
import com.sixt.service.framework.health.ReadinessCheckServer;
import com.sixt.service.framework.jetty.RpcServlet;
import com.sixt.service.framework.logging.SixtLogbackContext;
import com.sixt.service.framework.metrics.MetricsReporterProvider;
import com.sixt.service.framework.registry.ServiceDiscoveryProvider;
import com.sixt.service.framework.registry.ServiceRegistrationProvider;
import com.sixt.service.framework.rpc.LoadBalancerFactory;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class AbstractService {

    private static final Logger logger = LoggerFactory.getLogger(AbstractService.class);

    protected MethodHandlerDictionary methodHandlers = new MethodHandlerDictionary();

    protected ServiceProperties serviceProperties = new ServiceProperties();
    protected Server jettyServer;
    protected Injector injector = null;
    protected AtomicBoolean startupComplete = new AtomicBoolean(false);
    private ConfigurationManager configurationManager;
    private List<String> serviceRegistryPlugins;
    private List<String> configurationPlugins;
    private List<String> metricsReporterPlugins;
    private List<String> tracingPlugins;

    @SuppressWarnings("unchecked")
    public void registerMethodHandlers(List<String> rpcHandlers) {
        for (String className : rpcHandlers) {
            try {
                Class clazz = Class.forName(className);

                if ((clazz != ServiceMethodHandler.class) && ServiceMethodHandler.class.isAssignableFrom(clazz)) {
                    registerMethodHandlerFor(((RpcHandler) clazz.getAnnotation(RpcHandler.class)).value(), clazz);
                } else {
                    throw new IllegalArgumentException(String.format(
                            "RpcHandler annotation applied to class %s that does not implement ServiceMethodHandler",
                            clazz.getName()));
                }
            } catch (ClassNotFoundException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * @deprecated please use displayHelp(PrintStream out) instead
     */
    @Deprecated
    public void displayHelp() {
        displayHelp(System.out);
    }

    public abstract void displayHelp(PrintStream out);

    /**
     * Supplies additional modules specific to this service.
     * Provide sub-class implementation to override
     */
    protected List<Module> getGuiceModules() {
        return null;
    }

    public void initializeGuice() throws Exception {
        if (injector == null) {
            InjectionModule mainModule = new InjectionModule(serviceProperties);
            mainModule.setConfigurationManager(configurationManager);
            mainModule.setMethodHandlerDictionary(methodHandlers);
            ServiceRegistryModule registryModule = new ServiceRegistryModule(serviceProperties);
            registryModule.setServiceRegistryPlugins(serviceRegistryPlugins);
            MetricsReporterModule metricsModule = new MetricsReporterModule();
            metricsModule.setPlugins(metricsReporterPlugins);
            TracingModule tracingModule = new TracingModule(serviceProperties);
            tracingModule.setPlugins(tracingPlugins);

            List<Module> modules = getGuiceModules();
            if (modules == null) {
                modules = new ArrayList<>();
            } else {
                modules = new ArrayList<>(modules);
            }

            modules.add(0, new MessagingModule());
            modules.add(0, new OrangeServletModule());
            modules.add(0, registryModule);
            modules.add(0, metricsModule);
            modules.add(0, tracingModule);
            modules.add(0, mainModule);
            //^^^ the order of the modules is specifically controlled here
            injector = Guice.createInjector((Module[]) modules.toArray(new Module[0]));
        }
    }

    public void initProperties(String[] args) {
        serviceProperties.initialize(args);
    }

    /**
     * Override to verify any required command-line parameters or environment
     * variables have been set.
     */
    public void verifyEnvironment() {
    }

    public void startJettyContainer() throws Exception {
        jettyServer = new Server(serviceProperties.getServicePort());
        JettyComposer.compose(jettyServer);
        jettyServer.start();
        int port = ((ServerConnector) jettyServer.getConnectors()[0]).getLocalPort();
        logger.info("Jetty has started on port {}", port);
        serviceProperties.setServicePort(port);
    }

    public void bootstrapComplete() throws InterruptedException {
        startupComplete.set(true);
        injector.getInstance(RpcServlet.class).serveRequests();
        injector.getInstance(ReadinessCheckServer.class).serveRequests();
        jettyServer.join();
    }

    @SuppressWarnings("unchecked")
    public void initializeHealthCheckManager(List<String> hcProviders) {
        if (hcProviders != null && !hcProviders.isEmpty()) {
            HealthCheckManager hcManager = injector.getInstance(HealthCheckManager.class);
            for (String hcp : hcProviders) {
                try {
                    Class<HealthCheckContributor> hcClass = (Class<HealthCheckContributor>) Class.forName(hcp);
                    logger.debug("Found HealthCheckContributor: {}", hcClass.getSimpleName());
                    HealthCheckContributor contrib = injector.getInstance(hcClass);
                    if (contrib.shouldRegister()) {
                        hcManager.registerPollingContributor(contrib);
                    } else {
                        logger.debug("HealthCheckContributor is choosing not to join");
                    }
                } catch (ClassNotFoundException e) {
                    logger.error("Error loading HealthCheckProvider: " + hcp, e);
                }
            }
            hcManager.registerOneShotContributor(new HealthCheckContributor() {
                @Override
                public HealthCheck getHealthCheck() {
                    if (startupComplete.get()) {
                        return new HealthCheck();
                    } else {
                        return new HealthCheck("service_startup_completion", HealthCheck.Status.FAIL,
                                "Service startup not complete");
                    }
                }

                @Override
                public boolean shouldRegister() {
                    return true;
                }
            });
            hcManager.initialize();
        }
    }

    public void performDatabaseMigration() {
        SchemaMigrator migrator = injector.getInstance(SchemaMigrator.class);
        migrator.migrate();
    }

    /**
     * Override this method to register method handlers at startup.
     * Classpath-scanning is now also offered to find @RpcHandlers
     */
    public void registerMethodHandlers() {
    }

    @SuppressWarnings("unchecked")
    protected void registerMethodHandlerFor(String endpoint, Class<? extends ServiceMethodHandler> handlerClass) {
        methodHandlers.put(endpoint, injector.getInstance(handlerClass));
    }

    protected void registerPreMethodHandlerHookFor(String endpoint,
            Class<? extends ServiceMethodPreHook<? extends Message>> handlerClass) {
        methodHandlers.addPreHook(endpoint, injector.getInstance(handlerClass));
    }

    protected void registerPostMethodHandlerHookFor(String endpoint,
            Class<? extends ServiceMethodPostHook<? extends Message>> handlerClass) {
        methodHandlers.addPostHook(endpoint, injector.getInstance(handlerClass));
    }

    public Map<String, ServiceMethodHandler<? extends Message, ? extends Message>> getMethodHandlers() {
        return methodHandlers.getMethodHandlers();
    }

    @VisibleForTesting
    public void setInjector(Injector injector) {
        this.injector = injector;
    }

    public ServiceProperties getServiceProperties() {
        return serviceProperties;
    }

    public void enableJmx() {
        System.setProperty("com.sun.management.jmxremote.ssl", "false");
        System.setProperty("com.sun.management.jmxremote.authenticate", "false");
        System.setProperty("com.sun.management.jmxremote.port", "1099");
    }

    public void initializeServiceDiscovery() {
        initializeLoadBalancerFactory(injector);
    }

    public void initializeServiceRegistration() {
        injector.getInstance(ServiceRegistrationProvider.class);
    }

    public void initializeConfigurationManager() {
        if (StringUtils.equals(serviceProperties.getServiceName(), "com.sixt.service.configuration")) {
            return;
        }

        InjectionModule configBaseModule = new InjectionModule(serviceProperties);
        configurationManager = new ConfigurationManager(serviceProperties);
        configBaseModule.setConfigurationManager(configurationManager);

        ServiceRegistryModule serviceRegistryModule = new ServiceRegistryModule(serviceProperties);
        serviceRegistryModule.setServiceRegistryPlugins(serviceRegistryPlugins);
        ConfigurationModule configurationModule = new ConfigurationModule(serviceProperties);
        configurationModule.setConfigurationPlugins(configurationPlugins);

        Injector configInjector = Guice.createInjector(configBaseModule, serviceRegistryModule, configurationModule,
                new TracingModule(serviceProperties));

        ConfigurationProvider configProvider = configInjector.getInstance(ConfigurationProvider.class);
        if (configProvider == null) {
            return;
        }

        initializeLoadBalancerFactory(configInjector);
        configurationManager.registerChangeCallback(ServiceProperties.LOG_LEVEL_KEY,
                new LogLevelChangeCallback(new SixtLogbackContext()));
        configProvider.initialize();
        configurationManager.waitForInitialConfiguration();
    }

    private void initializeLoadBalancerFactory(Injector localInjector) {
        ServiceDiscoveryProvider provider = localInjector.getInstance(ServiceDiscoveryProvider.class);
        if (provider != null) {
            LoadBalancerFactory lbFactory = localInjector.getInstance(LoadBalancerFactory.class);
            lbFactory.initialize(provider);
        }
    }

    void setServiceRegistryPlugins(List<String> serviceRegistryPlugins) {
        this.serviceRegistryPlugins = serviceRegistryPlugins;
    }

    void setConfigurationPlugins(List<String> configurationPlugins) {
        this.configurationPlugins = configurationPlugins;
    }

    public void setMetricsReporterPlugins(List<String> metricsReporterPlugins) {
        this.metricsReporterPlugins = metricsReporterPlugins;
    }

    public void initializeMetricsReporting() {
        MetricsReporterProvider provider = injector.getInstance(MetricsReporterProvider.class);
        if (provider != null) {
            provider.initialize();
        }
    }

    public void setTracingPlugins(List<String> tracingPlugins) {
        this.tracingPlugins = tracingPlugins;
    }
}