content: /*
   * Copyright 1999-2012 Alibaba Group.
   *  
   * 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.alibaba.cobar;
  
  import java.io.IOException;
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.Map;
  import java.util.Timer;
  import java.util.TimerTask;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  import org.apache.log4j.Logger;
  import org.apache.log4j.helpers.LogLog;
  
  import com.alibaba.cobar.config.model.SystemConfig;
  import com.alibaba.cobar.manager.ManagerConnectionFactory;
  import com.alibaba.cobar.mysql.MySQLDataNode;
  import com.alibaba.cobar.net.NIOAcceptor;
  import com.alibaba.cobar.net.NIOConnector;
  import com.alibaba.cobar.net.NIOProcessor;
  import com.alibaba.cobar.parser.recognizer.mysql.lexer.MySQLLexer;
  import com.alibaba.cobar.server.ServerConnectionFactory;
  import com.alibaba.cobar.statistic.SQLRecorder;
  import com.alibaba.cobar.util.ExecutorUtil;
  import com.alibaba.cobar.util.NameableExecutor;
  import com.alibaba.cobar.util.TimeUtil;
  
  /**
   * @author xianmao.hexm 2011-4-19 下午02:58:59
   */
  public class CobarServer {
      public static final String NAME = "Cobar";
      private static final long LOG_WATCH_DELAY = 60000L;
      private static final long TIME_UPDATE_PERIOD = 20L;
      private static final CobarServer INSTANCE = new CobarServer();
      private static final Logger LOGGER = Logger.getLogger(CobarServer.class);
  
      public static final CobarServer getInstance() {
          return INSTANCE;
      }
  
      private final CobarConfig config;
      private final Timer timer;
      private final NameableExecutor managerExecutor;
      private final NameableExecutor timerExecutor;
      private final NameableExecutor initExecutor;
      private final SQLRecorder sqlRecorder;
      private final AtomicBoolean isOnline;
      private final long startupTime;
      private NIOProcessor[] processors;
      private NIOConnector connector;
      private NIOAcceptor manager;
      private NIOAcceptor server;
  
      private CobarServer() {
          this.config = new CobarConfig();
          SystemConfig system = config.getSystem();
          MySQLLexer.setCStyleCommentVersion(system.getParserCommentVersion());
          this.timer = new Timer(NAME + "Timer", true);
          this.initExecutor = ExecutorUtil.create("InitExecutor", system.getInitExecutor());
          this.timerExecutor = ExecutorUtil.create("TimerExecutor", system.getTimerExecutor());
          this.managerExecutor = ExecutorUtil.create("ManagerExecutor", system.getManagerExecutor());
          this.sqlRecorder = new SQLRecorder(system.getSqlRecordCount());
          this.isOnline = new AtomicBoolean(true);
          this.startupTime = TimeUtil.currentTimeMillis();
      }
  
      public CobarConfig getConfig() {
          return config;
      }
  
      public void beforeStart(String dateFormat) {
          String home = System.getProperty("cobar.home");
          if (home == null) {
              SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
              LogLog.warn(sdf.format(new Date()) + " [cobar.home] is not set.");
          } else {
              Log4jInitializer.configureAndWatch(home + "/conf/log4j.xml", LOG_WATCH_DELAY);
          }
      }
  
      public void startup() throws IOException {
          // server startup
          LOGGER.info("===============================================");
          LOGGER.info(NAME + " is ready to startup ...");
          SystemConfig system = config.getSystem();
          timer.schedule(updateTime(), 0L, TIME_UPDATE_PERIOD);
  
          // startup processors
          LOGGER.info("Startup processors ...");
          int handler = system.getProcessorHandler();
          int executor = system.getProcessorExecutor();
          int committer = system.getProcessorCommitter();
          processors = new NIOProcessor[system.getProcessors()];
          for (int i = 0; i < processors.length; i++) {
              processors[i] = new NIOProcessor("Processor" + i, handler, executor, committer);
              processors[i].startup();
          }
          timer.schedule(processorCheck(), 0L, system.getProcessorCheckPeriod());
  
          // startup connector
          LOGGER.info("Startup connector ...");
          connector = new NIOConnector(NAME + "Connector");
          connector.setProcessors(processors);
          connector.start();
  
          // init dataNodes
          Map<String, MySQLDataNode> dataNodes = config.getDataNodes();
          LOGGER.info("Initialize dataNodes ...");
          for (MySQLDataNode node : dataNodes.values()) {
              node.init(1, 0);
          }
          timer.schedule(dataNodeIdleCheck(), 0L, system.getDataNodeIdleCheckPeriod());
          timer.schedule(dataNodeHeartbeat(), 0L, system.getDataNodeHeartbeatPeriod());
  
          // startup manager
          ManagerConnectionFactory mf = new ManagerConnectionFactory();
          mf.setCharset(system.getCharset());
          mf.setIdleTimeout(system.getIdleTimeout());
          manager = new NIOAcceptor(NAME + "Manager", system.getManagerPort(), mf);
          manager.setProcessors(processors);
          manager.start();
          LOGGER.info(manager.getName() + " is started and listening on " + manager.getPort());
  
          // startup server
          ServerConnectionFactory sf = new ServerConnectionFactory();
          sf.setCharset(system.getCharset());
          sf.setIdleTimeout(system.getIdleTimeout());
          server = new NIOAcceptor(NAME + "Server", system.getServerPort(), sf);
          server.setProcessors(processors);
          server.start();
          timer.schedule(clusterHeartbeat(), 0L, system.getClusterHeartbeatPeriod());
  
          // server started
          LOGGER.info(server.getName() + " is started and listening on " + server.getPort());
          LOGGER.info("===============================================");
      }
  
      public NIOProcessor[] getProcessors() {
          return processors;
      }
  
      public NIOConnector getConnector() {
          return connector;
      }
  
      public NameableExecutor getManagerExecutor() {
          return managerExecutor;
      }
  
      public NameableExecutor getTimerExecutor() {
          return timerExecutor;
      }
  
      public NameableExecutor getInitExecutor() {
          return initExecutor;
      }
  
      public SQLRecorder getSqlRecorder() {
          return sqlRecorder;
      }
  
      public long getStartupTime() {
          return startupTime;
      }
  
      public boolean isOnline() {
          return isOnline.get();
      }
  
      public void offline() {
          isOnline.set(false);
      }
  
      public void online() {
          isOnline.set(true);
      }
  
      // 系统时间定时更新任务
      private TimerTask updateTime() {
          return new TimerTask() {
              @Override
              public void run() {
                  TimeUtil.update();
              }
          };
      }
  
      // 处理器定时检查任务
      private TimerTask processorCheck() {
          return new TimerTask() {
              @Override
              public void run() {
                  timerExecutor.execute(new Runnable() {
                      @Override
                      public void run() {
                          for (NIOProcessor p : processors) {
                              p.check();
                          }
                      }
                  });
              }
          };
      }
  
      // 数据节点定时连接空闲超时检查任务
      private TimerTask dataNodeIdleCheck() {
          return new TimerTask() {
              @Override
              public void run() {
                  timerExecutor.execute(new Runnable() {
                      @Override
                      public void run() {
                          Map<String, MySQLDataNode> nodes = config.getDataNodes();
                          for (MySQLDataNode node : nodes.values()) {
                              node.idleCheck();
                          }
                          Map<String, MySQLDataNode> _nodes = config.getBackupDataNodes();
                          if (_nodes != null) {
                              for (MySQLDataNode node : _nodes.values()) {
                                  node.idleCheck();
                              }
                          }
                      }
                  });
              }
          };
      }
  
      // 数据节点定时心跳任务
      private TimerTask dataNodeHeartbeat() {
          return new TimerTask() {
              @Override
              public void run() {
                  timerExecutor.execute(new Runnable() {
                      @Override
                      public void run() {
                          Map<String, MySQLDataNode> nodes = config.getDataNodes();
                          for (MySQLDataNode node : nodes.values()) {
                              node.doHeartbeat();
                          }
                      }
                  });
              }
          };
      }
  
      // 集群节点定时心跳任务
      private TimerTask clusterHeartbeat() {
          return new TimerTask() {
              @Override
              public void run() {
                  timerExecutor.execute(new Runnable() {
                      @Override
                      public void run() {
                          Map<String, CobarNode> nodes = config.getCluster().getNodes();
                          for (CobarNode node : nodes.values()) {
                              node.doHeartbeat();
                          }
                      }
                  });
              }
          };
      }
  
  }
  