Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
bear.main.phaser.ComputingGrid Maven / Gradle / Ivy
package bear.main.phaser;
import chaschev.lang.Functions2;
import chaschev.lang.Lists2;
import chaschev.lang.OpenBean;
import chaschev.util.CatchyRunnable;
import chaschev.util.Exceptions;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ArrayTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import static com.google.common.base.Optional.of;
/**
* Elegant sweep strategy: add listener for
*
* Table can be improved to represent only a computation frame:
* - present a default sweep strategy, add exclusions
* - provide a phase iterator
*
* @author Andrey Chaschev [email protected]
*/
public class ComputingGrid {
private final int partiesCount;
//todo this should be extracted into a builder
// private transient int builderPhaseCount;
// List> phases = new ArrayList<>();
final List> parties;
final List extends Phase, PHASE>> phases ;
final ArrayTable> table;
private final Map columnKeyToIndex;
private final Map rowKeyToIndex;
public ComputingGrid(List extends Phase, PHASE>> phases, Iterable extends C> columnKeys) {
this.phases = phases;
table = ArrayTable.create(
Iterables.transform(phases, Functions2.field("phase")),
columnKeys);
partiesCount = table.columnKeyList().size();
columnKeyToIndex = (Map) OpenBean.getFieldValue(table, "columnKeyToIndex");
rowKeyToIndex = (Map) OpenBean.getFieldValue(table, "rowKeyToIndex");
this.parties = new ArrayList>(partiesCount);
int i = 0;
for (C columnKey : columnKeys) {
this.parties.add(new PhaseParty(i, columnKey, this));
i++;
}
phaseEntered = new boolean[phases.size()];
// can't load these lazily as futures are accessed from outside
// will probably need to split out futures for lazy init or get the futures...
for (Phase, PHASE> phase : phases) {
addPhase(phase);
}
}
public List> phaseFutures(Phase phase, int relative) {
return phaseFutures(phaseToRowIndex(phase.phase) + relative, null);
}
public List> phaseFutures(Phase phase) {
return phaseFutures(phase, 0);
}
public List> phaseFutures(final int rowIndex, Class vClass) {
return Lists2.computingList(partiesCount, new Function>() {
public ListenableFuture apply(Integer colIndex) {
return (ListenableFuture) table.at(rowIndex, colIndex).getFuture();
}
});
}
public Integer phaseToRowIndex(PHASE phase) {
return rowKeyToIndex.get(phase);
}
public Integer partyToColumnIndex(C col) {
return columnKeyToIndex.get(col);
}
public ListenableFuture> aggregateSuccessful(Phase phase) {
return Futures.successfulAsList(phaseFutures(phase));
}
public ListenableFuture> aggregateSuccessful(int phase, Class vClass) {
return Futures.successfulAsList(phaseFutures(phase, vClass));
}
ComputingGrid awaitTermination(){
try {
aggregateSuccessful(phaseToRowIndex(lastPhase()), Object.class).get();
return this;
} catch (Exception e) {
throw Exceptions.runtime(e);
}
}
private PHASE lastPhase() {
ImmutableList row = table.rowKeyList();
return row.get(row.size() - 1);
}
public GridCell cell(int rowKey, C columnKey) {
return table.get(rowKey, columnKey);
}
public GridCell cell(Phase phase, C columnKey) {
return (GridCell) table.at(phaseToRowIndex(phase.phase), partyToColumnIndex(columnKey));
}
public GridCell cellAt(int rowIndex, int columnIndex) {
return table.at(rowIndex, columnIndex);
}
public GridCell cellAt(int rowIndex, int columnIndex, Class vClass) {
return (GridCell) table.at(rowIndex, columnIndex);
}
private final boolean[] phaseEntered;
@Nonnull
public Optional> phase(String name) {
for (Phase, PHASE> phase : phases) {
if(name.equals(phase.getName())) return (Optional) of(phase);
}
return Optional.absent();
}
public SettableFuture future(String phaseName, String partyName, Class vClass) {
return cell(phaseName, partyName, vClass).getFuture();
}
public GridCell cell(String phaseName, String partyName, Class vClass) {
Optional> phase = phase(phaseName);
if(!phase.isPresent()) throw new IllegalArgumentException("didn't find phase: " + phaseName);
Optional> party = party(partyName);
if(!party.isPresent()) {
throw new IllegalArgumentException("didn't find party: " + partyName);
}
return cellAt(phase.get().rowIndex, party.get().index);
}
@Nonnull
private Optional> party(String partyName) {
for (PhaseParty party : parties) {
if(partyName.equals(party.getName())){
return of(party);
}
}
return Optional.absent();
}
public static interface PartyListener{
void handle(Phase, PHASE> phase, PhaseParty party);
}
public static interface WhenAllFinished{
void run(int failedParties, int okParties);
}
protected PartyListener phaseEnterListener;
protected PartyListener partyFinishListener;
protected WhenAllFinished whenAllFinished;
private void checkPhaseEntered(Phase, PHASE> phase, PhaseParty party){
if(phaseEnterListener == null || phaseEntered[phase.rowIndex]){
return;
}
synchronized (phaseEntered){
if(phaseEntered[phase.rowIndex]) return;
phaseEntered[phase.rowIndex] = true;
}
phaseEnterListener.handle(phase, party);
}
public ComputingGrid startParties(ExecutorService service) {
final AtomicInteger partiesArrived = new AtomicInteger();
final AtomicInteger partiesFailed = new AtomicInteger();
for (int i = 0; i < partiesCount; i++) {
final int partyIndex = i;
final PhaseParty party = parties.get(i);
service.submit(new CatchyRunnable(new Runnable() {
@Override
public void run() {
Phase, PHASE> lastPhase = null;
try {
for (Phase, PHASE> phase : phases) {
lastPhase = phase;
GridCell cell = table.at(party.currentPhaseIndex, partyIndex)
.started();
checkPhaseEntered(phase, party);
Object result;
try {
result = cell.callable.call(party, party.currentPhaseIndex, phase);
party.lastResult = result;
cell.getFuture().set(result);
if (cell.whenDone != null) {
cell.whenDone.act(result, party);
}
} catch (Exception e) {
party.setException(
e instanceof GridException ? (GridException) e :
new GridException(e, phase, party)
);
partiesFailed.incrementAndGet();
LoggerFactory.getLogger("log").error(e.toString(), e);
break;
} finally {
cell.finishedAtMs = System.currentTimeMillis();
party.currentPhaseIndex++;
}
}
} finally {
partiesArrived.incrementAndGet();
if(whenAllFinished != null && partiesArrived.get() == partiesCount){
whenAllFinished.run(partiesFailed.get(), partiesCount);
}
if(partyFinishListener != null){
partyFinishListener.handle(lastPhase, party);
}
}
}
}));
}
return this;
}
private synchronized void addPhase(Phase phase) {
Integer rowIndex = phaseToRowIndex(phase.getPhase());
if(rowIndex != null && table.at(rowIndex, 0) != null){
return;
}
for (C columnKey : table.columnKeyList()) {
table.put(phase.phase, columnKey, new GridCell());
}
ImmutableList list = table.columnKeyList();
List extends PhaseCallable> phaseCallables = (List)phase.getParties(this);
phase.rowIndex = rowIndex;
for (int i = 0; i < list.size(); i++) {
table.at(rowIndex, i).callable = (PhaseCallable) phaseCallables.get(i);
}
}
public SettableFuture previousResult(PhaseParty party, int phaseIndex, Class aClass) {
return (SettableFuture) table.at(phaseIndex - 1, partyToColumnIndex(party.column)).getFuture();
}
public ComputingGrid setPhaseEnterListener(PartyListener partyListener) {
this.phaseEnterListener = partyListener;
return this;
}
public ComputingGrid setPartyFinishListener(PartyListener partyFinishListener) {
this.partyFinishListener = partyFinishListener;
return this;
}
public void setWhenAllFinished(WhenAllFinished whenAllFinished) {
this.whenAllFinished = whenAllFinished;
}
public ImmutableList phases() {
return table.rowKeyList();
}
public ImmutableList parties() {
return table.columnKeyList();
}
}