package com.bagri.client.hazelcast.impl; import static com.bagri.client.hazelcast.serialize.DataSerializationFactoryImpl.cli_QueuedCursor; import static com.bagri.client.hazelcast.serialize.DataSerializationFactoryImpl.factoryId; import static com.bagri.core.server.api.CacheConstants.PN_XDM_SCHEMA_POOL; import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bagri.client.hazelcast.task.query.ResultFetcher; import com.bagri.core.api.BagriException; import com.bagri.core.api.impl.ResultCursorBase; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IExecutorService; import com.hazelcast.core.IQueue; import com.hazelcast.core.Member; import com.hazelcast.core.MemberSelector; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; import com.hazelcast.nio.serialization.IdentifiedDataSerializable; public class QueuedCursorImpl extends ResultCursorBase implements IdentifiedDataSerializable { private static final transient Logger logger = LoggerFactory.getLogger(QueuedCursorImpl.class); private int batchSize; private int queueSize; private String clientId; private String memberId; private String queueName; private Object current; // server side private List<Object> results; private Iterator<Object> iter; private IQueue<Object> queue; private HazelcastInstance hzi; private MemberSelector selector = new ResultMemberSelector(); public QueuedCursorImpl() { // for de-serializer } public QueuedCursorImpl(List<Object> results, String clientId, int batchSize) { this.results = results; this.clientId = clientId; this.batchSize = batchSize; this.queueSize = UNKNOWN; } public QueuedCursorImpl(List<Object> results, String clientId, int batchSize, int queueSize, Iterator<Object> iter) { this(results, clientId, batchSize); this.queueSize = queueSize; this.iter = iter; } @Override public void close() { logger.trace("close.enter; queue remaining size: {}", queue.size()); queue.clear(); queue.destroy(); iter = null; // on the server side current = null; } protected Object getCurrent() { return current; } private IQueue<Object> getQueue() { if (queue == null) { queue = hzi.getQueue(queueName); } return queue; } // client side public void deserialize(HazelcastInstance hzi) { this.hzi = hzi; queue = getQueue(); current = null; position = 0; //-1; } private void initQueue(HazelcastInstance hzi) { this.hzi = hzi; memberId = hzi.getCluster().getLocalMember().getUuid(); queueName = "client:" + clientId + ":" + UUID.randomUUID().toString(); queue = getQueue(); } // server side public int serialize(HazelcastInstance hzi) { initQueue(hzi); int size = 0; if (iter == null) { iter = results.iterator(); } if (batchSize > 0) { for (int i = 0; i < batchSize && addNext(); i++) { size++; } if (queueSize < EMPTY) { if (size > 0) { if (iter.hasNext()) { queueSize = ONE_OR_MORE; } else { queueSize = ONE; } } else { queueSize = EMPTY; } } else if (size > queueSize) { logger.info("serialize; declared and current batch queue sizes do not match: {}/{}", queueSize, size); //queueSize = size; ?? } } else { while (iter.hasNext()) { addNext(); size++; } if (queueSize < EMPTY) { queueSize = size; } else if (size != queueSize) { logger.info("serialize; declared and current queue sizes do not match: {}/{}", queueSize, size); } } return size; } private boolean addNext() { if (iter.hasNext()) { Object o = iter.next(); logger.trace("addNext; next: {}", o); if (o != null) { if (queue.offer(o)) { position++; return true; } else { logger.warn("addNext; queue is full!"); } } } return false; } @Override public List<Object> getList() throws BagriException { return results; //throw new XDMException("Not implemented in queued cursor", XDMException.ecQuery); } @Override public boolean isFixed() { return false; } @Override public boolean next() { current = queue.poll(); boolean result = current != null; if (!result) { if (position < queueSize || ((queueSize < EMPTY) && (position % batchSize) == 0)) { logger.debug("hasNext; got end of the queue; position: {}; queueSize: {}", position, queueSize); // request next batch from server side.. IExecutorService exec = hzi.getExecutorService(PN_XDM_SCHEMA_POOL); Future<Boolean> fetcher = exec.submit(new ResultFetcher(clientId), selector); try { if (fetcher.get()) { current = queue.poll(); result = current != null; if (!result && position < queueSize) { logger.warn("next; declared and fetched queue sizes do not match: {}/{}", queueSize, position); } } } catch (InterruptedException | ExecutionException ex) { logger.error("next.error", ex); } } } else { position++; } logger.trace("next; returning: {}", result); return result; } @Override public int getFactoryId() { return factoryId; } @Override public int getId() { return cli_QueuedCursor; } @Override public void readData(ObjectDataInput in) throws IOException { clientId = in.readUTF(); queueSize = in.readInt(); memberId = in.readUTF(); batchSize = in.readInt(); queueName = in.readUTF(); } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeUTF(clientId); out.writeInt(queueSize); out.writeUTF(memberId); out.writeInt(batchSize); out.writeUTF(queueName); } @Override public String toString() { return "ResultCursor [clientId=" + clientId + ", memberId=" + memberId + ", queueSize=" + queueSize + ", position=" + position + ", batchSize=" + batchSize + "]"; } private class ResultMemberSelector implements MemberSelector { @Override public boolean select(Member member) { return memberId.equals(member.getUuid()); } } }