/* * Copyright 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.mongodb.net; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.collections.map.LRUMap; import org.apache.log4j.Logger; import org.bson.BasicBSONObject; import com.meidusa.amoeba.context.ProxyRuntimeContext; import com.meidusa.amoeba.mongodb.handler.AbstractSessionHandler; import com.meidusa.amoeba.mongodb.handler.AmoebaSequenceHandler; import com.meidusa.amoeba.mongodb.handler.CursorCloseMessageHandler; import com.meidusa.amoeba.mongodb.handler.DeleteMessageHandler; import com.meidusa.amoeba.mongodb.handler.GetMoreMessageHandler; import com.meidusa.amoeba.mongodb.handler.InsertMessageHandler; import com.meidusa.amoeba.mongodb.handler.KillCursorMessageHandler; import com.meidusa.amoeba.mongodb.handler.QueryMessageHandler; import com.meidusa.amoeba.mongodb.handler.UpdateMessageHandler; import com.meidusa.amoeba.mongodb.interceptor.PacketInterceptor; import com.meidusa.amoeba.mongodb.io.MongodbPacketConstant; import com.meidusa.amoeba.mongodb.packet.AbstractMongodbPacket; import com.meidusa.amoeba.mongodb.packet.CursorEntry; import com.meidusa.amoeba.mongodb.packet.DeleteMongodbPacket; import com.meidusa.amoeba.mongodb.packet.GetMoreMongodbPacket; import com.meidusa.amoeba.mongodb.packet.InsertMongodbPacket; import com.meidusa.amoeba.mongodb.packet.KillCursorsMongodbPacket; import com.meidusa.amoeba.mongodb.packet.MessageMongodbPacket; import com.meidusa.amoeba.mongodb.packet.MongodbPacketBuffer; import com.meidusa.amoeba.mongodb.packet.QueryMongodbPacket; import com.meidusa.amoeba.mongodb.packet.RequestMongodbPacket; import com.meidusa.amoeba.mongodb.packet.UpdateMongodbPacket; import com.meidusa.amoeba.net.poolable.ObjectPool; import com.meidusa.amoeba.util.Tuple; /** * * @author struct * */ @SuppressWarnings({ "unchecked", "deprecation" }) public class MongodbClientConnection extends AbstractMongodbConnection{ protected static Logger logger = Logger.getLogger(MongodbClientConnection.class); private LinkedBlockingQueue<byte[]> lastErrorQueue = new LinkedBlockingQueue<byte[]>(1); private Map<String,PacketInterceptor<AbstractMongodbPacket>> interceptors; private AtomicInteger LastErrorrequestId = new AtomicInteger(0); private AtomicInteger sequeceRequestId = new AtomicInteger(0); private AtomicLong currentCursorID = new AtomicLong(0x10001L); private LRUMap cursorMap = new LRUMap(10){ private static final long serialVersionUID = 1L; protected boolean removeLRU(LinkEntry entry){ boolean result = super.removeLRU(entry); List<Tuple<CursorEntry,ObjectPool>> tupleList = (List<Tuple<CursorEntry,ObjectPool>>)entry.getValue(); if(tupleList.size()>0){ new CursorCloseMessageHandler(MongodbClientConnection.this.getSocketId(),tupleList); } return result; } }; public byte[] getLastErrorMessage() { try { do{ byte[] byts = lastErrorQueue.poll(5, TimeUnit.SECONDS); if(byts == null){ return null; } if(LastErrorrequestId.get() == MongodbPacketBuffer.getResponseId(byts)){ return byts; } }while(true); } catch (InterruptedException e) { return null; } } public void setLastErrorMessage(byte[] lastErrorMessage) { int lastId = LastErrorrequestId.get(); int lastResponseId = MongodbPacketBuffer.getResponseId(lastErrorMessage); if(lastId == lastResponseId){ lastErrorQueue.clear(); lastErrorQueue.offer(lastErrorMessage); }else{ logger.warn("last ErrorMessage not fit: lastId="+lastId+", lastResponse="+lastResponseId); } } public long nextCursorID(){ return currentCursorID.incrementAndGet(); } public void putCursor(long cursorID,List<Tuple<CursorEntry,ObjectPool>> tuples){ synchronized (cursorMap) { cursorMap.put(cursorID, tuples); } } public void addCursorItem(long cursorID,Tuple<CursorEntry,ObjectPool> tuple){ synchronized (cursorMap) { List<Tuple<CursorEntry,ObjectPool>> tuples = (List<Tuple<CursorEntry,ObjectPool>>)cursorMap.get(cursorID); if(tuples == null){ List<Tuple<CursorEntry,ObjectPool>> newList = new ArrayList<Tuple<CursorEntry,ObjectPool>>(); newList.add(tuple); cursorMap.put(cursorID, newList); }else{ for(Tuple<CursorEntry,ObjectPool> storedTuple : tuples){ if(storedTuple.left.equals(tuple.left)){ return; } } tuples.add(tuple); } } } public void removeCursorItem(long cursorID,CursorEntry cursorEntry){ synchronized (cursorMap) { List<Tuple<CursorEntry,ObjectPool>> tuples = (List<Tuple<CursorEntry,ObjectPool>>)cursorMap.get(cursorID); if(tuples != null){ for(Tuple<CursorEntry,ObjectPool> storedTuple : tuples){ if(storedTuple.left.equals(cursorEntry)){ tuples.remove(storedTuple); if(tuples.size() == 0){ cursorMap.remove(cursorID); } break; } } } } } public List<Tuple<CursorEntry,ObjectPool>> removeCursor(long cursorID){ synchronized (cursorMap) { return (List<Tuple<CursorEntry,ObjectPool>>) cursorMap.remove(cursorID); } } public List<Tuple<CursorEntry,ObjectPool>> getCursor(long cursorID){ synchronized (cursorMap) { return (List<Tuple<CursorEntry,ObjectPool>>)cursorMap.get(cursorID); } } public synchronized RequestMongodbPacket getLastErrorRequest(){ QueryMongodbPacket packet = new QueryMongodbPacket(); packet.query = new BasicBSONObject(); packet.numberToReturn = -1; packet.fullCollectionName = "admin.$cmd"; packet.query.put("getlasterror", 1); packet.requestID = LastErrorrequestId.incrementAndGet(); return packet; } public int getNextRequestId(){ return sequeceRequestId.incrementAndGet(); } public Map<String, PacketInterceptor<AbstractMongodbPacket>> getInterceptors() { return interceptors; } public void setInterceptors( Map<String, PacketInterceptor<AbstractMongodbPacket>> interceptors) { this.interceptors = interceptors; } public MongodbClientConnection(SocketChannel channel, long createStamp) { super(channel, createStamp); } protected void doReceiveMessage(final byte[] message){ if(ProxyRuntimeContext.getInstance().getRuntimeContext().isUseMultipleThread()){ ProxyRuntimeContext.getInstance().getRuntimeContext().getClientSideExecutor().execute(new Runnable(){ public void run(){ doReceiveMessage0(message); } }); }else{ doReceiveMessage0(message); } } private void doReceiveMessage0(byte[] message){ int type = MongodbPacketBuffer.getOPMessageType(message); AbstractMongodbPacket packet = null; AbstractSessionHandler<?> handler = null; switch(type){ case MongodbPacketConstant.OP_QUERY: packet = new QueryMongodbPacket(); packet.init(message, this); if(MongodbPacketConstant.AMOEBA_SEQUENCE.equalsIgnoreCase(((QueryMongodbPacket)packet).fullCollectionName)){ handler = new AmoebaSequenceHandler(this,(QueryMongodbPacket)packet); }else{ handler = new QueryMessageHandler(this,(QueryMongodbPacket)packet); } break; case MongodbPacketConstant.OP_GET_MORE: packet = new GetMoreMongodbPacket(); packet.init(message, this); handler = new GetMoreMessageHandler(this,(GetMoreMongodbPacket)packet); break; case MongodbPacketConstant.OP_DELETE: packet = new DeleteMongodbPacket(); packet.init(message, this); handler = new DeleteMessageHandler(this,(DeleteMongodbPacket)packet); break; case MongodbPacketConstant.OP_KILL_CURSORS: packet = new KillCursorsMongodbPacket(); packet.init(message, this); handler = new KillCursorMessageHandler(this,(KillCursorsMongodbPacket)packet); break; case MongodbPacketConstant.OP_UPDATE: packet = new UpdateMongodbPacket(); packet.init(message, this); handler = new UpdateMessageHandler(this,(UpdateMongodbPacket)packet); break; case MongodbPacketConstant.OP_INSERT: packet = new InsertMongodbPacket(); packet.init(message, this); handler = new InsertMessageHandler(this,(InsertMongodbPacket)packet); break; case MongodbPacketConstant.OP_MSG: packet = new MessageMongodbPacket(); packet.init(message, this); break; } //debug packet info if(AbstractSessionHandler.PACKET_LOGGER.isDebugEnabled()){ if(packet != null){ AbstractSessionHandler.PACKET_LOGGER.debug(">>>---["+packet.requestID+"]--pakcet="+packet+"," +this.getSocketId()); }else{ AbstractSessionHandler.PACKET_LOGGER.debug("ERROR --->>>"+this.getSocketId()+" unknow type="+type); } } if(this.interceptors != null){ PacketInterceptor<AbstractMongodbPacket> interceptor = interceptors.get(packet.getClass().getName()); if(interceptor != null){ if(interceptor.doIntercept(packet)){ message = packet.toByteBuffer(this).array(); } } } handler.handleMessage(this,message); } /*@Override public void handleMessage(Connection conn) { try { new CommandMessageHandler(this).handleMessage(conn); } catch (Exception e) { e.printStackTrace(); } }*/ /*protected void messageProcess() { byte[] message = null; while((message = getInQueue().getNonBlocking()) != null){ CommandMessageHandler handler = new CommandMessageHandler(this); handler.handleMessage(this,message); } }*/ }