/***************************************************************************
* Copyright (C) 2012 by H-Store Project *
* Brown University *
* Massachusetts Institute of Technology *
* Yale University *
* *
* http://hstore.cs.brown.edu/ *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
package edu.brown.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import org.apache.log4j.Logger;
import org.voltdb.utils.Pair;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* @author pavlo
* @param <T>
* The input type from the given Iterable
* @param <U>
* The type of object that will generated by transform and queued at
* the Consumers
*/
public abstract class Producer<T, U> implements Runnable {
private static final Logger LOG = Logger.getLogger(Producer.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
private final Collection<Consumer<U>> consumers = new HashSet<Consumer<U>>();
private Iterable<T> it;
public Producer(Iterable<T> it) {
this.it = it;
}
public Producer(Iterable<T> it, Collection<Consumer<U>> consumers) {
this(it);
this.consumers.addAll(consumers);
}
public final void addConsumer(Consumer<U> consumer) {
this.consumers.add(consumer);
}
@Override
public final void run() {
for (Consumer<U> c : this.consumers) {
c.start();
} // FOR
int ctr = 0;
for (T t : this.it) {
Pair<Consumer<U>, U> p = this.transform(t);
assert (p != null);
assert (p.getFirst() != null) : "Null Consumer - " + p;
assert (p.getSecond() != null) : "Null Object - " + p;
p.getFirst().queue(p.getSecond());
if (debug.val && ++ctr % 100 == 0)
LOG.debug(String.format("Queued %d %s objects", ctr, t.getClass().getSimpleName()));
} // FOR
// Poke all our threads to let them know that they should stop when
// their
// queue is empty
for (Consumer<U> c : this.consumers)
c.stopWhenEmpty();
}
public abstract Pair<Consumer<U>, U> transform(T t);
/**
* Default transform implementation. Returns a pair that contains a random
* Consumer and the input parameter t case to type U
*
* @param t
* @return
*/
@SuppressWarnings("unchecked")
public final Pair<Consumer<U>, U> defaultTransform(T t) {
return Pair.of(CollectionUtil.random(this.consumers), (U) t);
}
/**
* Get the total number of items processed by all of the Consumers attached
* to this Producer
*
* @return
*/
public final int getTotalProcessed() {
int total = 0;
for (Consumer<?> c : this.consumers) {
total += c.getProcessedCounter();
} // FOR
return (total);
}
public final Collection<Consumer<U>> getConsumers() {
return (Collections.unmodifiableCollection(this.consumers));
}
public final List<Runnable> getRunnablesList() {
ArrayList<Runnable> runnables = new ArrayList<Runnable>();
runnables.add(this);
for (Consumer<U> c : this.consumers) {
runnables.add(c);
} // FOR
return (runnables);
}
/**
* Return a new instance of a Producer. This producer will simply assign
* each item in the iterable to a random Consumer
*
* @param <T>
* @param <U>
* @param it
* @return
*/
public static final <T, U> Producer<T, U> defaultProducer(Iterable<T> it) {
return new Producer<T, U>(it) {
public Pair<edu.brown.utils.Consumer<U>, U> transform(T t) {
return this.defaultTransform(t);
};
};
}
}