/* * Copyright (c) 2008-2013, 2015 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Simon McDuff - initial API and implementation * Eike Stepper - maintenance */ package org.eclipse.emf.cdo.common.util; import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.collection.Closeable; import java.util.Collection; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.Queue; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * The {@link Queue queue} that represents the result of a CDOQuery. * * @author Simon McDuff * @since 2.0 * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ public class CDOQueryQueue<E> implements Queue<E>, Closeable { // Static not allowed due to <E> private final QueueEntry<E> QUEUE_CLOSED = new QueueEntry<E>(); private PriorityBlockingQueue<QueueEntry<E>> queue = new PriorityBlockingQueue<QueueEntry<E>>(10); private boolean closed; private Object closeLock = new Object(); public CDOQueryQueue() { } public void setException(Throwable exception) { queue.add(new QueueEntry<E>(exception)); } public void close() { synchronized (closeLock) { if (!closed) { queue.add(QUEUE_CLOSED); } } } public boolean isClosed() { synchronized (closeLock) { return closed; } } public boolean add(E e) { QueueEntry<E> entry = new QueueEntry<E>(e); return queue.add(entry); } public void clear() { queue.clear(); } public boolean contains(Object o) { return queue.contains(o); } public E element() { return checkObject(queue.element()); } @Override public boolean equals(Object obj) { return queue.equals(obj); } @Override public int hashCode() { return queue.hashCode(); } public boolean isEmpty() { return queue.isEmpty(); } public BlockingCloseableIterator<E> iterator() { return new BlockingCloseableIteratorImpl(); } public boolean offer(E e, long timeout, TimeUnit unit) { return queue.offer(new QueueEntry<E>(e), timeout, unit); } public boolean offer(E e) { return queue.offer(new QueueEntry<E>(e)); } public E peek() { return checkObject(queue.peek()); } public E poll(long timeout, TimeUnit unit) throws InterruptedException { return checkObject(queue.poll(timeout, unit)); } public void put(E e) { queue.put(new QueueEntry<E>(e)); } public int remainingCapacity() { return queue.remainingCapacity(); } public E remove() { return checkObject(queue.remove()); } public boolean remove(Object o) { return queue.remove(o); } public int size() { return queue.size(); } public E take() throws InterruptedException { QueueEntry<E> entry = null; entry = queue.take(); return checkObject(entry); } public Object[] toArray() { return queue.toArray(); } @SuppressWarnings("unchecked") public Object[] toArray(Object[] a) { return queue.toArray(a); } @Override public String toString() { return queue.toString(); } public E poll() { QueueEntry<E> entry = queue.poll(); return checkObject(entry); } public Comparator<?> comparator() { throw new UnsupportedOperationException(); } public boolean containsAll(Collection<?> c) { throw new UnsupportedOperationException(); } public boolean addAll(Collection<? extends E> c) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } private E checkObject(QueueEntry<E> entry) { if (entry == QUEUE_CLOSED) { synchronized (closeLock) { closed = true; } return null; } if (entry == null) { return null; } return entry.getObjectWithException(); } /** * @author Simon McDuff * @since 2.0 */ private static class QueueEntry<E> implements Comparable<QueueEntry<E>> { private static final AtomicLong nextSeq = new AtomicLong(0); private long seqNumber; private Object internalObject; public QueueEntry() { seqNumber = Long.MAX_VALUE; } public QueueEntry(E object) { internalObject = object; seqNumber = nextSeq.getAndIncrement(); } public QueueEntry(Throwable object) { internalObject = object; seqNumber = nextSeq.getAndIncrement(); } @SuppressWarnings("unchecked") public E getObjectWithException() { Throwable exception = getException(); if (exception instanceof Exception) { throw WrappedException.wrap((Exception)exception); } if (exception instanceof Error) { throw (Error)exception; } return (E)internalObject; } public Throwable getException() { if (internalObject instanceof Throwable) { return (Throwable)internalObject; } return null; } public int compareTo(QueueEntry<E> o) { if (this == o) { return 0; } if (seqNumber == o.seqNumber) { // Should not be possible return 0; } return seqNumber < o.seqNumber ? -1 : 1; } // @Override // public boolean equals(Object obj) // { // if (this == obj) // { // return true; // } // // if (obj instanceof QueueEntry<?>) // { // @SuppressWarnings("unchecked") // QueueEntry<E> that = (QueueEntry<E>)obj; // return compareTo(that) == 0; // } // // return false; // } } /** * A blocking iterator that takes elements from a {@link CDOQueryQueue}. * * @author Simon McDuff * @since 2.0 */ public class BlockingCloseableIteratorImpl implements BlockingCloseableIterator<E> { private boolean closed; private E nextElement; public BlockingCloseableIteratorImpl() { } public E peek() { if (nextElement == null) { return CDOQueryQueue.this.peek(); } return nextElement; } public boolean hasNext() { privateNext(false); return nextElement != null; } private void privateNext(boolean failOnNull) { if (nextElement == null) { try { synchronized (closeLock) { if (CDOQueryQueue.this.isEmpty() && CDOQueryQueue.this.isClosed()) { if (failOnNull) { throw new NoSuchElementException(); } return; } } nextElement = take(); } catch (InterruptedException ex) { throw WrappedException.wrap(ex); } } } public E next() { try { privateNext(true); return nextElement; } finally { nextElement = null; } } public void remove() { throw new UnsupportedOperationException(); } public void close() { this.closed = true; } public boolean isClosed() { return this.closed; } } }