001: /*
002:
003: Derby - Class org.apache.derby.impl.drda.DRDAStatement
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021: package org.apache.derby.impl.drda;
022:
023: import java.io.ByteArrayInputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.BufferedInputStream;
027: import java.sql.ResultSet;
028: import java.sql.Blob;
029: import java.sql.Clob;
030: import java.sql.SQLException;
031:
032: import java.io.UnsupportedEncodingException;
033:
034: import org.apache.derby.iapi.reference.DRDAConstants;
035: import org.apache.derby.iapi.services.sanity.SanityManager;
036: import org.apache.derby.impl.jdbc.Util;
037:
038: /**
039: * @author marsden
040: *
041: * EXTDTAObjectHolder provides Externalized Large Object representation that
042: * does not hold locks until the end of the transaction (DERBY-255)
043: *
044: * It serves as a holder for lob data and is only valid as long as the original
045: * result set from which it came is on the same row.
046: *
047: *
048: */
049: class EXTDTAInputStream extends InputStream {
050:
051: private InputStream binaryInputStream = null;
052:
053: private boolean isEmptyStream;
054:
055: private ResultSet dataResultSet = null;
056: private Blob blob = null;
057: private Clob clob = null;
058:
059: private EXTDTAInputStream(ResultSet rs, int columnNumber,
060: int ndrdaType) throws SQLException, IOException {
061:
062: this .dataResultSet = rs;
063: this .isEmptyStream = !initInputStream(rs, columnNumber,
064: ndrdaType);
065:
066: }
067:
068: /**
069: * Retrieve stream from the ResultSet and column specified. Create an
070: * input stream for the large object being retrieved. Do not hold
071: * locks until end of transaction. DERBY-255.
072: *
073: *
074: * See DDMWriter.writeScalarStream
075: *
076: * @param rs
077: * result set from which to retrieve the lob
078: * @param column
079: * column number
080: * @param drdaType
081: * FD:OCA type of object one of
082: * DRDAConstants.DRDA_TYPE_NLOBBYTES
083: * DRDAConstants.DRDA_TYPE_LOBBYTES
084: * DRDAConstants.DRDA_TYPE_NLOBCMIXED
085: * DRDAConstants.DRDA_TYPE_LOBCMIXED
086: *
087: * @return null if the value is null or a new EXTDTAInputStream corresponding to
088: * rs.getBinaryStream(column) value and associated length
089: *
090: * @throws SQLException
091: */
092: public static EXTDTAInputStream getEXTDTAStream(ResultSet rs,
093: int column, int drdaType) throws SQLException {
094: try {
095: int ndrdaType = drdaType | 1; //nullable drdaType
096:
097: return new EXTDTAInputStream(rs, column, ndrdaType);
098:
099: } catch (IOException e) {
100: throw new SQLException(e.getMessage());
101: }
102:
103: }
104:
105: /**
106: * Get the length of the InputStream
107: * This method is currently not used because there seems to be no way to
108: * reset the she stream.
109: *
110: * @param binaryInputStream
111: * an InputStream whose length needs to be calclulated
112: * @return length of stream
113: */
114: private static long getInputStreamLength(
115: InputStream binaryInputStream) throws SQLException {
116: long length = 0;
117: if (binaryInputStream == null)
118: return length;
119:
120: try {
121: for (;;) {
122: int avail = binaryInputStream.available();
123: binaryInputStream.skip(avail);
124: if (avail == 0)
125: break;
126: length += avail;
127:
128: }
129: //binaryInputStream.close();
130: } catch (IOException ioe) {
131: throw Util.javaException(ioe);
132: }
133:
134: return length;
135:
136: }
137:
138: /**
139: *
140: *
141: * @see java.io.InputStream#read()
142: */
143: public int read() throws IOException {
144: return binaryInputStream.read();
145: }
146:
147: /**
148: *
149: *
150: * @see java.io.InputStream#available()
151: */
152: public int available() throws IOException {
153: return binaryInputStream.available();
154: }
155:
156: /**
157: *
158: *
159: * @see java.io.InputStream#close()
160: */
161: public void close() throws IOException {
162:
163: try {
164: if (binaryInputStream != null)
165: binaryInputStream.close();
166: binaryInputStream = null;
167:
168: } finally {
169:
170: blob = null;
171: clob = null;
172: dataResultSet = null;
173: }
174:
175: }
176:
177: /**
178: *
179: *
180: * @see java.lang.Object#equals(java.lang.Object)
181: */
182: public boolean equals(Object arg0) {
183: return binaryInputStream.equals(arg0);
184: }
185:
186: /**
187: *
188: *
189: * @see java.lang.Object#hashCode()
190: */
191: public int hashCode() {
192: return binaryInputStream.hashCode();
193: }
194:
195: /**
196: *
197: *
198: * @see java.io.InputStream#mark(int)
199: */
200: public void mark(int arg0) {
201: binaryInputStream.mark(arg0);
202: }
203:
204: /**
205: *
206: *
207: * @see java.io.InputStream#markSupported()
208: */
209: public boolean markSupported() {
210: return binaryInputStream.markSupported();
211: }
212:
213: /**
214: *
215: *
216: * @see java.io.InputStream#read(byte[])
217: */
218: public int read(byte[] arg0) throws IOException {
219: return binaryInputStream.read(arg0);
220: }
221:
222: /**
223: *
224: *
225: * @see java.io.InputStream#read(byte[], int, int)
226: */
227: public int read(byte[] arg0, int arg1, int arg2) throws IOException {
228: return binaryInputStream.read(arg0, arg1, arg2);
229: }
230:
231: /**
232: *
233: *
234: * @see java.io.InputStream#reset()
235: */
236: public void reset() throws IOException {
237: binaryInputStream.reset();
238: }
239:
240: /**
241: *
242: *
243: * @see java.io.InputStream#skip(long)
244: */
245: public long skip(long arg0) throws IOException {
246: return binaryInputStream.skip(arg0);
247: }
248:
249: protected boolean isEmptyStream() {
250: return isEmptyStream;
251: }
252:
253: /**
254: * This method takes information of ResultSet and
255: * initialize binaryInputStream variable of this object with not empty stream and return true.
256: * If the stream was empty, this method remain binaryInputStream null and return false.
257: *
258: * @param rs ResultSet object to get stream from.
259: * @param column index number of column in ResultSet to get stream.
260: * @param ndrdaType describe type column to get stream.
261: *
262: * @return true if the stream was not empty, false if the stream was empty.
263: *
264: */
265: private boolean initInputStream(ResultSet rs, int column,
266: int ndrdaType) throws SQLException, IOException {
267:
268: InputStream is = null;
269: try {
270: // BLOBS
271: if (ndrdaType == DRDAConstants.DRDA_TYPE_NLOBBYTES) {
272: blob = rs.getBlob(column);
273: if (blob == null) {
274: return false;
275: }
276:
277: is = blob.getBinaryStream();
278:
279: }
280: // CLOBS
281: else if (ndrdaType == DRDAConstants.DRDA_TYPE_NLOBCMIXED) {
282: try {
283: clob = rs.getClob(column);
284:
285: if (clob == null) {
286: return false;
287: }
288:
289: is = new ReEncodedInputStream(clob
290: .getCharacterStream());
291:
292: } catch (java.io.UnsupportedEncodingException e) {
293: throw new SQLException(e.getMessage());
294:
295: } catch (IOException e) {
296: throw new SQLException(e.getMessage());
297:
298: }
299:
300: } else {
301: if (SanityManager.DEBUG) {
302: SanityManager.THROWASSERT("NDRDAType: " + ndrdaType
303: + " not valid EXTDTA object type");
304: }
305: }
306:
307: boolean exist = is.read() > -1;
308:
309: is.close();
310: is = null;
311:
312: if (exist) {
313: openInputStreamAgain();
314: }
315:
316: return exist;
317:
318: } catch (IllegalStateException e) {
319: throw Util.javaException(e);
320:
321: } finally {
322: if (is != null)
323: is.close();
324:
325: }
326:
327: }
328:
329: /**
330: *
331: * This method is called from initInputStream and
332: * opens inputstream again to stream actually.
333: *
334: */
335: private void openInputStreamAgain() throws IllegalStateException,
336: SQLException {
337:
338: if (this .binaryInputStream != null) {
339: return;
340: }
341:
342: InputStream is = null;
343: try {
344:
345: if (SanityManager.DEBUG) {
346: SanityManager.ASSERT((blob != null && clob == null)
347: || (clob != null && blob == null),
348: "One of blob or clob must be non-null.");
349: }
350:
351: if (blob != null) {
352: is = blob.getBinaryStream();
353:
354: } else if (clob != null) {
355: is = new ReEncodedInputStream(clob.getCharacterStream());
356: }
357:
358: } catch (IOException e) {
359: throw new IllegalStateException(e.getMessage());
360: }
361:
362: if (!is.markSupported()) {
363: is = new BufferedInputStream(is);
364: }
365:
366: this .binaryInputStream = is;
367:
368: }
369:
370: protected void finalize() throws Throwable {
371: close();
372: }
373:
374: }
|