/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.util;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.ManagedObjectRemoval;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.TransactionNotActiveException;
import com.sun.sgs.impl.sharedutil.Objects;
import java.io.Serializable;
import java.util.AbstractQueue;
import java.util.Iterator;
/**
* A simple implementation of a persistent queue. When an element is
* removed from the queue, that element is also removed from the data
* store.
*
* <p>Note: The {@code size} and {@code iterator} methods are not
* supported.
*
* <p>Note: If a given instance of {@code ManagedQueue} contains a large
* number of elements, invoking the {@code clear} method on the queue may
* be a lengthy operation. Therefore, the queue should either contain a
* smaller number of elements, or elements should be removed from the queue
* a few at at time.
*
* @param <E> the type for elements in the queue
*
* TODO: The element type should not be required to be a managed object,
* but should just be serializable. Should it handle both?
*/
public class ManagedQueue<E>
extends AbstractQueue<E>
implements Serializable, ManagedObjectRemoval
{
/** The serialVersionUID for this class. */
private static final long serialVersionUID = 1L;
/** The head of the queue, or null. */
private ManagedReference<Entry<E>> headRef = null;
/** The tail of the queue, or null. */
private ManagedReference<Entry<E>> tailRef = null;
/**
* A queue entry consisting of a reference to the element object
* and a reference to the next entry in the queue.
*/
private static class Entry<E> implements ManagedObject, Serializable {
/** The serialVersionUID for this class. */
private static final long serialVersionUID = 1L;
/** The element. */
final ManagedReference<E> elementRef;
/** The reference to the next queue entry, or null. */
ManagedReference<Entry<E>> nextEntryRef = null;
/** Constructs an entry with the specified element. */
Entry(E element) {
// TBD: allow Serializable, but non-ManagedObjects as elements?
if (!(element instanceof Serializable)) {
throw new IllegalArgumentException(
"element does not implement Serializable");
} else if (!(element instanceof ManagedObject)) {
throw new IllegalArgumentException(
"element does not implement ManagedObject");
}
elementRef = AppContext.getDataManager().createReference(element);
}
/**
* Returns the element associated with this entry.
*/
E getElement() {
return elementRef.get();
}
}
/** {@inheritDoc} */
@Override
public boolean offer(E o) {
if (o == null) {
throw new NullPointerException("null element");
}
Entry<E> entry = new Entry<E>(o);
DataManager dataManager = AppContext.getDataManager();
dataManager.markForUpdate(this);
ManagedReference<Entry<E>> entryRef =
dataManager.createReference(entry);
if (tailRef == null) {
headRef = entryRef;
tailRef = entryRef;
} else {
Entry<E> tail = tailRef.getForUpdate();
tail.nextEntryRef = entryRef;
tailRef = entryRef;
}
return true;
}
/** {@inheritDoc} */
@Override
public E peek() {
return
(headRef != null) ?
getHead().getElement() :
null;
}
/** {@inheritDoc} */
@Override
public E poll() {
E element = null;
if (headRef != null) {
DataManager dataManager = AppContext.getDataManager();
dataManager.markForUpdate(this);
Entry<E> head = getHeadForUpdate();
element = head.getElement();
headRef = head.nextEntryRef;
if (headRef == null) {
// last element removed
tailRef = null;
}
dataManager.removeObject(head);
dataManager.removeObject(element);
}
return element;
}
/** {@inheritDoc}
*
* <p> This method is not supported.
*/
@Override
public int size() {
throw new UnsupportedOperationException("size not supported");
}
/** {@inheritDoc} */
@Override
public boolean isEmpty() {
return headRef == null;
}
/** {@inheritDoc}
*
* <p> This method is not supported.
*/
@Override
public Iterator<E> iterator() {
throw new UnsupportedOperationException("iterator not supported");
}
/* -- Implement Object.hashCode -- */
/** {@inheritDoc} */
@Override
public int hashCode() {
DataManager dataManager = AppContext.getDataManager();
return dataManager.createReference(this).getId().hashCode();
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof ManagedQueue)) {
return false;
}
ManagedQueue<E> d = Objects.uncheckedCast(o);
DataManager dm = AppContext.getDataManager();
return dm.createReference(this).getId().equals(
dm.createReference(d).getId());
}
/** {@inheritDoc} */
@Override
public String toString() {
try {
return getClass().getName() + '@' + Integer.toHexString(hashCode());
} catch (TransactionNotActiveException e) {
return super.toString();
}
}
/* -- Implement ManagedObjectRemoval -- */
/**
* {@inheritDoc}
*
* <p>This implementation clears all elements from the queue,
* thus removing all elements from the data store.
*
* TODO: For queues with a large number of elements, removing the
* enqueued elements should be performed in a separate task (or
* tasks).
*/
public void removingObject() {
clear();
}
/* -- Other methods -- */
private Entry<E> getHead() {
return headRef.get();
}
private Entry<E> getHeadForUpdate() {
return headRef.getForUpdate();
}
}