KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > value > ValueLob


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.value;
6
7 import java.io.BufferedInputStream JavaDoc;
8 import java.io.ByteArrayInputStream JavaDoc;
9 import java.io.File JavaDoc;
10 import java.io.IOException JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.io.Reader JavaDoc;
13 import java.sql.PreparedStatement JavaDoc;
14 import java.sql.SQLException JavaDoc;
15
16 import org.h2.engine.Constants;
17 import org.h2.message.Message;
18 import org.h2.store.DataHandler;
19 import org.h2.store.FileStore;
20 import org.h2.store.FileStoreInputStream;
21 import org.h2.store.FileStoreOutputStream;
22 import org.h2.util.ByteUtils;
23 import org.h2.util.FileUtils;
24 import org.h2.util.IOUtils;
25 import org.h2.util.RandomUtils;
26 import org.h2.util.StringUtils;
27
28 /**
29  * @author Thomas
30  */

31
32 public class ValueLob extends Value {
33     // TODO lob: concatenate function for blob and clob (to create a large blob from pieces)
34
// and a getpart function (to get it in pieces) and make sure a file is created!
35

36     private int type;
37     private long precision;
38     private DataHandler handler;
39     private int tableId;
40     private int objectId;
41     private String JavaDoc fileName;
42     private boolean linked;
43     private byte[] small;
44     private int hash;
45     private boolean compression;
46     private FileStore tempFile;
47
48     private ValueLob(int type, DataHandler handler, String JavaDoc fileName, int tableId, int objectId, boolean linked, long precision, boolean compression) {
49         this.type = type;
50         this.handler = handler;
51         this.fileName = fileName;
52         this.tableId = tableId;
53         this.objectId = objectId;
54         this.linked = linked;
55         this.precision = precision;
56         this.compression = compression;
57     }
58
59     private static ValueLob copy(ValueLob lob) {
60         ValueLob copy = new ValueLob(lob.type, lob.handler, lob.fileName, lob.tableId, lob.objectId, lob.linked, lob.precision, lob.compression);
61         copy.small = lob.small;
62         copy.hash = lob.hash;
63         return copy;
64     }
65
66     private ValueLob(int type, byte[] small) throws SQLException JavaDoc {
67         this.type = type;
68         this.small = small;
69         if(small != null) {
70             if(type == Value.BLOB) {
71                 this.precision = small.length;
72             } else {
73                 this.precision = getString().length();
74             }
75         }
76     }
77
78     public static ValueLob createSmallLob(int type, byte[] small) throws SQLException JavaDoc {
79         return new ValueLob(type, small);
80     }
81
82     private static String JavaDoc getFileName(DataHandler handler, int tableId, int objectId) {
83         if (Constants.CHECK && tableId == 0 && objectId == 0) {
84             throw Message.getInternalError("0 LOB");
85         }
86         if(Constants.LOB_FILES_IN_DIRECTORIES) {
87             String JavaDoc table = tableId < 0 ? ".temp" : ".t" + tableId;
88             return getFileNamePrefix(handler.getDatabasePath(), objectId) + table + Constants.SUFFIX_LOB_FILE;
89         } else {
90             return handler.getDatabasePath() + "." + tableId + "." + objectId + Constants.SUFFIX_LOB_FILE;
91         }
92     }
93
94     public static ValueLob open(int type, DataHandler handler, int tableId, int objectId, long precision, boolean compression) {
95         String JavaDoc fileName = getFileName(handler, tableId, objectId);
96         return new ValueLob(type, handler, fileName, tableId, objectId, true, precision, compression);
97     }
98
99 // public static ValueLob createClobFromReader(Reader in, long length) throws SQLException {
100
// try {
101
// String s = IOUtils.readStringAndClose(in, (int)length);
102
// byte[] buff = StringUtils.utf8Encode(s);
103
// return new ValueLob(CLOB, buff);
104
// } catch (IOException e) {
105
// throw Message.convert(e);
106
// }
107
// }
108

109 // public static ValueLob createBlobFromInputStream(InputStream in, long length) throws SQLException {
110
// try {
111
// byte[] buff = IOUtils.readBytesAndClose(in, (int)length);
112
// return new ValueLob(BLOB, buff);
113
// } catch (IOException e) {
114
// throw Message.convert(e);
115
// }
116
// }
117

118     public static ValueLob createClob(Reader JavaDoc in, long length, DataHandler handler) throws SQLException JavaDoc {
119         try {
120             boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
121             long remaining = Long.MAX_VALUE;
122             if (length >= 0 && length < remaining) {
123                 remaining = length;
124             }
125             int len = getBufferSize(handler, compress, remaining);
126             char[] buff = new char[len];
127             len = IOUtils.readFully(in, buff, len);
128             len = len < 0 ? 0 : len;
129             if (len <= handler.getMaxLengthInplaceLob()) {
130                 byte[] small = StringUtils.utf8Encode(new String JavaDoc(buff, 0, len));
131                 return ValueLob.createSmallLob(Value.CLOB, small);
132             }
133             ValueLob lob = new ValueLob(Value.CLOB, null);
134             lob.createFromReader(buff, len, in, remaining, handler);
135             return lob;
136         } catch (IOException JavaDoc e) {
137             throw Message.convert(e);
138         }
139     }
140
141     private static int getBufferSize(DataHandler handler, boolean compress, long remaining) {
142         int bufferSize = compress ? Constants.IO_BUFFER_SIZE_COMPRESS : Constants.IO_BUFFER_SIZE;
143         while(bufferSize < remaining && bufferSize <= handler.getMaxLengthInplaceLob()) {
144             // the buffer size must be bigger than the inplace lob, otherwise we can't
145
// know if it must be stored in-place or not
146
bufferSize += Constants.IO_BUFFER_SIZE;
147         }
148         bufferSize = (int) Math.min(remaining, bufferSize);
149         return bufferSize;
150     }
151
152     private void createFromReader(char[] buff, int len, Reader JavaDoc in, long remaining, DataHandler handler) throws SQLException JavaDoc {
153         try {
154             FileStoreOutputStream out = initLarge(handler);
155             boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
156             try {
157                 while (true) {
158                     precision += len;
159                     byte[] b = StringUtils.utf8Encode(new String JavaDoc(buff, 0, len));
160                     out.write(b, 0, b.length);
161                     remaining -= len;
162                     if (remaining <= 0) {
163                         break;
164                     }
165                     len = getBufferSize(handler, compress, remaining);
166                     len = IOUtils.readFully(in, buff, len);
167                     if (len <= 0) {
168                         break;
169                     }
170                 }
171             } finally {
172                 out.close();
173             }
174         } catch (IOException JavaDoc e) {
175             throw Message.convert(e);
176         }
177     }
178
179     private static String JavaDoc getFileNamePrefix(String JavaDoc path, int objectId) {
180         String JavaDoc name;
181         int f = objectId % Constants.LOB_FILES_PER_DIRECTORY;
182         if(f > 0) {
183             name = File.separator + objectId;
184         } else {
185             name = "";
186         }
187         objectId /= Constants.LOB_FILES_PER_DIRECTORY;
188         while(objectId > 0) {
189             f = objectId % Constants.LOB_FILES_PER_DIRECTORY;
190             name = File.separator + f + Constants.SUFFIX_LOBS_DIRECTORY + name;
191             objectId /= Constants.LOB_FILES_PER_DIRECTORY;
192         }
193         name = path + Constants.SUFFIX_LOBS_DIRECTORY + name;
194         return name;
195     }
196
197     private static int getNewObjectId(String JavaDoc path) throws SQLException JavaDoc {
198         int objectId;
199         objectId = 0;
200         while(true) {
201             String JavaDoc dir = getFileNamePrefix(path, objectId);
202             String JavaDoc[] list = FileUtils.listFiles(dir);
203             int fileCount = 0;
204             boolean[] used = new boolean[Constants.LOB_FILES_PER_DIRECTORY];
205             for(int i=0; i<list.length; i++) {
206                 String JavaDoc name = list[i];
207                 if(name.endsWith(".db")) {
208                     name = name.substring(name.lastIndexOf(File.separatorChar) + 1);
209                     String JavaDoc n = name.substring(0, name.indexOf('.'));
210                     int id;
211                     try {
212                         id = Integer.parseInt(n);
213                     } catch(NumberFormatException JavaDoc e) {
214                         id = -1;
215                     }
216                     if(id > 0) {
217                         fileCount++;
218                         used[id % Constants.LOB_FILES_PER_DIRECTORY] = true;
219                     }
220                 }
221             }
222             int fileId = -1;
223             if(fileCount < Constants.LOB_FILES_PER_DIRECTORY) {
224                 for(int i=1; i<Constants.LOB_FILES_PER_DIRECTORY; i++) {
225                     if(!used[i]) {
226                         fileId = i;
227                         break;
228                     }
229                 }
230             }
231             if(fileId > 0) {
232                 objectId += fileId;
233                 break;
234             } else {
235                 if(objectId > Integer.MAX_VALUE / Constants.LOB_FILES_PER_DIRECTORY) {
236                     // this directory path is full: start from zero
237
// (this can happen only theoretically, for example if the random number generator is broken)
238
objectId = 0;
239                 } else {
240                     // start with 1 (otherwise we don't know the number of directories)
241
int dirId = RandomUtils.nextInt(Constants.LOB_FILES_PER_DIRECTORY - 1) + 1;
242                     objectId = objectId * Constants.LOB_FILES_PER_DIRECTORY;
243                     objectId += dirId * Constants.LOB_FILES_PER_DIRECTORY;
244                 }
245             }
246         }
247         return objectId;
248     }
249
250     public static ValueLob createBlob(InputStream JavaDoc in, long length, DataHandler handler) throws SQLException JavaDoc {
251         try {
252             long remaining = Long.MAX_VALUE;
253             boolean compress = handler.getLobCompressionAlgorithm(Value.BLOB) != null;
254             if (length >= 0 && length < remaining) {
255                 remaining = length;
256             }
257             int len = getBufferSize(handler, compress, remaining);
258             byte[] buff = new byte[len];
259             len = IOUtils.readFully(in, buff, len);
260             len = len < 0 ? 0 : len;
261             if (len <= handler.getMaxLengthInplaceLob()) {
262                 byte[] small = new byte[len];
263                 System.arraycopy(buff, 0, small, 0, len);
264                 return ValueLob.createSmallLob(Value.BLOB, small);
265             }
266             ValueLob lob = new ValueLob(Value.BLOB, null);
267             lob.createFromStream(buff, len, in, remaining, handler);
268             return lob;
269         } catch (IOException JavaDoc e) {
270             throw Message.convert(e);
271         }
272     }
273
274     private FileStoreOutputStream initLarge(DataHandler handler) throws IOException JavaDoc, SQLException JavaDoc {
275         this.handler = handler;
276         this.tableId = 0;
277         this.linked = false;
278         this.precision = 0;
279         this.small = null;
280         this.hash = 0;
281         String JavaDoc compressionAlgorithm = handler.getLobCompressionAlgorithm(type);
282         this.compression = compressionAlgorithm != null;
283         synchronized(handler) {
284             if(Constants.LOB_FILES_IN_DIRECTORIES) {
285                 objectId = getNewObjectId(handler.getDatabasePath());
286                 fileName = getFileNamePrefix(handler.getDatabasePath(), objectId) + ".temp.db";
287             } else {
288                 objectId = handler.allocateObjectId(false, true);
289                 fileName = handler.createTempFile();
290             }
291             tempFile = handler.openFile(fileName, false);
292             tempFile.autoDelete();
293         }
294         FileStoreOutputStream out = new FileStoreOutputStream(tempFile, handler, compressionAlgorithm);
295         return out;
296     }
297
298     private void createFromStream(byte[] buff, int len, InputStream JavaDoc in, long remaining, DataHandler handler) throws SQLException JavaDoc {
299         try {
300             FileStoreOutputStream out = initLarge(handler);
301             boolean compress = handler.getLobCompressionAlgorithm(Value.BLOB) != null;
302             try {
303                 while (true) {
304                     precision += len;
305                     out.write(buff, 0, len);
306                     remaining -= len;
307                     if (remaining <= 0) {
308                         break;
309                     }
310                     len = getBufferSize(handler, compress, remaining);
311                     len = IOUtils.readFully(in, buff, len);
312                     if (len <= 0) {
313                         break;
314                     }
315                 }
316             } finally {
317                 out.close();
318             }
319         } catch (IOException JavaDoc e) {
320             throw Message.convert(e);
321         }
322     }
323
324     public Value convertTo(int t) throws SQLException JavaDoc {
325         if (t == type) {
326             return this;
327         } else if (t == Value.CLOB) {
328             ValueLob copy = ValueLob.createClob(getReader(), -1, handler);
329             return copy;
330         } else if(t == Value.BLOB) {
331             ValueLob copy = ValueLob.createBlob(getInputStream(), -1, handler);
332             return copy;
333         }
334         return super.convertTo(t);
335     }
336     
337     public boolean isLinked() {
338         return linked;
339     }
340
341     public void unlink(DataHandler handler) throws SQLException JavaDoc {
342         if (linked && fileName != null) {
343             String JavaDoc temp;
344             if(Constants.LOB_FILES_IN_DIRECTORIES) {
345                 temp = getFileName(handler, -1, objectId);
346             } else {
347                 // just to get a filename - an empty file will be created
348
temp = handler.createTempFile();
349             }
350             // delete the temp file
351
// TODO could there be a race condition? maybe another thread creates the file again?
352
FileUtils.delete(temp);
353             // rename the current file to the temp file
354
FileUtils.rename(fileName, temp);
355             tempFile = FileStore.open(handler, temp, null);
356             tempFile.autoDelete();
357             tempFile.closeSilently();
358             fileName = temp;
359             linked = false;
360         }
361     }
362
363     public Value link(DataHandler handler, int tabId) throws SQLException JavaDoc {
364         if(fileName == null) {
365             this.tableId = tabId;
366             return this;
367         }
368         if(linked) {
369             ValueLob copy = ValueLob.copy(this);
370             if(Constants.LOB_FILES_IN_DIRECTORIES) {
371                 copy.objectId = getNewObjectId(handler.getDatabasePath());
372             } else {
373                 copy.objectId = handler.allocateObjectId(false, true);
374             }
375             copy.tableId = tabId;
376             String JavaDoc live = getFileName(handler, copy.tableId, copy.objectId);
377             FileUtils.copy(fileName, live);
378             copy.fileName = live;
379             copy.linked = true;
380             return copy;
381         }
382         if (!linked) {
383             this.tableId = tabId;
384             String JavaDoc live = getFileName(handler, tableId, objectId);
385             tempFile.stopAutoDelete();
386             tempFile = null;
387             FileUtils.rename(fileName, live);
388             fileName = live;
389             linked = true;
390         }
391         return this;
392     }
393
394     public int getTableId() {
395         return tableId;
396     }
397
398     public int getObjectId() {
399         return objectId;
400     }
401
402     public int getType() {
403         return type;
404     }
405
406     public long getPrecision() {
407         return precision;
408     }
409
410     public String JavaDoc getString() throws SQLException JavaDoc {
411         int len = precision > Integer.MAX_VALUE || precision == 0 ? Integer.MAX_VALUE : (int)precision;
412         try {
413             if (type == Value.CLOB) {
414                 if (small != null) {
415                     return StringUtils.utf8Decode(small);
416                 }
417                 return IOUtils.readStringAndClose(getReader(), len);
418             } else {
419                 byte[] buff;
420                 if (small != null) {
421                     buff = small;
422                 } else {
423                     buff = IOUtils.readBytesAndClose(getInputStream(), len);
424                 }
425                 return ByteUtils.convertBytesToString(buff);
426             }
427         } catch (IOException JavaDoc e) {
428             throw Message.convert(e);
429         }
430     }
431
432     public byte[] getBytes() throws SQLException JavaDoc {
433         byte[] data = getBytesNoCopy();
434         return ByteUtils.cloneByteArray(data);
435     }
436
437     public byte[] getBytesNoCopy() throws SQLException JavaDoc {
438         if (small != null) {
439             return small;
440         }
441         try {
442             return IOUtils.readBytesAndClose(getInputStream(), Integer.MAX_VALUE);
443         } catch (IOException JavaDoc e) {
444             throw Message.convert(e);
445         }
446     }
447
448     public int hashCode() {
449         if (hash == 0) {
450             try {
451                 hash = ByteUtils.getByteArrayHash(getBytes());
452             } catch(SQLException JavaDoc e) {
453                 // TODO hash code for lob: should not ignore exception
454
}
455         }
456         return hash;
457     }
458
459     protected int compareSecure(Value v, CompareMode mode) throws SQLException JavaDoc {
460         if(type == Value.CLOB) {
461             int c = getString().compareTo(v.getString());
462             return c == 0 ? 0 : (c < 0 ? -1 : 1);
463         } else {
464             byte[] v2 = v.getBytesNoCopy();
465             return ByteUtils.compareNotNull(getBytes(), v2);
466         }
467     }
468
469     public Object JavaDoc getObject() throws SQLException JavaDoc {
470         if(type == Value.CLOB) {
471             return getReader();
472         } else {
473             return getInputStream();
474         }
475     }
476
477     public Reader JavaDoc getReader() throws SQLException JavaDoc {
478         return IOUtils.getReader(getInputStream());
479     }
480
481     public InputStream JavaDoc getInputStream() throws SQLException JavaDoc {
482         if (fileName == null) {
483             return new ByteArrayInputStream JavaDoc(small);
484         }
485         FileStore store = handler.openFile(fileName, true);
486         return new BufferedInputStream JavaDoc(new FileStoreInputStream(store, handler, compression), Constants.IO_BUFFER_SIZE);
487     }
488
489     public void set(PreparedStatement JavaDoc prep, int parameterIndex) throws SQLException JavaDoc {
490         long p = getPrecision();
491         // TODO test if setBinaryStream with -1 works for other databases a well
492
if(p > Integer.MAX_VALUE || p <= 0) {
493             p = -1;
494         }
495         if(type == Value.BLOB) {
496             prep.setBinaryStream(parameterIndex, getInputStream(), (int)p);
497         } else {
498             prep.setCharacterStream(parameterIndex, getReader(), (int)p);
499         }
500     }
501
502     public String JavaDoc getSQL() {
503         try {
504             String JavaDoc s;
505             if(type == Value.CLOB) {
506                 if(precision < Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB) {
507                     s = getString();
508                     return StringUtils.quoteStringSQL(s);
509                 } else {
510                     return "READ_CLOB('" + fileName + "', "+precision+")";
511                     // TODO
512
}
513             } else {
514                 if(precision < Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB) {
515                     byte[] buff = getBytes();
516                     s = ByteUtils.convertBytesToString(buff);
517                     return "X'" + s + "'";
518                 } else {
519                     return "READ_BLOB('"+ fileName +"', "+precision+")";
520                 }
521             }
522         } catch(SQLException JavaDoc e) {
523             throw Message.convertToInternal(e);
524         }
525     }
526
527     public byte[] getSmall() {
528         return small;
529     }
530
531 // public String getJavaString() {
532
// // TODO value: maybe use another trick (at least the size should be ok?)
533
// return StringUtils.quoteJavaString(getSQL());
534
// }
535

536     public int getDisplaySize() {
537         // TODO displaysize of lob?
538
return 40;
539     }
540
541     protected boolean isEqual(Value v) {
542         try {
543             return compareSecure(v, null) == 0;
544         } catch(SQLException JavaDoc e) {
545             // TODO exceptions: improve concept, maybe remove throws SQLException almost everywhere
546
throw Message.getInternalError("compare", e);
547         }
548     }
549
550     public void convertToFileIfRequired(DataHandler handler) throws SQLException JavaDoc {
551         if(Constants.AUTO_CONVERT_LOB_TO_FILES && small != null && small.length > handler.getMaxLengthInplaceLob()) {
552             boolean compress = handler.getLobCompressionAlgorithm(type) != null;
553             int len = getBufferSize(handler, compress, Long.MAX_VALUE);
554             int tabId = tableId;
555             if(type == Value.BLOB) {
556                 createFromStream(new byte[len], 0, getInputStream(), Long.MAX_VALUE, handler);
557             } else {
558                 createFromReader(new char[len], 0, getReader(), Long.MAX_VALUE, handler);
559             }
560             Value v2 = link(handler, tabId);
561             if(Constants.CHECK && v2 != this) {
562                 throw Message.getInternalError();
563             }
564         }
565     }
566
567     public static void removeAllForTable(DataHandler handler, int tableId) throws SQLException JavaDoc {
568         if(Constants.LOB_FILES_IN_DIRECTORIES) {
569             String JavaDoc dir = getFileNamePrefix(handler.getDatabasePath(), 0);
570             removeAllForTable(handler, dir, tableId);
571         } else {
572             String JavaDoc prefix = handler.getDatabasePath();
573             String JavaDoc dir = FileUtils.getParent(prefix);
574             String JavaDoc[] list = FileUtils.listFiles(dir);
575             for(int i=0; i<list.length; i++) {
576                 String JavaDoc name = list[i];
577                 if(name.startsWith(prefix+"." + tableId) && name.endsWith(".lob.db")) {
578                     FileUtils.delete(name);
579                 }
580             }
581         }
582     }
583
584     private static void removeAllForTable(DataHandler handler, String JavaDoc dir, int tableId) throws SQLException JavaDoc {
585         String JavaDoc[] list = FileUtils.listFiles(dir);
586         for(int i=0; i<list.length; i++) {
587             if(FileUtils.isDirectory(list[i])) {
588                 removeAllForTable(handler, list[i], tableId);
589             } else {
590                 String JavaDoc name = list[i];
591                 if(name.endsWith(".t" + tableId + ".lob.db")) {
592                     FileUtils.delete(name);
593                 }
594             }
595         }
596     }
597
598     public boolean useCompression() {
599         return compression;
600     }
601
602 }
603
Popular Tags