001: /*
002:
003: Derby - Class org.apache.derby.client.am.Lob
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:
022: package org.apache.derby.client.am;
023:
024: import java.io.InputStream;
025: import java.io.IOException;
026: import java.util.ArrayList;
027:
028: import org.apache.derby.shared.common.reference.SQLState;
029:
030: public abstract class Lob implements UnitOfWorkListener {
031: // The following flags specify the data type(s) a LOB instance currently contains
032: public static final int STRING = 2;
033: public static final int ASCII_STREAM = 4;
034: public static final int UNICODE_STREAM = 8;
035: public static final int CHARACTER_STREAM = 16;
036: public static final int BINARY_STREAM = 32;
037: public static final int BINARY_STRING = 64;
038:
039: //---------------------navigational members-----------------------------------
040: protected Agent agent_;
041:
042: //-----------------------------state------------------------------------------
043: protected int dataType_ = 0; // data type(s) the LOB instance currently contains
044:
045: protected long sqlLength_; // length of the LOB value, as defined by the server
046: protected boolean lengthObtained_;
047:
048: //---------------------constructors/finalizer---------------------------------
049: protected Lob(Agent agent) {
050: agent_ = agent;
051: lengthObtained_ = false;
052: }
053:
054: protected void finalize() throws java.lang.Throwable {
055: super .finalize();
056: }
057:
058: // ---------------------------jdbc 2------------------------------------------
059:
060: // should only be called by a synchronized method.
061:
062: // should only be called by a synchronized method.
063: public long sqlLength() throws SqlException {
064: checkForClosedConnection();
065:
066: return sqlLength_;
067: }
068:
069: //-----------------------event callback methods-------------------------------
070:
071: public void listenToUnitOfWork() {
072: agent_.connection_.CommitAndRollbackListeners_.put(this , null);
073: }
074:
075: public void completeLocalCommit(java.util.Iterator listenerIterator) {
076: listenerIterator.remove();
077: }
078:
079: public void completeLocalRollback(
080: java.util.Iterator listenerIterator) {
081: listenerIterator.remove();
082: }
083:
084: //----------------------------helper methods----------------------------------
085:
086: public Agent getAgent() {
087: return agent_;
088: }
089:
090: void checkForClosedConnection() throws SqlException {
091: if (agent_.connection_.isClosedX()) {
092: agent_.checkForDeferredExceptions();
093: throw new SqlException(agent_.logWriter_,
094: new ClientMessageId(
095: SQLState.LOB_METHOD_ON_CLOSED_CONNECTION));
096: } else {
097: agent_.checkForDeferredExceptions();
098: }
099: }
100:
101: void completeLocalRollback() {
102: ;
103: }
104:
105: void completeLocalCommit() {
106: ;
107: }
108:
109: /**
110: * Materialize the given stream into memory and update the internal
111: * length variable.
112: *
113: * @param is stream to use for input
114: * @param typeDesc description of the data type we are inserting,
115: * for instance <code>java.sql.Clob</code>
116: * @return a stream whose source is the materialized data
117: * @throws SqlException if the stream exceeds 2 GB, or an error happens
118: * while reading from the stream
119: */
120: protected InputStream materializeStream(InputStream is,
121: String typeDesc) throws SqlException {
122: final int GROWBY = 32 * 1024; // 32 KB
123: ArrayList byteArrays = new ArrayList();
124: byte[] curBytes = new byte[GROWBY];
125: int totalLength = 0;
126: int partLength = 0;
127: // Read all data from the stream, storing it in a number of arrays.
128: try {
129: do {
130: partLength = is.read(curBytes, 0, curBytes.length);
131: if (partLength == curBytes.length) {
132: byteArrays.add(curBytes);
133: // Make sure we don't exceed 2 GB by checking for overflow.
134: int newLength = totalLength + GROWBY;
135: if (newLength < 0 || newLength == Integer.MAX_VALUE) {
136: curBytes = new byte[Integer.MAX_VALUE
137: - totalLength];
138: } else {
139: curBytes = new byte[GROWBY];
140: }
141: }
142: if (partLength > 0) {
143: totalLength += partLength;
144: }
145: } while (partLength == GROWBY);
146: // Make sure stream is exhausted.
147: if (is.read() != -1) {
148: // We have exceeded 2 GB.
149: throw new SqlException(null, new ClientMessageId(
150: SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE),
151: typeDesc);
152: }
153: if (partLength > 0) {
154: byteArrays.add(curBytes);
155: }
156:
157: // Cleanup and set state.
158: curBytes = null;
159: sqlLength_ = totalLength;
160: lengthObtained_ = true;
161: // Return a stream whose source is a list of byte arrays.
162: // This avoids having to copy all the data into a single big array.
163: return new ByteArrayCombinerStream(byteArrays, totalLength);
164: } catch (IOException ioe) {
165: throw new SqlException(null, new ClientMessageId(
166: SQLState.LANG_STREAMING_COLUMN_I_O_EXCEPTION),
167: typeDesc, ioe);
168: }
169: }
170: }
|