KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > scriptella > jdbc > Lobs


1 /*
2  * Copyright 2006-2007 The Scriptella Project Team.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package scriptella.jdbc;
17
18 import scriptella.util.IOUtils;
19 import scriptella.util.StringUtils;
20
21 import java.io.ByteArrayInputStream JavaDoc;
22 import java.io.ByteArrayOutputStream JavaDoc;
23 import java.io.Closeable JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileInputStream JavaDoc;
26 import java.io.FileNotFoundException JavaDoc;
27 import java.io.FileOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.InputStreamReader JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.io.OutputStreamWriter JavaDoc;
33 import java.io.Reader JavaDoc;
34 import java.io.StringReader JavaDoc;
35 import java.io.UnsupportedEncodingException JavaDoc;
36 import java.io.Writer JavaDoc;
37 import java.net.URL JavaDoc;
38 import java.net.URLConnection JavaDoc;
39 import java.sql.Blob JavaDoc;
40 import java.sql.Clob JavaDoc;
41 import java.sql.SQLException JavaDoc;
42
43 /**
44  * Factory for LOBs.
45  *
46  * @author Fyodor Kupolov
47  * @version 1.0
48  */

49 class Lobs {
50     private Lobs() { //Singleton
51
}
52
53     /**
54      * Create a new read-only BLOB for specified input stream.
55      * <p>The stream will be lazily read into memory or saved on disk depending on its length.
56      *
57      * @param is input stream to create blob.
58      * @return read-only Blob instance.
59      */

60     public static Blob JavaDoc newBlob(InputStream JavaDoc is) {
61         return new ReadonlyBlob(is);
62     }
63
64     /**
65      * Create a new read-only BLOB for specified input stream.
66      * <p>The stream will be lazily read into memory or saved on disk depending on its length.
67      *
68      * @param is input stream to create blob.
69      * @param length input stream length.
70      * @return read-only Blob instance.
71      */

72     public static Blob JavaDoc newBlob(InputStream JavaDoc is, long length) {
73         return new ReadonlyBlob(is, length);
74     }
75
76     /**
77      * Create a new read-only BLOB for specified URL.
78      * <p>The URL content will be lazily fetched into memory or saved on disk depending on its length.
79      *
80      * @param url URL of the blob content.
81      * @return read-only Blob instance.
82      * @see #newBlob(java.io.InputStream)
83      */

84     public static Blob JavaDoc newBlob(URL JavaDoc url) {
85         return new UrlBlob(url);
86     }
87
88     /**
89      * Create a new read-only CLOB for specified reader.
90      * <p>The reader will be lazily read into memory or saved on disk depending on its length.
91      *
92      * @param reader reader to create CLOB.
93      * @return read-only Clob instance.
94      */

95     public static Clob JavaDoc newClob(Reader JavaDoc reader) {
96         return new ReadonlyClob(reader);
97     }
98
99     /**
100      * Create a new read-only CLOB for specified reader.
101      * <p>The reader will be lazily read into memory or saved on disk depending on its length.
102      *
103      * @param reader reader to create CLOB.
104      * @param length content length.
105      * @return read-only Clob instance.
106      */

107     public static Clob JavaDoc newClob(Reader JavaDoc reader, long length) {
108         return new ReadonlyClob(reader, length);
109     }
110
111     /**
112      * Base class for LOBs.
113      * <p>Defines a common functionality and helper methods.
114      */

115     static abstract class AbstractLob<T extends Closeable JavaDoc> implements Closeable JavaDoc {
116         static int LOB_MAX_MEM = 100 * 1024; //100Kb
117
protected File JavaDoc tmpFile;
118         protected long length = -1;
119         protected T source;
120
121         /**
122          * For custom instantiation.
123          */

124         protected AbstractLob() {
125         }
126
127         protected AbstractLob(T source) {
128             if (source == null) {
129                 throw new IllegalArgumentException JavaDoc("Input source cannot be null");
130             }
131             this.source = source;
132         }
133
134         protected AbstractLob(T source, long length) {
135             if (source == null) {
136                 throw new IllegalArgumentException JavaDoc("Input source cannot be null");
137             }
138             if (length < 0) {
139                 throw new IllegalArgumentException JavaDoc("Input source length cannot be negative");
140             }
141             this.source = source;
142             this.length = length;
143         }
144
145         public void close() {
146             if (tmpFile != null) {
147                 tmpFile.delete();
148                 tmpFile = null;
149             }
150         }
151
152         public final void free() throws SQLException JavaDoc {
153             close();
154         }
155
156         /**
157          * Read bytes/chars from source stream/reader.
158          *
159          * @param inmemory true if data should be read into a memory.
160          * @return number of bytes/chars read.
161          * @throws IOException
162          */

163         protected abstract int read(boolean inmemory) throws IOException JavaDoc;
164
165         /**
166          * Flush the data stored in a memory to disk.
167          * <p>This method is invoked 0 or 1 time.
168          *
169          * @throws IOException
170          */

171         protected abstract void flushToDisk() throws IOException JavaDoc;
172
173         protected abstract void onInitComplete();
174
175         /**
176          * Performs a LOB initialization in following steps:
177          * <ul>
178          * <li>Reads content into memory until the size exceeds {@link #LOB_MAX_MEM}.
179          * <li>{@link #flushToDisk() Flushes} memory conent to disk.
180          * <li>Copy the left bytes to disk.
181          * </ul>
182          */

183         protected void init() {
184             if (source == null && length >= 0) {
185                 return;
186             }
187
188             int n;
189             try {
190                 for (length = 0; (n = read(true)) >= 0;) {
191                     length += n;
192                     if (length > LOB_MAX_MEM) {
193                         break;
194                     }
195                 }
196                 if (n >= 0) {
197                     flushToDisk();
198                     for (; (n = read(false)) >= 0;) {
199                         length += n;
200                     }
201                 }
202             } catch (IOException JavaDoc e) {
203                 throw new JdbcException("Cannot initialize temprorary file storage", e);
204             } finally {
205                 IOUtils.closeSilently(source);
206                 source = null;
207                 onInitComplete();
208             }
209         }
210
211         /**
212          * Returns true if this LOB is stored in memory.
213          */

214         public boolean isInMemory() {
215             return tmpFile == null;
216         }
217
218         /**
219          * Creates a temprorary file.
220          *
221          * @return output stream for temprorary file created.
222          * @throws IOException if I/O error occurs.
223          */

224         protected OutputStream JavaDoc createTempFile() throws IOException JavaDoc {
225             tmpFile = File.createTempFile("blob_", null);
226             tmpFile.deleteOnExit();
227             return new FileOutputStream JavaDoc(tmpFile);
228         }
229
230         /**
231          * Returns an input stream for temprorary file.
232          */

233         protected InputStream JavaDoc getTempFileInputStream() {
234             if (tmpFile == null) {
235                 throw new IllegalStateException JavaDoc("Internal error - temprorary file was not created");
236             }
237             try {
238                 return new FileInputStream JavaDoc(tmpFile);
239             } catch (FileNotFoundException JavaDoc e) {
240                 throw new JdbcException("Cannot open stream - temprorary file has been removed", e);
241             }
242         }
243
244         /**
245          * Returns the length of this LOB.
246          */

247         public long length() {
248             if (length < 0) {
249                 init();
250             }
251             return length;
252
253         }
254
255     }
256
257     /**
258      * Represents a BLOB located at specified URL.
259      */

260     static class UrlBlob extends ReadonlyBlob {
261         private URL JavaDoc url;
262
263         public UrlBlob(URL JavaDoc url) {
264             if (url == null) {
265                 throw new IllegalArgumentException JavaDoc("URL cannot be null");
266             }
267             this.url = url;
268         }
269
270         URL JavaDoc getUrl() {
271             return url;
272         }
273
274         @Override JavaDoc
275         protected void init() {
276             if (length < 0) {
277                 try {
278                     final URLConnection JavaDoc c = url.openConnection();
279                     source = c.getInputStream();
280                     length = c.getContentLength();
281                     if (length < 0) { //if length is undefined - fetch the url
282
super.init();
283                     }
284                 } catch (IOException JavaDoc e) {
285                     throw new JdbcException("Unable to read content for file " + url +
286                             ": " + e.getMessage(), e);
287                 }
288             }
289         }
290
291         @Override JavaDoc
292         public InputStream JavaDoc getBinaryStream() {
293             InputStream JavaDoc src = source;
294             if (src != null) {
295                 source = null;
296                 return src;
297             } else {
298                 length = -1;
299                 init();
300                 src = source;
301                 source = null;
302                 return (src == null) ? super.getBinaryStream() : src;
303             }
304         }
305     }
306
307     /**
308      * Readonly implementation of {@link java.sql.Blob}.
309      *
310      * @author Fyodor Kupolov
311      * @version 1.0
312      */

313     static class ReadonlyBlob extends AbstractLob<InputStream JavaDoc> implements Blob JavaDoc {
314         private byte[] bytes;
315         private byte[] buffer = new byte[8192];
316         private ByteArrayOutputStream JavaDoc memStream;
317         private OutputStream JavaDoc diskStream;
318
319         /**
320          * For custom instantion.
321          */

322         protected ReadonlyBlob() {
323         }
324
325         public ReadonlyBlob(InputStream JavaDoc source) {
326             super(source);
327         }
328
329         public ReadonlyBlob(InputStream JavaDoc source, long length) {
330             super(source, length);
331         }
332
333         protected int read(boolean inmemory) throws IOException JavaDoc {
334             int n = source.read(buffer);
335             if (n > 0) {
336                 if (inmemory) {
337                     if (memStream == null) {
338                         memStream = new ByteArrayOutputStream JavaDoc(n);
339                     }
340                     memStream.write(buffer, 0, n);
341                 } else {
342                     diskStream.write(buffer, 0, n);
343                 }
344             }
345             return n;
346         }
347
348         protected void flushToDisk() throws IOException JavaDoc {
349             diskStream = createTempFile();
350             memStream.writeTo(diskStream);
351             memStream = null;
352         }
353
354         protected void onInitComplete() {
355             if (diskStream != null) {
356                 IOUtils.closeSilently(diskStream);
357                 diskStream = null;
358             }
359             if (memStream != null) {
360                 bytes = memStream.toByteArray();
361                 memStream = null;
362             }
363         }
364
365         public InputStream JavaDoc getBinaryStream() {
366             init();
367             if (isInMemory()) {
368                 return new ByteArrayInputStream JavaDoc(bytes);
369             } else {
370                 return getTempFileInputStream();
371             }
372         }
373
374         public void close() {
375             super.close();
376             if (bytes != null) {
377                 bytes = null;
378             }
379         }
380
381         public String JavaDoc toString() {
382             try {
383                 return "BLOB: " + StringUtils.consoleFormat(
384                         new String JavaDoc(IOUtils.toByteArray(getBinaryStream(), 1024)));
385             } catch (Exception JavaDoc e) {
386                 return "BLOB: " + e;
387             }
388         }
389
390         //--------------- Unsupported methods
391
public byte[] getBytes(long pos, int length) throws SQLException JavaDoc {
392             throw new SQLException JavaDoc("Unsupported operation"); //Due to performance reasons
393
}
394
395         public long position(byte pattern[], long start) throws SQLException JavaDoc {
396             throw new SQLException JavaDoc("Unsupported operation");
397         }
398
399         public long position(Blob JavaDoc pattern, long start) throws SQLException JavaDoc {
400             throw new SQLException JavaDoc("Unsupported operation");
401         }
402
403         public int setBytes(long pos, byte[] bytes) throws SQLException JavaDoc {
404             throw new SQLException JavaDoc("Unsupported operation");
405         }
406
407         public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException JavaDoc {
408             throw new SQLException JavaDoc("Unsupported operation");
409         }
410
411         public OutputStream JavaDoc setBinaryStream(long pos) throws SQLException JavaDoc {
412             throw new SQLException JavaDoc("Unsupported operation");
413         }
414
415         public void truncate(long len) throws SQLException JavaDoc {
416             throw new SQLException JavaDoc("Unsupported operation");
417         }
418
419         public InputStream JavaDoc getBinaryStream(long pos, long length) throws SQLException JavaDoc {
420             throw new SQLException JavaDoc("Unsupported operation");
421         }
422
423     }
424
425     /**
426      * Represents a read-only CLOB.
427      */

428     static class ReadonlyClob extends AbstractLob<Reader JavaDoc> implements Clob JavaDoc {
429         private String JavaDoc string;
430         private char[] buffer = new char[8192];
431         private StringBuilder JavaDoc mem;
432         private Writer JavaDoc diskWriter;
433
434         public ReadonlyClob(Reader JavaDoc source) {
435             super(source);
436         }
437
438         public ReadonlyClob(Reader JavaDoc source, long length) {
439             super(source, length);
440         }
441
442         protected int read(boolean inmemory) throws IOException JavaDoc {
443             int n = source.read(buffer);
444             if (n > 0) {
445                 if (inmemory) {
446                     if (mem == null) {
447                         mem = new StringBuilder JavaDoc(n);
448                     }
449                     mem.append(buffer, 0, n);
450                 } else {
451                     diskWriter.write(buffer, 0, n);
452                 }
453             }
454             return n;
455         }
456
457         protected void flushToDisk() throws IOException JavaDoc {
458             diskWriter = new OutputStreamWriter JavaDoc(createTempFile(), "UTF-8");
459             diskWriter.append(mem);
460             mem = null;
461         }
462
463         protected void onInitComplete() {
464             if (diskWriter != null) {
465                 IOUtils.closeSilently(diskWriter);
466                 diskWriter = null;
467             }
468             if (mem != null) {
469                 string = mem.toString();
470                 mem = null;
471             }
472         }
473
474         public Reader JavaDoc getCharacterStream() {
475             init();
476             if (isInMemory()) {
477                 return new StringReader JavaDoc(string);
478             } else {
479                 try {
480                     return new InputStreamReader JavaDoc(getTempFileInputStream(), "UTF-8");
481                 } catch (UnsupportedEncodingException JavaDoc e) {
482                     throw new IllegalStateException JavaDoc(e); //should never happen
483
}
484             }
485         }
486
487         /**
488          * For debug purposes.
489          */

490         public String JavaDoc toString() {
491             try {
492                 return "CLOB: " + StringUtils.consoleFormat(IOUtils.toString(getCharacterStream(), 1024));
493             } catch (Exception JavaDoc e) {
494                 return "CLOB: " + e;
495             }
496         }
497
498         //--------------- Unsupported methods
499
public String JavaDoc getSubString(long pos, int length) throws SQLException JavaDoc {
500             throw new SQLException JavaDoc("Unsupported operation");
501         }
502
503
504         public InputStream JavaDoc getAsciiStream() throws SQLException JavaDoc {
505             throw new SQLException JavaDoc("Unsupported operation");
506         }
507
508         public long position(String JavaDoc searchstr, long start) throws SQLException JavaDoc {
509             throw new SQLException JavaDoc("Unsupported operation");
510         }
511
512         public long position(Clob JavaDoc searchstr, long start) throws SQLException JavaDoc {
513             throw new SQLException JavaDoc("Unsupported operation");
514         }
515
516         public int setString(long pos, String JavaDoc str) throws SQLException JavaDoc {
517             throw new SQLException JavaDoc("Unsupported operation");
518         }
519
520         public int setString(long pos, String JavaDoc str, int offset, int len) throws SQLException JavaDoc {
521             throw new SQLException JavaDoc("Unsupported operation");
522         }
523
524         public OutputStream JavaDoc setAsciiStream(long pos) throws SQLException JavaDoc {
525             throw new SQLException JavaDoc("Unsupported operation");
526         }
527
528         public Writer JavaDoc setCharacterStream(long pos) throws SQLException JavaDoc {
529             throw new SQLException JavaDoc("Unsupported operation");
530         }
531
532         public void truncate(long len) throws SQLException JavaDoc {
533             throw new SQLException JavaDoc("Unsupported operation");
534         }
535
536         public Reader JavaDoc getCharacterStream(long pos, long length) throws SQLException JavaDoc {
537             throw new SQLException JavaDoc("Unsupported operation");
538         }
539     }
540
541 }
542
Popular Tags