/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * For information about the authors of this project Have a look * at the AUTHORS file in the root of this project. */ package net.sourceforge.fullsync.buffer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import org.slf4j.Logger; public class BlockBuffer implements ExecutionBuffer { Logger logger; int maxSize; int maxEntries; int freeBytes; int numberBytes; int numberEntries; byte[] buffer; Entry[] entries; int flushes; ArrayList<EntryFinishedListener> finishedListeners; public BlockBuffer(Logger logger) { this.logger = logger; maxSize = 1024 * 1024 * 10; maxEntries = 5000; numberBytes = 0; numberEntries = 0; freeBytes = maxSize; buffer = null; entries = null; flushes = 0; finishedListeners = new ArrayList<EntryFinishedListener>(); } @Override public void load() { if (buffer == null) { buffer = new byte[maxSize]; entries = new Entry[maxEntries]; } } @Override public void unload() { buffer = null; entries = null; } @Override public void flush() throws IOException { for (int i = 0; i < numberEntries; ++i) { Entry e = entries[i]; EntryDescriptor desc = e.descriptor; IOException ioe = null; try { OutputStream out = desc.getOutputStream(); if (out != null) { out.write(buffer, e.start, e.length); } if ((e.internalSegment & Segment.LAST) > 0) { desc.finishWrite(); String opDesc = desc.getOperationDescription(); if (opDesc != null) { logger.info(opDesc); } } } catch (IOException ex) { ioe = ex; logger.error("Exception", ex); } if ((e.internalSegment & Segment.LAST) > 0) { for (EntryFinishedListener listener : finishedListeners) { listener.entryFinished(desc, ioe); } } } Arrays.fill(entries, null); numberBytes = 0; numberEntries = 0; freeBytes = maxSize; flushes++; } // may not read as much as length says protected Entry storeBytes(InputStream inStream, long length) throws IOException { if (length > freeBytes) { length = freeBytes; } int start = numberBytes; int read = inStream.read(buffer, start, (int) length); //FIXME: read might return -1 which subsequently (in flush) throws an exception numberBytes += read; freeBytes -= read; Entry entry = new Entry(start, read); entries[numberEntries] = entry; numberEntries++; return entry; } private int store(InputStream inStream, long alreadyRead, long lengthLeft, EntryDescriptor descriptor) throws IOException { Entry entry = storeBytes(inStream, lengthLeft); int s = Segment.MIDDLE; if (alreadyRead == 0) { s |= Segment.FIRST; } if (entry.length == lengthLeft) { s |= Segment.LAST; } entry.internalSegment = s; entry.descriptor = descriptor; return entry.length; } private void storeEntry(InputStream data, long size, EntryDescriptor descriptor) throws IOException { long alreadyRead = 0; long lengthLeft = size; do { if ((lengthLeft > freeBytes) || (numberEntries == maxEntries)) { flush(); } int read = store(data, alreadyRead, lengthLeft, descriptor); alreadyRead += read; lengthLeft -= read; } while (lengthLeft > 0); } @Override public void storeEntry(final EntryDescriptor descriptor) throws IOException { Entry entry; if (descriptor.getSize() == 0) { if (numberEntries == maxEntries) { flush(); } entry = new Entry(numberBytes, 0); entry.descriptor = descriptor; entries[numberEntries] = entry; numberEntries++; } else { storeEntry(descriptor.getInputStream(), descriptor.getSize(), descriptor); } descriptor.finishStore(); } @Override public void addEntryFinishedListener(final EntryFinishedListener listener) { finishedListeners.add(listener); } @Override public void removeEntryFinishedListener(final EntryFinishedListener listener) { finishedListeners.remove(listener); } }