package org.corfudb.runtime.object;
import org.corfudb.protocols.logprotocol.ISMRConsumable;
import org.corfudb.protocols.logprotocol.SMREntry;
import org.corfudb.protocols.wireprotocol.DataType;
import org.corfudb.protocols.wireprotocol.ILogData;
import org.corfudb.protocols.wireprotocol.TokenResponse;
import org.corfudb.runtime.CorfuRuntime;
import org.corfudb.runtime.view.Address;
import org.corfudb.runtime.view.stream.IStreamView;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* StreamViewSMRAdapter wraps a stream and implements the ISMRStream API over
* it.
*
* This is a relatively thin wrapper. For example, an underlying stream returns
* from current() a LogData entry. StreamViewSMRAdapter verifies that the
* entry contains data (otherwise, return null), and that the payload is of type
* ISMRConsumable (otherwise, again return null). Since the underlying log supports multi-stream entries,
* it collects and returns the SMREntries related to the current stream.
*
* Created by mwei on 3/10/17.
*/
public class StreamViewSMRAdapter implements ISMRStream {
/** The stream view backing this adapter. */
final IStreamView streamView;
/** Necessary until the runtime is no longer necessary for deserialization. */
final CorfuRuntime runtime;
public StreamViewSMRAdapter(CorfuRuntime runtime,
IStreamView streamView) {
this.runtime = runtime;
this.streamView = streamView;
}
public List<SMREntry> remainingUpTo(long maxGlobal) {
return streamView.remainingUpTo(maxGlobal).stream()
.filter(m -> m.getType() == DataType.DATA)
.filter(m -> m.getPayload(runtime) instanceof ISMRConsumable)
.map(logData -> ((ISMRConsumable)logData.getPayload(runtime)).getSMRUpdates(streamView.getID()))
.flatMap(List::stream)
.collect(Collectors.toList());
}
public List<SMREntry> current() {
ILogData data = streamView.current();
if (data != null) {
if (data.getType() == DataType.DATA &&
data.getPayload(runtime)
instanceof ISMRConsumable) {
return ((ISMRConsumable)data.getPayload(runtime))
.getSMRUpdates(streamView.getID());
}
}
return null;
}
public List<SMREntry> previous() {
ILogData data = streamView.previous();
while (Address.isAddress(streamView.getCurrentGlobalPosition())
&& data != null) {
if (data.getType() == DataType.DATA &&
data.getPayload(runtime)
instanceof ISMRConsumable) {
return ((ISMRConsumable)data.getPayload(runtime))
.getSMRUpdates(streamView.getID());
}
data = streamView.previous();
}
return null;
}
public long pos() {
return streamView.getCurrentGlobalPosition();
}
public void reset() {
streamView.reset();
}
public void seek(long globalAddress) {
streamView.seek(globalAddress);
}
@Override
public Stream<SMREntry> stream() {
return streamUpTo(Address.MAX);
}
@Override
public Stream<SMREntry> streamUpTo(long maxGlobal) {
return streamView.streamUpTo(maxGlobal)
.filter(m -> m.getType() == DataType.DATA)
.filter(m -> m.getPayload(runtime) instanceof ISMRConsumable)
.map(logData -> ((ISMRConsumable)logData.getPayload(runtime)).getSMRUpdates(streamView.getID()))
.flatMap(List::stream);
}
/** Append a SMREntry to the stream, returning the global address
* it was written at.
* <p>
* Optionally, provide a method to be called when an address is acquired,
* and also a method to be called when an address is released (due to
* an unsuccessful append).
* </p>
* @param entry The SMR entry to append.
* @param acquisitionCallback A function to call when an address is
* acquired.
* It should return true to continue with the
* append.
* @param deacquisitionCallback A function to call when an address is
* released. It should return true to retry
* writing.
* @return The (global) address the object was written at.
*/
public long append(SMREntry entry,
Function<TokenResponse, Boolean> acquisitionCallback,
Function<TokenResponse, Boolean> deacquisitionCallback) {
return streamView.append(entry, acquisitionCallback, deacquisitionCallback);
}
/** {@inheritDoc} */
@Override
public UUID getID() {
return streamView.getID();
}
}