/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.logfs; import io.datakernel.async.AsyncIterator; import io.datakernel.async.IteratorCallback; import io.datakernel.async.ResultCallback; import io.datakernel.eventloop.Eventloop; import io.datakernel.serializer.BufferSerializer; import io.datakernel.stream.*; import io.datakernel.stream.processor.StreamBinaryDeserializer; import io.datakernel.stream.processor.StreamLZ4Decompressor; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; public class LogStreamProducer<T> extends StreamProducerDecorator<T> { private final String logPartition; private final LogPosition startPosition; private final LogFile endFile; private LogFile currentLogFile; private StreamLZ4Decompressor currentDecompressor; private LogFileSystem fileSystem; private BufferSerializer<T> serializer; private final ResultCallback<LogPosition> positionCallback; private final StreamForwarder<T> forwarder; private final Eventloop eventloop; private LogStreamProducer(final Eventloop eventloop, LogFileSystem fileSystem, BufferSerializer<T> serializer, String logPartition, LogPosition startPosition, LogFile endFile, ResultCallback<LogPosition> positionCallback) { this.eventloop = eventloop; this.logPartition = logPartition; this.startPosition = startPosition; this.endFile = endFile; this.fileSystem = fileSystem; this.serializer = serializer; this.positionCallback = positionCallback; this.forwarder = StreamForwarder.create(eventloop); setActualProducer(forwarder.getOutput()); fileSystem.list(logPartition, new ResultCallback<List<LogFile>>() { @Override protected void onResult(List<LogFile> entries) { producerForList(entries).streamTo(forwarder.getInput()); } @Override protected void onException(Exception exception) { new StreamProducers.ClosingWithError<T>(eventloop, exception).streamTo(forwarder.getInput()); } }); } public static <T> LogStreamProducer<T> create(final Eventloop eventloop, LogFileSystem fileSystem, BufferSerializer<T> serializer, String logPartition, LogPosition startPosition, LogFile endFile, ResultCallback<LogPosition> positionCallback) { return new LogStreamProducer<T>(eventloop, fileSystem, serializer, logPartition, startPosition, endFile, positionCallback); } private boolean readFile(LogFile logFile) { if (startPosition.getLogFile() != null && logFile.compareTo(startPosition.getLogFile()) < 0) return false; if (endFile != null && logFile.compareTo(endFile) > 0) return false; return true; } private StreamProducer<T> producerForList(List<LogFile> logFiles) { List<LogFile> logFilesToRead = new ArrayList<>(logFiles.size()); for (LogFile logFile : logFiles) { if (readFile(logFile)) logFilesToRead.add(logFile); } Collections.sort(logFilesToRead); final Iterator<LogFile> it = logFilesToRead.iterator(); return StreamProducers.concat(eventloop, new AsyncIterator<StreamProducer<T>>() { @Override public void next(final IteratorCallback<StreamProducer<T>> callback) { if (!it.hasNext()) { callback.setEnd(); if (positionCallback != null) { positionCallback.setResult(getLogPosition()); } return; } boolean first = currentLogFile == null; currentLogFile = it.next(); currentDecompressor = StreamLZ4Decompressor.create(eventloop); fileSystem.read(logPartition, currentLogFile, first ? startPosition.getPosition() : 0L, currentDecompressor.getInput()); StreamBinaryDeserializer<T> currentDeserializer = StreamBinaryDeserializer.create(eventloop, serializer); ErrorIgnoringTransformer<T> errorIgnoringTransformer = ErrorIgnoringTransformer.create(eventloop); currentDecompressor.getOutput().streamTo(currentDeserializer.getInput()); currentDeserializer.getOutput().streamTo(errorIgnoringTransformer.getInput()); callback.setNext(errorIgnoringTransformer.getOutput()); } }); } public String getLogPartition() { return logPartition; } public LogPosition getLogPosition() { if (currentLogFile == null) return startPosition; if (currentLogFile.equals(startPosition.getLogFile())) return LogPosition.create(currentLogFile, startPosition.getPosition() + currentDecompressor.getInputStreamPosition()); return LogPosition.create(currentLogFile, currentDecompressor.getInputStreamPosition()); } }