/* * Copyright 2016 The Simple File Server Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sfs.nodes; import com.google.common.base.Optional; import com.google.common.net.HostAndPort; import io.vertx.core.buffer.Buffer; import io.vertx.core.logging.Logger; import io.vertx.core.streams.ReadStream; import org.sfs.Server; import org.sfs.VertxContext; import org.sfs.filesystem.volume.DigestBlob; import org.sfs.filesystem.volume.HeaderBlob; import org.sfs.filesystem.volume.ReadStreamBlob; import org.sfs.filesystem.volume.Volume; import org.sfs.filesystem.volume.VolumeManager; import org.sfs.filesystem.volume.WriteStreamBlob; import org.sfs.io.BufferEndableWriteStream; import org.sfs.io.DigestEndableWriteStream; import org.sfs.io.DigestReadStream; import org.sfs.io.NullEndableWriteStream; import org.sfs.rx.Defer; import org.sfs.rx.HandleServerToBusy; import org.sfs.rx.Holder2; import org.sfs.util.MessageDigestFactory; import org.sfs.vo.TransientServiceDef; import rx.Observable; import rx.functions.Func1; import static com.google.common.base.Joiner.on; import static com.google.common.base.Optional.of; import static com.google.common.net.HostAndPort.fromHost; import static io.vertx.core.logging.LoggerFactory.getLogger; import static java.lang.String.format; import static org.sfs.rx.Defer.just; import static rx.Observable.defer; public class LocalNode extends AbstractNode { private static final Logger LOGGER = getLogger(LocalNode.class); private final VolumeManager volumeManager; private final HostAndPort hostAndPort; private final VertxContext<Server> vertxContext; public LocalNode(VertxContext<Server> vertxContext, VolumeManager volumeManager) { this.vertxContext = vertxContext; this.volumeManager = volumeManager; this.hostAndPort = fromHost("127.0.0.1"); } @Override public Observable<Optional<TransientServiceDef>> getNodeStats() { NodeStats nodeStats = vertxContext.verticle().getNodeStats(); return Defer.just(nodeStats.getStats()); } @Override public Observable<Optional<DigestBlob>> checksum(String volumeId, long position, Optional<Long> oOffset, Optional<Long> oLength, MessageDigestFactory... messageDigestFactories) { return defer(() -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("begin head {volumeId=%s,position=%d,messageDigests=%s}", volumeId, position, on(',').join(messageDigestFactories))); } return createReadStream(volumeId, position, oOffset, oLength) .flatMap(oReadStreamBlob -> { if (oReadStreamBlob.isPresent()) { ReadStreamBlob readStreamBlob = oReadStreamBlob.get(); final DigestBlob digestBlob = new DigestBlob(readStreamBlob); if (messageDigestFactories.length > 0) { BufferEndableWriteStream nullWriteStream = new NullEndableWriteStream(); final DigestEndableWriteStream digestWriteStream = new DigestEndableWriteStream(nullWriteStream, messageDigestFactories); return readStreamBlob.produce(digestWriteStream) .map(aVoid -> { for (Holder2<MessageDigestFactory, byte[]> digest : digestWriteStream.digests()) { digestBlob.withDigest(digest.value0(), digest.value1()); } return of(digestBlob); }); } else { return just(of(digestBlob)); } } else { return just(Optional.<DigestBlob>absent()); } }) .map(digestBlobOptional -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("end head {volumeId=%s,position=%d,messageDigests=%s} = %s", volumeId, position, on(',').join(messageDigestFactories), digestBlobOptional)); } return digestBlobOptional; }) .onErrorResumeNext(new HandleServerToBusy<>()); }); } @Override public Observable<Optional<HeaderBlob>> acknowledge(String volumeId, long position) { return defer(() -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("begin acknowledge {volumeId=%s,position=%d}", volumeId, position)); } Volume volume = volumeManager.get(volumeId).get(); return volume.acknowledge(vertxContext.vertx(), position) .map(headerBlobOptional -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("end acknowledge {volumeId=%s,position=%d}", volumeId, position)); } return headerBlobOptional; }) .onErrorResumeNext(new HandleServerToBusy<>()); }); } @Override public Observable<Optional<HeaderBlob>> delete(String volumeId, long position) { return defer(() -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("delete {volumeId=%s,position=%d}", volumeId, position)); } Volume volume = volumeManager.get(volumeId).get(); return volume.delete(vertxContext.vertx(), position) .onErrorResumeNext(new HandleServerToBusy<>()); }); } @Override public Observable<Optional<ReadStreamBlob>> createReadStream(String volumeId, long position, Optional<Long> offset, Optional<Long> length) { return defer(() -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("createReadStream {volumeId=%s,position=%d,offset=%s,length=%s}", volumeId, position, offset, length)); } Volume volume = volumeManager.get(volumeId).get(); return volume.getDataStream(vertxContext.vertx(), position, offset, length) .onErrorResumeNext(new HandleServerToBusy<>()); }); } @Override public Observable<Boolean> canReadVolume(String volumeId) { return canWriteVolume(volumeId); } @Override public Observable<Boolean> canWriteVolume(String volumeId) { return defer(() -> { LOGGER.debug("Volume Manager is Open " + volumeManager.isOpen()); if (volumeManager.isOpen()) { Optional<Volume> oVolume = volumeManager.get(volumeId); LOGGER.debug("Volume is " + oVolume); if (oVolume.isPresent()) { Volume volume = oVolume.get(); Volume.Status status = volume.status(); LOGGER.debug("Volume status is " + status); if (Volume.Status.STARTED.equals(status)) { return Defer.just(true); } } } return Defer.just(false); }); } @Override public Observable<NodeWriteStreamBlob> createWriteStream(String volumeId, long length, final MessageDigestFactory... messageDigestFactories) { LocalNode _this = this; return defer(() -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(format("createWriteStream {volumeId=%s,length=%d,messageDigests=%s}", volumeId, length, on(',').join(messageDigestFactories))); } final Volume volume = volumeManager.get(volumeId).get(); return volume.putDataStream(vertxContext.vertx(), length) .map((Func1<WriteStreamBlob, NodeWriteStreamBlob>) writeStreamBlob -> new NodeWriteStreamBlob(_this) { @Override public Observable<DigestBlob> consume(ReadStream<Buffer> src) { DigestReadStream digestWriteStream = new DigestReadStream(src, messageDigestFactories); return writeStreamBlob.consume(digestWriteStream) .map(aVoid -> { DigestBlob digestBlob = new DigestBlob(writeStreamBlob.getVolume(), writeStreamBlob.getPosition(), writeStreamBlob.getLength()); for (MessageDigestFactory messageDigestFactory : messageDigestFactories) { digestBlob.withDigest(messageDigestFactory, digestWriteStream.getDigest(messageDigestFactory).get()); } return digestBlob; }); } }); }).onErrorResumeNext(new HandleServerToBusy<>()); } @Override public String toString() { return "LocalNode{" + "hostAndPort=" + hostAndPort + '}'; } }