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.
   */
  /**
   * (created at 2012-5-4)
   */
  package com.alibaba.cobar.mysql.nio.handler;
  
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.locks.ReentrantLock;
  
  import com.alibaba.cobar.config.ErrorCode;
  import com.alibaba.cobar.mysql.nio.MySQLConnection;
  import com.alibaba.cobar.net.mysql.ErrorPacket;
  import com.alibaba.cobar.server.session.NonBlockingSession;
  import com.alibaba.cobar.util.StringUtil;
  
  /**
   * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a>
   */
  public abstract class MultiNodeHandler implements ResponseHandler, Terminatable {
      protected final ReentrantLock lock = new ReentrantLock();
      protected final NonBlockingSession session;
      protected AtomicBoolean isFail = new AtomicBoolean(false);
      private ErrorPacket error;
      protected byte packetId;
  
      public MultiNodeHandler(NonBlockingSession session) {
          if (session == null) {
              throw new IllegalArgumentException("session is null!");
          }
          this.session = session;
      }
  
      private int nodeCount;
      private Runnable terminateCallBack;
  
      @Override
      public void terminate(Runnable terminateCallBack) {
          boolean zeroReached = false;
          lock.lock();
          try {
              if (nodeCount > 0) {
                  this.terminateCallBack = terminateCallBack;
              } else {
                  zeroReached = true;
              }
          } finally {
              lock.unlock();
          }
          if (zeroReached) {
              terminateCallBack.run();
          }
      }
  
      protected void decrementCountToZero() {
          Runnable callback;
          lock.lock();
          try {
              nodeCount = 0;
              callback = this.terminateCallBack;
              this.terminateCallBack = null;
          } finally {
              lock.unlock();
          }
          if (callback != null) {
              callback.run();
          }
      }
  
      protected boolean decrementCountBy(int finished) {
          boolean zeroReached = false;
          Runnable callback = null;
          lock.lock();
          try {
              if (zeroReached = --nodeCount == 0) {
                  callback = this.terminateCallBack;
                  this.terminateCallBack = null;
              }
          } finally {
              lock.unlock();
          }
          if (zeroReached && callback != null) {
              callback.run();
          }
          return zeroReached;
      }
  
      protected void reset(int initCount) {
          nodeCount = initCount;
          isFail.set(false);
          error = null;
          packetId = 0;
      }
  
      protected void backendConnError(MySQLConnection conn, String errMsg) {
          ErrorPacket err = new ErrorPacket();
          err.packetId = 1;// ERROR_PACKET
          err.errno = ErrorCode.ER_YES;
          err.message = StringUtil.encode(errMsg, session.getSource().getCharset());
          backendConnError(conn, err);
      }
  
      protected void backendConnError(MySQLConnection conn, ErrorPacket err) {
          conn.setRunning(false);
          lock.lock();
          try {
              if (error == null) {
                  error = err;
              }
          } finally {
              lock.unlock();
          }
          isFail.set(true);
          if (decrementCountBy(1)) {
              notifyError();
          }
      }
  
      protected void notifyError() {
          recycleResources();
          byte errPacketId = ++packetId;
          ErrorPacket err;
          session.clearConnections();
          session.getSource().setTxInterrupt();
          err = error;
          if (err == null) {
              err = new ErrorPacket();
              err.packetId = errPacketId;
              err.errno = ErrorCode.ER_YES;
              err.message = StringUtil.encode("unknown error", session.getSource().getCharset());
          }
          err.packetId = errPacketId;
          err.write(session.getSource());
      }
  
      protected void notifyError(byte errPacketId) {
          recycleResources();
          ErrorPacket err;
          session.clearConnections();
          session.getSource().setTxInterrupt();
          err = error;
          if (err == null) {
              err = new ErrorPacket();
              err.packetId = errPacketId;
              err.errno = ErrorCode.ER_YES;
              err.message = StringUtil.encode("unknown error", session.getSource().getCharset());
          }
          err.packetId = errPacketId;
          err.write(session.getSource());
      }
  
      protected void recycleResources() {
      }
  }
  