/* * Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.impl; import com.hazelcast.config.ItemListenerConfig; import com.hazelcast.config.MapConfig; import com.hazelcast.config.QueueConfig; import com.hazelcast.core.*; import com.hazelcast.impl.base.PacketProcessor; import com.hazelcast.impl.base.RuntimeInterruptedException; import com.hazelcast.impl.base.ScheduledAction; import com.hazelcast.impl.monitor.LocalQueueStatsImpl; import com.hazelcast.nio.Address; import com.hazelcast.nio.Data; import com.hazelcast.nio.DataSerializable; import com.hazelcast.nio.Packet; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.*; import java.util.concurrent.*; import java.util.logging.Level; import static com.hazelcast.nio.IOUtil.toData; import static com.hazelcast.nio.IOUtil.toObject; public class BlockingQueueManager extends BaseManager { private final static long BILLION = 1000 * 1000 * 1000; BlockingQueueManager(Node node) { super(node); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_ITERATE, new InitializationAwareOperationHandler() { @Override void doOperation(BQ queue, Request request) { queue.iterate(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_SIZE, new InitializationAwareOperationHandler() { void doOperation(BQ queue, Request request) { queue.size(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_GET_KEY_BY_INDEX, new InitializationAwareOperationHandler() { public void doOperation(BQ queue, Request request) { queue.doGetKeyByIndex(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_GET_INDEX_BY_KEY, new InitializationAwareOperationHandler() { public void doOperation(BQ queue, Request request) { queue.doGetIndexByKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_TAKE_KEY, new InitializationAwareOperationHandler() { public void doOperation(BQ queue, Request request) { queue.doTakeKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_CANCEL_TAKE_KEY, new InitializationAwareOperationHandler() { public void doOperation(BQ queue, Request request) { queue.cancelTakeKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_SET, new InitializationAwareOperationHandler() { public void doOperation(BQ queue, Request request) { queue.doSet(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_PEEK_KEY, new ResponsiveOperationHandler() { public void handle(Request request) { handlePeekKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_ADD_KEY, new ResponsiveOperationHandler() { public void handle(Request request) { handleAddKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_REMOVE_KEY, new InitializationAwareOperationHandler() { void doOperation(BQ queue, Request request) { queue.removeKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_GENERATE_KEY, new ResponsiveOperationHandler() { public void handle(Request request) { handleGenerateKey(request); } }); node.clusterService.registerPacketProcessor(ClusterOperation.BLOCKING_OFFER_KEY, new PacketProcessor() { public void process(Packet packet) { handleOfferKey(packet); } }); } public void destroy(String name) { mapBQ.remove(name); node.listenerManager.removeAllRegisteredListeners(name); } public void syncForDead(MemberImpl deadMember) { for (BQ queue : mapBQ.values()) { queue.invalidateScheduledActionsFor(deadMember); } } abstract class InitializationAwareOperationHandler extends ResponsiveOperationHandler { abstract void doOperation(BQ queue, Request request); public void handle(Request request) { if (isMaster() && ready(request)) { BQ bq = getOrCreateBQ(request.name); doOperation(bq, request); } else { returnRedoResponse(request); } } } boolean addKeyAsync = false; private void sendKeyToMaster(final String queueName, final Data key, final int index) { enqueueAndReturn(new Processable() { public void process() { if (isMaster()) { doAddKey(queueName, key, index); } else { Packet packet = obtainPacket(); packet.name = queueName; packet.setKey(key); packet.operation = ClusterOperation.BLOCKING_OFFER_KEY; packet.longValue = index; boolean sent = send(packet, getMasterAddress()); } } }); } public int size(String name) { ThreadContext threadContext = ThreadContext.get(); TransactionImpl txn = threadContext.getCallContext().getTransaction(); int size = queueSize(name); if (txn != null && txn.getStatus() == Transaction.TXN_STATUS_ACTIVE) { size += txn.size(name); } return size; } public boolean remove(final String name, Object obj) { final Data dataValue = toData(obj); Set<Long> keys = getValueKeys(name, dataValue); if (keys != null) { for (Long key : keys) { Data keyData = toData(key); if (removeKey(name, keyData)) { try { getStorageMap(name).tryRemove(keyData, 0, TimeUnit.SECONDS); } catch (TimeoutException ignored) { } final BQ bq = getBQ(name); if (bq != null && bq.mapListeners.size() > 0) { enqueueAndReturn(new Processable() { public void process() { fireMapEvent(bq.mapListeners, name, EntryEvent.TYPE_REMOVED, dataValue, thisAddress); } }); } return true; } } } return false; } public boolean add(String name, Object obj, int index) { try { return offer(name, obj, index, 0); } catch (InterruptedException ignored) { return false; } } public boolean offer(String name, Object obj, long timeout) throws InterruptedException { return offer(name, obj, Integer.MAX_VALUE, timeout); } public boolean offer(final String name, Object obj, int index, long timeout) throws InterruptedException { Long key = generateKey(name, timeout); ThreadContext threadContext = ThreadContext.get(); TransactionImpl txn = threadContext.getCallContext().getTransaction(); if (key != -1) { final Data dataItem = toData(obj); if (txn != null && txn.getStatus() == Transaction.TXN_STATUS_ACTIVE) { txn.attachPutOp(name, key, dataItem, timeout, true); } else { storeQueueItem(name, key, dataItem, index); final BQ bq = getBQ(name); if (bq != null && bq.mapListeners.size() > 0) { enqueueAndReturn(new Processable() { public void process() { fireMapEvent(bq.mapListeners, name, EntryEvent.TYPE_ADDED, dataItem, thisAddress); } }); } } return true; } return false; } public Object set(String name, Object newValue, int index) { if (index < 0) { throw new IllegalArgumentException(); } Data key = getKeyByIndex(name, index); if (key == null) { throw new IndexOutOfBoundsException(); } IMap imap = getStorageMap(name); return imap.put(key, newValue); } public Object remove(String name, int index) { if (index < 0) { throw new IllegalArgumentException(); } Data key = null; try { key = takeKey(name, index, 0L); } catch (InterruptedException ignored) { } if (key == null) { throw new IndexOutOfBoundsException(); } IMap imap = getStorageMap(name); try { return imap.tryRemove(key, 0, TimeUnit.SECONDS); } catch (TimeoutException e) { return null; } } public void offerCommit(String name, Object key, Object obj) { storeQueueItem(name, key, obj, Integer.MAX_VALUE); } public void rollbackPoll(String name, Object key, Object obj) { final Data dataKey = toData(key); if (addKeyAsync) { sendKeyToMaster(name, dataKey, 0); } else { addKey(name, dataKey, 0); } } private void storeQueueItem(String name, Object key, Object obj, int index) { IMap imap = getStorageMap(name); final Data dataKey = toData(key); imap.put(dataKey, obj); if (addKeyAsync) { sendKeyToMaster(name, dataKey, index); } else { addKey(name, dataKey, index); } } public Object poll(final String name, long timeout) throws InterruptedException { if (timeout == -1) { timeout = Long.MAX_VALUE; } Object removedItem = null; long start = System.currentTimeMillis(); while (removedItem == null && timeout >= 0) { Data key = takeKey(name, timeout); if (key == null) { return null; } IMap imap = getStorageMap(name); try { removedItem = imap.tryRemove(key, 0, TimeUnit.MILLISECONDS); if (removedItem != null) { ThreadContext threadContext = ThreadContext.get(); TransactionImpl txn = threadContext.getCallContext().getTransaction(); final Data removedItemData = toData(removedItem); if (txn != null && txn.getStatus() == Transaction.TXN_STATUS_ACTIVE) { txn.attachRemoveOp(name, key, removedItemData, true); } final BQ bq = getBQ(name); if (bq != null && bq.mapListeners.size() > 0) { enqueueAndReturn(new Processable() { public void process() { fireMapEvent(bq.mapListeners, name, EntryEvent.TYPE_REMOVED, removedItemData, thisAddress); } }); } } } catch (TimeoutException e) { } long now = System.currentTimeMillis(); timeout -= (now - start); start = now; } return removedItem; } public Object peek(String name) { Data key = peekKey(name); if (key == null) { return null; } IMap imap = getStorageMap(name); return imap.get(key); } private Data takeKey(String name, long timeout) throws InterruptedException { return takeKey(name, -1, timeout); } private Data takeKey(String name, int index, long timeout) throws InterruptedException { try { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_TAKE_KEY, name, timeout); op.request.longValue = index; op.request.txnId = ThreadContext.get().getThreadId(); op.initOp(); return (Data) op.getResultAsIs(); } catch (Exception e) { if (e instanceof RuntimeInterruptedException) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_CANCEL_TAKE_KEY, name, timeout); op.request.longValue = index; op.request.txnId = ThreadContext.get().getThreadId(); op.initOp(); throw new InterruptedException(); } } return null; } int getIndexOf(String name, Object obj, boolean first) { Set<Long> keys = getValueKeys(name, toData(obj)); if (keys == null || keys.size() == 0) return -1; Long key = null; if (first) { key = keys.iterator().next(); } else { key = (Long) keys.toArray()[keys.size() - 1]; } return getIndexByKey(name, toData(key)); } Set<Long> getValueKeys(String name, Data item) { while (true) { node.checkNodeState(); try { return doGetValueKeys(name, item); } catch (Throwable e) { try { //noinspection BusyWait Thread.sleep(500); } catch (InterruptedException e1) { } } } } Set<Long> doGetValueKeys(String name, Data item) throws ExecutionException, InterruptedException { Set<Member> members = node.getClusterImpl().getMembers(); List<Future<Keys>> lsFutures = new ArrayList<Future<Keys>>(members.size()); for (Member member : members) { GetValueKeysCallable callable = new GetValueKeysCallable(name, item); DistributedTask<Keys> dt = new DistributedTask<Keys>(callable, member); lsFutures.add(dt); node.factory.getExecutorService().execute(dt); } Set<Long> foundKeys = new TreeSet<Long>(); for (Future<Keys> future : lsFutures) { Keys keys = future.get(); if (keys != null) { for (Data keyData : keys.getKeys()) { foundKeys.add((Long) toObject(keyData)); } } } return foundKeys; } public static class GetValueKeysCallable implements Callable<Keys>, DataSerializable, HazelcastInstanceAware { HazelcastInstance hazelcast; Data item; String name; public GetValueKeysCallable() { } public GetValueKeysCallable(String name, Data item) { this.name = name; this.item = item; } public Keys call() throws Exception { IMap imap = hazelcast.getMap(name); Set localKeys = imap.localKeySet(); if (localKeys != null) { Object itemObject = toObject(item); Keys keys = new Keys(); for (Object key : localKeys) { Object v = imap.get(key); if (v != null && v.equals(itemObject)) { keys.add(toData(key)); } } return keys; } return null; } public void writeData(DataOutput out) throws IOException { out.writeUTF(name); item.writeData(out); } public void readData(DataInput in) throws IOException { name = in.readUTF(); item = new Data(); item.readData(in); } public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { this.hazelcast = hazelcastInstance; } } Object getItemByIndex(String name, int index) { if (index < 0) { throw new IllegalArgumentException(); } Data key = getKeyByIndex(name, index); if (key == null) { throw new IndexOutOfBoundsException(); } IMap imap = getStorageMap(name); return imap.get(key); } private Data getKeyByIndex(String name, int index) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_GET_KEY_BY_INDEX, name, 0); op.request.longValue = index; op.initOp(); return (Data) op.getResultAsIs(); } private Integer getIndexByKey(String name, Data keyData) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_GET_INDEX_BY_KEY, name, 0); op.request.key = keyData; op.initOp(); return (Integer) op.getResultAsObject(); } private Data peekKey(String name) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_PEEK_KEY, name, 0); op.initOp(); return (Data) op.getResultAsIs(); } public IMap getStorageMap(String queueName) { return node.factory.getMap(queueName); } CMap getStorageCMap(String queueName) { return node.concurrentMapManager.getMap(Prefix.MAP + queueName); } CMap getOrCreateStorageCMap(String queueName) { return node.concurrentMapManager.getOrCreateMap(Prefix.MAP + queueName); } public Iterator iterate(final String name) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_ITERATE, name, 0); op.initOp(); Keys keys = (Keys) op.getResultAsObject(); final Collection<Data> dataKeys = keys.getKeys(); final Collection allKeys = new ArrayList(dataKeys); TransactionImpl txn = ThreadContext.get().getCallContext().getTransaction(); Map txnOfferItems = null; if (txn != null) { txnOfferItems = txn.newKeys(name); if (txnOfferItems != null) { allKeys.addAll(txnOfferItems.keySet()); } } final Map txnMap = txnOfferItems; final Iterator it = allKeys.iterator(); final IMap imap = getStorageMap(name); return new Iterator() { Object key = null; Object next = null; boolean hasNext = false; boolean set = false; public boolean hasNext() { if (!set) { set(); } boolean result = hasNext; hasNext = false; set = false; return result; } public Object next() { if (!set) { set(); } Object result = next; set = false; next = null; return result; } public void remove() { if (key != null) { try { Data dataKey = toData(key); imap.tryRemove(dataKey, 0, TimeUnit.MILLISECONDS); removeKey(name, dataKey); } catch (TimeoutException ignored) { } } } void set() { try { while (next == null) { hasNext = it.hasNext(); if (hasNext) { key = it.next(); if (txnMap != null) { next = txnMap.get(key); } if (next == null) { next = imap.get(key); } } else { return; } } } finally { set = true; } } }; } public boolean addKey(String name, Data key, int index) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_ADD_KEY, name, 0); op.request.key = key; op.request.setBooleanRequest(); op.request.longValue = index; op.initOp(); return op.getResultAsBoolean(); } public Data set(String name, Data key, int index) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_SET, name, 0); op.request.key = key; op.request.setBooleanRequest(); op.request.longValue = index; op.initOp(); return (Data) op.getResultAsIs(); } public boolean removeKey(String name, Data key) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_REMOVE_KEY, name, 0); op.request.key = key; op.request.setBooleanRequest(); op.initOp(); return op.getResultAsBoolean(); } public long generateKey(String name, long timeout) throws InterruptedException { try { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_GENERATE_KEY, name, timeout); op.request.setLongRequest(); op.request.txnId = ThreadContext.get().getThreadId(); op.initOp(); return (Long) op.getResultAsObject(); } catch (Exception e) { if (e instanceof RuntimeInterruptedException) { throw new InterruptedException(); } } return -1; } public int queueSize(String name) { MasterOp op = new MasterOp(ClusterOperation.BLOCKING_SIZE, name, 0); op.request.setLongRequest(); op.initOp(); return ((Long) op.getResultAsObject()).intValue(); } long getKey(String queueName) { return node.factory.getIdGenerator(queueName).newId(); } class MasterOp extends TargetAwareOp { private final ClusterOperation op; private final String name; private final long timeout; MasterOp(ClusterOperation op, String name, long timeout) { this.op = op; this.name = name; this.timeout = timeout; } @Override public void setTarget() { target = getMasterAddress(); } void initOp() { request.operation = op; request.name = name; request.timeout = timeout; doOp(); } } class Lease { final long timeout; final Address address; Lease(Address address) { this.address = address; timeout = System.currentTimeMillis() + 10000; } } final Map<String, BQ> mapBQ = new ConcurrentHashMap<String, BQ>(); BQ getOrCreateBQ(String name) { BQ bq = mapBQ.get(name); if (bq == null) { bq = new BQ(name); mapBQ.put(name, bq); } return bq; } BQ getBQ(String name) { return mapBQ.get(name); } final void handlePeekKey(Request req) { if (isMaster() && ready(req)) { BQ bq = getOrCreateBQ(req.name); bq.doPeekKey(req); } else { returnRedoResponse(req); } } final void handleOfferKey(Packet packet) { if (isMaster()) { doAddKey(packet.name, packet.getKeyData(), (int) packet.longValue); } releasePacket(packet); } final void doAddKey(String name, Data key, int index) { BQ bq = getOrCreateBQ(name); bq.doAddKey(key, index); } final void handleAddKey(Request req) { if (isMaster() && ready(req)) { BQ bq = getOrCreateBQ(req.name); bq.doAddKey(req.key, (int) req.longValue); req.key = null; req.response = Boolean.TRUE; returnResponse(req); } else { returnRedoResponse(req); } } final void handleGenerateKey(Request req) { if (isMaster() && ready(req)) { BQ bq = getOrCreateBQ(req.name); bq.doGenerateKey(req); } else { returnRedoResponse(req); } } enum MasterState { NOT_INITIALIZED, INITIALIZING, READY } boolean ready(Request request) { BQ q = getOrCreateBQ(request.name); if (q.state == MasterState.READY) { return true; } else if (q.state == MasterState.NOT_INITIALIZED) { q.state = MasterState.INITIALIZING; initialize(request.name); } return false; } void initialize(final String queueName) { final BQ q = getOrCreateBQ(queueName); final CMap cmapStorage = getOrCreateStorageCMap(queueName); executeLocally(new Runnable() { public void run() { TreeSet itemKeys = null; if (cmapStorage.loader != null) { Set keys = cmapStorage.loader.loadAllKeys(); if (keys != null && keys.size() > 0) { itemKeys = new TreeSet<Long>(keys); } } Set keys = getStorageMap(queueName).keySet(); if (keys != null && keys.size() > 0) { if (itemKeys == null) { itemKeys = new TreeSet<Long>(keys); } else { itemKeys.addAll(keys); } } if (itemKeys != null) { final Set<Long> queueKeys = itemKeys; enqueueAndReturn(new Processable() { public void process() { for (Long key : queueKeys) { Data keyData = toData(key); if (q.keys.add(keyData)) { q.queue.add(new QData(keyData)); q.nextKey = Math.max(q.nextKey, key); } } q.nextKey += BILLION; q.state = MasterState.READY; } }); } else { q.state = MasterState.READY; } } }); } public void addItemListener(final String name, final ItemListener listener, final boolean includeValue) { node.listenerManager.addListener(name, listener, null, includeValue, Instance.InstanceType.QUEUE); } public void removeItemListener(final String name, final ItemListener listener) { List<ListenerManager.ListenerItem> lsListenerItems = node.listenerManager.getOrCreateListenerList(name); for (ListenerManager.ListenerItem listenerItem : lsListenerItems) { if (listenerItem.listener == listener) { lsListenerItems.remove(listenerItem); return; } } } void registerListener(boolean add, String name, Data key, Address address, boolean includeValue) { BQ queue = getOrCreateBQ(name); if (add) { queue.mapListeners.put(address, includeValue); } else { queue.mapListeners.remove(address); } } class BQ { final Map<Address, Boolean> mapListeners = new ConcurrentHashMap<Address, Boolean>(1); final LinkedList<ScheduledAction> offerWaitList = new LinkedList<ScheduledAction>(); final LinkedList<PollAction> pollWaitList = new LinkedList<PollAction>(); final LinkedList<Lease> leases = new LinkedList<Lease>(); final LinkedList<QData> queue = new LinkedList<QData>(); final Set<Data> keys = new HashSet<Data>(1000); final int maxSizePerJVM; final long ttl; final String name; final QueueConfig queueConfig; long nextKey = 0; volatile MasterState state = MasterState.NOT_INITIALIZED; BQ(String name) { this.name = name; String shortName = name.substring(Prefix.QUEUE.length()); queueConfig = node.getConfig().findMatchingQueueConfig(shortName); MapConfig backingMapConfig = node.getConfig().findMatchingMapConfig(queueConfig.getBackingMapRef()); int backingMapTTL = backingMapConfig.getTimeToLiveSeconds(); this.maxSizePerJVM = (queueConfig.getMaxSizePerJVM() == 0) ? Integer.MAX_VALUE : queueConfig.getMaxSizePerJVM(); this.ttl = (backingMapTTL == 0) ? Integer.MAX_VALUE : TimeUnit.SECONDS.toMillis(backingMapTTL); initializeListeners(); } private void initializeListeners() { for (ItemListenerConfig lc : queueConfig.getItemListenerConfigs()) { try { node.listenerManager.createAndAddListenerItem(name, lc, Instance.InstanceType.QUEUE); for (MemberImpl member : node.clusterManager.getMembers()) { mapListeners.put(member.getAddress(), lc.isIncludeValue()); } } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); } } } int maxSize() { return (maxSizePerJVM == Integer.MAX_VALUE) ? Integer.MAX_VALUE : maxSizePerJVM * lsMembers.size(); } void doGenerateKey(Request req) { if (size() >= maxSize()) { if (req.hasEnoughTimeToSchedule()) { addOfferAction(new OfferAction(req)); } else { req.response = -1L; returnResponse(req); } } else { generateKeyAndLease(req); returnResponse(req); } } void size(Request req) { req.response = Long.valueOf(queue.size()); returnResponse(req); } void generateKeyAndLease(Request req) { leases.add(new Lease(req.caller)); req.response = nextKey++; } void doSet(Request req) { int index = (int) req.longValue; Data key = req.key; Data oldKey = null; boolean added = false; if (queue.size() >= index) { queue.add(new QData(key)); added = true; } else { QData old = queue.set(index, new QData(key)); if (isValid(old, System.currentTimeMillis())) { oldKey = old.data; } else { added = true; } } if (added) { takeOne(); } req.response = oldKey; returnResponse(req); } void doAddKey(Data key, int index) { if (keys.add(key)) { if (leases.size() > 0) { leases.removeFirst(); } if (index == Integer.MAX_VALUE || index >= queue.size()) { queue.add(new QData(key)); } else if (index == 0) { queue.addFirst(new QData(key)); } else { queue.add(index, new QData(key)); } takeOne(); } } public void removeKey(Request request) { if (keys.remove(request.key)) { Iterator<QData> it = queue.iterator(); while (it.hasNext()) { QData qData = it.next(); if (qData.data.equals(request.key)) { it.remove(); request.response = Boolean.TRUE; break; } } } if (request.response == null) { request.response = Boolean.FALSE; } returnResponse(request); } void takeOne() { while (pollWaitList.size() > 0) { ScheduledAction scheduledActionPoll = pollWaitList.removeFirst(); if (!scheduledActionPoll.expired() && scheduledActionPoll.isValid()) { scheduledActionPoll.consume(); node.clusterManager.deregisterScheduledAction(scheduledActionPoll); return; } } } void offerOne() { while (offerWaitList.size() > 0) { ScheduledAction scheduledActionOffer = offerWaitList.removeFirst(); if (!scheduledActionOffer.expired() && scheduledActionOffer.isValid()) { scheduledActionOffer.consume(); node.clusterManager.deregisterScheduledAction(scheduledActionOffer); return; } } } QData pollValidItem() { QData qdata = queue.poll(); if (qdata == null) { return null; } long now = System.currentTimeMillis(); if (isValid(qdata, now)) { return qdata; } while (qdata != null) { qdata = queue.poll(); if (isValid(qdata, now)) { return qdata; } } return qdata; } QData removeItemByIndex(int index) { if (index >= queue.size()) { return null; } return queue.remove(index); } boolean isValid(QData qdata, long now) { return qdata != null && (now - qdata.createDate) < ttl; } void cancelTakeKey(Request req) { cancelPollAction(req); } void doTakeKey(Request req) { QData qdata; if (req.longValue > 0) { qdata = removeItemByIndex((int) req.longValue); } else { qdata = pollValidItem(); } if (qdata != null) { keys.remove(qdata.data); req.response = qdata.data; returnResponse(req); offerOne(); } else { if (req.hasEnoughTimeToSchedule()) { addPollAction(new PollAction(req)); } else { req.response = null; returnResponse(req); } } } void doGetIndexByKey(Request req) { Data keyData = req.key; int i = -1; for (QData qdata : queue) { if (qdata.data.equals(keyData)) { i++; break; } else { i++; } } req.response = toData(i); returnResponse(req); } void doGetKeyByIndex(Request req) { int index = (int) req.longValue; long now = System.currentTimeMillis(); int i = 0; QData key = null; for (QData qdata : queue) { if (isValid(qdata, now)) { if (index == i) { key = qdata; break; } i++; } } req.response = (key == null) ? null : key.data; returnResponse(req); } void doPeekKey(Request req) { QData qdata = queue.peek(); req.response = (qdata == null) ? null : qdata.data; returnResponse(req); } void addPollAction(PollAction pollAction) { pollWaitList.add(pollAction); node.clusterManager.registerScheduledAction(pollAction); } void cancelPollAction(Request req) { PollAction toCancel = null; for (PollAction pollAction : pollWaitList) { Request pReq = pollAction.getRequest(); if (pReq.caller.equals(req.caller) && pReq.longValue == req.longValue && pReq.txnId == req.txnId) { toCancel = pollAction; } } if (toCancel != null) { pollWaitList.remove(toCancel); node.clusterManager.deregisterScheduledAction(toCancel); } } void addOfferAction(OfferAction offerAction) { offerWaitList.add(offerAction); node.clusterManager.registerScheduledAction(offerAction); } public int size() { return queue.size() + leases.size(); } public void iterate(Request request) { Keys keys = new Keys(); for (QData qData : queue) { keys.add(qData.data); } request.response = keys; returnResponse(request); } public int getMaxSizePerJVM() { return maxSizePerJVM; } public LocalQueueStatsImpl getQueueStats() { long now = System.currentTimeMillis(); CMap cmap = getStorageCMap(name); IMap storageMap = getStorageMap(name); Set<Object> localKeys = storageMap.localKeySet(); int total = cmap != null ? cmap.mapRecords.size() : 0; int ownedCount = localKeys.size(); int backupCount = Math.abs(total - ownedCount); long minAge = Long.MAX_VALUE; long maxAge = Long.MIN_VALUE; long totalAge = 0; for (Object localKey : localKeys) { MapEntry entry = storageMap.getMapEntry(localKey); if (entry != null) { long age = (now - entry.getCreationTime()); minAge = Math.min(minAge, age); maxAge = Math.max(maxAge, age); totalAge += age; } } long aveAge = (ownedCount == 0) ? 0 : (totalAge / ownedCount); return new LocalQueueStatsImpl(ownedCount, backupCount, minAge, maxAge, aveAge); } public void invalidateScheduledActionsFor(MemberImpl deadMember) { for (PollAction pollAction : pollWaitList) { if (deadMember.address.equals(pollAction.getRequest().caller)) { pollAction.setValid(false); node.clusterManager.deregisterScheduledAction(pollAction); } } for (ScheduledAction offerAction : offerWaitList) { if (deadMember.address.equals(offerAction.getRequest().caller)) { offerAction.setValid(false); node.clusterManager.deregisterScheduledAction(offerAction); } } } public class OfferAction extends ScheduledAction { public OfferAction(Request request) { super(request); } @Override public boolean consume() { generateKeyAndLease(request); returnResponse(request); setValid(false); return true; } @Override public void onExpire() { request.response = -1L; offerWaitList.remove(this); returnResponse(request); setValid(false); } } public class PollAction extends ScheduledAction { public PollAction(Request request) { super(request); } @Override public boolean consume() { doTakeKey(request); setValid(false); return true; } @Override public void onExpire() { request.response = null; pollWaitList.remove(this); returnResponse(request); setValid(false); } } } class QData { final Data data; final long createDate; QData(Data data) { this.data = data; this.createDate = System.currentTimeMillis(); } } }