/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Copyright (c) 2010, 2011 PostgreSQL Global Development Group * * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://wiki.tada.se/index.php?title=PLJava_License */ package org.postgresql.pljava.jdbc; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Reader; import java.nio.ByteBuffer; import java.sql.Blob; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; /** * @author Thomas Hallgren */ public class BlobValue extends InputStream implements Blob { public static int getStreamLength(InputStream value) throws SQLException { try { value.mark(Integer.MAX_VALUE); long length = value.skip(Long.MAX_VALUE); if(length > Integer.MAX_VALUE) throw new SQLException("stream content too large"); value.reset(); return (int)length; } catch(IOException e) { throw new SQLException(e.getMessage()); } } private long m_markPos; private final long m_nBytes; private final InputStream m_stream; private long m_streamPos; public BlobValue(byte[] bytes) { this(new ByteArrayInputStream(bytes), bytes.length); } public BlobValue(InputStream stream, long nBytes) { m_stream = stream; m_nBytes = nBytes; m_streamPos = 0L; m_markPos = 0L; } //*************************************** // Implementation of java.io.InputStream //*************************************** public int available() throws IOException { return m_stream.available(); } public InputStream getBinaryStream() { return this; } public byte[] getBytes(long pos, int length) throws SQLException { if(pos < 0L || length < 0) throw new IllegalArgumentException(); if(length == 0) return new byte[0]; if(pos + length > m_nBytes) throw new SQLException("Attempt to read beyond end of Blob data"); long skip = pos - m_streamPos; if(skip < 0) throw new SQLException("Cannot position Blob stream backwards"); try { if(skip > 0) this.skip(skip); byte[] buf = new byte[length]; this.read(buf); return buf; } catch(IOException e) { throw new SQLException("Error reading Blob data: " + e.getMessage()); } } /** * Called from within... * @param buf a buffer that reflects the internally allocated bytea buffer. * This size of this buffer will be exactly the size returned by a call to * {@link #length()}. * @throws IOException */ public void getContents(ByteBuffer buf) throws IOException { int rs = 0; if(buf.hasArray()) { byte[] bytes = buf.array(); rs = m_stream.read(bytes); } else { byte[] trBuf = new byte[1024]; int br; while((br = m_stream.read(trBuf)) > 0) { buf.put(trBuf, 0, br); rs += br; } } if(rs != m_nBytes) throw new IOException("Not all bytes could be read"); m_streamPos += rs; } //*************************************** // Implementation of java.sql.Blob //*************************************** public long length() { return m_nBytes; } public synchronized void mark(int readLimit) { m_stream.mark(readLimit); m_markPos = m_streamPos; } public boolean markSupported() { return m_stream.markSupported(); } /** * Not supported. */ public long position(Blob pattern, long start) { throw new UnsupportedOperationException(); } /** * Not supported. */ public long position(byte[] pattern, long start) { throw new UnsupportedOperationException(); } public synchronized int read() throws IOException { int rs = m_stream.read(); m_streamPos++; return rs; } public synchronized int read(byte[] b) throws IOException { int rs = m_stream.read(b); m_streamPos += rs; return rs; } public synchronized int read(byte[] b, int off, int len) throws IOException { int rs = m_stream.read(b, off, len); m_streamPos += rs; return rs; } public synchronized void reset() throws IOException { m_stream.reset(); m_streamPos = m_markPos; } //************************************************************************* // Implementation of java.sql.Blob JDK 1.4 methods // // Those method are intended to provide a channel to the underlying data // storage as an alternatvie to the setBinaryStream // on the preparedStatement and are not implemented by the BlobValue. // //************************************************************************* /** * In this method is not supported by <code>BlobValue</code> */ public OutputStream setBinaryStream(long pos) { throw new UnsupportedOperationException(); } /** * In this method is not supported by <code>BlobValue</code> */ public int setBytes(long pos, byte[] bytes) { throw new UnsupportedOperationException(); } /** * In this method is not supported by <code>BlobValue</code> */ public int setBytes(long pos, byte[] bytes, int offset, int len) { throw new UnsupportedOperationException(); } public synchronized long skip(long nBytes) throws IOException { long skipped = m_stream.skip(nBytes); m_streamPos += skipped; return skipped; } /** * In this method is not supported by <code>BlobValue</code> */ public void truncate(long len) { throw new UnsupportedOperationException(); } // ************************************************************ // Non-implementation of JDBC 4 methods. // ************************************************************ public InputStream getBinaryStream(long pos, long length) throws SQLException { throw new SQLFeatureNotSupportedException ( this.getClass() + ".getBinaryStream( long,long ) not " + "implemented yet.", "0A000" ); } public void free() throws SQLException { throw new SQLFeatureNotSupportedException ( this.getClass() + ".free() not implemented yet.", "0A000" ); } }