package com.ibm.jactors.test;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.ibm.jactors.Actor;
import com.ibm.jactors.ActorManager;
import com.ibm.jactors.DefaultMessage;
import com.ibm.jactors.Message;
/**
* An actor that can do calculations via a suppled MapReducer.
*
* @author BFEIGENB
*
*/
public class MapReduceActor extends TestableActor {
protected int partitionSize = 10;
public int getPartitionSize() {
return partitionSize;
}
public void setPartitionSize(int partitionSize) {
this.partitionSize = partitionSize;
}
public static final int MAX_ACTOR_COUNT = 25;
// carries state as a message parameter.
protected static class MapReduceParameters implements Comparable<MapReduceParameters> {
protected static int idCount;
protected int id = idCount++;;
public Object[] values;
public Integer start;
public Integer end;
public Object[] target;
public Integer posn;
// depth && targetDepth currently unused
// public Integer depth;
// public Integer targetDepth;
public MapReduceer mr;
public Actor sender;
protected String set;
public static Map<String, Map<Integer, MapReduceParameters>> pendingResponses = new ConcurrentHashMap<String, Map<Integer, MapReduceParameters>>();
public MapReduceParameters(String set, Object[] values, Object[] target, MapReduceer mr, Actor sender) {
this(set, values, 0, values.length - 1, target, 0, 0, 5, mr, sender);
}
public MapReduceParameters(String set, Object[] values, Object[] target, Integer depth, Integer targetDepth,
MapReduceer mr, Actor sender) {
this(set, values, 0, values.length - 1, target, 0, depth, targetDepth, mr, sender);
}
public MapReduceParameters(String set, Object[] values, Integer start, Integer end, Object[] target, int posn,
Integer depth, Integer targetDepth, MapReduceer mr, Actor sender) {
this.set = set;
this.values = values;
this.start = start;
this.end = end;
this.target = target;
this.posn = posn;
// this.depth = depth;
// this.targetDepth = targetDepth;
this.mr = mr;
this.sender = sender;
// rememberReference();
}
public MapReduceParameters(MapReduceParameters p) {
this.set = p.set;
this.values = p.values;
this.start = p.start;
this.end = p.end;
this.target = p.target;
this.posn = p.posn;
// this.depth = p.depth;
// this.targetDepth = p.targetDepth;
this.mr = p.mr;
this.sender = p.sender;
rememberReference();
}
private void rememberReference() {
Map<Integer, MapReduceParameters> m = pendingResponses.get(set);
if (m == null) {
m = new ConcurrentHashMap<Integer, MapReduceParameters>();
pendingResponses.put(set, m);
}
m.put(id, this);
}
public void complete() {
Map<Integer, MapReduceParameters> m = pendingResponses.get(set);
if (m != null) {
m.remove(id);
if (m.isEmpty()) {
pendingResponses.remove(set);
}
}
}
public boolean isSetComplete() {
Map<Integer, MapReduceParameters> m = pendingResponses.get(set);
//logger.info("isSetComplete: %d", m != null ? m.size() : 0);
return m == null || m.isEmpty();
}
@Override
public int compareTo(MapReduceParameters o) {
return this.id - o.id;
}
@Override
public String toString() {
return getClass().getName() + "[id=" + id + ", values.length=" + (values != null ? values.length : 0)
+ ", start=" + start + ", end=" + end + ", target.length=" + (target != null ? target.length : 0)
+ ", posn=" + posn + ", sender=" + sender + ", set=" + set + "]";
}
}
@Override
protected void loopBody(Message m) {
ActorManager manager = getManager();
String subject = m.getSubject();
if ("mapReduce".equals(subject)) {
try {
MapReduceParameters p = (MapReduceParameters) m.getData();
int index = 0;
int count = (p.end - p.start + 1 + partitionSize - 1) / partitionSize;
logger.info("mapReduce i=%d, c=%d, s=%d: %s", index, count, partitionSize, p);
sleep(1000);
// split up into partition size chunks
while (p.end - p.start + 1 >= partitionSize) {
MapReduceParameters xp = new MapReduceParameters(p);
xp.end = xp.start + partitionSize - 1;
DefaultMessage lm = new DefaultMessage("createPartition", new Object[] { xp, index, count });
manager.send(lm, this, getCategory());
p.start += partitionSize;
index++;
}
if (p.end - p.start + 1 > 0) {
DefaultMessage lm = new DefaultMessage("createPartition", new Object[] { p, index, count });
manager.send(lm, this, getCategory());
}
} catch (Exception e) {
triageException("mapFailed", m, e);
}
} else if ("createPartition".equals(subject)) {
try {
Object[] oa = (Object[]) m.getData();
MapReduceParameters p = (MapReduceParameters) oa[0];
int index = (Integer) oa[1];
int count = (Integer) oa[2];
logger.info("createPartition i=%d, c=%d: %s", index, count, p);
sleep(500);
createMapReduceActor(this);
DefaultMessage lm = new DefaultMessage("mapWorker", new Object[] { p, index, count });
manager.send(lm, this, getCategory());
} catch (Exception e) {
triageException("createPartitionFailed", m, e);
}
} else if ("mapWorker".equals(subject)) {
try {
Object[] oa = (Object[]) m.getData();
MapReduceParameters p = (MapReduceParameters) oa[0];
int index = (Integer) oa[1];
int count = (Integer) oa[2];
logger.info("mapWorker %d: %s", index, p);
sleep(100);
p.mr.map(p.values, p.start, p.end);
DefaultMessage rm = new DefaultMessage("mapResponse", new Object[] { p, index, count });
manager.send(rm, this, getCategoryName());
} catch (Exception e) {
triageException("mapWorkerFailed", m, e);
}
} else if ("mapResponse".equals(subject)) {
try {
Object[] oa = (Object[]) m.getData();
MapReduceParameters p = (MapReduceParameters) oa[0];
int index = (Integer) oa[1];
int count = (Integer) oa[2];
logger.info("mapResponse i=%d, c=%s: %s", index, count, p);
sleep(100);
p.complete();
// if (p.isSetComplete()) {
DefaultMessage rm = new DefaultMessage("reduce", new Object[] { p, index, count });
manager.send(rm, this, getCategoryName());
// }
} catch (Exception e) {
triageException("mapResponseFailed", m, e);
}
} else if ("reduce".equals(subject)) {
try {
MapReduceParameters p = null;
int index = 0, count = 0;
Object o = m.getData();
if (o instanceof MapReduceParameters) {
p = (MapReduceParameters) o;
} else {
Object[] oa = (Object[]) o;
p = (MapReduceParameters) oa[0];
index = (Integer) oa[1];
count = (Integer) oa[2];
}
logger.info("reduce i=%d: %s", index, p);
sleep(100);
if (p.end - p.start + 1 > 0) {
createMapReduceActor(this);
MapReduceParameters xp = new MapReduceParameters(p);
DefaultMessage lm = new DefaultMessage("reduceWorker", new Object[] { xp, index, count });
manager.send(lm, this, getCategory());
}
} catch (Exception e) {
triageException("reduceFailed", m, e);
}
} else if ("reduceWorker".equals(subject)) {
try {
Object[] oa = (Object[]) m.getData();
MapReduceParameters p = (MapReduceParameters) oa[0];
int index = (Integer) oa[1];
int count = (Integer) oa[2];
logger.info("reduceWorker i=%d, c=%d: %s", index, count, p);
sleep(100);
if (index >= 0) {
p.mr.reduce(p.values, p.start, p.end, p.target, index);
DefaultMessage rm = new DefaultMessage("reduceResponse", new Object[] { p, index, count });
manager.send(rm, this, getCategory());
} else {
Object[] res = new Object[1];
p.mr.reduce(p.target, 0, count - 1, res, 0);
DefaultMessage rm = new DefaultMessage("done", new Object[] { p, res[0] });
manager.send(rm, this, getCategory());
}
} catch (Exception e) {
triageException("reduceWorkerFailed", m, e);
}
} else if ("reduceResponse".equals(subject)) {
try {
Object[] oa = (Object[]) m.getData();
MapReduceParameters p = (MapReduceParameters) oa[0];
int index = (Integer) oa[1];
int count = (Integer) oa[2];
logger.info("reduceResponse i=%d, c=%d: %s", index, count, p);
sleep(100);
p.complete();
if (p.isSetComplete()) {
if (count > 0) {
createMapReduceActor(this);
MapReduceParameters xp = new MapReduceParameters(p);
DefaultMessage lm = new DefaultMessage("reduceWorker", new Object[] { xp, -1, count });
manager.send(lm, this, getCategory());
}
}
} catch (Exception e) {
triageException("mapResponseFailed", m, e);
}
} else if ("done".equals(subject)) {
try {
Object[] oa = (Object[]) m.getData();
MapReduceParameters p = (MapReduceParameters) oa[0];
Object res = oa[1];
logger.info("done %s: %s", res, p);
sleep(100);
logger.trace("**** mapReduce done with result %s", res);
} catch (Exception e) {
triageException("mapResponseFailed", m, e);
}
} else if ("init".equals(subject)) {
try {
Object[] params = (Object[]) m.getData();
if (params != null) {
Object[] values = (Object[]) params[0];
Object[] targets = (Object[]) params[1];
Class clazz = (Class) params[2];
logger.info("init %d, %d, %s ", values.length, targets.length, clazz);
MapReduceer mr = (MapReduceer) clazz.newInstance();
sleep(2 * 1000);
MapReduceParameters p = new MapReduceParameters("mrSet_" + setCount++, values, targets, mr, this);
DefaultMessage rm = new DefaultMessage("mapReduce", p);
manager.send(rm, this, getCategoryName());
}
} catch (Exception e) {
triageException("initFailed", m, e);
}
} else {
logger.warning("**** MapReduceActor:%s loopBody unexpected subject: %s", getName(), subject);
}
}
protected void triageException(String subject, Message m, Exception e) {
logger.error("triageException %s: %s", subject, m, e);
Actor target = m.getSource();
DefaultMessage dm = new DefaultMessage(subject, new Object[] { m.getData(), e });
if (m.getData() instanceof MapReduceParameters) {
MapReduceParameters p = (MapReduceParameters) m.getData();
if (p != null && p.sender != null) {
target = p.sender;
}
}
getManager().send(dm, this, target);
}
public static MapReduceActor createMapReduceActor(MapReduceActor mra) {
ActorManager manager = mra.getManager();
return (MapReduceActor) (manager.getActorCount(MapReduceActor.class) < MAX_ACTOR_COUNT ? createMapReduceActor(
manager, mra.getPartitionSize()) : mra);
}
public static MapReduceActor createMapReduceActor(ActorManager manager, int partitionSize) {
MapReduceActor res = (MapReduceActor) manager.createAndStartActor(MapReduceActor.class, getActorName());
res.setCategory(getCategoryName());
res.setPartitionSize(partitionSize);
return res;
}
public static String getCategoryName() {
return "mapReduceActor";
}
protected static int setCount;
protected static int idCount;
public static String nextId() {
return "mapReduce_" + idCount++;
}
protected static int actorCount;
protected static String getActorName() {
return MapReduceActor.class.getSimpleName() + '_' + actorCount++;
}
}