All Downloads are FREE. Search and download functionalities are using the official Maven repository.

stream.io.Join Maven / Gradle / Ivy

The newest version!
/*
 *  streams library
 *
 *  Copyright (C) 2011-2014 by Christian Bockermann, Hendrik Blom
 * 
 *  streams is a library, API and runtime environment for processing high
 *  volume data streams. It is composed of three submodules "stream-api",
 *  "stream-core" and "stream-runtime".
 *
 *  The streams library (and its submodules) is free software: you can 
 *  redistribute it and/or modify it under the terms of the 
 *  GNU Affero General Public License as published by the Free Software 
 *  Foundation, either version 3 of the License, or (at your option) any 
 *  later version.
 *
 *  The stream.ai library (and its submodules) 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/.
 */
package stream.io;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import stream.Data;
import stream.annotations.Parameter;
import cern.colt.GenericSorting;
import cern.colt.Swapper;
import cern.colt.function.IntComparator;

/**
 * 
 * 
 * @author Hendrik Blom
 * 
 */
public class Join extends AbstractQueue {

	private static final Logger log = LoggerFactory.getLogger(Join.class);

	protected AtomicBoolean closed = new AtomicBoolean(false);

	private int count;

	private int reads;
	private int read;
	private String[] readQueue;
	private Data[] dataQueue;
	private long[] accs;

	private Set streams;
	private String index;
	private String sync;

	public Join() {
		super();
		count = 0;
		read = 0;
	}

	/**
	 * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
	 * 
	 * @param capacity
	 *            the capacity of this queue
	 * @throws IllegalArgumentException
	 *             if {@code capacity} is not greater than zero
	 */
	public Join(int capacity) {
		this();
		if (capacity <= 0)
			throw new IllegalArgumentException();
		this.capacity = capacity;
	}

	public String getIndex() {
		return index;
	}

	@Parameter(required = true, description = "the index key, e.g. a timestamp")
	public void setIndex(String index) {
		this.index = index;
	}

	public String[] getStreams() {
		return streams.toArray(new String[streams.size()]);
	}

	@Parameter(required = true, description = "identifiers of the different streams (in Combination with sync key )")
	public void setStreams(String[] streams) {
		this.streams = new HashSet();
		for (String unit : streams) {
			this.streams.add(unit);
		}
	}

	public String getSync() {
		return sync;
	}

	@Parameter(required = true, description = "the sync key, e.g. @stream )")
	public void setSync(String sync) {
		this.sync = sync;

	}

	private Map> queues = new ConcurrentHashMap>();

	private Swapper swapper = new Swapper() {
		@Override
		public void swap(int a, int b) {
			long t = accs[a];
			accs[a] = accs[b];
			accs[b] = t;

			Data d = dataQueue[a];
			dataQueue[a] = dataQueue[b];
			dataQueue[b] = d;

			String s = readQueue[a];
			readQueue[a] = readQueue[b];
			readQueue[b] = s;
		}
	};

	private IntComparator comp = new IntComparator() {
		public int compare(int a, int b) {
			return accs[a] == accs[b] ? 0 : (accs[a] < accs[b] ? -1 : 1);
		}
	};

	/** Lock held by take, poll, etc */
	private final ReentrantLock takeLock = new ReentrantLock();

	public int size() {
		int min = Integer.MAX_VALUE;
		for (ArrayBlockingQueue queue : queues.values()) {
			if (min > queue.size())
				min = queue.size();
		}
		return min;

	}

	public int remainingCapacity() {
		return capacity - size();
	}

	/**
	 * @see stream.io.Source#init()
	 */
	@Override
	public void init() throws Exception {
		if (getCapacity() < 1)
			throw new IllegalArgumentException("Invalid queue-capacity '"
					+ getCapacity() + "'!");
		if (index == null || index.isEmpty())
			throw new IllegalArgumentException("Index is not specified");
		if (streams == null || streams.size() == 0)
			throw new IllegalArgumentException("Index is not specified");
		if (sync == null || sync.isEmpty())
			throw new IllegalArgumentException("Index is not specified");
		for (String unit : streams) {
			queues.put(unit, new ArrayBlockingQueue(capacity));
		}

		reads = streams.size();
		readQueue = new String[reads];
		dataQueue = new Data[reads];
		accs = new long[reads];
		int j = 0;
		for (String unit : streams) {
			readQueue[j] = unit;
			j++;
		}
	}

	/**
	 * @see stream.io.Stream#close()
	 */
	public void close() throws Exception {
		log.debug("Closing queue '{}'...", getId());
		if (closed.get()) {
			log.debug("Queue '{}' already closed.", getId());
			return;
		}
		// log.debug("queue: {}", queue);
		closed.getAndSet(true);

	}

	/**
	 * @see stream.io.AbstractStream#readItem(stream.Data)
	 */
	@Override
	public Data read() throws Exception {
		log.trace("Reading from queue {}", getId());

		final ReentrantLock takeLock = this.takeLock;
		takeLock.lockInterruptibly();

		Data result = null;
		try {
			if (closed.get() && count == 0) {
				log.debug("Queue '{}' is closed and empty => null", getId());
				return null;
			}
			if (count == 0) {
				read = 0;
				// Read accs
				for (int i = 0; i < reads; i++) {
					dataQueue[i] = queues.get(readQueue[i]).take();
					Serializable s = dataQueue[i].get(index);
					if (s != null && s instanceof Long) {
						accs[i] = (Long) s;
						// log.info("read from {} ts {}", readQueue[i], (Long)
						// s);
					}
				}
				// Sort accs and readqueues
				GenericSorting.quickSort(0, streams.size(), comp, swapper);

				// ?
				count = 0;
				for (int i = 1; i < streams.size(); i++) {
					if (accs[i - 1] == accs[i])
						count = i;
					else
						break;
				}

				++count;
				reads = count;

			}
			--count;
			result = dataQueue[read];

			++read;
		} finally {
			takeLock.unlock();
		}
		return result;
	}

	/**
	 * @see stream.io.Sink#write(stream.Data)
	 */
	@Override
	public boolean write(Data item) throws Exception {
		return insert(item);
	}

	public boolean insert(Data data) {

		if (data == null)
			return false;

		if (closed.get())
			return false;

		// unit
		final Serializable s2 = data.get(sync);
		String unit = null;
		if (s2 != null)
			unit = s2.toString();
		// if (streams.contains(unit)) {
		try {
			// Serializable s = data.get(index);
			// if (s != null && s instanceof Long)
			// log.info("data from {}: {}", unit, (Long) s);
			ArrayBlockingQueue queue = queues.get(unit);
			if (queue != null) {
				queue.put(data);
				return true;
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// }
		return false;
	}

	public boolean insert(Collection data) {

		if (data == null)
			return false;

		if (closed.get())
			return false;

		boolean first = true;
		ArrayBlockingQueue queue = null;
		for (Data d : data) {
			if (first) {
				final Serializable s2 = d.get(sync);
				String unit = null;
				if (s2 == null)
					return false;
				unit = s2.toString();
				queue = queues.get(unit);
				if (queue == null)
					return false;
				first = false;
			}
			try {
				queue.put(d);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return true;
	}

	@Override
	public boolean write(Collection data) throws Exception {
		return insert(data);
	}

	/**
	 * @see stream.io.Barrel#clear()
	 */
	@Override
	public int clear() {
		for (ArrayBlockingQueue queue : queues.values()) {
			queue.clear();
		}
		return -1;
	}

	/**
	 * @see stream.io.QueueService#level()
	 */
	@Override
	public int level() {
		return size();
	}

	/**
	 * @see stream.io.QueueService#capacity()
	 */
	@Override
	public int capacity() {
		return capacity;
	}

	@Override
	public Integer getSize() {
		return size();
	}

	/**
	 * @see stream.service.Service#reset()
	 */
	@Override
	public void reset() throws Exception {
	}

	public String toString() {
		return "stream.io.Join['" + id + "']";
	}

	@Override
	public Data poll() {
		return null;
	}

	@Override
	public Data take() {
		return null;
	}

	@Override
	public boolean enqueue(Data item) {
		return false;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy