/*
* 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.io.IOException;
import java.io.UnsupportedEncodingException;
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.mysql.nio.handler.ResponseHandler;
import org.opencloudb.net.BackendAIOConnection;
import org.opencloudb.net.mysql.AuthPacket;
import org.opencloudb.net.mysql.CommandPacket;
import org.opencloudb.net.mysql.HandshakePacket;
import org.opencloudb.net.mysql.MySQLPacket;
import org.opencloudb.net.mysql.QuitPacket;
import org.opencloudb.route.RouteResultsetNode;
import org.opencloudb.server.ServerConnection;
import org.opencloudb.util.TimeUtil;
/**
* @author mycat
*/
public class MySQLDetector extends BackendAIOConnection {
private static final Logger LOGGER = Logger.getLogger(MySQLDetector.class);
private static final long CLIENT_FLAGS = initClientFlags();
private MySQLHeartbeat heartbeat;
private final long clientFlags;
private HandshakePacket handshake;
private int charsetIndex;
private boolean isAuthenticated;
private String user;
private String password;
private String schema;
private long heartbeatTimeout;
private final AtomicBoolean isQuit;
private String charset;
public MySQLDetector(AsynchronousSocketChannel channel) {
super(channel);
this.clientFlags = CLIENT_FLAGS;
this.handler = new MySQLDetectorAuthenticator(this);
this.isQuit = new AtomicBoolean(false);
}
public MySQLHeartbeat getHeartbeat() {
return heartbeat;
}
public void setHeartbeat(MySQLHeartbeat heartbeat) {
this.heartbeat = heartbeat;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
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.database = schema;
packet.write(this);
}
public void heartbeat() {
if (isAuthenticated) {
String sql = heartbeat.getHeartbeatSQL();
if (sql != null) {
CommandPacket packet = new CommandPacket();
packet.packetId = 0;
packet.command = MySQLPacket.COM_QUERY;
packet.arg = sql.getBytes();
packet.write(this);
}
}
else {
// System.out.println("auth ");
authenticate();
}
}
public void quit() {
if (isQuit.compareAndSet(false, true)) {
if (isAuthenticated) {
write(writeToBuffer(QuitPacket.QUIT, allocate()));
write(allocate());
close("heart beat quit normal");
} else {
close("heartbeat quit");
}
}
}
public boolean isQuit() {
return isQuit.get();
}
@Override
public void error(int errCode, Throwable t) {
LOGGER.warn(" error code: " + errCode + " exception: " + t + " con: "
+ this);
switch (errCode) {
case ErrorCode.ERR_HANDLE_DATA:
heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this, false,"heartbeat transfererr");
break;
default:
heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this, true,null);
}
}
@Override
public void idleCheck() {
if (isIdleTimeout()) {
LOGGER.warn(toString() + " heatbeat idle timeout");
quit();
}
}
public String toString() {
return new StringBuilder().append("[thread=")
.append(Thread.currentThread().getName()).append(",class=")
.append(getClass().getSimpleName()).append(",host=")
.append(host).append(",port=").append(port)
.append(",localPort=").append(localPort).append(",schema=")
.append(schema).append(']').toString();
}
private static 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 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);
}
@Override
public void onConnectFailed(Throwable e) {
heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this, true,"hearbeat connecterr");
}
@Override
public boolean isModifiedSQLExecuted() {
return false;
}
@Override
public boolean isFromSlaveDB() {
return false;
}
@Override
public long getLastTime() {
return 0;
}
@Override
public boolean isClosedOrQuit() {
return isQuit.get();
}
@Override
public void setAttachment(Object attachment) {
}
@Override
public void setLastTime(long currentTimeMillis) {
}
@Override
public void release() {
}
@Override
public void setRunning(boolean running) {
}
@Override
public boolean setResponseHandler(ResponseHandler commandHandler) {
return false;
}
@Override
public void commit() {
}
@Override
public void query(String sql) throws UnsupportedEncodingException {
}
@Override
public Object getAttachment() {
return null;
}
@Override
public void execute(RouteResultsetNode node, ServerConnection source,
boolean autocommit) throws IOException {
}
@Override
public void recordSql(String host, String schema, String statement) {
}
@Override
public boolean syncAndExcute() {
return false;
}
@Override
public void rollback() {
}
@Override
public boolean isRunning() {
return false;
}
@Override
public boolean isBorrowed() {
return false;
}
@Override
public void setBorrowed(boolean borrowed) {
}
@Override
public int getTxIsolation() {
return 0;
}
@Override
public boolean isAutocommit() {
return false;
}
}