default:
throw new AssertionError("Unexpected ImportSharingMode: "
+ task.getSettings().getSharingMode());
}
// Look up if already imported according to PerUserTable; if so, we have nothing to do.
boolean alreadyImportedForThisUser = new RetryHelper().run(
new RetryHelper.Body<Boolean>() {
@Override public Boolean run() throws RetryableFailure, PermanentFailure {
CheckedTransaction tx = datastore.beginTransaction();
try {
@Nullable RemoteConvWavelet entry =
perUserTable.getWavelet(tx, userId, instance, waveletName);
if (isPrivate) {
if (entry != null && entry.getPrivateLocalId() != null
&& !(task.hasExistingSlobIdToIgnore()
&& new SlobId(task.getExistingSlobIdToIgnore()).equals(
entry.getPrivateLocalId()))) {
log.info("Private import already exists, aborting: " + entry);
return true;
} else {
return false;
}
} else {
if (entry != null && entry.getSharedLocalId() != null
&& !(task.hasExistingSlobIdToIgnore()
&& new SlobId(task.getExistingSlobIdToIgnore()).equals(
entry.getSharedLocalId()))) {
log.info("Shared import already exists, aborting: " + entry);
return true;
} else {
return false;
}
}
} finally {
tx.close();
}
}
});
if (alreadyImportedForThisUser) {
throw TaskCompleted.noFollowup();
}
if (!isPrivate) {
@Nullable SlobId existingSharedImport =
sharedImportTable.lookupWithoutTx(instance, waveletName);
if (existingSharedImport != null
&& !(task.hasExistingSlobIdToIgnore()
&& new SlobId(task.getExistingSlobIdToIgnore()).equals(existingSharedImport))) {
log.info("Found existing shared import " + existingSharedImport + ", re-using");
ensureParticipant(existingSharedImport);
addToPerUserTableWithoutTx(existingSharedImport, isPrivate);
throw TaskCompleted.noFollowup();
}
}
Map<String, AttachmentId> attachmentMapping = getAttachmentsAndMapping(snapshot);
List<WaveletOperation> participantFixup = Lists.newArrayList();
if (isPrivate) {
for (String participant : ImmutableList.copyOf(wavelet.getParticipantList())) {
participantFixup.add(
HistorySynthesizer.newRemoveParticipant(importingUser.getAddress(),
wavelet.getLastModifiedTimeMillis(), participant));
}
participantFixup.add(
HistorySynthesizer.newAddParticipant(importingUser.getAddress(),
wavelet.getLastModifiedTimeMillis(), importingUser.getAddress()));
} else {
if (!wavelet.getParticipantList().contains(importingUser.getAddress())) {
log.info(
importingUser + " is not a participant, adding: " + wavelet.getParticipantList());
participantFixup.add(
HistorySynthesizer.newAddParticipant(importingUser.getAddress(),
wavelet.getLastModifiedTimeMillis(), importingUser.getAddress()));
}
}
log.info("participantFixup=" + participantFixup);
boolean preserveHistory = !task.getSettings().getSynthesizeHistory();
log.info("preserveHistory=" + preserveHistory);
ImportMetadata importMetadata = new ImportMetadataGsonImpl();
importMetadata.setImportBeginTimeMillis(System.currentTimeMillis());
importMetadata.setImportFinished(false);
importMetadata.setOriginalImporter(userId.getId());
importMetadata.setSourceInstance(instance.serialize());
importMetadata.setRemoteWaveId(waveletName.waveId.serialise());
importMetadata.setRemoteWaveletId(waveletName.waveletId.serialise());
importMetadata.setRemoteHistoryCopied(preserveHistory);
importMetadata.setRemoteVersionImported(snapshot.getFirst().getVersion());
ConvMetadataGsonImpl convMetadata = new ConvMetadataGsonImpl();
convMetadata.setImportMetadata(importMetadata);
final SlobId newId;
if (!preserveHistory) {
List<WaveletOperation> history = Lists.newArrayList();
WaveletHistoryConverter converter = new WaveletHistoryConverter(
getConvNindoConverter(attachmentMapping));
for (WaveletOperation op :
new HistorySynthesizer().synthesizeHistory(wavelet, snapshot.getSecond())) {
history.add(converter.convertAndApply(convertGooglewaveToGmail(op)));
}
history.addAll(participantFixup);
newId = waveletCreator.newConvWithGeneratedId(
ImmutableList.<WaveletOperation>of(), convMetadata, true);
ConvHistoryWriter historyWriter = new ConvHistoryWriter(newId);
try {
for (WaveletOperation op : history) {
historyWriter.append(op);
}
historyWriter.finish();
} catch (ChangeRejected e) {
log.warning("Synthesized history rejected: " + history);
throw new RuntimeException("Synthesized history rejected", e);
}
} else {
long version = 0;
newId = waveletCreator.newConvWithGeneratedId(
ImmutableList.<WaveletOperation>of(), convMetadata, true);
ConvHistoryWriter historyWriter = new ConvHistoryWriter(newId);
WaveletHistoryConverter converter =
new WaveletHistoryConverter(getConvNindoConverter(attachmentMapping));
try {
// NOTE(ohler): We have to stop at snapshot.getFirst().getVersion() even if
// getRawDeltas gives us more, since otherwise, participantFixup may be out-of-date.
while (version < snapshot.getFirst().getVersion()) {
log.info("converter state: " + converter);
List<ProtocolAppliedWaveletDelta> rawDeltas =
robotApi.getRawDeltas(waveletName, version);
for (ProtocolAppliedWaveletDelta rawDelta : rawDeltas) {
WaveletDelta delta = CoreWaveletOperationSerializer.deserialize(
ProtocolWaveletDelta.parseFrom(rawDelta.getSignedOriginalDelta().getDelta()));
for (WaveletOperation badOp : delta) {
Preconditions.checkState(badOp.getContext().getTimestamp() == -1,
"Unexpected timestamp: %s in delta %s", badOp, delta);
// TODO(ohler): Rename
// CoreWaveletOperationSerializer.deserialize() to
// deserializeWithNoTimestamp() or something.
WaveletOperation withTimestamp = WaveletOperation.cloneOp(badOp,
new WaveletOperationContext(badOp.getContext().getCreator(),
rawDelta.getApplicationTimestamp(),
badOp.getContext().getVersionIncrement()));
WaveletOperation converted =
converter.convertAndApply(convertGooglewaveToGmail(withTimestamp));
//log.info(version + ": " + op + " -> " + converted);
historyWriter.append(converted);
version++;
}
}
}
historyWriter.append(participantFixup);
historyWriter.finish();
} catch (ChangeRejected e) {
log.log(Level.SEVERE, "Change rejected somewhere at or before version " + version
+ ", re-importing without history", e);
ImportSettings settings = task.getSettings();
settings.setSynthesizeHistory(true);
task.setSettings(settings);
throw TaskCompleted.withFollowup(task);
}
}
log.info("Imported wavelet " + waveletName + " as local id " + newId);
boolean abandonAndRetry = new RetryHelper().run(
new RetryHelper.Body<Boolean>() {
@Override public Boolean run() throws RetryableFailure, PermanentFailure {
CheckedTransaction tx = datastore.beginTransactionXG();
try {
if (!isPrivate) {