/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source 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. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Nam Nguyen */ package com.caucho.vfs; import java.io.InterruptedIOException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.util.logging.*; /** * Specialized stream to handle sockets. * * <p>Unlike VfsStream, when the read() throws and IOException or * a SocketException, SocketStream will throw a ClientDisconnectException. */ public class DatagramStream extends StreamImpl { private static final Logger log = Logger.getLogger(DatagramStream.class.getName()); private DatagramSocket _s; private DatagramInputStream _is; private DatagramOutputStream _os; private boolean _throwReadInterrupts = false; private long _totalReadBytes; private long _totalWriteBytes; public DatagramStream(DatagramSocket s) { _s = s; _is = new DatagramInputStream(s); _os = new DatagramOutputStream(s); } public InputStream getInputStream() { return _is; } public OutputStream getOutputStream() { return _os; } /** * If true, throws read interrupts instead of returning an end of * fail. Defaults to false. */ public void setThrowReadInterrupts(boolean allowThrow) { _throwReadInterrupts = allowThrow; } /** * If true, throws read interrupts instead of returning an end of * fail. Defaults to false. */ public boolean getThrowReadInterrupts() { return _throwReadInterrupts; } /** * Returns the position. */ public long getPosition() { return _totalReadBytes; } public boolean setPosition(long offset) { return false; } /** * Unread the last byte. */ public void unread() throws IOException { _is.unread(); } /** * Returns true if stream is readable and bytes can be skipped. */ @Override public boolean hasSkip() { return canRead(); } /** * Returns true since the socket stream can be read. */ @Override public boolean canRead() { return _s != null; } /** * Skips bytes in the file. * * @param n the number of bytes to skip * * @return the actual bytes skipped. */ public long skip(long n) throws IOException { if (_s == null) return -1; return _is.skip(n); } /** * Reads bytes from the socket. * * @param buf byte buffer receiving the bytes * @param offset offset into the buffer * @param length number of bytes to read * @return number of bytes read or -1 * @exception throws ClientDisconnectException if the connection is dropped */ public int read(byte []buf, int offset, int length) throws IOException { try { if (_s == null || _is == null) return -1; int readLength = _is.read(buf, offset, length); if (readLength >= 0) _totalReadBytes += readLength; return readLength; } catch (InterruptedIOException e) { if (_throwReadInterrupts) throw e; log.log(Level.FINEST, e.toString(), e); } catch (IOException e) { if (_throwReadInterrupts) throw e; log.log(Level.FINER, e.toString(), e); } return -1; } /** * Reads bytes from the socket. * * @param buf byte buffer receiving the bytes * @param offset offset into the buffer * @param length number of bytes to read * @return number of bytes read or -1 * @exception throws ClientDisconnectException if the connection is dropped */ public int readTimeout(byte []buf, int offset, int length, long timeout) throws IOException { if (_s == null || _is == null) return -1; int oldTimeout = _s.getSoTimeout(); try { _s.setSoTimeout((int) timeout); int readLength = read(buf, offset, length); return readLength; } finally { _s.setSoTimeout(oldTimeout); } } /** * Returns the number of bytes available to be read from the input stream. */ public int getAvailable() throws IOException { if (_is == null) return -1; return _is.available(); } public boolean canWrite() { return _s != null; } /** * Writes bytes to the socket. * * @param buf byte buffer containing the bytes * @param offset offset into the buffer * @param length number of bytes to read * @param isEnd if the write is at a close. * * @exception throws ClientDisconnectException if the connection is dropped */ public void write(byte []buf, int offset, int length, boolean isEnd) throws IOException { if (_s == null) return; try { _os.write(buf, offset, length); _totalWriteBytes += length; } catch (IOException e) { try { close(); } catch (IOException e1) { } throw ClientDisconnectException.create(e); } } /** * Flushes the socket. */ /* public void flush() throws IOException { if (_os == null || ! _needsFlush) return; _needsFlush = false; try { _os.flush(); } catch (IOException e) { try { close(); } catch (IOException e1) { } throw ClientDisconnectException.create(e); } } */ public void resetTotalBytes() { _totalReadBytes = 0; _totalWriteBytes = 0; } public long getTotalReadBytes() { return _totalReadBytes; } public long getTotalWriteBytes() { return _totalWriteBytes; } /** * Closes the write half of the stream. */ public void closeWrite() throws IOException { OutputStream os = _os; _os = null; os.close(); } /** * Closes the underlying sockets and socket streams. */ public void close() throws IOException { DatagramSocket s = _s; _s = null; OutputStream os = _os; _os = null; InputStream is = _is; _is = null; try { if (os != null) os.close(); if (is != null) is.close(); } finally { if (s != null) s.close(); } } static class DatagramInputStream extends InputStream { private final DatagramSocket _s; private final DatagramPacket _packet; private final byte []_buf; private int _length; private int _offset; DatagramInputStream(DatagramSocket s) { _s = s; _buf = new byte[1024 * 64]; _packet = new DatagramPacket(_buf, _buf.length); } public int available() throws IOException { return _length - _offset; } public void unread() throws IOException { if (_offset > 0) _offset--; } public int read() throws IOException { byte []b = new byte[1]; read(b, 0, 1); return b[0]; } public int read(byte []b, int off, int len) throws IOException { if (_length - _offset == 0) receivePacket(); int sublen = Math.min(_length - _offset, len); System.arraycopy(_buf, _offset, b, off, sublen); _offset += sublen; return sublen; } /* private int readPacket(byte []b, int off, int len) throws IOException { if (_length - _offset == 0) receivePacket(); int sublen = Math.min(_length - _offset, len); System.arraycopy(_buf, _offset, b, off, sublen); _offset += sublen; return sublen; } */ public long skip(long n) throws IOException { int sublen = (int) Math.min(_length - _offset, n); _offset += sublen; return sublen; /* while (readLength < n) { if (_length - _offset == 0) receivePacket(); int sublen = (int) Math.min(_length - _offset, n - readLength); _offset += sublen; readLength += sublen; } */ } private void receivePacket() throws IOException { _s.receive(_packet); //_buf = _packet.getData(); //_offset = _packet.getOffset(); _offset = 0; _length = _packet.getLength(); } } static class DatagramOutputStream extends OutputStream { private final DatagramSocket _s; private final DatagramPacket _packet; DatagramOutputStream(DatagramSocket s) { _s = s; _packet = new DatagramPacket(new byte[0], 0, _s.getInetAddress(), _s.getPort()); } public void write(int b) throws IOException { write(new byte[] {(byte) b}, 0, 1); } public void write(byte []b, int off, int len) throws IOException { /* int writeLength = 0; while (writeLength < len) { int sublen = Math.min(len - writeLength, 65507); _packet.setData(b, off + writeLength, sublen); _s.send(_packet); writeLength += sublen; } */ _packet.setData(b, off, len); _s.send(_packet); } } }