Java tutorial
/* * ------------------------------------------------------------------------------ * Hermes FTP Server * Copyright (c) 2005-2014 Lars Behnke * ------------------------------------------------------------------------------ * * This file is part of Hermes FTP Server. * * Hermes FTP Server 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. * * Hermes FTP Server 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 Hermes FTP Server; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ------------------------------------------------------------------------------ */ package com.apporiented.hermesftp.streams; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.ArrayUtils; /** * Reads a stream formatted in block mode, extracts the data contents and registers restart markers. * * @author Lars Behnke */ public class BlockModeInputStream extends InputStream implements RecordReadSupport, BlockModeConstants { private static final String UNEXPECTED_END_OF_STREAM = "Unexpected end of stream."; private byte[] buffer; private int idx; private long byteCount; private InputStream is; private Map<Long, Long> restartMarkers = new HashMap<Long, Long>(); private byte[] eorMarkerBytes; private boolean eof; private boolean eor; /** * Constructor. * * @param is the input stream. * @param eorMarker The marker bytes for EOR. * @param restartMarkers Hash table to be filled with restart markers. */ public BlockModeInputStream(InputStream is, byte[] eorMarker, Map<Long, Long> restartMarkers) { super(); this.is = is; if (eorMarker == null) { String lineSep = System.getProperty("line.separator"); eorMarker = lineSep.getBytes(); } this.eorMarkerBytes = eorMarker; if (restartMarkers == null) { restartMarkers = new HashMap<Long, Long>(); } this.restartMarkers = restartMarkers; } /** * Constructor. * * @param is The input stream. */ public BlockModeInputStream(InputStream is) { this(is, null, null); } /** * {@inheritDoc} */ public byte[] readRecord() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { while (!(eor && idx == 0)) { int b = read(); if (b == -1) { throw new IOException(UNEXPECTED_END_OF_STREAM); } baos.write(b); } } finally { baos.close(); } return baos.toByteArray(); } /** * {@inheritDoc} */ public int read() throws IOException { if (buffer == null) { if (eof) { return -1; } int descriptor = is.read(); int hi = is.read(); int lo = is.read(); checkHeader(descriptor, hi, lo); int len = hi << 8 | lo; buffer = new byte[len]; int realLength = is.read(buffer); if (realLength != len) { throw new IOException(UNEXPECTED_END_OF_STREAM); } eof = (descriptor & BlockModeConstants.DESC_CODE_EOF) > 0; eor = (descriptor & BlockModeConstants.DESC_CODE_EOR) > 0; boolean mark = (descriptor & BlockModeConstants.DESC_CODE_REST) > 0; if (mark) { setRestartMarker(); buffer = null; return read(); } byteCount += len; if (eor) { buffer = ArrayUtils.addAll(buffer, eorMarkerBytes); } } int result = buffer[idx]; idx++; if (idx >= buffer.length) { buffer = null; idx = 0; } return result; } private void checkHeader(int descriptor, int hi, int lo) throws IOException { if (descriptor == -1 || hi == -1 || lo == -1) { throw new IOException(UNEXPECTED_END_OF_STREAM); } if ((descriptor & BlockModeConstants.DESC_CODE_ERR) > 0) { throw new IOException("Error flag in descriptor code set."); } } private void setRestartMarker() throws IOException { if (buffer.length > 8) { throw new IOException("Marker size exceeds 8 bytes."); } long marker = 0; int len = buffer.length; for (int i = 0; i < len; i++) { marker |= buffer[i] << (8 * (len - i - 1)); } restartMarkers.put(marker, byteCount); } /** * Getter method for the java bean <code>restartMarkers</code>. * * @return Returns the value of the java bean <code>restartMarkers</code>. */ public Map<Long, Long> getRestartMarkers() { return restartMarkers; } }