/** * 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.sixt.service.framework.annotation.*; import com.sixt.service.framework.util.Sleeper; import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.annotation.Annotation; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; public class JettyServiceBase { private static final Logger logger = LoggerFactory.getLogger(JettyServiceBase.class); public static void main(String[] args) { try { verifyDefaultCharset(); // find main class, rpc handlers, healthcheck providers, serviceRegistryPlugins, // configuration plugins, and metrics reporting plugins List<String> serviceEntries = new ArrayList<>(); List<String> rpcHandlers = new ArrayList<>(); List<String> hcProviders = new ArrayList<>(); List<String> serviceRegistryPlugins = new ArrayList<>(); List<String> configPlugins = new ArrayList<>(); List<String> metricsReportingPlugins = new ArrayList<>(); List<String> tracingPlugins = new ArrayList<>(); new FastClasspathScanner() .matchClassesWithAnnotation(OrangeMicroservice.class, matchingClass -> serviceEntries.add(matchingClass.getName())) .matchClassesWithAnnotation(RpcHandler.class, matchingClass -> rpcHandlers.add(matchingClass.getName())) .matchClassesWithAnnotation(HealthCheckProvider.class, matchingClass -> hcProviders.add(matchingClass.getName())) .matchClassesWithAnnotation(ServiceRegistryPlugin.class, matchingClass -> serviceRegistryPlugins.add(matchingClass.getName())) .matchClassesWithAnnotation(ConfigurationPlugin.class, matchingClass -> configPlugins.add(matchingClass.getName())) .matchClassesWithAnnotation(MetricsReporterPlugin.class, matchingClass -> metricsReportingPlugins.add(matchingClass.getName())) .matchClassesWithAnnotation(TracingPlugin.class, matchingClass -> tracingPlugins.add(matchingClass.getName())) .scan(); if (serviceEntries.isEmpty()) { logger.error("No OrangeMicroservice classes found"); System.exit(-1); } else if (serviceEntries.size() > 1) { logger.error("More than one OrangeMicroservice class found: {}", serviceEntries); System.exit(-1); } //create main class Class<?> serviceClass = Class.forName(serviceEntries.get(0)); AbstractService service = (AbstractService) serviceClass.newInstance(); displayHelpAndExitIfNeeded(args, service); service.enableJmx(); service.initProperties(args); service.setConfigurationPlugins(configPlugins); service.setServiceRegistryPlugins(serviceRegistryPlugins); service.setMetricsReporterPlugins(metricsReportingPlugins); service.setTracingPlugins(tracingPlugins); service.initializeConfigurationManager(); service.initializeGuice(); service.initializeMetricsReporting(); service.initializeServiceDiscovery(); service.registerMethodHandlers(rpcHandlers); service.registerMethodHandlers(); //we have to start the container before service registration happens to know the port service.startJettyContainer(); Annotation[] serviceAnnos = serviceClass.getAnnotations(); service.initializeServiceRegistration(); service.verifyEnvironment(); //we start health checks first so we can see services with bad state if (!hcProviders.isEmpty()) { service.initializeHealthCheckManager(hcProviders); } if (hasAnnotation(serviceAnnos, EnableDatabaseMigration.class)) { //this will block until the database is available. //it will then attempt a migration. if the migration fails, // the process emits an error and pauses. it's senseless to continue. service.performDatabaseMigration(); } service.bootstrapComplete(); } catch (Exception ex) { logger.error("Uncaught exception running service", ex); } } private static void displayHelpAndExitIfNeeded(String[] args, AbstractService service) { for (String arg : args) { if (arg.endsWith("help")) { service.displayHelp(System.out); System.exit(0); } } } private static void verifyDefaultCharset() { String charset = Charset.defaultCharset().name(); logger.info("Found charset {}", charset); if (! charset.equals("UTF-8")) { Exception ex = new IllegalStateException("The default character set is not UTF-8.\n" + "Please configure your runtime with the JVM option -Dfile.encoding=UTF8"); logger.error("Bad configuration", ex); try { new Sleeper().sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) { } } } private static boolean hasAnnotation(Annotation[] annotations, Class<?> targetAnnotation) { if ((annotations == null) || (annotations.length == 0)) { return false; } for (Annotation annotation : annotations) { if (annotation.annotationType().getSimpleName().equals(targetAnnotation.getSimpleName())) { return true; } } return false; } }