package com.neverwinterdp.registry.queue; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import com.neverwinterdp.registry.NodeCreateMode; import com.neverwinterdp.registry.Registry; import com.neverwinterdp.registry.RegistryException; import com.neverwinterdp.registry.Transaction; import com.neverwinterdp.registry.event.NodeEvent; import com.neverwinterdp.registry.event.NodeWatcher; import com.neverwinterdp.util.JSONSerializer; /** * This queue implement the java.util.Queue api with the take method * @author Tuan */ public class DistributedQueue { private final String path; private Registry registry; public DistributedQueue(Registry registry, String path) throws RegistryException { this.path = path; this.registry = registry; registry.createIfNotExist(path) ; } public String getPath() { return this.path ; } public Registry getRegistry() { return this.registry; } /** * Inserts the specified element into this queue if it is possible to do * so immediately without violating capacity restrictions. * When using a capacity-restricted queue, this method is generally * preferable to {@link #add}, which can fail to insert an element only * by throwing an exception. * @throws RegistryException */ public void offer(byte[] data) throws RegistryException { registry.create(path + "/", data, NodeCreateMode.PERSISTENT_SEQUENTIAL); } public void offer(Transaction transaction, byte[] data) throws RegistryException { transaction.create(path + "/", data, NodeCreateMode.PERSISTENT_SEQUENTIAL); } public <T> void offerAs(T object) throws RegistryException { byte[] data = JSONSerializer.INSTANCE.toBytes(object); offer(data); } /** * Retrieves and removes the head of this queue. This method differs * from {@link #poll poll} only in that it throws an exception if this * queue is empty. */ public byte[] remove() throws RegistryException, Exception { byte[] data = poll(); if(data == null) throw new Exception("The queue is empty") ; return data; } public <T> T removeAs(Class<T> type) throws RegistryException, Exception { byte[] data = remove(); return JSONSerializer.INSTANCE.fromBytes(data, type); } /** * Retrieves and removes the head of this queue, or returns <tt>null</tt> if this queue is empty. */ public byte[] poll() throws RegistryException { List<String> orderedChildren = orderedChildren(); if(orderedChildren.size() == 0) return null ; String headChild = orderedChildren.get(0) ; String headChildPath = path + "/" + headChild ; byte[] data = registry.getData(headChildPath) ; registry.delete(headChildPath); return data; } public <T> T pollAs(Class<T> type) throws RegistryException { byte[] data = poll(); return JSONSerializer.INSTANCE.fromBytes(data, type); } /** * Retrieves, but does not remove, the head of this queue. This method differs from {@link #peek peek} * only in that it throws an exception if this queue is empty. */ public byte[] element() throws RegistryException { byte[] data = peek() ; return data; } /** * Retrieves, but does not remove, the head of this queue, or returns <tt>null</tt> if this queue is empty. */ public byte[] peek() throws RegistryException { List<String> orderedChildren = orderedChildren(); if(orderedChildren.size() == 0) return null ; String headChild = orderedChildren.get(0) ; String headChildPath = path + "/" + headChild ; byte[] data = registry.getData(headChildPath) ; return data; } /** * This method suppose to wait if the queue is empty and return when the queue entry is available * @return * @throws RegistryException * @throws InterruptedException */ public byte[] take() throws RegistryException, InterruptedException { while(true){ List<String> orderedChildren = orderedChildren(); if(orderedChildren.size() == 0) { LatchChildWatcher childWatcher = new LatchChildWatcher(); registry.watchChildren(path, childWatcher); childWatcher.await(); continue; } String headNode = orderedChildren.get(0); String headNodePath = path +"/"+headNode; byte[] data = registry.getData(headNodePath); registry.delete(headNodePath); return data; } } public <T> T takeAs(Class<T> type) throws RegistryException, InterruptedException { byte[] data = take() ; return JSONSerializer.INSTANCE.fromBytes(data, type); } /** * Returns a List of the children, ordered by id. */ private List<String> orderedChildren() throws RegistryException { List<String> orderedChildren = registry.getChildren(path); Collections.sort(orderedChildren); return orderedChildren; } private class LatchChildWatcher extends NodeWatcher { CountDownLatch latch; public LatchChildWatcher(){ latch = new CountDownLatch(1); } @Override public void onEvent(NodeEvent event) { latch.countDown(); } public void await() throws InterruptedException { latch.await(); } } }