content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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 io.jafka.server;
  
  import io.jafka.http.HttpRequestHandler;
  import io.jafka.http.HttpServer;
  import io.jafka.log.LogManager;
  import io.jafka.mx.ServerInfo;
  import io.jafka.mx.SocketServerStats;
  import io.jafka.network.SocketServer;
  import io.jafka.utils.Mx4jLoader;
  import io.jafka.utils.Scheduler;
  import io.jafka.utils.Utils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.Closeable;
  import java.io.File;
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  /**
   * The main server container
   *
   * @author adyliu (imxylz@gmail.com)
   * @since 1.0
   */
  public class Server implements Closeable {
  
      final String CLEAN_SHUTDOWN_FILE = ".jafka_cleanshutdown";
  
      final private Logger logger = LoggerFactory.getLogger(Server.class);
  
      final ServerConfig config;
  
      final Scheduler scheduler = new Scheduler(1, "jafka-logcleaner-", false);
  
      private LogManager logManager;
  
      private final CountDownLatch shutdownLatch = new CountDownLatch(1);
  
      final AtomicBoolean isShuttingDown = new AtomicBoolean(false);
  
      private SocketServer socketServer;
      private HttpServer httpServer;
  
      private final File logDir;
  
      private final ServerInfo serverInfo = new ServerInfo();
  
      //
      public Server(ServerConfig config) {
          this.config = config;
          logDir = new File(config.getLogDir());
          if (!logDir.exists()) {
              logDir.mkdirs();
          }
      }
  
      public void startup() {
          try {
              final long start = System.currentTimeMillis();
              logger.info("Starting Jafka server {} (brokerid={})", serverInfo.getVersion(), this.config.getBrokerId());
              Utils.registerMBean(serverInfo);
              boolean needRecovery = true;
              File cleanShutDownFile = new File(new File(config.getLogDir()), CLEAN_SHUTDOWN_FILE);
              if (cleanShutDownFile.exists()) {
                  needRecovery = false;
                  cleanShutDownFile.delete();
              }
              this.logManager = new LogManager(config,//
                      scheduler,//
                      1000L * 60 * config.getLogCleanupIntervalMinutes(),//
                      1000L * 60 * 60 * config.getLogRetentionHours(),//
                      needRecovery);
              this.logManager.setRollingStategy(config.getRollingStrategy());
              logManager.load();
  
              RequestHandlers handlers = new RequestHandlers(logManager);
              socketServer = new SocketServer(handlers, config);
              Utils.registerMBean(socketServer.getStats());
              socketServer.startup();
              //
              final int httpPort = config.getHttpPort();
              if (httpPort > 0) {
                  HttpRequestHandler httpRequestHandler = new HttpRequestHandler(logManager);
                  httpServer = new HttpServer(httpPort, httpRequestHandler);
                  httpServer.start();
              }
  
              Mx4jLoader.maybeLoad();
              /**
               * Registers this broker in ZK. After this, consumers can connect to broker. So
               * this should happen after socket server start.
               */
              logManager.startup();
              final long cost = (System.currentTimeMillis() - start) / 1000;
              logger.info("Jafka(brokerid={}) started at *:{}, cost {} seconds", config.getBrokerId(), config.getPort(), cost);
              serverInfo.started();
          } catch (Exception ex) {
              logger.error("========================================");
              logger.error("Fatal error during startup.", ex);
              logger.error("========================================");
              close();
          }
      }
  
      public void close() {
          boolean canShutdown = isShuttingDown.compareAndSet(false, true);
          if (!canShutdown) return;//CLOSED
  
          logger.info("Shutting down Jafka server(brokerid={})...", this.config.getBrokerId());
          try {
              scheduler.shutdown();
              if (socketServer != null) {
                  socketServer.close();
                  Utils.unregisterMBean(socketServer.getStats());
              }
              if (httpServer != null) {
                  httpServer.close();
              }
              if (logManager != null) {
                  logManager.close();
              }
  
              File cleanShutDownFile = new File(new File(config.getLogDir()), CLEAN_SHUTDOWN_FILE);
              cleanShutDownFile.createNewFile();
          } catch (Exception ex) {
              logger.error(ex.getMessage(), ex);
          }
          shutdownLatch.countDown();
          logger.info("Shutdown Jafka server {} (brokerid={}) completed", serverInfo.getVersion(), config.getBrokerId());
  
      }
  
      public void awaitShutdown() throws InterruptedException {
          shutdownLatch.await();
      }
  
  
      public LogManager getLogManager() {
          return logManager;
      }
  
      public SocketServerStats getStats() {
          return socketServer.getStats();
      }
  
  }
  