KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > common > protocol > ByteArrayBlob


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2005 Emic Networks
4  * Contact: sequoia@continuent.org
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Initial developer(s): Marc Herbert
19  * Contributor(s): ______________________.
20  */

21
22 package org.continuent.sequoia.common.protocol;
23
24 import java.io.ByteArrayInputStream JavaDoc;
25 import java.io.DataInputStream JavaDoc;
26 import java.io.FileInputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.Serializable JavaDoc;
29 import java.sql.SQLException JavaDoc;
30
31 import org.continuent.sequoia.common.exceptions.NotImplementedException;
32 import org.continuent.sequoia.common.exceptions.driver.DriverSQLException;
33
34 /**
35  * The representation (mapping) in the Java <sup><small>TM </small> </sup>
36  * programming language of an SQL <code>BLOB</code> value. By default drivers
37  * implement <code>Blob</code> using an SQL <code>locator(BLOB)</code>,
38  * which means that a <code>Blob</code> object contains a logical pointer to
39  * the SQL <code>BLOB</code> data rather than the data itself. But since this
40  * is highly database-specific, we are unable to do that and implement Blobs
41  * using a simple private byte array copy instead. This may consume a lot of
42  * memory but this is both portable across databases and even legal from a JDBC
43  * standard point of view as long as our method
44  * {@link org.continuent.sequoia.driver.DatabaseMetaData#locatorsUpdateCopy()}
45  * returns true.
46  *
47  * @see java.sql.Blob
48  * @author <a HREF="mailto:Marc.Herbert@emicnetworks.com">Marc Herbert </a>
49  * @since JDK 1.2
50  */

51 public class ByteArrayBlob implements java.sql.Blob JavaDoc, Serializable JavaDoc
52 {
53   private static final long serialVersionUID = -3473780865755702765L;
54
55   /** The binary data that makes up this <code>BLOB</code>. */
56   byte[] internalArray;
57
58   // ------------- JDBC 2.1 / JDK 1.2-------------------
59

60   /**
61    * @see java.sql.Blob#length()
62    */

63   public long length() throws SQLException JavaDoc
64   {
65     checkInitialized();
66     return internalArray.length;
67   }
68
69   /**
70    * @see java.sql.Blob#getBytes(long, int)
71    */

72   public byte[] getBytes(long sqlPos, int length) throws SQLException JavaDoc
73   {
74     checkInitialized();
75     checkSQLRangeIsSupported(sqlPos, length);
76
77     int arrayPos = (int) (sqlPos - 1);
78     return resizedByteArray(internalArray, arrayPos, //
79
Math.min(length, // no more than asked for
80
internalArray.length - arrayPos)); // no more than what we have
81
}
82
83   /**
84    * @see java.sql.Blob#getBinaryStream()
85    */

86   public java.io.InputStream JavaDoc getBinaryStream() throws SQLException JavaDoc
87   {
88     checkInitialized();
89     return new ByteArrayInputStream JavaDoc(internalArray);
90   }
91
92   /**
93    * @see java.sql.Blob#position(byte[], long)
94    */

95   public long position(byte[] pattern, long sqlStart) throws SQLException JavaDoc
96   {
97     checkInitialized();
98     checkSQLRangeIsSupported(sqlStart, 0);
99
100     throw new NotImplementedException("position not yet implemented");
101   }
102
103   /**
104    * @see java.sql.Blob#position(java.sql.Blob, long)
105    */

106   public long position(java.sql.Blob JavaDoc pattern, long sqlStart)
107       throws SQLException JavaDoc
108   {
109     checkInitialized();
110     checkSQLRangeIsSupported(sqlStart, 0);
111
112     // FIXME: implement me
113
return position(pattern.getBytes(0, (int) pattern.length()), sqlStart);
114   }
115
116   // ------------- JDBC 3.0 / JDK 1.4-------------------
117

118   /**
119    * @see java.sql.Blob#setBytes(long, byte[], int, int)
120    */

121   public int setBytes(long sqlStartPos, byte[] srcArray) throws SQLException JavaDoc
122   {
123     return this.setBytes(sqlStartPos, srcArray, 0, srcArray.length);
124   }
125
126   /**
127    * @see java.sql.Blob#setBytes(long, byte[], int, int)
128    */

129   public int setBytes(long sqlStartPos, byte[] srcArray, int srcArrayOffset,
130       int copiedLength) throws SQLException JavaDoc
131   {
132     checkInitialized();
133     checkSQLRangeIsSupported(sqlStartPos, copiedLength);
134
135     int minimumLengthNeeded = (int) (sqlStartPos - 1) + copiedLength;
136
137     // If we are too small, let's extend ourselves
138
// FIXME: do the specs say we should do this?
139
if (this.length() < minimumLengthNeeded)
140       internalArray = resizedByteArray(internalArray, 0, minimumLengthNeeded);
141
142     // else FIXME: when we are "longer", should we remove our tail or keep
143
// it? Do the specs say something about this? Let's keep the tail for now.
144

145     // Finally copy argument to ourselves.
146
// Bytes between binaryData.length and pos-1 will stay to zero....
147
// FIXME: do the specs say something about this?
148
System.arraycopy(srcArray, srcArrayOffset, internalArray,
149         (int) (sqlStartPos - 1), copiedLength);
150
151     /*
152      * huh, what else ? OK, something else would make sense in case we don't
153      * extend the array.
154      */

155     return copiedLength;
156   }
157
158   /**
159    * @see java.sql.Blob#setBinaryStream(long)
160    */

161   public java.io.OutputStream JavaDoc setBinaryStream(long sqlStart)
162       throws SQLException JavaDoc
163   {
164     checkInitialized();
165     checkSQLRangeIsSupported(sqlStart, 0);
166
167     return new ByteArrayBlobOutputStream(this, (int) (sqlStart - 1));
168   }
169
170   /**
171    * @see java.sql.Blob#truncate(long)
172    */

173   public void truncate(long newLen) throws SQLException JavaDoc
174   {
175     checkInitialized();
176
177     if (newLen >= this.length())
178       return;
179
180     internalArray = resizedByteArray(internalArray, 0, (int) newLen);
181   }
182
183   // ----------- JDBC 4.0 --------------
184
/**
185    * This method frees the Blob object and releases the resources that it holds.
186    */

187   public void free()
188   {
189     internalArray = null;
190   }
191
192   // -------- non-standard, convenience constructors --------
193

194   /**
195    * Creates a new <code>Blob</code> object built from a copy of the given
196    * byte array.
197    *
198    * @param src the array to copy
199    */

200   public ByteArrayBlob(byte[] src)
201   {
202     // just clone the array
203
this.internalArray = resizedByteArray(src, 0, src.length);
204   }
205
206   /**
207    * Creates a Blob from the given File
208    *
209    * @param file file to read from
210    * @exception IOException if read fails
211    * @exception NotImplementedException file too big to fit into a byte array
212    */

213   public ByteArrayBlob(FileInputStream JavaDoc file) throws IOException JavaDoc,
214       NotImplementedException
215   {
216     long len = file.available();
217     if (len > Integer.MAX_VALUE)
218       throw new NotImplementedException("file too big");
219     internalArray = new byte[(int) len];
220     DataInputStream JavaDoc stream = new DataInputStream JavaDoc(file);
221     stream.readFully(internalArray);
222   }
223
224   // ------------------ BLOB internals ------------
225

226   /**
227    * BlobOutputStream needs it
228    */

229   byte[] getInternalByteArray()
230   {
231     return internalArray;
232   }
233
234   private void checkInitialized() throws DriverSQLException
235   {
236     if (null == internalArray)
237       throw new DriverSQLException("Blob has been freed");
238   }
239
240   /**
241    * Checks that we can handle SQL indexes (sqlStart) and (sqlEnd =
242    * sqlStart+len-1). Valid sqlStart begins at 1 (SQL-style). A reasonable
243    * sqlEnd of Blob is no more than Integer.MAX_VALUE+1 because we implement
244    * using Java arrays. This method is basically a check to use before casting
245    * from long to int.
246    *
247    * @param sqlStart start index
248    * @param len length
249    * @throws SQLException
250    */

251   static void checkSQLRangeIsSupported(long sqlStart, int len)
252       throws SQLException JavaDoc
253   {
254     long arrayStart = sqlStart - 1;
255     if (arrayStart < 0)
256     {
257       throw new DriverSQLException("Illegal argument: start of Blob/Clob ("
258           + sqlStart + ") cannot be less than 1");
259     }
260     if (arrayStart + len - 1 > Integer.MAX_VALUE)
261     {
262       throw new NotImplementedException("End of Blob/Clob ("
263           + (sqlStart + len - 1) + ") is too large. Blobs larger than "
264           + Integer.MAX_VALUE + " are not supported");
265     }
266   }
267
268   /**
269    * Returns a copy of the byte array argument starting at srcFrom and extended
270    * or shortened to newLength. srcFrom index starts from zero (regular style).
271    * <p>
272    * This roughly double the memory used... *sigh*
273    * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4655503 is tagged
274    * "fixed" but nothing changed ?!
275    */

276   private byte[] resizedByteArray(byte[] src, int srcStart, int newSize)
277   {
278     byte[] newArray = new byte[newSize];
279     System.arraycopy(src, srcStart, newArray, 0, Math.min(
280         src.length - srcStart, // don't pass the old size
281
newSize)); // don't pass the new size
282
return newArray;
283   }
284
285 }
Popular Tags