Package net.yacy.cora.storage

Source Code of net.yacy.cora.storage.WeakPriorityBlockingQueue

/**
*  WeakPriorityBlockingQueue
*  an priority blocking queue that drains elements if it gets too large
*  Copyright 2010 by Michael Peter Christen, mc@yacy.net, Frankfurt a. M., Germany
*  First released 09.09.2010 at http://yacy.net
*
*  $LastChangedDate: 2011-03-08 02:51:51 +0100 (Di, 08. Mär 2011) $
*  $LastChangedRevision: 7567 $
*  $LastChangedBy: low012 $
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*  This library 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
*  Lesser General Public License for more details.
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program in the file lgpl21.txt
*  If not, see <http://www.gnu.org/licenses/>.
*/

package net.yacy.cora.storage;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
* implements a stack where elements 'float' on-top of the stack according to a weight value.
* objects pushed on the stack must implement the hashCode() method to provide a handle
* for a double-check.
* If the queue gets larger that the given maxsize, then elements from the tail of the queue
* are drained (deleted).
*/
public class WeakPriorityBlockingQueue<E> {

   
    private final TreeSet<Element<E>>   queue;    // object within the stack, ordered using a TreeSet
    private final Semaphore    enqueued; // semaphore for elements in the stack
    private final ArrayList<Element<E>> drained;  // objects that had been on the stack but had been removed
    protected int maxsize;

    /**
     * create a new WeakPriorityBlockingQueue
     * all elements in the stack are not ordered by their insert order but by a given element weight
     * weights that are preferred are returned first when a pop from the stack is made
     * @param maxsize the maximum size of the stack. When the stack exceeds this number, then entries are removed
     */
    public WeakPriorityBlockingQueue(final int maxsize) {
        // the maxsize is the maximum number of entries in the stack
        // if this is set to -1, the size is unlimited
        this.queue = new TreeSet<Element<E>>();
        this.drained = new ArrayList<Element<E>>();
        this.enqueued = new Semaphore(0);
        this.maxsize = maxsize;
    }

    /**
     * clear the queue
     */
    public synchronized void clear() {
        this.drained.clear();
        this.queue.clear();
        this.enqueued.drainPermits();
    }
   
    /**
     * test if the queue is empty
     * @return true if the queue is empty, false if not
     */
    public boolean isEmpty() {
        return this.queue.isEmpty() & this.drained.isEmpty();
    }
   
    /**
     * get the number of elements in the queue, waiting to be removed with take() or poll()
     * @return
     */
    public synchronized int sizeQueue() {
        return this.queue.size();
    }


    /**
     * get the number of elements that had been drained so far and are wainting
     * in a list to get enumerated with element()
     * @return
     */
    public synchronized int sizeDrained() {
        return this.drained.size();
    }

    /**
     * get the number of elements that are available for retrieval
     * this is a combined number of sizeQueue() and sizeDrained();
     * @return
     */
    public synchronized int sizeAvailable() {
        return this.queue.size() + this.drained.size();
    }
   
    /**
     * put a element on the stack using a order of the weight
     * elements that had been on the stack cannot be put in again,
     * they are checked against the drained list
     * @param element the element (must have a equals() method)
     * @param weight the weight of the element
     * @param remove - the rating of the element that shall be removed in case that the stack has an size overflow
     */
    public synchronized void put(final Element<E> element) {
        // put the element on the stack
        if (this.drained.contains(element)) return;
        if (this.queue.size() == this.maxsize) {
            // remove last elements if stack is too large
            if (this.queue.add(element)) this.queue.remove(this.queue.last());
        } else {
            // just add entry but only release semaphore if entry was not double
            if (this.queue.add(element)) this.enqueued.release();
        }
        assert this.queue.size() >= this.enqueued.availablePermits() : "(put) queue.size() = " + this.queue.size() + ", enqueued.availablePermits() = " + this.enqueued.availablePermits();
    }
   
    /**
     * return the element with the smallest weight and remove it from the stack
     * @return null if no element is on the queue or the head of the queue
     */
    public Element<E> poll() {
        boolean a = this.enqueued.tryAcquire();
        if (!a) return null;
        synchronized (this) {
            return takeUnsafe();
        }
    }
   
    /**
     * Retrieves and removes the head of this queue, waiting if necessary
     * up to the specified wait time if no elements are present on this queue.
     * @param timeout milliseconds until timeout
     * @return the head element from the queue
     * @throws InterruptedException
     */
    public Element<E> poll(long timeout) throws InterruptedException {
        boolean a = (timeout <= 0) ? this.enqueued.tryAcquire() : this.enqueued.tryAcquire(timeout, TimeUnit.MILLISECONDS);
        if (!a) return null;
        synchronized (this) {
            return takeUnsafe();
        }
    }
   
    /**
     * Retrieves and removes the head of this queue, waiting if no elements are present on this queue.
     * @return the head element from the queue
     * @throws InterruptedException
     */
    public Element<E> take() throws InterruptedException {
        this.enqueued.acquire();
        synchronized (this) {
            return takeUnsafe();
        }
    }
   
    private Element<E> takeUnsafe() {
        final Element<E> element = this.queue.first();
        assert element != null;
        this.queue.remove(element);
        this.drained.add(element);
        assert this.queue.size() >= this.enqueued.availablePermits() : "(take) queue.size() = " + this.queue.size() + ", enqueued.availablePermits() = " + this.enqueued.availablePermits();
        return element;
    }

   
    /**
     * return the element with the smallest weight, but do not remove it
     * @return null if no element is on the queue or the head of the queue
     */
    public synchronized Element<E> peek() {
        if (this.queue.isEmpty()) return null;
        return this.queue.first();
    }
   
    /**
     * all objects that have been returned by poll or take are stored in a back-up list
     * where they can be retrieved afterward. The elements from that list are stored in
     * the specific order as they had been retrieved. This method returns the elements
     * in that specific order and if the list is not large enough, elements available
     * with poll() are taken and written to the list until the required position is
     * written. If the stach size together with the recorded list is not large enough,
     * null is returned
     * @param position inside the drained queue
     * @return the element from the recorded position or null if that position is not available
     */
    public Element<E> element(final int position) {
        if (position < this.drained.size()) {
            return this.drained.get(position);
        }
        synchronized (this) {
            if (position >= this.queue.size() + this.drained.size()) return null; // we don't have that element
            Element<E> p;
            int s;
            while (position >= this.drained.size()) {
                s = this.drained.size();
                p = this.poll();
                if (this.drained.size() <= s) break;
                if (p == null) break;
            }
            if (position >= this.drained.size()) return null;
            return this.drained.get(position);
        }
    }
   
    /**
     * retrieve an element from the drained queue but wait until a timeout
     * until returning null when no element will be available within the time
     * from the input queue
     * @param position inside the drained queue
     * @param time the timeout
     * @return the element from the recorded position or null if that position is not available within the timeout
     * @throws InterruptedException
     */
    public Element<E> element(final int position, long time) throws InterruptedException {
        long timeout = System.currentTimeMillis() + time;
        if (position < this.drained.size()) {
            return this.drained.get(position);
        }
        while (position >= this.drained.size()) {
            long t = timeout - System.currentTimeMillis();
            if (t <= 0) break;
            this.poll(t);
        }
        if (position >= this.drained.size()) return null; // we still don't have that element
        return this.drained.get(position);
    }
   
    /**
     * return the specific amount of entries as they would be retrievable with element()
     * if count is < 0 then all elements are taken
     * the returned list is not cloned from the internal list and shall not be modified in any way (read-only)
     * @param count
     * @return a list of elements in the stack
     */
    public synchronized ArrayList<Element<E>> list(final int count) {
        if (count < 0) {
            return list();
        }
        if (count > sizeAvailable()) throw new RuntimeException("list(" + count + ") exceeded avaiable number of elements (" + sizeAvailable() + ")");
        while (count > this.drained.size()) this.poll();
        return this.drained;
    }
   
    /**
     * return all entries as they would be retrievable with element()
     * @return a list of all elements in the stack
     */
    public synchronized ArrayList<Element<E>> list() {
        // shift all elements
        while (!this.queue.isEmpty()) this.poll();
        return this.drained;
    }
   
    /**
     * iterate over all elements available. All elements that are still in the queue are drained to recorded positions
     * @return an iterator over all drained positions.
     */
    public synchronized Iterator<Element<E>> iterator() {
        // shift all elements to the offstack
        while (!this.queue.isEmpty()) this.poll();
        return this.drained.iterator();
    }

    public interface Element<E> {
        public long getWeight();
        public E getElement();
        public boolean equals(Element<E> o);
        @Override
        public int hashCode();
        @Override
        public String toString();
    }
   
    protected abstract static class AbstractElement<E> implements Element<E> {

        public long weight;
        public E element;
       
        public long getWeight() {
            return this.weight;
        }
       
        public E getElement() {
            return this.element;
        }
       
        public boolean equals(Element<E> o) {
            return this.element.equals(o.getElement());
        }

        @Override
        public int hashCode() {
            return this.element.hashCode();
        }
       
        @Override
        public String toString() {
            return element.toString() + "/" + weight;
        }
    }
   
    /**
     * natural ordering elements, can be used as container of objects <E> in the priority queue
     * the elements with smallest ordering weights are first in the queue when elements are taken
     */
    public static class NaturalElement<E> extends AbstractElement<E> implements Element<E>, Comparable<NaturalElement<E>>, Comparator<NaturalElement<E>> {

        public NaturalElement(final E element, final long weight) {
            this.element = element;
            this.weight = weight;
        }

        public int compare(NaturalElement<E> o1, NaturalElement<E> o2) {
            return o1.compareTo(o2);
        }
       
        public int compareTo(NaturalElement<E> o) {
            if (this.element == o.getElement()) return 0;
            if (this.element.equals(o.getElement())) return 0;
            if (this.weight > o.getWeight()) return 1;
            if (this.weight < o.getWeight()) return -1;
            int o1h = this.hashCode();
            int o2h = o.hashCode();
            if (o1h > o2h) return 1;
            if (o1h < o2h) return -1;
            return 0;
        }
       
    }
   
    /**
     * reverse ordering elements, can be used as container of objects <E> in the priority queue
     * the elements with highest ordering weights are first in the queue when elements are taken
     */
    public static class ReverseElement<E> extends AbstractElement<E> implements Element<E>, Comparable<ReverseElement<E>>, Comparator<ReverseElement<E>> {

        public ReverseElement(final E element, final long weight) {
            this.element = element;
            this.weight = weight;
        }

        public int compare(ReverseElement<E> o1, ReverseElement<E> o2) {
            return o1.compareTo(o2);
        }
       
        public int compareTo(ReverseElement<E> o) {
            if (this.element == o.getElement()) return 0;
            if (this.element.equals(o.getElement())) return 0;
            if (this.weight > o.getWeight()) return -1;
            if (this.weight < o.getWeight()) return 1;
            int o1h = this.hashCode();
            int o2h = o.hashCode();
            if (o1h > o2h) return -1;
            if (o1h < o2h) return 1;
            return 0;
        }
    }
   
    public static void main(String[] args) {
        final WeakPriorityBlockingQueue<String> a = new WeakPriorityBlockingQueue<String>(3);
        //final Element<String> REVERSE_POISON = new ReverseElement<String>("", Long.MIN_VALUE);
        new Thread(){
            @Override
            public void run() {
                Element<String> e;
                try {
                    while ((e = a.poll(1000)) != null) System.out.println("> " + e.toString());
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }.start();
        a.put(new ReverseElement<String>("abc", 1));
        //a.poll();
        a.put(new ReverseElement<String>("abcx", 2));
        a.put(new ReverseElement<String>("6s_7dfZk4xvc", 3));
        a.put(new ReverseElement<String>("6s_7dfZk4xvcx", 4));
        //a.put((Element<String>) REVERSE_POISON);
        //a.poll();
        System.out.println("size = " + a.sizeAvailable());
        //while (a.sizeQueue() > 0) System.out.println("> " + a.poll().toString());
    }
}
TOP

Related Classes of net.yacy.cora.storage.WeakPriorityBlockingQueue

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.