/** * Deserializes the snapshot contained in the {@link WaveletSnapshot} into an {@link * ObservableWaveletData}. * * @param snapshot the {@link WaveletSnapshot} to deserialize. * @throws OperationException if the ops in the snapshot can not be applied. * @throws InvalidParticipantAddress * @throws InvalidIdException */ public static ObservableWaveletData deserializeWavelet(WaveletSnapshot snapshot, WaveId waveId) throws OperationException, InvalidParticipantAddress, InvalidIdException { ObservableWaveletData.Factory<? extends ObservableWaveletData> factory = WaveletDataImpl.Factory.create( ObservablePluggableMutableDocument.createFactory(SchemaCollection.empty())); ParticipantId author = ParticipantId.of(snapshot.getCreator()); WaveletId waveletId = ModernIdSerialiser.INSTANCE.deserialiseWaveletId(snapshot.getWaveletId()); long creationTime = snapshot.getCreationTime(); ObservableWaveletData wavelet = factory.create( new EmptyWaveletSnapshot( waveId, waveletId, author, CoreWaveletOperationSerializer.deserialize(snapshot.getVersion()), creationTime)); for (String participant : snapshot.getParticipantIdList()) { wavelet.addParticipant(getParticipantId(participant)); } for (DocumentSnapshot document : snapshot.getDocumentList()) { addDocumentSnapshotToWavelet(document, wavelet); } wavelet.setVersion(snapshot.getVersion().getVersion()); wavelet.setLastModifiedTime(snapshot.getLastModifiedTime()); // The creator and creation time are set when the empty wavelet template is // created above. return wavelet; }
/** * Reverses the operations detailed in the list of deltas on the given wavelet. * * @param wavelet {@link ObservableWaveletData} to apply operations to * @param deltas the {@link WaveletDelta} containing the operations which we should revert on the * given wavelet. * @throws OperationException if the operations can not be rolled back. */ private static void rollback(ObservableWaveletData wavelet, List<TransformedWaveletDelta> deltas) throws OperationException { List<WaveletOperation> inverseOps = Lists.newArrayList(); // Go through everything in reverse order for (int i = deltas.size() - 1; i >= 0; i--) { TransformedWaveletDelta delta = deltas.get(i); // Metadata such as the last modified ts will change due to the rollback // of operations. for (int j = delta.size() - 1; j >= 0; j--) { WaveletOperation op = delta.get(j); WaveletOperation inverseOp = WaveletOperationInverter.invert(op); inverseOps.add(inverseOp); } } long startVersion = wavelet.getVersion(); int opCount = 0; for (WaveletOperation inverseOp : inverseOps) { inverseOp.apply(wavelet); opCount++; } if (wavelet.getVersion() != startVersion - opCount) { throw new OperationException( "Expected end version " + (startVersion - opCount) + " doesn't match the version of the wavelet " + wavelet.getVersion()); } }
private ParticipantId computeCreator(WaveViewData wave) { for (ObservableWaveletData wavelet : wave.getWavelets()) { if (IdUtil.isConversationRootWaveletId(wavelet.getWaveletId())) { return wavelet.getCreator(); } } // If not found creator - compare with UNKNOWN_CREATOR; return UNKNOWN_CREATOR; }
private long computeCreatedTime(WaveViewData wave) { long creationTime = -1; for (ObservableWaveletData wavelet : wave.getWavelets()) { creationTime = creationTime < wavelet.getCreationTime() ? wavelet.getCreationTime() : creationTime; } return creationTime; }
private long computeLmt(WaveViewData wave) { long lmt = -1; for (ObservableWaveletData wavelet : wave.getWavelets()) { // Skip non conversational wavelets. if (!IdUtil.isConversationalId(wavelet.getWaveletId())) { continue; } lmt = lmt < wavelet.getLastModifiedTime() ? wavelet.getLastModifiedTime() : lmt; } return lmt; }
/** * Verifies whether the wavelet matches the filter criteria. * * @param wavelet the wavelet. * @param user the logged in user. * @param withList the list of participants to be used in 'with' filter. * @param creatorList the list of participants to be used in 'creator' filter. * @param isAllQuery true if the search results should include shared for this domain waves. */ private boolean matches( ObservableWaveletData wavelet, ParticipantId user, ParticipantId sharedDomainParticipantId, List<ParticipantId> withList, List<ParticipantId> creatorList, boolean isAllQuery) throws WaveletStateException { // If it is user data wavelet for the user - return true. if (IdUtil.isUserDataWavelet(wavelet.getWaveletId()) && wavelet.getCreator().equals(user)) { return true; } // Filter by creator. This is the fastest check so we perform it first. for (ParticipantId creator : creatorList) { if (!creator.equals(wavelet.getCreator())) { // Skip. return false; } } // The wavelet should have logged in user as participant for 'in:inbox' // query. if (!isAllQuery && !wavelet.getParticipants().contains(user)) { return false; } // Or if it is an 'all' query - then either logged in user or shared domain // participant should be present in the wave. if (isAllQuery && !WaveletDataUtil.checkAccessPermission(wavelet, user, sharedDomainParticipantId)) { return false; } // If not returned 'false' above - then logged in user is either // explicit or implicit participant and therefore has access permission. // Now filter by 'with'. for (ParticipantId otherUser : withList) { if (!wavelet.getParticipants().contains(otherUser)) { // Skip. return false; } } return true; }
@Override public void onOperationChannelCreated( OperationChannel channel, ObservableWaveletData snapshot, Accessibility accessibility) { WaveletId wid = snapshot.getWaveletId(); String id = ModernIdSerialiser.INSTANCE.serialiseWaveletId(wid); Preconditions.checkState(!channels.containsKey(id)); channels.put(id, channel); if (wave.getWavelet(wid) != null) { connect(id); } else { // This will trigger the onWaveletAdded callback above. wave.addWavelet(operationalizer.operationalize(snapshot)); } }
/** Opens a mux, binding its operation channels with operation-supporting wavelets. */ public static void openAndBind( WaveletOperationalizer operationalizer, WaveViewImpl<OpBasedWavelet> wave, WaveDocuments<? extends CcDocument> docRegistry, OperationChannelMultiplexer mux, IdFilter filter, Command whenOpened) { StaticChannelBinder staticBinder = new StaticChannelBinder(operationalizer, docRegistry); LiveChannelBinder liveBinder = new LiveChannelBinder(staticBinder, operationalizer, wave, mux, whenOpened); final Collection<KnownWavelet> remoteWavelets = CollectionUtils.createQueue(); final Collection<ObservableWaveletData> localWavelets = CollectionUtils.createQueue(); for (ObservableWaveletData wavelet : operationalizer.getWavelets()) { // Version 0 wavelets must be wavelets that the client has created in this // session. They are not to be included in the known-wavelet collection, // because the server does not know about them. if (wavelet.getVersion() > 0) { remoteWavelets.add( new KnownWavelet(wavelet, wavelet.getHashedVersion(), Accessibility.READ_WRITE)); } else { localWavelets.add(wavelet); } } // Start listening to wave events and channel events. wave.addListener(liveBinder); // This binder only starts getting events once open() has been called, since // that is what sets this binder as a mux listener. Since wavelet-to-channel // binding occurs through event callbacks, this listener setting must occur // before trying to bind localWavelets. mux.open(liveBinder, filter, remoteWavelets); for (ObservableWaveletData local : localWavelets) { mux.createOperationChannel(local.getWaveletId(), local.getCreator()); } }
/** Returns the {@link HashedVersion} of the wavelet after all deltas have been applied. */ public HashedVersion getVersionAfterDeltas() { return deltas.isEmpty() ? snapshotAfterDeltas.getHashedVersion() : deltas.getEndVersion(); }