package org.swellrt.beta.client; import java.util.Collections; import java.util.concurrent.ExecutionException; import org.swellrt.beta.client.js.Console; import org.swellrt.beta.client.wave.RemoteViewServiceMultiplexer; import org.swellrt.beta.client.wave.WaveLoader; import org.swellrt.beta.common.SException; import org.swellrt.beta.model.SStatusEvent; import org.swellrt.beta.model.remote.SObjectRemote; import org.waveprotocol.wave.concurrencycontrol.common.ChannelException; import org.waveprotocol.wave.concurrencycontrol.common.ResponseCode; import org.waveprotocol.wave.concurrencycontrol.common.TurbulenceListener; import org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListener; import org.waveprotocol.wave.model.id.IdGenerator; import org.waveprotocol.wave.model.id.ModernIdSerialiser; import org.waveprotocol.wave.model.id.WaveId; import org.waveprotocol.wave.model.util.Preconditions; import org.waveprotocol.wave.model.wave.ParticipantId; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import com.google.gwt.user.client.Command; /** * A class wrapping Wave components to be managed by the {@ServiceContext}. * Handles Wave life cycle and captures Channel Exceptions. * * @author pablojan@gmail.com (Pablo Ojanguren) * */ public class WaveContext implements UnsavedDataListener, TurbulenceListener, WaveStatus { private static final int INACTIVE = 0; private static final int ACTIVE = 1; private static final int ERROR = 2; private int state = INACTIVE; private final WaveId waveId; private final String waveDomain; private final ParticipantId participant; private final ServiceStatus serviceStatus; private WaveLoader loader; private SettableFuture<SObjectRemote> sobjectFuture; private ChannelException lastException; public WaveContext(WaveId waveId, String waveDomain, ParticipantId participant, ServiceStatus serviceStatus) { super(); this.waveId = waveId; this.waveDomain = waveDomain; this.participant = participant; this.serviceStatus = serviceStatus; this.sobjectFuture = SettableFuture.<SObjectRemote> create(); } public void init(RemoteViewServiceMultiplexer viewServiceMultiplexer, IdGenerator idGenerator) { Preconditions.checkArgument(viewServiceMultiplexer != null, "Can't init Wave context with a null Remote Service Multiplexer"); Preconditions.checkArgument(idGenerator != null, "Can't init Wave context with a null Id Generator"); // Clean up listener on the channel multiplexer if (loader != null) loader.destroy(); // Create a future for the object if (this.sobjectFuture == null || this.sobjectFuture.isDone()) this.sobjectFuture = SettableFuture.<SObjectRemote> create(); // Load the wave and bind to the object state = ACTIVE; loader = new WaveLoader(waveId, viewServiceMultiplexer, idGenerator, waveDomain, Collections.<ParticipantId> emptySet(), participant, this, this); try { loader.load(new Command() { @Override public void execute() { try { // there was exception during loading process? check(); SObjectRemote sobject = SObjectRemote.inflateFromWave(participant, loader.getIdGenerator(), loader.getLocalDomain(), loader.getWave(), PlatformBasedFactory.getFactory(loader), WaveContext.this); sobjectFuture.set(sobject); } catch (SException ex) { sobjectFuture.setException(ex); } } }); } catch (RuntimeException ex) { sobjectFuture.setException(ex.getCause()); } } public void getSObject(FutureCallback<SObjectRemote> callback) { Futures.addCallback(this.sobjectFuture, callback); } public void close() { if (loader != null) loader.destroy(); } @Override public void onFailure(ChannelException e) { this.lastException = e; this.state = ERROR; Console.log("ChannelException: " + e.toString()); // If an exception occurs during stage loader (WaveLoader) // it will reach here. Check the future so. if (!this.sobjectFuture.isDone()) { this.sobjectFuture.setException(new SException(e)); } else { try { this.sobjectFuture.get().onStatusEvent(new SStatusEvent( ModernIdSerialiser.INSTANCE.serialiseWaveId(waveId), new SException(e))); } catch (InterruptedException | ExecutionException e1) { // } } serviceStatus.raise(ModernIdSerialiser.INSTANCE.serialiseWaveId(waveId), new SException(e)); close(); } @Override public void onUpdate(UnsavedDataInfo unsavedDataInfo) { if (this.sobjectFuture.isDone()) { try { this.sobjectFuture.get() .onStatusEvent(new SStatusEvent(ModernIdSerialiser.INSTANCE.serialiseWaveId(waveId), unsavedDataInfo.inFlightSize(), unsavedDataInfo.estimateUnacknowledgedSize(), unsavedDataInfo.estimateUncommittedSize(), unsavedDataInfo.laskAckVersion(), unsavedDataInfo.lastCommitVersion())); } catch (InterruptedException | ExecutionException e1) { throw new RuntimeException(e1); } } } @Override public void onClose(boolean everythingCommitted) { if (this.sobjectFuture.isDone()) { try { this.sobjectFuture.get().onStatusEvent(new SStatusEvent( ModernIdSerialiser.INSTANCE.serialiseWaveId(waveId), everythingCommitted)); } catch (InterruptedException | ExecutionException e1) { throw new RuntimeException(e1); } } } public boolean isError() { return this.state == ERROR; } public boolean isActive() { return this.state == ACTIVE; } @Override public void check() throws SException { serviceStatus.check(); if (this.state == INACTIVE || this.state == ERROR) { if (lastException != null) throw new SException(lastException); else throw new SException(ResponseCode.UNKNOWN); } } }