/* * Copyright 2008-2108 amoeba.meidusa.com * * This program is free software; you can redistribute it and/or modify it under the terms of * the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program 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 AFFERO GENERAL PUBLIC LICENSE for more details. * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.meidusa.amoeba.mysql.handler; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import com.meidusa.amoeba.mysql.handler.session.CommandStatus; import com.meidusa.amoeba.mysql.handler.session.ConnectionStatuts; import com.meidusa.amoeba.mysql.net.CommandInfo; import com.meidusa.amoeba.mysql.net.MysqlClientConnection; import com.meidusa.amoeba.mysql.net.MysqlServerConnection; import com.meidusa.amoeba.mysql.net.packet.EOFPacket; import com.meidusa.amoeba.mysql.net.packet.ErrorPacket; import com.meidusa.amoeba.mysql.net.packet.MysqlPacketBuffer; import com.meidusa.amoeba.mysql.net.packet.OkPacket; import com.meidusa.amoeba.mysql.net.packet.QueryCommandPacket; import com.meidusa.amoeba.net.Connection; import com.meidusa.amoeba.net.packet.Packet; import com.meidusa.amoeba.parser.statement.InsertStatement; import com.meidusa.amoeba.parser.statement.Statement; import com.meidusa.amoeba.util.StringUtil; class CommandQueue{ private static Logger logger = Logger.getLogger(CommandQueue.class); protected List<CommandInfo> sessionInitQueryQueue; //所有的从客户端发送过来的 command 队列 protected CommandInfo currentCommand;//当前的query protected Map<MysqlServerConnection,ConnectionStatuts> connStatusMap = new HashMap<MysqlServerConnection,ConnectionStatuts>(); boolean mainCommandExecuted; private MysqlClientConnection source; protected Statement statment; public CommandQueue(MysqlClientConnection source,Statement statment){ this.source = source; this.statment = statment; } public boolean isMultiple(){ return connStatusMap.size()>1; } public void clearAllBuffer(){ Collection<ConnectionStatuts> collection = connStatusMap.values(); for(ConnectionStatuts status : collection){ status.clearBuffer(); } } /** * 尝试下一个命令,如果返回false,表示队列中没有命令了。 * * @return */ boolean tryNextCommandTuple(){ if(sessionInitQueryQueue == null){ return false; }else{ if(sessionInitQueryQueue.size()>0){ currentCommand = sessionInitQueryQueue.get(0); if(logger.isDebugEnabled()){ QueryCommandPacket command = new QueryCommandPacket(); command.init(currentCommand.getBuffer(),source); logger.debug(command); } return true; } return false; } } /** * 判断返回的数据是否是当前命令的结束包。 * 当前全部连接都全部返回以后则表示当前命令完全结束。 * @param conn * @param buffer * @return */ protected CommandStatus checkResponseCompleted(Connection conn,byte[] buffer){ boolean isCompleted = false; ConnectionStatuts connStatus = connStatusMap.get(conn); if(connStatus == null){ logger.error("connection Status not Found, byffer="+StringUtil.dumpAsHex(buffer, buffer.length)); } try{ connStatus.buffers.add(buffer); isCompleted = connStatus.isCompleted(buffer); /** * 如果是多个连接的,需要将数据缓存起来,等待命令全部完成以后,将数据进行组装,然后发送到客户端 * {@link #CommandMessageHandler.mergeMessageToClient} */ if(isCompleted){ //set last insert id to client connection; if(conn != source){ if(connStatus.packetIndex == 0 && MysqlPacketBuffer.isOkPacket(buffer)){ if(statment instanceof InsertStatement && currentCommand.isMain()){ OkPacket packet = new OkPacket(); packet.init(buffer,conn); if(packet.insertId>0){ source.setLastInsertId(packet.insertId); } } } } boolean isAllCompleted = true; if (currentCommand.isMain()) { isAllCompleted = currentCommand.getCompletedCount().incrementAndGet() == connStatusMap.size(); } if(isAllCompleted){ connStatus.isMerged = true; } if(isAllCompleted){ Packet packet = null; if(MysqlPacketBuffer.isErrorPacket(buffer)){ packet = new ErrorPacket(); }else if(MysqlPacketBuffer.isEofPacket(buffer)){ packet = new EOFPacket(); }else if(MysqlPacketBuffer.isOkPacket(buffer)){ packet = new OkPacket(); } packet.init(buffer,conn); if(logger.isDebugEnabled()){ logger.debug("returned Packet:"+packet); } return CommandStatus.AllCompleted; }else{ return CommandStatus.ConnectionCompleted; } }else{ return CommandStatus.ConnectionNotComplete; } }finally{ connStatus.packetIndex ++; } } /** * 是否append 成功,如果成功则表示以前曾经堆积过,需要队列来保证发送命令的循序。 * 如果队列中没有堆积的命令,则返回false. * 否则返回true, 则表示可直接发送命令 * @param commandInfo * @param force 强制append 命令,返回为true * @return */ public synchronized boolean appendCommand(CommandInfo commandInfo,boolean force){ if(force){ if(sessionInitQueryQueue == null){ sessionInitQueryQueue = Collections.synchronizedList(new ArrayList<CommandInfo>()); } if(!sessionInitQueryQueue.contains(commandInfo)){ sessionInitQueryQueue.add(commandInfo); } return true; }else{ if(sessionInitQueryQueue == null){ return false; }else{ if(sessionInitQueryQueue.size() ==0){ return false; } if(!sessionInitQueryQueue.contains(commandInfo)){ sessionInitQueryQueue.add(commandInfo); } return true; } } } }