package com.onaro.sanscreen.client.view.changes; import java.util.Date; import java.util.EnumSet; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ChangesChunk { public static final EnumSet DONE_STATES = EnumSet.of(LoadState.COMPLETE, LoadState.ERROR, LoadState.CANCELLED); private long from; private long to; private Changes changes; private LoadState state = LoadState.PENDING; private String errorDescription; private Throwable cause; private final ReentrantLock lock; private final Condition lockCondition; private int numberOfChanges; public ChangesChunk(long from,long to, int numberOfChanges) { this.numberOfChanges = numberOfChanges; assert to >= from : "from time '"+from+"' is bigger than the to time '"+to+"'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ lock = new ReentrantLock(); lockCondition = lock.newCondition(); this.from = from; this.to = to; } public ChangesChunk(long from,long to, Changes changes) { this(from, to, changes.size()); setChanges(changes); } public int getNumberOfChanges() { return numberOfChanges; } public void setChanges(Changes changes) { lock.lock(); try { this.changes = changes; state = LoadState.COMPLETE; lockCondition.signal(); } finally { lock.unlock(); } } public void cancell(){ lock.lock(); try { changes = null; state = LoadState.CANCELLED; lockCondition.signal(); } finally { lock.unlock(); } } public void error(String desc, Throwable cause){ lock.lock(); try { this.changes = null; this.state = LoadState.ERROR; this.errorDescription = desc; this.cause = cause; lockCondition.signal(); } finally { lock.unlock(); } } public String getErrorDescription() { return errorDescription; } public Throwable getCause() { return cause; } public long getTo() { return to; } public long getFrom() { return from; } public Changes getChanges() { return changes; } public LoadState getState() { lock.lock(); try { return state; } finally { lock.unlock(); } } /** * Blocks until a changes chunk is done loading - whether by completing loading, failing or being cancelled. * * @return the chunk if is done * @throws InterruptedException if the thread is interrupted * @throws TimeoutException if the chunk is not done by {@link com.onaro.sanscreen.client.view.changes.ChangesConfig#getChunkLoadWaitTimeoutMillis()} millis * @param timeoutInMillis */ public ChangesChunk getWhenDone(long timeoutInMillis) throws InterruptedException,TimeoutException { lock.lockInterruptibly(); try { long nanos = TimeUnit.MILLISECONDS.toNanos(timeoutInMillis); while (nanos > 0) { if (isDone()) { //return the chunk if it's ready return this; } else { //else wait to be notified nanos = lockCondition.awaitNanos(nanos); } } } finally { lock.unlock(); } throw new TimeoutException("Failed loading a chunk of the changes between '"+new Date(from)+"' and '"+ new Date(to)+"'. timeout - " + timeoutInMillis + " milliseconds"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } private boolean isDone() { return DONE_STATES.contains(getState()); } public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("ChangesChunk"); //$NON-NLS-1$ sb.append("{changes=").append(changes); //$NON-NLS-1$ sb.append(",start=").append(from); //$NON-NLS-1$ sb.append(",end=").append(to); //$NON-NLS-1$ sb.append(",state=").append(state); //$NON-NLS-1$ sb.append('}'); return sb.toString(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final ChangesChunk that = (ChangesChunk) o; if (to != that.to) return false; if (from != that.from) return false; if (changes != null ? !changes.equals(that.changes) : that.changes != null) return false; if (state != that.state) return false; return true; } public int hashCode() { int result; result = (int) (from ^ (from >>> 32)); result = 29 * result + (int) (to ^ (to >>> 32)); result = 29 * result + (changes != null ? changes.hashCode() : 0); result = 29 * result + (state != null ? state.hashCode() : 0); return result; } }