| 1 16 package scriptella.jdbc; 17 18 import scriptella.util.IOUtils; 19 import scriptella.util.StringUtils; 20 21 import java.io.ByteArrayInputStream ; 22 import java.io.ByteArrayOutputStream ; 23 import java.io.Closeable ; 24 import java.io.File ; 25 import java.io.FileInputStream ; 26 import java.io.FileNotFoundException ; 27 import java.io.FileOutputStream ; 28 import java.io.IOException ; 29 import java.io.InputStream ; 30 import java.io.InputStreamReader ; 31 import java.io.OutputStream ; 32 import java.io.OutputStreamWriter ; 33 import java.io.Reader ; 34 import java.io.StringReader ; 35 import java.io.UnsupportedEncodingException ; 36 import java.io.Writer ; 37 import java.net.URL ; 38 import java.net.URLConnection ; 39 import java.sql.Blob ; 40 import java.sql.Clob ; 41 import java.sql.SQLException ; 42 43 49 class Lobs { 50 private Lobs() { } 52 53 60 public static Blob newBlob(InputStream is) { 61 return new ReadonlyBlob(is); 62 } 63 64 72 public static Blob newBlob(InputStream is, long length) { 73 return new ReadonlyBlob(is, length); 74 } 75 76 84 public static Blob newBlob(URL url) { 85 return new UrlBlob(url); 86 } 87 88 95 public static Clob newClob(Reader reader) { 96 return new ReadonlyClob(reader); 97 } 98 99 107 public static Clob newClob(Reader reader, long length) { 108 return new ReadonlyClob(reader, length); 109 } 110 111 115 static abstract class AbstractLob<T extends Closeable > implements Closeable { 116 static int LOB_MAX_MEM = 100 * 1024; protected File tmpFile; 118 protected long length = -1; 119 protected T source; 120 121 124 protected AbstractLob() { 125 } 126 127 protected AbstractLob(T source) { 128 if (source == null) { 129 throw new IllegalArgumentException ("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 ("Input source cannot be null"); 137 } 138 if (length < 0) { 139 throw new IllegalArgumentException ("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 { 153 close(); 154 } 155 156 163 protected abstract int read(boolean inmemory) throws IOException ; 164 165 171 protected abstract void flushToDisk() throws IOException ; 172 173 protected abstract void onInitComplete(); 174 175 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 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 214 public boolean isInMemory() { 215 return tmpFile == null; 216 } 217 218 224 protected OutputStream createTempFile() throws IOException { 225 tmpFile = File.createTempFile("blob_", null); 226 tmpFile.deleteOnExit(); 227 return new FileOutputStream (tmpFile); 228 } 229 230 233 protected InputStream getTempFileInputStream() { 234 if (tmpFile == null) { 235 throw new IllegalStateException ("Internal error - temprorary file was not created"); 236 } 237 try { 238 return new FileInputStream (tmpFile); 239 } catch (FileNotFoundException e) { 240 throw new JdbcException("Cannot open stream - temprorary file has been removed", e); 241 } 242 } 243 244 247 public long length() { 248 if (length < 0) { 249 init(); 250 } 251 return length; 252 253 } 254 255 } 256 257 260 static class UrlBlob extends ReadonlyBlob { 261 private URL url; 262 263 public UrlBlob(URL url) { 264 if (url == null) { 265 throw new IllegalArgumentException ("URL cannot be null"); 266 } 267 this.url = url; 268 } 269 270 URL getUrl() { 271 return url; 272 } 273 274 @Override  275 protected void init() { 276 if (length < 0) { 277 try { 278 final URLConnection c = url.openConnection(); 279 source = c.getInputStream(); 280 length = c.getContentLength(); 281 if (length < 0) { super.init(); 283 } 284 } catch (IOException e) { 285 throw new JdbcException("Unable to read content for file " + url + 286 ": " + e.getMessage(), e); 287 } 288 } 289 } 290 291 @Override  292 public InputStream getBinaryStream() { 293 InputStream 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 313 static class ReadonlyBlob extends AbstractLob<InputStream > implements Blob { 314 private byte[] bytes; 315 private byte[] buffer = new byte[8192]; 316 private ByteArrayOutputStream memStream; 317 private OutputStream diskStream; 318 319 322 protected ReadonlyBlob() { 323 } 324 325 public ReadonlyBlob(InputStream source) { 326 super(source); 327 } 328 329 public ReadonlyBlob(InputStream source, long length) { 330 super(source, length); 331 } 332 333 protected int read(boolean inmemory) throws IOException { 334 int n = source.read(buffer); 335 if (n > 0) { 336 if (inmemory) { 337 if (memStream == null) { 338 memStream = new ByteArrayOutputStream (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 { 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 getBinaryStream() { 366 init(); 367 if (isInMemory()) { 368 return new ByteArrayInputStream (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 toString() { 382 try { 383 return "BLOB: " + StringUtils.consoleFormat( 384 new String (IOUtils.toByteArray(getBinaryStream(), 1024))); 385 } catch (Exception e) { 386 return "BLOB: " + e; 387 } 388 } 389 390 public byte[] getBytes(long pos, int length) throws SQLException { 392 throw new SQLException ("Unsupported operation"); } 394 395 public long position(byte pattern[], long start) throws SQLException { 396 throw new SQLException ("Unsupported operation"); 397 } 398 399 public long position(Blob pattern, long start) throws SQLException { 400 throw new SQLException ("Unsupported operation"); 401 } 402 403 public int setBytes(long pos, byte[] bytes) throws SQLException { 404 throw new SQLException ("Unsupported operation"); 405 } 406 407 public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { 408 throw new SQLException ("Unsupported operation"); 409 } 410 411 public OutputStream setBinaryStream(long pos) throws SQLException { 412 throw new SQLException ("Unsupported operation"); 413 } 414 415 public void truncate(long len) throws SQLException { 416 throw new SQLException ("Unsupported operation"); 417 } 418 419 public InputStream getBinaryStream(long pos, long length) throws SQLException { 420 throw new SQLException ("Unsupported operation"); 421 } 422 423 } 424 425 428 static class ReadonlyClob extends AbstractLob<Reader > implements Clob { 429 private String string; 430 private char[] buffer = new char[8192]; 431 private StringBuilder mem; 432 private Writer diskWriter; 433 434 public ReadonlyClob(Reader source) { 435 super(source); 436 } 437 438 public ReadonlyClob(Reader source, long length) { 439 super(source, length); 440 } 441 442 protected int read(boolean inmemory) throws IOException { 443 int n = source.read(buffer); 444 if (n > 0) { 445 if (inmemory) { 446 if (mem == null) { 447 mem = new StringBuilder (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 { 458 diskWriter = new OutputStreamWriter (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 getCharacterStream() { 475 init(); 476 if (isInMemory()) { 477 return new StringReader (string); 478 } else { 479 try { 480 return new InputStreamReader (getTempFileInputStream(), "UTF-8"); 481 } catch (UnsupportedEncodingException e) { 482 throw new IllegalStateException (e); } 484 } 485 } 486 487 490 public String toString() { 491 try { 492 return "CLOB: " + StringUtils.consoleFormat(IOUtils.toString(getCharacterStream(), 1024)); 493 } catch (Exception e) { 494 return "CLOB: " + e; 495 } 496 } 497 498 public String getSubString(long pos, int length) throws SQLException { 500 throw new SQLException ("Unsupported operation"); 501 } 502 503 504 public InputStream getAsciiStream() throws SQLException { 505 throw new SQLException ("Unsupported operation"); 506 } 507 508 public long position(String searchstr, long start) throws SQLException { 509 throw new SQLException ("Unsupported operation"); 510 } 511 512 public long position(Clob searchstr, long start) throws SQLException { 513 throw new SQLException ("Unsupported operation"); 514 } 515 516 public int setString(long pos, String str) throws SQLException { 517 throw new SQLException ("Unsupported operation"); 518 } 519 520 public int setString(long pos, String str, int offset, int len) throws SQLException { 521 throw new SQLException ("Unsupported operation"); 522 } 523 524 public OutputStream setAsciiStream(long pos) throws SQLException { 525 throw new SQLException ("Unsupported operation"); 526 } 527 528 public Writer setCharacterStream(long pos) throws SQLException { 529 throw new SQLException ("Unsupported operation"); 530 } 531 532 public void truncate(long len) throws SQLException { 533 throw new SQLException ("Unsupported operation"); 534 } 535 536 public Reader getCharacterStream(long pos, long length) throws SQLException { 537 throw new SQLException ("Unsupported operation"); 538 } 539 } 540 541 } 542 | Popular Tags |