package com.alibaba.hsclient.impl; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import com.alibaba.hsclient.Command; import com.alibaba.hsclient.HSClient; import com.alibaba.hsclient.IndexSession; import com.alibaba.hsclient.bean.FilterInfo; import com.alibaba.hsclient.bean.InInfo; import com.alibaba.hsclient.bean.IndexInfo; import com.alibaba.hsclient.bean.LimitInfo; import com.alibaba.hsclient.bean.ModInfo; import com.alibaba.hsclient.bean.ResultInfo; import com.alibaba.hsclient.commond.DecrementCommand; import com.alibaba.hsclient.commond.DeleteCommand; import com.alibaba.hsclient.commond.FindCommand; import com.alibaba.hsclient.commond.IncrementCommand; import com.alibaba.hsclient.commond.InsertCommand; import com.alibaba.hsclient.commond.OpenIndexCommand; import com.alibaba.hsclient.commond.UpdateCommand; import com.alibaba.hsclient.conf.HSConfig; import com.alibaba.hsclient.exception.HandlerSocketException; import com.alibaba.hsclient.util.CompareOperator; import com.alibaba.hsclient.util.HSUtils; import com.alibaba.hsclient.util.ModOperator; public class HSClientImpl implements HSClient { private HSConfig hsConfig; private SocketChannel socket; private Selector selector; private BlockingQueue<byte[]> commandBuffer; private int currentResultSize = 0; private final ConcurrentHashMap<Integer, IndexInfo> indexIdMap = new ConcurrentHashMap<Integer, IndexInfo>(); private static AtomicInteger INDEXID_COUNTER = new AtomicInteger(); public HSClientImpl(HSConfig hsConfig) throws IOException { this.hsConfig = hsConfig; commandBuffer = new LinkedBlockingQueue<byte[]>(); } public Map<Integer, IndexInfo> getIndexMap() { return Collections .<Integer, IndexInfo> unmodifiableMap(this.indexIdMap); } public void connect() throws IOException { connect(InetAddress.getByName(this.hsConfig.getHost()), this.hsConfig.isReadOnly() ? this.hsConfig.getRPort() : this.hsConfig.getWrPort()); } private void connect(InetAddress address, int port) throws IOException { if (socket != null && socket.isConnected()) { close(); } selector = Selector.open(); socket = SocketChannel.open(); socket.configureBlocking(this.hsConfig.isBlocking()); socket.socket().setReceiveBufferSize( this.hsConfig.getReceiveBufferSize()); socket.socket().setSendBufferSize(this.hsConfig.getSendBufferSize()); socket.socket().setSoTimeout(this.hsConfig.getSoTimeout()); socket.socket().setTcpNoDelay(this.hsConfig.isTcpNoDelay()); socket.socket().setReuseAddress(this.hsConfig.isReuseAddress()); if (this.hsConfig.isHardClose()) { socket.socket().setSoLinger(true, 0); } socket.connect(new InetSocketAddress(address, port)); while (!socket.finishConnect()) { } } private synchronized ResultInfo execute() throws IOException { if (commandBuffer.size() == 0) { return null; } currentResultSize = 0; socket.register(selector, socket.validOps()); ResultInfo result = new ResultInfo(); try { boolean processComplete = false; while (!processComplete && selector.select() > 0) { Iterator<SelectionKey> iterator = selector.selectedKeys() .iterator(); while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); iterator.remove(); if (key.isWritable()) { SocketChannel channel = (SocketChannel) key.channel(); final ByteArrayOutputStream buf = new ByteArrayOutputStream(); for (byte[] command; (command = commandBuffer.poll()) != null;) { buf.write(command); } channel.register(selector, SelectionKey.OP_READ); ByteBuffer wb = ByteBuffer.wrap(buf.toByteArray()); while (wb.remaining() > 0) { channel.write(wb); } } else if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteBuffer rb = ByteBuffer.allocate(this.hsConfig .getExecuteBufferSize()); rb.clear(); for (int size = 0; (size = channel.read(rb)) > 0;) { currentResultSize += size; rb.flip(); buffer.write(rb.array(), 0, size); rb.position(0); rb.clear(); if (size < this.hsConfig.getExecuteBufferSize()) { break; } } HSUtils parser = new HSUtils( this.hsConfig.getEncoding()); result = parser.parse(buffer.toByteArray()); processComplete = true; break; } } } } finally { } return result; } public void close() throws IOException { socket.socket().close(); socket.close(); try { Iterator<SelectionKey> itr = selector.keys().iterator(); while (itr.hasNext()) { SelectionKey key = (SelectionKey) itr.next(); key.channel().close(); key.cancel(); } } catch (IOException e) { throw e; } selector.close(); } public int getCurrentResponseSize() { return currentResultSize; } public void clear() { this.commandBuffer.clear(); this.currentResultSize = 0; } public boolean openIndex(int indexId, String dbName, String tableName, String indexName, String[] columns, String[] fColumns) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { this.checkParams(dbName, tableName, indexName, columns, fColumns); IndexInfo record = new IndexInfo(indexId, dbName, tableName, indexName, columns, fColumns); this.indexIdMap.put(indexId, record); Command command = new OpenIndexCommand(indexId, dbName, tableName, indexName, columns, fColumns); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); ResultInfo resultInfo = this.execute(); return resultInfo.getErrorCode() == 0 ? true : false; } public boolean openIndex(int indexId, String dbName, String tableName, String indexName, String[] columns) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.openIndex(indexId, dbName, tableName, indexName, columns, null); } public ResultInfo find(int indexId, CompareOperator operator, String[] fieldValues, LimitInfo limitInfo, InInfo inInfo, FilterInfo[] filterInfos) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { Command command = new FindCommand(indexId, operator, fieldValues, limitInfo, inInfo, filterInfos); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); return this.execute(); } public ResultInfo find(int indexId, String[] fieldValues) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.find(indexId, CompareOperator.EQ, fieldValues, null, null, null); } public boolean update(int indexId, CompareOperator operator, String[] fieldValues, LimitInfo limitInfo, InInfo inInfo, FilterInfo[] filterInfos, ModInfo modInfo) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { if (this.hsConfig.isReadOnly()) { throw new UnsupportedOperationException( "update is not supported for readonly sesion"); } Command command = new UpdateCommand(indexId, operator, fieldValues, limitInfo, inInfo, filterInfos, modInfo); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); ResultInfo resultInfo = this.execute(); return resultInfo.getErrorCode() == 0 ? true : false; } public boolean update(int indexId, CompareOperator operator, String[] fieldValues, ModInfo modInfo) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.update(indexId, operator, fieldValues, new LimitInfo(), null, null, modInfo); } public boolean delete(int indexId, CompareOperator operator, String[] fieldValues, LimitInfo limitInfo, InInfo inInfo, FilterInfo[] filterInfos) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { if (this.hsConfig.isReadOnly()) { throw new UnsupportedOperationException( "delete is not supported for readonly sesion"); } ModInfo modInfo = new ModInfo(); modInfo.setModType(ModOperator.D); Command command = new DeleteCommand(indexId, operator, fieldValues, limitInfo, inInfo, filterInfos, modInfo); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); ResultInfo resultInfo = this.execute(); return resultInfo.getErrorCode() == 0 ? true : false; } public boolean delete(int indexId, CompareOperator operator, String[] fieldValues) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.delete(indexId, operator, fieldValues, new LimitInfo(), null, null); } public boolean delete(int indexId, String[] fieldValues) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.delete(indexId, CompareOperator.EQ, fieldValues, new LimitInfo(), null, null); } public boolean insert(int indexId, String[] fieldValues) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { if (this.hsConfig.isReadOnly()) { throw new UnsupportedOperationException( "insert is not supported for readonly sesion"); } Command command = new InsertCommand(indexId, fieldValues); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); ResultInfo resultInfo = this.execute(); return resultInfo.getErrorCode() == 0 ? true : false; } public boolean increment(int indexId, CompareOperator operator, String[] fieldValues, LimitInfo limitInfo, InInfo inInfo, FilterInfo[] filterInfos, ModInfo modInfo) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { if (this.hsConfig.isReadOnly()) { throw new UnsupportedOperationException( "increment is not supported for readonly sesion"); } Command command = new IncrementCommand(indexId, operator, fieldValues, limitInfo, inInfo, filterInfos, modInfo); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); ResultInfo resultInfo = this.execute(); return resultInfo.getErrorCode() == 0 ? true : false; } public boolean increment(int indexId, CompareOperator operator, String[] fieldValues, ModInfo modInfo) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.increment(indexId, operator, fieldValues, null, null, null, modInfo); } public boolean decrement(int indexId, CompareOperator operator, String[] fieldValues, LimitInfo limitInfo, InInfo inInfo, FilterInfo[] filterInfos, ModInfo modInfo) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { if (this.hsConfig.isReadOnly()) { throw new UnsupportedOperationException( "decrement is not supported for readonly sesion"); } Command command = new DecrementCommand(indexId, operator, fieldValues, limitInfo, inInfo, filterInfos, modInfo); this.commandBuffer.add(command.toByte(this.hsConfig.getEncoding())); ResultInfo resultInfo = this.execute(); return resultInfo.getErrorCode() == 0 ? true : false; } public boolean decrement(int indexId, CompareOperator operator, String[] fieldValues, ModInfo modInfo) throws InterruptedException, TimeoutException, HandlerSocketException, UnsupportedEncodingException, IOException { return this.decrement(indexId, operator, fieldValues, null, null, null, modInfo); } private void checkParams(String dbname, String tableName, String indexName, String[] columns, String[] fcolumns) { if (HSUtils.isBlank(dbname)) { throw new IllegalArgumentException("blank dbName:" + dbname); } if (HSUtils.isBlank(tableName)) { throw new IllegalArgumentException("blank tableName:" + tableName); } if (HSUtils.isBlank(indexName)) { throw new IllegalArgumentException("blank indexName:" + indexName); } if (columns == null || columns.length == 0) { throw new IllegalArgumentException("empty columns"); } for (String col : columns) { if (HSUtils.isBlank(col)) { throw new IllegalArgumentException("blank column name:" + col); } } if (fcolumns != null && fcolumns.length != 0) { for (String col : fcolumns) { if (HSUtils.isBlank(col)) { throw new IllegalArgumentException("blank fcolumn name:" + col); } } } } public IndexSession openIndexSession(int indexId, String dbname, String tableName, String indexName, String[] columns, String[] fcolumns) throws InterruptedException, TimeoutException, HandlerSocketException, IOException { this.checkParams(dbname, tableName, indexName, columns, fcolumns); if (this.openIndex(indexId, dbname, tableName, indexName, columns, fcolumns)) { return new IndexSessionImpl(this, indexId, columns); } else { return null; } } public IndexSession openIndexSession(int indexId, String dbname, String tableName, String indexName, String[] columns) throws InterruptedException, TimeoutException, HandlerSocketException, IOException { return this.openIndexSession(indexId, dbname, tableName, indexName, columns, null); } public IndexSession openIndexSession(String dbname, String tableName, String indexName, String[] columns) throws InterruptedException, TimeoutException, HandlerSocketException, IOException { return this.openIndexSession(INDEXID_COUNTER.incrementAndGet(), dbname, tableName, indexName, columns); } public IndexSession openIndexSession(String dbname, String tableName, String indexName, String[] columns, String[] fcolumns) throws InterruptedException, TimeoutException, HandlerSocketException, IOException { return this.openIndexSession(INDEXID_COUNTER.incrementAndGet(), dbname, tableName, indexName, columns, fcolumns); } }