/* * Copyright © 2014-2016 Cask Data, Inc. * * 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 co.cask.cdap; import co.cask.cdap.api.metrics.MetricsCollectionService; import co.cask.cdap.app.guice.AppFabricServiceRuntimeModule; import co.cask.cdap.app.guice.AuthorizationModule; import co.cask.cdap.app.guice.ProgramRunnerRuntimeModule; import co.cask.cdap.app.guice.ServiceStoreModules; import co.cask.cdap.app.store.ServiceStore; import co.cask.cdap.common.ServiceBindException; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.common.guice.ConfigModule; import co.cask.cdap.common.guice.DiscoveryRuntimeModule; import co.cask.cdap.common.guice.IOModule; import co.cask.cdap.common.guice.KafkaClientModule; import co.cask.cdap.common.guice.LocationRuntimeModule; import co.cask.cdap.common.guice.ZKClientModule; import co.cask.cdap.common.io.URLConnections; import co.cask.cdap.common.namespace.guice.NamespaceClientRuntimeModule; import co.cask.cdap.common.startup.ConfigurationLogger; import co.cask.cdap.common.utils.DirUtils; import co.cask.cdap.common.utils.Networks; import co.cask.cdap.common.utils.OSDetector; import co.cask.cdap.data.runtime.DataFabricModules; import co.cask.cdap.data.runtime.DataSetServiceModules; import co.cask.cdap.data.runtime.DataSetsModules; import co.cask.cdap.data.stream.StreamAdminModules; import co.cask.cdap.data.stream.service.StreamService; import co.cask.cdap.data.stream.service.StreamServiceRuntimeModule; import co.cask.cdap.data.view.ViewAdminModules; import co.cask.cdap.data2.audit.AuditModule; import co.cask.cdap.data2.datafabric.dataset.service.DatasetService; import co.cask.cdap.explore.client.ExploreClient; import co.cask.cdap.explore.executor.ExploreExecutorService; import co.cask.cdap.explore.guice.ExploreClientModule; import co.cask.cdap.explore.guice.ExploreRuntimeModule; import co.cask.cdap.explore.service.ExploreServiceUtils; import co.cask.cdap.gateway.router.NettyRouter; import co.cask.cdap.gateway.router.RouterModules; import co.cask.cdap.internal.app.services.AppFabricServer; import co.cask.cdap.logging.appender.LogAppenderInitializer; import co.cask.cdap.logging.guice.LoggingModules; import co.cask.cdap.metadata.MetadataService; import co.cask.cdap.metadata.MetadataServiceModule; import co.cask.cdap.metrics.guice.MetricsClientRuntimeModule; import co.cask.cdap.metrics.guice.MetricsHandlerModule; import co.cask.cdap.metrics.query.MetricsQueryService; import co.cask.cdap.notifications.feeds.guice.NotificationFeedServiceRuntimeModule; import co.cask.cdap.notifications.guice.NotificationServiceRuntimeModule; import co.cask.cdap.security.guice.SecurityModules; import co.cask.cdap.security.server.ExternalAuthenticationServer; import co.cask.cdap.store.guice.NamespaceStoreModule; import co.cask.tephra.inmemory.InMemoryTransactionService; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.Service; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.mapreduce.counters.Limits; import org.apache.twill.kafka.client.KafkaClientService; import org.apache.twill.zookeeper.ZKClientService; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.util.Collections; import java.util.List; /** * Standalone Main. * NOTE: Use AbstractIdleService */ public class StandaloneMain { // A special key in the CConfiguration to disable UI. It's mainly used for unit-tests that start Standalone. public static final String DISABLE_UI = "standalone.disable.ui"; public static final String LOCAL_ZOOKEEPER_SERVER_PORT = "local.zkserver.port"; public static final String LOCAL_KAFKA_DIR = "local.kafka.dir"; private static final Logger LOG = LoggerFactory.getLogger(StandaloneMain.class); private final Injector injector; private final UserInterfaceService userInterfaceService; private final NettyRouter router; private final MetricsQueryService metricsQueryService; private final AppFabricServer appFabricServer; private final ServiceStore serviceStore; private final StreamService streamService; private final MetricsCollectionService metricsCollectionService; private final LogAppenderInitializer logAppenderInitializer; private final InMemoryTransactionService txService; private final MetadataService metadataService; private final boolean securityEnabled; private final boolean sslEnabled; private final CConfiguration cConf; private final DatasetService datasetService; private final ExploreClient exploreClient; private final ZKClientService zkClient; private final KafkaClientService kafkaClient; private final ExternalJavaProcessExecutor kafkaProcessExecutor; private final ExternalJavaProcessExecutor zookeeperProcessExecutor; private ExternalAuthenticationServer externalAuthenticationServer; private ExploreExecutorService exploreExecutorService; private StandaloneMain(List<Module> modules, CConfiguration cConf) { this.cConf = cConf; injector = Guice.createInjector(modules); // Start ZK client, Kafka client, ZK Server and Kafka Server only when audit is enabled if (cConf.getBoolean(Constants.Audit.ENABLED)) { zkClient = injector.getInstance(ZKClientService.class); kafkaClient = injector.getInstance(KafkaClientService.class); File zkLogs = new File(cConf.get(Constants.CFG_LOCAL_DATA_DIR), "zk-logs"); String zkPort = cConf.get(LOCAL_ZOOKEEPER_SERVER_PORT); zookeeperProcessExecutor = new ExternalJavaProcessExecutor(ZooKeeperServerMain.class.getCanonicalName(), ImmutableList.of(zkPort, zkLogs.getAbsolutePath())); String kafkaClassPath = new File(cConf.get(LOCAL_KAFKA_DIR), "*").getAbsolutePath(); kafkaProcessExecutor = new ExternalJavaProcessExecutor("co.cask.cdap.kafka.run.KafkaServerMain", Collections.<String>emptyList(), ImmutableMap.of("KAFKA_HEAP_OPTS", "-Xmx1G -Xms1G"), kafkaClassPath); } else { zkClient = null; kafkaClient = null; zookeeperProcessExecutor = null; kafkaProcessExecutor = null; } txService = injector.getInstance(InMemoryTransactionService.class); router = injector.getInstance(NettyRouter.class); metricsQueryService = injector.getInstance(MetricsQueryService.class); appFabricServer = injector.getInstance(AppFabricServer.class); logAppenderInitializer = injector.getInstance(LogAppenderInitializer.class); metricsCollectionService = injector.getInstance(MetricsCollectionService.class); datasetService = injector.getInstance(DatasetService.class); serviceStore = injector.getInstance(ServiceStore.class); streamService = injector.getInstance(StreamService.class); if (cConf.getBoolean(DISABLE_UI, false)) { userInterfaceService = null; } else { userInterfaceService = injector.getInstance(UserInterfaceService.class); } sslEnabled = cConf.getBoolean(Constants.Security.SSL_ENABLED); securityEnabled = cConf.getBoolean(Constants.Security.ENABLED); if (securityEnabled) { externalAuthenticationServer = injector.getInstance(ExternalAuthenticationServer.class); } boolean exploreEnabled = cConf.getBoolean(Constants.Explore.EXPLORE_ENABLED); if (exploreEnabled) { ExploreServiceUtils.checkHiveSupport(getClass().getClassLoader()); exploreExecutorService = injector.getInstance(ExploreExecutorService.class); } exploreClient = injector.getInstance(ExploreClient.class); metadataService = injector.getInstance(MetadataService.class); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { shutDown(); } catch (Throwable e) { LOG.error("Failed to shutdown", e); // Because shutdown hooks execute concurrently, the logger may be closed already: thus also print it. System.err.println("Failed to shutdown: " + e.getMessage()); e.printStackTrace(System.err); } } }); } /** * INTERNAL METHOD. Returns the guice injector of Standalone. It's for testing only. Use with extra caution. */ @VisibleForTesting public Injector getInjector() { return injector; } /** * Start the service. */ public void startUp() throws Exception { // Workaround for release of file descriptors opened by URLClassLoader - https://issues.cask.co/browse/CDAP-2841 URLConnections.setDefaultUseCaches(false); cleanupTempDir(); ConfigurationLogger.logImportantConfig(cConf); // Start all the services. if (zookeeperProcessExecutor != null) { zookeeperProcessExecutor.startAndWait(); } if (kafkaProcessExecutor != null) { kafkaProcessExecutor.startAndWait(); } if (zkClient != null) { zkClient.startAndWait(); } if (kafkaClient != null) { kafkaClient.startAndWait(); } txService.startAndWait(); metricsCollectionService.startAndWait(); datasetService.startAndWait(); serviceStore.startAndWait(); streamService.startAndWait(); // It is recommended to initialize log appender after datasetService is started, // since log appender instantiates a dataset. logAppenderInitializer.initialize(); Service.State state = appFabricServer.startAndWait(); if (state != Service.State.RUNNING) { throw new Exception("Failed to start Application Fabric"); } metricsQueryService.startAndWait(); router.startAndWait(); if (userInterfaceService != null) { userInterfaceService.startAndWait(); } if (securityEnabled) { externalAuthenticationServer.startAndWait(); } if (exploreExecutorService != null) { exploreExecutorService.startAndWait(); } metadataService.startAndWait(); String protocol = sslEnabled ? "https" : "http"; int dashboardPort = sslEnabled ? cConf.getInt(Constants.Dashboard.SSL_BIND_PORT) : cConf.getInt(Constants.Dashboard.BIND_PORT); System.out.println("Standalone CDAP started successfully."); System.out.printf("Connect to the CDAP UI at %s://%s:%d\n", protocol, "localhost", dashboardPort); } /** * Shutdown the service. */ public void shutDown() { LOG.info("Shutting down Standalone CDAP"); try { // order matters: first shut down UI 'cause it will stop working after router is down if (userInterfaceService != null) { userInterfaceService.stopAndWait(); } // shut down router to stop all incoming traffic router.stopAndWait(); // now the stream writer and the explore service (they need tx) streamService.stopAndWait(); if (exploreExecutorService != null) { exploreExecutorService.stopAndWait(); } exploreClient.close(); metadataService.stopAndWait(); serviceStore.stopAndWait(); // app fabric will also stop all programs appFabricServer.stopAndWait(); // all programs are stopped: dataset service, metrics, transactions can stop now datasetService.stopAndWait(); metricsQueryService.stopAndWait(); txService.stopAndWait(); if (securityEnabled) { // auth service is on the side anyway externalAuthenticationServer.stopAndWait(); } logAppenderInitializer.close(); if (kafkaClient != null) { kafkaClient.stopAndWait(); } if (zkClient != null) { zkClient.startAndWait(); } if (kafkaProcessExecutor != null) { kafkaProcessExecutor.stopAndWait(); } if (zookeeperProcessExecutor != null) { zookeeperProcessExecutor.stopAndWait(); } } catch (Throwable e) { LOG.error("Exception during shutdown", e); // We can't do much but exit. Because there was an exception, some non-daemon threads may still be running. // Therefore System.exit() won't do it, we need to force a halt. Runtime.getRuntime().halt(1); } finally { cleanupTempDir(); } } private void cleanupTempDir() { File tmpDir = new File(cConf.get(Constants.CFG_LOCAL_DATA_DIR), cConf.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile(); if (!tmpDir.isDirectory()) { return; } try { DirUtils.deleteDirectoryContents(tmpDir, true); } catch (IOException e) { // It's ok not able to cleanup temp directory. LOG.debug("Failed to cleanup temp directory {}", tmpDir, e); } } public static void main(String[] args) { StandaloneMain main = create(); try { if (args.length > 0) { System.out.printf("%s takes no arguments\n", StandaloneMain.class.getSimpleName()); System.out.println("These arguments are being ignored:"); for (int i = 0; i <= args.length - 1; i++) { System.out.printf("Parameter #%d: %s\n", i, args[i]); } } main.startUp(); } catch (Throwable e) { @SuppressWarnings("ThrowableResultOfMethodCallIgnored") Throwable rootCause = Throwables.getRootCause(e); if (rootCause instanceof ServiceBindException) { LOG.error("Failed to start Standalone CDAP: {}", rootCause.getMessage()); System.err.println("Failed to start Standalone CDAP: " + rootCause.getMessage()); } else { // exception stack trace will be logged by // UncaughtExceptionIdleService.UNCAUGHT_EXCEPTION_HANDLER LOG.error("Failed to start Standalone CDAP"); System.err.println("Failed to start Standalone CDAP"); e.printStackTrace(System.err); } Runtime.getRuntime().halt(-2); } } /** * The root of all goodness! */ public static StandaloneMain create() { return create(CConfiguration.create(), new Configuration()); } public static StandaloneMain create(CConfiguration cConf, Configuration hConf) { // This is needed to use LocalJobRunner with fixes (we have it in app-fabric). // For the modified local job runner hConf.addResource("mapred-site-local.xml"); hConf.reloadConfiguration(); // Due to incredibly stupid design of Limits class, once it is initialized, it keeps its settings. We // want to make sure it uses our settings in this hConf, so we have to force it initialize here before // someone else initializes it. Limits.init(hConf); File localDataDir = new File(cConf.get(Constants.CFG_LOCAL_DATA_DIR)); hConf.set(Constants.CFG_LOCAL_DATA_DIR, localDataDir.getAbsolutePath()); hConf.set(Constants.AppFabric.OUTPUT_DIR, cConf.get(Constants.AppFabric.OUTPUT_DIR)); hConf.set("hadoop.tmp.dir", new File(localDataDir, cConf.get(Constants.AppFabric.TEMP_DIR)).getAbsolutePath()); // Windows specific requirements if (OSDetector.isWindows()) { // not set anywhere by the project, expected to be set from IDEs if running from the project instead of sdk // hadoop.dll is at cdap-unit-test\src\main\resources\hadoop.dll for some reason String hadoopDLLPath = System.getProperty("hadoop.dll.path"); if (hadoopDLLPath != null) { System.load(hadoopDLLPath); } else { // this is where it is when the standalone sdk is built String userDir = System.getProperty("user.dir"); System.load(Joiner.on(File.separator).join(userDir, "lib", "native", "hadoop.dll")); } } //Run dataset service on random port List<Module> modules = createPersistentModules(cConf, hConf); return new StandaloneMain(modules, cConf); } private static List<Module> createPersistentModules(CConfiguration cConf, Configuration hConf) { cConf.setIfUnset(Constants.CFG_DATA_LEVELDB_DIR, Constants.DEFAULT_DATA_LEVELDB_DIR); cConf.set(Constants.CFG_DATA_INMEMORY_PERSISTENCE, Constants.InMemoryPersistenceType.LEVELDB.name()); // configure all services except for router to bind to 127.0.0.1 String localhost = InetAddress.getLoopbackAddress().getHostAddress(); cConf.set(Constants.AppFabric.SERVER_ADDRESS, localhost); cConf.set(Constants.Transaction.Container.ADDRESS, localhost); cConf.set(Constants.Dataset.Manager.ADDRESS, localhost); cConf.set(Constants.Dataset.Executor.ADDRESS, localhost); cConf.set(Constants.Stream.ADDRESS, localhost); cConf.set(Constants.Metrics.ADDRESS, localhost); cConf.set(Constants.Metrics.SERVER_ADDRESS, localhost); cConf.set(Constants.MetricsProcessor.ADDRESS, localhost); cConf.set(Constants.LogSaver.ADDRESS, localhost); cConf.set(Constants.Security.AUTH_SERVER_BIND_ADDRESS, localhost); cConf.set(Constants.Explore.SERVER_ADDRESS, localhost); cConf.set(Constants.Metadata.SERVICE_BIND_ADDRESS, localhost); return ImmutableList.of( new ConfigModule(cConf, hConf), new IOModule(), new ZKClientModule(), new KafkaClientModule(), new MetricsHandlerModule(), new DiscoveryRuntimeModule().getStandaloneModules(), new LocationRuntimeModule().getStandaloneModules(), new AppFabricServiceRuntimeModule().getStandaloneModules(), new ProgramRunnerRuntimeModule().getStandaloneModules(), new DataFabricModules().getStandaloneModules(), new DataSetsModules().getStandaloneModules(), new DataSetServiceModules().getStandaloneModules(), new MetricsClientRuntimeModule().getStandaloneModules(), new LoggingModules().getStandaloneModules(), new RouterModules().getStandaloneModules(), new SecurityModules().getStandaloneModules(), new StreamServiceRuntimeModule().getStandaloneModules(), new ExploreRuntimeModule().getStandaloneModules(), new ServiceStoreModules().getStandaloneModules(), new ExploreClientModule(), new NotificationFeedServiceRuntimeModule().getStandaloneModules(), new NotificationServiceRuntimeModule().getStandaloneModules(), new ViewAdminModules().getStandaloneModules(), new StreamAdminModules().getStandaloneModules(), new NamespaceClientRuntimeModule().getStandaloneModules(), new NamespaceStoreModule().getStandaloneModules(), new MetadataServiceModule(), new AuditModule().getStandaloneModules(), new AuthorizationModule() ); } }