/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package org.opencloudb.heartbeat;
import java.nio.channels.AsynchronousSocketChannel;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
import org.opencloudb.config.Capabilities;
import org.opencloudb.config.ErrorCode;
import org.opencloudb.mysql.SecurityUtil;
import org.opencloudb.net.BackendConnection;
import org.opencloudb.net.mysql.AuthPacket;
import org.opencloudb.net.mysql.HandshakePacket;
import org.opencloudb.net.mysql.HeartbeatPacket;
import org.opencloudb.net.mysql.MySQLPacket;
import org.opencloudb.util.TimeUtil;
/**
* @author mycat
*/
public class MyCATDetector extends BackendConnection {
private static final Logger LOGGER = Logger.getLogger(MyCATDetector.class);
private static final long CLIENT_FLAGS = initClientFlags();
private static final Logger HEARTBEAT = Logger.getLogger("heartbeat");
private MyCATHeartbeat heartbeat;
private final long clientFlags;
private HandshakePacket handshake;
private int charsetIndex;
private boolean isAuthenticated;
private String user;
private String password;
private long heartbeatTimeout;
private final AtomicBoolean isQuit;
public MyCATDetector(AsynchronousSocketChannel channel) {
super(channel);
this.clientFlags = CLIENT_FLAGS;
this.handler = new MyCATDetectorAuthenticator(this);
this.isQuit = new AtomicBoolean(false);
}
public MyCATHeartbeat getHeartbeat() {
return heartbeat;
}
public void setHeartbeat(MyCATHeartbeat heartbeat) {
this.heartbeat = heartbeat;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getHeartbeatTimeout() {
return heartbeatTimeout;
}
public void setHeartbeatTimeout(long heartbeatTimeout) {
this.heartbeatTimeout = heartbeatTimeout;
}
public boolean isHeartbeatTimeout() {
return TimeUtil.currentTimeMillis() > Math.max(lastWriteTime, lastReadTime) + heartbeatTimeout;
}
public long lastReadTime() {
return lastReadTime;
}
public long lastWriteTime() {
return lastWriteTime;
}
public boolean isAuthenticated() {
return isAuthenticated;
}
public void setAuthenticated(boolean isAuthenticated) {
this.isAuthenticated = isAuthenticated;
}
public HandshakePacket getHandshake() {
return handshake;
}
public void setHandshake(HandshakePacket handshake) {
this.handshake = handshake;
}
public void setCharsetIndex(int charsetIndex) {
this.charsetIndex = charsetIndex;
}
public void authenticate() {
AuthPacket packet = new AuthPacket();
packet.packetId = 1;
packet.clientFlags = clientFlags;
packet.maxPacketSize = maxPacketSize;
packet.charsetIndex = charsetIndex;
packet.user = user;
try {
packet.password = getPass(password, handshake);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage());
}
packet.write(this);
}
public void heartbeat() {
if (isAuthenticated) {
HeartbeatPacket hp = new HeartbeatPacket();
hp.packetId = 0;
hp.command = MySQLPacket.COM_HEARTBEAT;
hp.id = heartbeat.detectCount.incrementAndGet();
hp.write(this);
if (HEARTBEAT.isInfoEnabled()) {
HEARTBEAT.info(heartbeat.requestMessage(MyCATHeartbeat.SEND, String.valueOf(hp.id).getBytes()));
}
} else {
authenticate();
}
}
public void quit() {
if (isQuit.compareAndSet(false, true)) {
close("heartbeat quit");
}
}
public boolean isQuit() {
return isQuit.get();
}
@Override
public void error(int errCode, Throwable t) {
LOGGER.warn(toString(), t);
switch (errCode) {
case ErrorCode.ERR_HANDLE_DATA:
heartbeat.setResult(MyCATHeartbeat.ERROR_STATUS, this, false, null);
break;
default:
heartbeat.setResult(MyCATHeartbeat.ERROR_STATUS, this, true, null);
}
}
@Override
protected void idleCheck() {
if (isIdleTimeout()) {
LOGGER.warn(toString() + " heartbeat idle timeout");
quit();
}
}
private static final long initClientFlags() {
int flag = 0;
flag |= Capabilities.CLIENT_LONG_PASSWORD;
flag |= Capabilities.CLIENT_FOUND_ROWS;
flag |= Capabilities.CLIENT_LONG_FLAG;
// flag |= Capabilities.CLIENT_CONNECT_WITH_DB;
// flag |= Capabilities.CLIENT_NO_SCHEMA;
// flag |= Capabilities.CLIENT_COMPRESS;
flag |= Capabilities.CLIENT_ODBC;
// flag |= Capabilities.CLIENT_LOCAL_FILES;
flag |= Capabilities.CLIENT_IGNORE_SPACE;
flag |= Capabilities.CLIENT_PROTOCOL_41;
flag |= Capabilities.CLIENT_INTERACTIVE;
// flag |= Capabilities.CLIENT_SSL;
flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;
flag |= Capabilities.CLIENT_TRANSACTIONS;
// flag |= Capabilities.CLIENT_RESERVED;
flag |= Capabilities.CLIENT_SECURE_CONNECTION;
// client extension
// flag |= Capabilities.CLIENT_MULTI_STATEMENTS;
// flag |= Capabilities.CLIENT_MULTI_RESULTS;
return flag;
}
private static final byte[] getPass(String src, HandshakePacket hsp) throws NoSuchAlgorithmException {
if (src == null || src.length() == 0) {
return null;
}
byte[] passwd = src.getBytes();
int sl1 = hsp.seed.length;
int sl2 = hsp.restOfScrambleBuff.length;
byte[] seed = new byte[sl1 + sl2];
System.arraycopy(hsp.seed, 0, seed, 0, sl1);
System.arraycopy(hsp.restOfScrambleBuff, 0, seed, sl1, sl2);
return SecurityUtil.scramble411(passwd, seed);
}
}