|
OracleDataStore |
|
1 /* 2 * OracleDataStore.java 3 * 4 * Copyright (c) 1998-2001, The University of Sheffield. 5 * 6 * This file is part of GATE (see http://gate.ac.uk/), and is free 7 * software, licenced under the GNU Library General Public License, 8 * Version 2, June 1991 (in the distribution as file licence.html, 9 * and also available at http://gate.ac.uk/gate/licence.html). 10 * 11 * Marin Dimitrov, 18/Sep/2001 12 * 13 * $Id: OracleDataStore.java,v 1.146 2002/03/07 19:41:03 marin Exp $ 14 */ 15 16 package gate.persist; 17 18 import java.sql.*; 19 import java.net.*; 20 import java.util.*; 21 import java.io.*; 22 23 import oracle.sql.*; 24 import oracle.jdbc.driver.*; 25 26 27 import junit.framework.*; 28 29 import gate.*; 30 import gate.util.*; 31 import gate.event.*; 32 import gate.security.*; 33 import gate.security.SecurityException; //hide the more general exception 34 import gate.corpora.*; 35 import gate.annotation.*; 36 import gate.creole.ResourceData; 37 38 public class OracleDataStore extends JDBCDataStore { 39 40 /** Name of this resource */ 41 public static final String DS_COMMENT = "GATE Oracle datastore"; 42 43 /** the icon for this resource */ 44 public static final String DS_ICON_NAME = "ora_ds.gif"; 45 46 /** Debug flag */ 47 private static final boolean DEBUG = false; 48 49 /** "true" value for Oracle (supports no boolean type) */ 50 private static final int ORACLE_TRUE = 1; 51 /** "false" value for Oracle (supports no boolean type) */ 52 private static final int ORACLE_FALSE = 0; 53 54 /** used internaly, may change in the future */ 55 private static final int READ_ACCESS = 0; 56 /** used internaly, may change in the future */ 57 private static final int WRITE_ACCESS = 1; 58 59 /** size of the Oracle varrays used for bulc inserts */ 60 private static final int VARRAY_SIZE = 10; 61 62 /** the size in bytes if varchar2 column in Oracle 63 * when a String is stored in Oracle it may be too long 64 * for a varchar2 value, and then CLOB will be used 65 * Note that the limit is in bytes, not in characters, so 66 * in the worst case this will limit the string to 4000/3 characters 67 * */ 68 private static final int ORACLE_VARCHAR_LIMIT_BYTES = 4000; 69 70 /** maximum number of bytes that represent a char in UTF8 database */ 71 private static final int UTF_BYTES_PER_CHAR_MAX = 3; 72 73 /** maximum number of characters per string stored as varchar2 74 * if longer then stored as CLOB 75 * */ 76 private static final int ORACLE_VARCHAR_MAX_SYMBOLS = 77 ORACLE_VARCHAR_LIMIT_BYTES/UTF_BYTES_PER_CHAR_MAX; 78 79 /** read buffer size (for reading CLOBs) */ 80 private static final int INTERNAL_BUFFER_SIZE = 16*1024; 81 82 /** default constructor - just call the super constructor 83 * (may change in the future) 84 * */ 85 public OracleDataStore() { 86 87 super(); 88 } 89 90 /** 91 * Save: synchonise the in-memory image of the LR with the persistent 92 * image. 93 */ 94 public String getComment() { 95 return OracleDataStore.DS_COMMENT; 96 } 97 98 /** 99 * Returns the name of the icon to be used when this datastore is displayed 100 * in the GUI 101 */ 102 public String getIconName() { 103 return OracleDataStore.DS_ICON_NAME; 104 } 105 106 107 108 /** Get the name of an LR from its ID. */ 109 public String getLrName(Object lrId) 110 throws PersistenceException { 111 112 if (false == lrId instanceof Long) { 113 throw new IllegalArgumentException(); 114 } 115 116 Long ID = (Long)lrId; 117 118 CallableStatement stmt = null; 119 120 try { 121 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.get_lr_name(?,?) }"); 122 stmt.setLong(1,ID.longValue()); 123 stmt.registerOutParameter(2,java.sql.Types.VARCHAR); 124 stmt.execute(); 125 String result = stmt.getString(2); 126 127 return result; 128 } 129 catch(SQLException sqle) { 130 throw new PersistenceException("can't get LR name from DB: ["+ sqle.getMessage()+"]"); 131 } 132 finally { 133 DBHelper.cleanup(stmt); 134 } 135 } 136 137 138 139 /** Set the URL for the underlying storage mechanism. */ 140 public void setStorageUrl(String storageUrl) throws PersistenceException { 141 142 super.setStorageUrl(storageUrl); 143 144 } 145 146 147 148 /** Get the URL for the underlying storage mechanism. */ 149 public String getStorageUrl() { 150 151 return super.getStorageUrl(); 152 } 153 154 155 156 /** 157 * Create a new data store. <B>NOTE:</B> for some data stores 158 * creation is an system administrator task; in such cases this 159 * method will throw an UnsupportedOperationException. 160 */ 161 public void create() 162 throws PersistenceException, UnsupportedOperationException { 163 164 super.create(); 165 } 166 167 168 169 /** Open a connection to the data store. */ 170 public void open() throws PersistenceException { 171 172 super.open(); 173 174 try { 175 //set statement caching for Oracle 176 ((OracleConnection)this.jdbcConn).setStmtCacheSize(50); 177 } 178 catch(SQLException sqle) { 179 throw new PersistenceException(sqle); 180 } 181 } 182 183 184 185 /** Close the data store. */ 186 public void close() throws PersistenceException { 187 188 super.close(); 189 } 190 191 192 193 /** 194 * Delete the data store. <B>NOTE:</B> for some data stores 195 * deletion is an system administrator task; in such cases this 196 * method will throw an UnsupportedOperationException. 197 */ 198 public void delete() 199 throws PersistenceException, UnsupportedOperationException { 200 201 //0. user session should be set 202 /* if (null == this.session) { 203 throw new SecurityException("user session not set"); 204 } 205 */ 206 super.delete(); 207 } 208 209 210 211 /** 212 * Delete a resource from the data store. 213 * @param lrId a data-store specific unique identifier for the resource 214 * @param lrClassName class name of the type of resource 215 */ 216 public void delete(String lrClassName, Object lrId) 217 throws PersistenceException,SecurityException { 218 //0. preconditions 219 if (false == lrId instanceof Long) { 220 throw new IllegalArgumentException(); 221 } 222 223 if (!lrClassName.equals(DBHelper.DOCUMENT_CLASS) && 224 !lrClassName.equals(DBHelper.CORPUS_CLASS)) { 225 throw new IllegalArgumentException("Only Corpus and Document classes are supported" + 226 " by Database data store"); 227 } 228 229 //1. check session 230 if (null == this.session) { 231 throw new SecurityException("session not set"); 232 } 233 234 if (false == this.ac.isValidSession(this.session)) { 235 throw new SecurityException("invalid session supplied"); 236 } 237 238 //2. check permissions 239 if (false == canWriteLR(lrId)) { 240 throw new SecurityException("insufficient privileges"); 241 } 242 243 //3. try to lock document, so that we'll be sure no one is editing it 244 //NOTE: use the private method 245 User lockingUser = this.getLockingUser((Long)lrId); 246 User currUser = this.session.getUser(); 247 248 if (null != lockingUser && false == lockingUser.equals(currUser)) { 249 //oops, someone is editing now 250 throw new PersistenceException("LR locked by another user"); 251 } 252 253 boolean transFailed = false; 254 try { 255 //4. autocommit should be FALSE because of LOBs 256 // this.jdbcConn.setAutoCommit(false); 257 beginTrans(); 258 259 //5. perform changes, if anything goes wrong, rollback 260 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) { 261 deleteDocument((Long)lrId); 262 } 263 else { 264 deleteCorpus((Long)lrId); 265 } 266 267 //6. done, commit 268 // this.jdbcConn.commit(); 269 commitTrans(); 270 } 271 // catch(SQLException sqle) { 272 // transFailed = true; 273 // throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]"); 274 // } 275 catch(PersistenceException pe) { 276 transFailed = true; 277 throw(pe); 278 } 279 finally { 280 //problems? 281 if (transFailed) { 282 // this.jdbcConn.rollback(); 283 rollbackTrans(); 284 } 285 } 286 287 //7, unlock 288 //do nothing - the resource does not exist anymore 289 290 //8. delete from the list of dependent resources 291 boolean resourceFound = false; 292 Iterator it = this.dependentResources.iterator(); 293 while (it.hasNext()) { 294 LanguageResource lr = (LanguageResource)it.next(); 295 if (lr.getLRPersistenceId().equals(lrId)) { 296 resourceFound = true; 297 it.remove(); 298 break; 299 } 300 } 301 302 //Assert.assertTrue(resourceFound); 303 304 //9. let the world know about it 305 fireResourceDeleted( 306 new DatastoreEvent(this, DatastoreEvent.RESOURCE_DELETED, null, lrId)); 307 308 //10. unload the resource form the GUI 309 try { 310 unloadLR((Long)lrId); 311 } 312 catch(GateException ge) { 313 Err.prln("can't unload resource from GUI..."); 314 } 315 } 316 317 318 319 /** 320 * helper method for delete() 321 * never call it directly beause proper events will not be fired 322 */ 323 private void deleteDocument(Long lrId) 324 throws PersistenceException { 325 326 //0. preconditions 327 Assert.assertNotNull(lrId); 328 329 CallableStatement stmt = null; 330 331 //1. delete from DB 332 try { 333 stmt = this.jdbcConn.prepareCall( 334 "{ call "+Gate.DB_OWNER+".persist.delete_document(?) }"); 335 stmt.setLong(1,lrId.longValue()); 336 stmt.execute(); 337 } 338 catch(SQLException sqle) { 339 throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]"); 340 } 341 finally { 342 DBHelper.cleanup(stmt); 343 } 344 } 345 346 347 348 /** 349 * helper method for delete() 350 * never call it directly beause proper events will not be fired 351 */ 352 private void deleteCorpus(Long lrId) 353 throws PersistenceException { 354 355 Long ID = (Long)lrId; 356 357 CallableStatement stmt = null; 358 359 try { 360 stmt = this.jdbcConn.prepareCall( 361 "{ call "+Gate.DB_OWNER+".persist.delete_corpus(?) }"); 362 stmt.setLong(1,ID.longValue()); 363 stmt.execute(); 364 } 365 catch(SQLException sqle) { 366 throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]"); 367 } 368 finally { 369 DBHelper.cleanup(stmt); 370 } 371 } 372 373 374 375 /** 376 * Save: synchonise the in-memory image of the LR with the persistent 377 * image. 378 */ 379 public void sync(LanguageResource lr) 380 throws PersistenceException,SecurityException { 381 382 //4.delegate (open a new transaction) 383 _sync(lr,true); 384 } 385 386 387 388 /** 389 * Save: synchonise the in-memory image of the LR with the persistent 390 * image. 391 */ 392 private void _sync(LanguageResource lr, boolean openNewTrans) 393 throws PersistenceException,SecurityException { 394 395 //0.preconditions 396 Assert.assertNotNull(lr); 397 Long lrID = (Long)lr.getLRPersistenceId(); 398 399 if (false == lr instanceof Document && 400 false == lr instanceof Corpus) { 401 //only documents and corpuses could be serialized in DB 402 throw new IllegalArgumentException("only Documents and Corpuses could "+ 403 "be serialized in DB"); 404 } 405 406 // check that this LR is one of ours (i.e. has been adopted) 407 if( null == lr.getDataStore() || false == lr.getDataStore().equals(this)) 408 throw new PersistenceException( 409 "This LR is not stored in this DataStore" 410 ); 411 412 413 //1. check session 414 if (null == this.session) { 415 throw new SecurityException("session not set"); 416 } 417 418 if (false == this.ac.isValidSession(this.session)) { 419 throw new SecurityException("invalid session supplied"); 420 } 421 422 //2. check permissions 423 if (false == canWriteLR(lrID)) { 424 throw new SecurityException("insufficient privileges"); 425 } 426 427 //3. is the resource locked? 428 User lockingUser = getLockingUser(lr); 429 User currUser = this.session.getUser(); 430 431 if (lockingUser != null && false == lockingUser.equals(currUser)) { 432 throw new PersistenceException("document is locked by another user and cannot be synced"); 433 } 434 435 436 boolean transFailed = false; 437 try { 438 //2. autocommit should be FALSE because of LOBs 439 if (openNewTrans) { 440 // this.jdbcConn.setAutoCommit(false); 441 beginTrans(); 442 } 443 444 //3. perform changes, if anything goes wrong, rollback 445 if (lr instanceof Document) { 446 syncDocument((Document)lr); 447 } 448 else { 449 syncCorpus((Corpus)lr); 450 } 451 452 //4. done, commit 453 if (openNewTrans) { 454 // this.jdbcConn.commit(); 455 commitTrans(); 456 } 457 } 458 catch(PersistenceException pe) { 459 transFailed = true; 460 throw(pe); 461 } 462 finally { 463 //problems? 464 if (transFailed) { 465 rollbackTrans(); 466 // try { 467 // this.jdbcConn.rollback(); 468 // } 469 // catch(SQLException sqle) { 470 // throw new PersistenceException(sqle); 471 // } 472 } 473 } 474 475 // let the world know about it 476 fireResourceWritten( 477 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, lr, lr.getLRPersistenceId())); 478 479 } 480 481 482 483 /** 484 * Set method for the autosaving behaviour of the data store. 485 * <B>NOTE:</B> many types of datastore have no auto-save function, 486 * in which case this will throw an UnsupportedOperationException. 487 */ 488 public void setAutoSaving(boolean autoSaving) 489 throws UnsupportedOperationException,PersistenceException { 490 491 super.setAutoSaving(autoSaving); 492 } 493 494 495 496 /** Get the autosaving behaviour of the LR. */ 497 public boolean isAutoSaving() { 498 throw new MethodNotImplementedException(); 499 } 500 501 502 /** Adopt a resource for persistence. */ 503 public LanguageResource adopt(LanguageResource lr, SecurityInfo secInfo) 504 throws PersistenceException,SecurityException { 505 //open a new transaction 506 return _adopt(lr,secInfo,true); 507 } 508 509 /** helper for adopt() 510 * @param openNewTrans shows if a new transaction should be started or the adopt 511 * is performed in the context of an already opened transaction 512 */ 513 private LanguageResource _adopt(LanguageResource lr, 514 SecurityInfo secInfo, 515 boolean openNewTrans) 516 throws PersistenceException,SecurityException { 517 518 LanguageResource result = null; 519 520 //-1. preconditions 521 Assert.assertNotNull(lr); 522 Assert.assertNotNull(secInfo); 523 if (false == lr instanceof Document && 524 false == lr instanceof Corpus) { 525 //only documents and corpuses could be serialized in DB 526 throw new IllegalArgumentException("only Documents and Corpuses could "+ 527 "be serialized in DB"); 528 } 529 530 //0. check SecurityInfo 531 if (false == this.ac.isValidSecurityInfo(secInfo)) { 532 throw new SecurityException("Invalid security settings supplied"); 533 } 534 535 //1. user session should be set 536 if (null == this.session) { 537 throw new SecurityException("user session not set"); 538 } 539 540 //2. check the LR's current DS 541 DataStore currentDS = lr.getDataStore(); 542 if(currentDS == null) { 543 // an orphan - do the adoption (later) 544 } 545 else if(currentDS.equals(this)){ // adopted already 546 return lr; 547 } 548 else { // someone else's child 549 throw new PersistenceException( 550 "Can't adopt a resource which is already in a different datastore"); 551 } 552 553 554 //3. is the LR one of Document or Corpus? 555 if (false == lr instanceof Document && 556 false == lr instanceof Corpus) { 557 558 throw new IllegalArgumentException("Database datastore is implemented only for "+ 559 "Documents and Corpora"); 560 } 561 562 //4.is the document already stored in this storage? 563 Object persistID = lr.getLRPersistenceId(); 564 if (persistID != null) { 565 throw new PersistenceException("This LR is already stored in the " + 566 " database (persistance ID is =["+(Long)persistID+"] )"); 567 } 568 569 boolean transFailed = false; 570 try { 571 //5 autocommit should be FALSE because of LOBs 572 if (openNewTrans) { 573 // this.jdbcConn.setAutoCommit(false); 574 beginTrans(); 575 } 576 577 //6. perform changes, if anything goes wrong, rollback 578 if (lr instanceof Document) { 579 result = createDocument((Document)lr,secInfo); 580 //System.out.println("result ID=["+result.getLRPersistenceId()+"]"); 581 } 582 else { 583 //adopt each document from the corpus in a separate transaction context 584 result = createCorpus((Corpus)lr,secInfo,true); 585 } 586 587 //7. done, commit 588 if (openNewTrans) { 589 // this.jdbcConn.commit(); 590 commitTrans(); 591 } 592 } 593 /* 594 catch(SQLException sqle) { 595 transFailed = true; 596 throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]"); 597 } 598 */ 599 catch(PersistenceException pe) { 600 transFailed = true; 601 throw(pe); 602 } 603 catch(SecurityException se) { 604 transFailed = true; 605 throw(se); 606 } 607 finally { 608 //problems? 609 if (transFailed) { 610 rollbackTrans(); 611 /* try { 612 this.jdbcConn.rollback(); 613 } 614 catch(SQLException sqle) { 615 throw new PersistenceException(sqle); 616 } 617 */ 618 } 619 } 620 621 //8. let the world know 622 fireResourceAdopted( 623 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, 624 result, 625 result.getLRPersistenceId()) 626 ); 627 628 //9. fire also resource written event because it's now saved 629 fireResourceWritten( 630 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, 631 result, 632 result.getLRPersistenceId() 633 ) 634 ); 635 636 //10. add the resource to the list of dependent resources - i.e. the ones that the 637 //data store should take care upon closing [and call sync()] 638 this.dependentResources.add(result); 639 640 return result; 641 } 642 643 644 645 /** 646 * helper for adopt() 647 * never call directly 648 */ 649 private Long createLR(String lrType, 650 String lrName, 651 SecurityInfo si, 652 Long lrParentID) 653 throws PersistenceException,SecurityException { 654 655 //0. preconditions 656 Assert.assertNotNull(lrName); 657 658 //1. check the session 659 // if (this.ac.isValidSession(s) == false) { 660 // throw new SecurityException("invalid session provided"); 661 // } 662 663 //2. create a record in DB 664 CallableStatement stmt = null; 665 666 try { 667 stmt = this.jdbcConn.prepareCall( 668 "{ call "+Gate.DB_OWNER+".persist.create_lr(?,?,?,?,?,?,?) }"); 669 stmt.setLong(1,si.getUser().getID().longValue()); 670 stmt.setLong(2,si.getGroup().getID().longValue()); 671 stmt.setString(3,lrType); 672 stmt.setString(4,lrName); 673 stmt.setInt(5,si.getAccessMode()); 674 if (null == lrParentID) { 675 stmt.setNull(6,java.sql.Types.BIGINT); 676 } 677 else { 678 stmt.setLong(6,lrParentID.longValue()); 679 } 680 //Oracle numbers are BIGNINT 681 stmt.registerOutParameter(7,java.sql.Types.BIGINT); 682 stmt.execute(); 683 684 Long result = new Long(stmt.getLong(7)); 685 return result; 686 } 687 catch(SQLException sqle) { 688 689 switch(sqle.getErrorCode()) { 690 case DBHelper.X_ORACLE_INVALID_LR_TYPE: 691 throw new PersistenceException("can't create LR [step 3] in DB, invalid LR Type"); 692 default: 693 throw new PersistenceException( 694 "can't create LR [step 3] in DB : ["+ sqle.getMessage()+"]"); 695 } 696 } 697 finally { 698 DBHelper.cleanup(stmt); 699 } 700 } 701 702 703 704 /** 705 * updates the content of the document if it is binary or a long string 706 * (that does not fit into VARCHAR2) 707 */ 708 private void updateDocumentContent(Long docContentID,DocumentContent content) 709 throws PersistenceException { 710 711 //1. get LOB locators from DB 712 PreparedStatement pstmt = null; 713 ResultSet rs = null; 714 CallableStatement cstmt = null; 715 try { 716 String sql = "select dc_content_type, " + 717 " dc_character_content, " + 718 " dc_binary_content " + 719 "from "+gate.Gate.DB_OWNER+".t_doc_content " + 720 "where dc_id = ? " + 721 "for update "; 722 pstmt = this.jdbcConn.prepareStatement(sql); 723 pstmt.setLong(1,docContentID.longValue()); 724 rs = pstmt.executeQuery(); 725 726 //rs = pstmt.getResultSet(); 727 728 rs.next(); 729 //important: read the objects in the order they appear in 730 //the ResultSet, otherwise data may be lost 731 long contentType = rs.getLong("DC_CONTENT_TYPE"); 732 Clob clob = (Clob)rs.getClob("dc_character_content"); 733 Blob blob = (Blob)rs.getBlob("dc_binary_content"); 734 735 Assert.assertTrue(contentType == DBHelper.CHARACTER_CONTENT || 736 contentType == DBHelper.BINARY_CONTENT || 737 contentType == DBHelper.EMPTY_CONTENT); 738 739 740 //2. write data using the LOB locators 741 //NOTE: so far only character content is supported 742 writeCLOB(content.toString(),clob); 743 long newContentType = DBHelper.CHARACTER_CONTENT; 744 745 //3. update content type 746 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.change_content_type(?,?) }"); 747 cstmt.setLong(1,docContentID.longValue()); 748 cstmt.setLong(2,newContentType); 749 cstmt.execute(); 750 } 751 catch(IOException ioe) { 752 throw new PersistenceException("can't update document content in DB : ["+ 753 ioe.getMessage()+"]"); 754 } 755 catch(SQLException sqle) { 756 throw new PersistenceException("can't update document content in DB : ["+ 757 sqle.getMessage()+"]"); 758 } 759 finally { 760 DBHelper.cleanup(rs); 761 DBHelper.cleanup(pstmt); 762 DBHelper.cleanup(cstmt); 763 } 764 765 } 766 767 768 769 /** 770 * helper for adopt 771 * creates a LR of type Document 772 */ 773 private Document createDocument(Document doc,SecurityInfo secInfo) 774 throws PersistenceException,SecurityException { 775 776 //delegate, set to Null 777 return createDocument(doc,null,secInfo); 778 } 779 780 781 782 /** 783 * helper for adopt 784 * creates a LR of type Document 785 */ 786 private Document createDocument(Document doc, Long corpusID,SecurityInfo secInfo) 787 throws PersistenceException,SecurityException { 788 789 //-1. preconditions 790 Assert.assertNotNull(doc); 791 Assert.assertNotNull(secInfo); 792 793 //0. check securoity settings 794 if (false == this.ac.isValidSecurityInfo(secInfo)) { 795 throw new SecurityException("Invalid security settings"); 796 } 797 798 //1. get the data to be stored 799 AnnotationSet defaultAnnotations = doc.getAnnotations(); 800 DocumentContent docContent = doc.getContent(); 801 FeatureMap docFeatures = doc.getFeatures(); 802 String docName = doc.getName(); 803 URL docURL = doc.getSourceUrl(); 804 Boolean docIsMarkupAware = doc.getMarkupAware(); 805 Long docStartOffset = doc.getSourceUrlStartOffset(); 806 Long docEndOffset = doc.getSourceUrlEndOffset(); 807 String docEncoding = null; 808 try { 809 docEncoding = (String)doc. 810 getParameterValue(Document.DOCUMENT_ENCODING_PARAMETER_NAME); 811 } 812 catch(gate.creole.ResourceInstantiationException re) { 813 throw new PersistenceException("cannot create document: error getting " + 814 " document encoding ["+re.getMessage()+"]"); 815 } 816 817 818 //3. create a Language Resource (an entry in T_LANG_RESOURCE) for this document 819 Long lrID = createLR(DBHelper.DOCUMENT_CLASS,docName,secInfo,null); 820 821 //4. create a record in T_DOCUMENT for this document 822 CallableStatement stmt = null; 823 Long docID = null; 824 Long docContentID = null; 825 826 try { 827 stmt = this.jdbcConn.prepareCall( 828 "{ call "+Gate.DB_OWNER+".persist.create_document(?,?,?,?,?,?,?,?,?) }"); 829 stmt.setLong(1,lrID.longValue()); 830 stmt.setString(2,docURL.toString()); 831 //do we have doc encoding? 832 if (null == docEncoding) { 833 stmt.setNull(3,java.sql.Types.VARCHAR); 834 } 835 else { 836 stmt.setString(3,docEncoding); 837 } 838 //do we have start offset? 839 if (null==docStartOffset) { 840 stmt.setNull(4,java.sql.Types.NUMERIC); 841 } 842 else { 843 stmt.setLong(4,docStartOffset.longValue()); 844 } 845 //do we have end offset? 846 if (null==docEndOffset) { 847 stmt.setNull(5,java.sql.Types.NUMERIC); 848 } 849 else { 850 stmt.setLong(5,docEndOffset.longValue()); 851 } 852 853 stmt.setBoolean(6,docIsMarkupAware.booleanValue()); 854 855 //is the document part of a corpus? 856 if (null == corpusID) { 857 stmt.setNull(7,java.sql.Types.BIGINT); 858 } 859 else { 860 stmt.setLong(7,corpusID.longValue()); 861 } 862 863 //results 864 stmt.registerOutParameter(8,java.sql.Types.BIGINT); 865 stmt.registerOutParameter(9,java.sql.Types.BIGINT); 866 867 stmt.execute(); 868 docID = new Long(stmt.getLong(8)); 869 docContentID = new Long(stmt.getLong(9)); 870 } 871 catch(SQLException sqle) { 872 throw new PersistenceException("can't create document [step 4] in DB: ["+ sqle.getMessage()+"]"); 873 } 874 finally { 875 DBHelper.cleanup(stmt); 876 } 877 878 //5. fill document content (record[s] in T_DOC_CONTENT) 879 880 //do we have content at all? 881 if (docContent.size().longValue() > 0) { 882 updateDocumentContent(docContentID,docContent); 883 } 884 885 //6. insert annotations, etc 886 887 //6.1. create default annotation set 888 createAnnotationSet(lrID,defaultAnnotations); 889 890 //6.2. create named annotation sets 891 Map namedAnns = doc.getNamedAnnotationSets(); 892 //the map may be null 893 if (null != namedAnns) { 894 Set setAnns = namedAnns.entrySet(); 895 Iterator itAnns = setAnns.iterator(); 896 897 while (itAnns.hasNext()) { 898 Map.Entry mapEntry = (Map.Entry)itAnns.next(); 899 //String currAnnName = (String)mapEntry.getKey(); 900 AnnotationSet currAnnSet = (AnnotationSet)mapEntry.getValue(); 901 902 //create a-sets 903 createAnnotationSet(lrID,currAnnSet); 904 } 905 } 906 907 //7. create features 908 // createFeatures(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures); 909 createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures); 910 911 //9. create a DatabaseDocument wrapper and return it 912 913 /* Document dbDoc = new DatabaseDocumentImpl(this.jdbcConn, 914 doc.getName(), 915 this, 916 lrID, 917 doc.getContent(), 918 doc.getFeatures(), 919 doc.getMarkupAware(), 920 doc.getSourceUrl(), 921 doc.getSourceUrlStartOffset(), 922 doc.getSourceUrlEndOffset(), 923 doc.getAnnotations(), 924 doc.getNamedAnnotationSets()); 925 */ 926 Document dbDoc = null; 927 FeatureMap params = Factory.newFeatureMap(); 928 929 HashMap initData = new HashMap(); 930 initData.put("JDBC_CONN",this.jdbcConn); 931 initData.put("DS",this); 932 initData.put("LR_ID",lrID); 933 initData.put("DOC_NAME",doc.getName()); 934 initData.put("DOC_CONTENT",doc.getContent()); 935 initData.put("DOC_FEATURES",doc.getFeatures()); 936 initData.put("DOC_MARKUP_AWARE",doc.getMarkupAware()); 937 initData.put("DOC_SOURCE_URL",doc.getSourceUrl()); 938 initData.put("DOC_SOURCE_URL_START",doc.getSourceUrlStartOffset()); 939 initData.put("DOC_SOURCE_URL_END",doc.getSourceUrlEndOffset()); 940 initData.put("DOC_DEFAULT_ANNOTATIONS",doc.getAnnotations()); 941 initData.put("DOC_NAMED_ANNOTATION_SETS",doc.getNamedAnnotationSets()); 942 943 params.put("initData__$$__", initData); 944 945 try { 946 //here we create the persistent LR via Factory, so it's registered 947 //in GATE 948 dbDoc = (Document)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params); 949 } 950 catch (gate.creole.ResourceInstantiationException ex) { 951 throw new GateRuntimeException(ex.getMessage()); 952 } 953 954 return dbDoc; 955 } 956 957 958 959 /** creates an entry for annotation set in the database */ 960 private void createAnnotationSet(Long lrID, AnnotationSet aset) 961 throws PersistenceException { 962 963 //1. create a-set 964 String asetName = aset.getName(); 965 Long asetID = null; 966 967 //DB stuff 968 CallableStatement stmt = null; 969 try { 970 stmt = this.jdbcConn.prepareCall( 971 "{ call "+Gate.DB_OWNER+".persist.create_annotation_set(?,?,?) }"); 972 stmt.setLong(1,lrID.longValue()); 973 974 if (null == asetName) { 975 stmt.setNull(2,java.sql.Types.VARCHAR); 976 } 977 else { 978 stmt.setString(2,asetName); 979 } 980 stmt.registerOutParameter(3,java.sql.Types.BIGINT); 981 stmt.execute(); 982 983 asetID = new Long(stmt.getLong(3)); 984 } 985 catch(SQLException sqle) { 986 throw new PersistenceException("can't create a-set [step 1] in DB: ["+ sqle.getMessage()+"]"); 987 } 988 finally { 989 DBHelper.cleanup(stmt); 990 } 991 992 993 //2. insert annotations/nodes for DEFAULT a-set 994 //for now use a stupid cycle 995 //TODO: pass all the data with one DB call (?) 996 997 try { 998 stmt = this.jdbcConn.prepareCall( 999 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }"); 1000 1001 Iterator itAnnotations = aset.iterator(); 1002 1003 while (itAnnotations.hasNext()) { 1004 Annotation ann = (Annotation)itAnnotations.next(); 1005 Node start = (Node)ann.getStartNode(); 1006 Node end = (Node)ann.getEndNode(); 1007 String type = ann.getType(); 1008 1009 //DB stuff 1010 Long annGlobalID = null; 1011 stmt.setLong(1,lrID.longValue()); 1012 stmt.setLong(2,ann.getId().longValue()); 1013 stmt.setLong(3,asetID.longValue()); 1014 stmt.setLong(4,start.getId().longValue()); 1015 stmt.setLong(5,start.getOffset().longValue()); 1016 stmt.setLong(6,end.getId().longValue()); 1017 stmt.setLong(7,end.getOffset().longValue()); 1018 stmt.setString(8,type); 1019 stmt.registerOutParameter(9,java.sql.Types.BIGINT); 1020 1021 stmt.execute(); 1022 1023 annGlobalID = new Long(stmt.getLong(9)); 1024 1025 //2.1. set annotation features 1026 FeatureMap features = ann.getFeatures(); 1027 Assert.assertNotNull(features); 1028// createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features); 1029 createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features); 1030 } //while 1031 }//try 1032 catch(SQLException sqle) { 1033 1034 switch(sqle.getErrorCode()) { 1035 1036 case DBHelper.X_ORACLE_INVALID_ANNOTATION_TYPE: 1037 throw new PersistenceException( 1038 "can't create annotation in DB, [invalid annotation type]"); 1039 default: 1040 throw new PersistenceException( 1041 "can't create annotation in DB: ["+ sqle.getMessage()+"]"); 1042 }//switch 1043 }//catch 1044 finally { 1045 DBHelper.cleanup(stmt); 1046 } 1047 }//func 1048 1049 1050 1051 /** creates a LR of type Corpus */ 1052 private Corpus createCorpus(Corpus corp,SecurityInfo secInfo, boolean newTransPerDocument) 1053 throws PersistenceException,SecurityException { 1054 1055 //1. create an LR entry for the corpus (T_LANG_RESOURCE table) 1056 Long lrID = createLR(DBHelper.CORPUS_CLASS,corp.getName(),secInfo,null); 1057 1058 //2.create am entry in the T_COPRUS table 1059 Long corpusID = null; 1060 //DB stuff 1061 CallableStatement stmt = null; 1062 try { 1063 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.create_corpus(?,?) }"); 1064 stmt.setLong(1,lrID.longValue()); 1065 stmt.registerOutParameter(2,java.sql.Types.BIGINT); 1066 stmt.execute(); 1067 corpusID = new Long(stmt.getLong(2)); 1068 } 1069 catch(SQLException sqle) { 1070 throw new PersistenceException("can't create corpus [step 2] in DB: ["+ sqle.getMessage()+"]"); 1071 } 1072 finally { 1073 DBHelper.cleanup(stmt); 1074 } 1075 1076 //3. for each document in the corpus call createDocument() 1077 Iterator itDocuments = corp.iterator(); 1078 Vector dbDocs = new Vector(); 1079 while (itDocuments.hasNext()) { 1080 Document doc = (Document)itDocuments.next(); 1081 1082 //3.1. ensure that the document is either transient or is from the ... 1083 // same DataStore 1084 if (doc.getLRPersistenceId() == null) { 1085 //transient document 1086 1087 //now this is a bit ugly patch, the transaction related functionality 1088 //should not be in this method 1089 if (newTransPerDocument) { 1090 beginTrans(); 1091 } 1092 1093 Document dbDoc = createDocument(doc,corpusID,secInfo); 1094 1095 if (newTransPerDocument) { 1096 commitTrans(); 1097 } 1098 1099 dbDocs.add(dbDoc); 1100 //8. let the world know 1101 fireResourceAdopted(new DatastoreEvent(this, 1102 DatastoreEvent.RESOURCE_ADOPTED, 1103 dbDoc, 1104 dbDoc.getLRPersistenceId() 1105 ) 1106 ); 1107 1108 //9. fire also resource written event because it's now saved 1109 fireResourceWritten(new DatastoreEvent(this, 1110 DatastoreEvent.RESOURCE_WRITTEN, 1111 dbDoc, 1112 dbDoc.getLRPersistenceId() 1113 ) 1114 ); 1115 1116 } 1117 else if (doc.getDataStore().equals(this)) { 1118 //persistent doc from the same DataStore 1119 fireResourceAdopted( 1120 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, 1121 doc, 1122 doc.getLRPersistenceId())); 1123 1124 //6. fire also resource written event because it's now saved 1125 fireResourceWritten( 1126 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, 1127 doc, 1128 doc.getLRPersistenceId())); 1129 } 1130 else { 1131 //persistent doc from other datastore 1132 //skip 1133 gate.util.Err.prln("document ["+doc.getLRPersistenceId()+"] is adopted from another "+ 1134 " datastore. Skipped."); 1135 } 1136 } 1137 1138 //4. create features 1139// createFeatures(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures()); 1140 createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures()); 1141 1142 //5. create a DatabaseCorpusImpl and return it 1143/* Corpus dbCorpus = new DatabaseCorpusImpl(corp.getName(), 1144 this, 1145 lrID, 1146 corp.getFeatures(), 1147 dbDocs); 1148*/ 1149 1150 Corpus dbCorpus = null; 1151 FeatureMap params = Factory.newFeatureMap(); 1152 HashMap initData = new HashMap(); 1153 1154 initData.put("DS",this); 1155 initData.put("LR_ID",lrID); 1156 initData.put("CORP_NAME",corp.getName()); 1157 initData.put("CORP_FEATURES",corp.getFeatures()); 1158 initData.put("CORP_SUPPORT_LIST",dbDocs); 1159 1160 params.put("initData__$$__", initData); 1161 1162 try { 1163 //here we create the persistent LR via Factory, so it's registered 1164 //in GATE 1165 dbCorpus = (Corpus)Factory.createResource("gate.corpora.DatabaseCorpusImpl", params); 1166 } 1167 catch (gate.creole.ResourceInstantiationException ex) { 1168 throw new GateRuntimeException(ex.getMessage()); 1169 } 1170 1171 //6. done 1172 return dbCorpus; 1173 } 1174 1175 1176 1177 /** 1178 * Get a resource from the persistent store. 1179 * <B>Don't use this method - use Factory.createResource with 1180 * DataStore and DataStoreInstanceId parameters set instead.</B> 1181 */ 1182 public LanguageResource getLr(String lrClassName, Object lrPersistenceId) 1183 throws PersistenceException,SecurityException { 1184 1185 LanguageResource result = null; 1186 1187 //0. preconditions 1188 Assert.assertNotNull(lrPersistenceId); 1189 1190 //1. check session 1191 if (null == this.session) { 1192 throw new SecurityException("session not set"); 1193 } 1194 1195 if (false == this.ac.isValidSession(this.session)) { 1196 throw new SecurityException("invalid session supplied"); 1197 } 1198 1199 //2. check permissions 1200 if (false == canReadLR(lrPersistenceId)) { 1201 throw new SecurityException("insufficient privileges"); 1202 } 1203 1204 //3. get resource from DB 1205 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) { 1206 result = readDocument(lrPersistenceId); 1207 Assert.assertTrue(result instanceof DatabaseDocumentImpl); 1208 } 1209 else if (lrClassName.equals(DBHelper.CORPUS_CLASS)) { 1210 result = readCorpus(lrPersistenceId); 1211 Assert.assertTrue(result instanceof DatabaseCorpusImpl); 1212 } 1213 else { 1214 throw new IllegalArgumentException("resource class should be either Document or Corpus"); 1215 } 1216 1217 //4. postconditions 1218 Assert.assertNotNull(result.getDataStore()); 1219 Assert.assertTrue(result.getDataStore() instanceof DatabaseDataStore); 1220 Assert.assertNotNull(result.getLRPersistenceId()); 1221 1222 //5. register the read doc as listener for sync events 1223 addDatastoreListener((DatastoreListener)result); 1224 1225 //6. add the resource to the list of dependent resources - i.e. the ones that the 1226 //data store should take care upon closing [and call sync()] 1227 this.dependentResources.add(result); 1228 1229 //7. done 1230 return result; 1231 } 1232 1233 1234 1235 /** Get a list of the types of LR that are present in the data store. */ 1236 public List getLrTypes() throws PersistenceException { 1237 1238 Vector lrTypes = new Vector(); 1239 Statement stmt = null; 1240 ResultSet rs = null; 1241 1242 try { 1243 stmt = this.jdbcConn.createStatement(); 1244 rs = stmt.executeQuery(" SELECT lrtp_type " + 1245 " FROM "+Gate.DB_OWNER+".t_lr_type LRTYPE "); 1246 1247 while (rs.next()) { 1248 //access by index is faster 1249 String lrType = rs.getString(1); 1250 lrTypes.add(lrType); 1251 } 1252 1253 return lrTypes; 1254 } 1255 catch(SQLException sqle) { 1256 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]"); 1257 } 1258 finally { 1259 DBHelper.cleanup(rs); 1260 DBHelper.cleanup(stmt); 1261 } 1262 } 1263 1264 1265 1266 /** Get a list of the IDs of LRs of a particular type that are present. */ 1267 public List getLrIds(String lrType) throws PersistenceException { 1268 1269 Vector lrIDs = new Vector(); 1270 PreparedStatement stmt = null; 1271 ResultSet rs = null; 1272 1273 try { 1274 stmt = this.jdbcConn.prepareStatement( 1275 " SELECT lr_id " + 1276 " FROM "+Gate.DB_OWNER+".t_lang_resource LR, " + 1277 " "+Gate.DB_OWNER+".t_lr_type LRTYPE " + 1278 " WHERE LR.lr_type_id = LRTYPE.lrtp_id " + 1279 " AND LRTYPE.lrtp_type = ? " + 1280 " ORDER BY lr_name" 1281 ); 1282 stmt.setString(1,lrType); 1283 ((OraclePreparedStatement)stmt).setRowPrefetch(DBHelper.CHINK_SIZE_SMALL); 1284 stmt.execute(); 1285 rs = stmt.getResultSet(); 1286 1287 while (rs.next()) { 1288 //access by index is faster 1289 Long lrID = new Long(rs.getLong(1)); 1290 lrIDs.add(lrID); 1291 } 1292 1293 return lrIDs; 1294 } 1295 catch(SQLException sqle) { 1296 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]"); 1297 } 1298 finally { 1299 DBHelper.cleanup(rs); 1300 DBHelper.cleanup(stmt); 1301 } 1302 1303 } 1304 1305 1306 1307 /** Get a list of the names of LRs of a particular type that are present. */ 1308 public List getLrNames(String lrType) throws PersistenceException { 1309 1310 Vector lrNames = new Vector(); 1311 PreparedStatement stmt = null; 1312 ResultSet rs = null; 1313 1314 try { 1315 stmt = this.jdbcConn.prepareStatement( 1316 " SELECT lr_name " + 1317 " FROM "+Gate.DB_OWNER+".t_lang_resource LR, " + 1318 " t_lr_type LRTYPE " + 1319 " WHERE LR.lr_type_id = LRTYPE.lrtp_id " + 1320 " AND LRTYPE.lrtp_type = ? " + 1321 " ORDER BY lr_name desc" 1322 ); 1323 stmt.setString(1,lrType); 1324 ((OraclePreparedStatement)stmt).setRowPrefetch(DBHelper.CHINK_SIZE_SMALL); 1325 stmt.execute(); 1326 rs = stmt.getResultSet(); 1327 1328 while (rs.next()) { 1329 //access by index is faster 1330 String lrName = rs.getString(1); 1331 lrNames.add(lrName); 1332 } 1333 1334 return lrNames; 1335 } 1336 catch(SQLException sqle) { 1337 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]"); 1338 } 1339 finally { 1340 DBHelper.cleanup(rs); 1341 DBHelper.cleanup(stmt); 1342 } 1343 } 1344 1345 1346 1347 /** Gets a timestamp marker that will be used for all changes made in 1348 * the database so that subsequent calls to deleteSince() could restore (partly) 1349 * the database state as it was before the update. <B>NOTE:</B> Restoring the previous 1350 * state may not be possible at all (i.e. if DELETE is performed) 1351 * */ 1352 public Long timestamp() 1353 throws PersistenceException{ 1354 1355 CallableStatement stmt = null; 1356 1357 try { 1358 stmt = this.jdbcConn.prepareCall( 1359 "{ call "+Gate.DB_OWNER+".persist.get_timestamp(?)} "); 1360 //numbers generated from Oracle sequences are BIGINT 1361 stmt.registerOutParameter(1,java.sql.Types.BIGINT); 1362 stmt.execute(); 1363 long result = stmt.getLong(1); 1364 1365 return new Long(result); 1366 } 1367 catch(SQLException sqle) { 1368 throw new PersistenceException("can't get a timestamp from DB: ["+ sqle.getMessage()+"]"); 1369 } 1370 finally { 1371 DBHelper.cleanup(stmt); 1372 } 1373 } 1374 1375 1376 1377 /** 1378 * Checks if the user (identified by the sessionID) 1379 * has read access to the LR 1380 */ 1381 public boolean canReadLR(Object lrID) 1382 throws PersistenceException, SecurityException{ 1383 1384 return canAccessLR((Long) lrID,READ_ACCESS); 1385 } 1386 1387 1388 1389 /** 1390 * Checks if the user (identified by the sessionID) 1391 * has write access to the LR 1392 */ 1393 public boolean canWriteLR(Object lrID) 1394 throws PersistenceException, SecurityException{ 1395 1396 return canAccessLR((Long) lrID,WRITE_ACCESS); 1397 } 1398 1399 1400 1401 /** 1402 * Checks if the user (identified by the sessionID) 1403 * has some access (read/write) to the LR 1404 */ 1405 private boolean canAccessLR(Long lrID,int mode) 1406 throws PersistenceException, SecurityException{ 1407 1408 //0. preconditions 1409 Assert.assertTrue(READ_ACCESS == mode || WRITE_ACCESS == mode); 1410 1411 //1. is session initialised? 1412 if (null == this.session) { 1413 throw new SecurityException("user session not set"); 1414 } 1415 1416 //2.first check the session and then check whether the user is member of the group 1417 if (this.ac.isValidSession(this.session) == false) { 1418 throw new SecurityException("invalid session supplied"); 1419 } 1420 1421 CallableStatement stmt = null; 1422 1423 try { 1424 stmt = this.jdbcConn.prepareCall( 1425 "{ call "+Gate.DB_OWNER+".security.has_access_to_lr(?,?,?,?,?)} "); 1426 stmt.setLong(1,lrID.longValue()); 1427 stmt.setLong(2,this.session.getUser().getID().longValue()); 1428 stmt.setLong(3,this.session.getGroup().getID().longValue()); 1429 stmt.setLong(4,mode); 1430 1431 stmt.registerOutParameter(5,java.sql.Types.NUMERIC); 1432 stmt.execute(); 1433 int result = stmt.getInt(5); 1434 1435 return (ORACLE_TRUE == result); 1436 } 1437 catch(SQLException sqle) { 1438 throw new PersistenceException("can't check permissions in DB: ["+ sqle.getMessage()+"]"); 1439 } 1440 finally { 1441 DBHelper.cleanup(stmt); 1442 } 1443 } 1444 1445 1446 1447 /** reads the content of a CLOB into the specified StringBuffer */ 1448 public static void readCLOB(java.sql.Clob src, StringBuffer dest) 1449 throws SQLException, IOException { 1450 1451 int readLength = 0; 1452 1453 //1. empty the dest buffer 1454 dest.delete(0,dest.length()); 1455 1456 //2. get Oracle CLOB 1457 CLOB clo = (CLOB)src; 1458 1459 //3. create temp buffer 1460 int buffSize = Math.max(INTERNAL_BUFFER_SIZE,clo.getBufferSize()); 1461 char[] readBuffer = new char[buffSize]; 1462 1463 //3. get Unicode stream 1464 Reader input = clo.getCharacterStream(); 1465 1466 //4. read 1467 BufferedReader buffInput = new BufferedReader(input,INTERNAL_BUFFER_SIZE); 1468 1469 while ((readLength = buffInput.read(readBuffer, 0, INTERNAL_BUFFER_SIZE)) != -1) { 1470 dest.append(readBuffer, 0, readLength); 1471 } 1472 1473 //5.close streams 1474 buffInput.close(); 1475 input.close(); 1476 1477 } 1478 1479 1480 1481 /** writes the content of a String into the specified CLOB object */ 1482 public static void writeCLOB(String src,java.sql.Clob dest) 1483 throws SQLException, IOException { 1484 1485 //preconditions 1486 Assert.assertNotNull(src); 1487 1488 //1. get Oracle CLOB 1489 CLOB clo = (CLOB)dest; 1490 1491 //2. get Unicode stream 1492 Writer output = clo.getCharacterOutputStream(); 1493 1494 //3. write 1495 BufferedWriter buffOutput = new BufferedWriter(output,INTERNAL_BUFFER_SIZE); 1496 buffOutput.write(src.toString()); 1497 1498 //4. flushing is a good idea [although BufferedWriter::close() calls it this is 1499 //implementation specific] 1500 buffOutput.flush(); 1501 output.flush(); 1502 1503 //5.close streams 1504 buffOutput.close(); 1505 output.close(); 1506 } 1507 1508 1509 1510 /** writes the content of a StringBuffer into the specified CLOB object */ 1511 public static void writeCLOB(StringBuffer src,java.sql.Clob dest) 1512 throws SQLException, IOException { 1513 1514 //delegate 1515 writeCLOB(src.toString(),dest); 1516 } 1517 1518 1519 1520 /** 1521 * reads the content of the specified BLOB object and returns the object 1522 * contained. 1523 * NOTE: the BLOB is expected to contain serializable objects, not just any 1524 * binary stream 1525 */ 1526 public static Object readBLOB(java.sql.Blob src) 1527 throws SQLException, IOException,ClassNotFoundException { 1528 1529 int readLength = 0; 1530 Object result = null; 1531 1532 //0. preconditions 1533 Assert.assertNotNull(src); 1534 1535 //2. get Oracle BLOB 1536 BLOB blo = (BLOB)src; 1537 1538 //3. get binary stream 1539 InputStream input = blo.getBinaryStream(); 1540 Assert.assertNotNull(input); 1541 1542 //4. read 1543 ObjectInputStream ois = new ObjectInputStream(input); 1544 result = ois.readObject(); 1545 1546 //5.close streams 1547 ois.close(); 1548 input.close(); 1549 1550 return result; 1551 } 1552 1553 1554 1555 /** 1556 * writes the specified object into the BLOB 1557 * NOTE: the object should be serializable 1558 */ 1559 public static void writeBLOB(Object src,java.sql.Blob dest) 1560 throws SQLException, IOException { 1561 1562 //preconditions 1563 Assert.assertNotNull(src); 1564 1565 //1. get Oracle CLOB 1566 BLOB blo = (BLOB)dest; 1567 1568 //2. get Unicode stream 1569 OutputStream output = blo.getBinaryOutputStream(); 1570 1571 //3. write 1572 ObjectOutputStream oos = new ObjectOutputStream(output); 1573 oos.writeObject(src); 1574 1575 //4. flushing is a good idea 1576 //[although ::close() calls it this is implementation specific] 1577 oos.flush(); 1578 output.flush(); 1579 1580 //5.close streams 1581 oos.close(); 1582 output.close(); 1583 } 1584 1585 1586 1587 /** 1588 * creates a feature of the specified type/value/valueType/key for the specified entity 1589 * Entity is one of: LR, Annotation 1590 * Value types are: boolean, int, long, string, float, Object 1591 */ 1592 private Long _createFeature(Long entityID, 1593 int entityType, 1594 String key, 1595 Object value, 1596 int valueType, 1597 CallableStatement stmt) 1598 throws PersistenceException { 1599 1600 //1. store in DB 1601 Long featID = null; 1602// CallableStatement stmt = null; 1603 1604 try { 1605// stmt = this.jdbcConn.prepareCall( 1606// "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} "); 1607 1608 //1.1 set known values + NULLs 1609 stmt.setLong(1,entityID.longValue()); 1610 stmt.setLong(2,entityType); 1611 stmt.setString(3,key); 1612 stmt.setNull(4,java.sql.Types.NUMERIC); 1613 stmt.setNull(5,java.sql.Types.VARCHAR); 1614 stmt.setLong(6,valueType); 1615 stmt.registerOutParameter(7,java.sql.Types.BIGINT); 1616 1617 //1.2 set proper data 1618 switch(valueType) { 1619 1620 case DBHelper.VALUE_TYPE_NULL: 1621 break; 1622 1623 case DBHelper.VALUE_TYPE_BOOLEAN: 1624 1625 boolean b = ((Boolean)value).booleanValue(); 1626 stmt.setLong(4, b ? this.ORACLE_TRUE : this.ORACLE_FALSE); 1627 break; 1628 1629 case DBHelper.VALUE_TYPE_INTEGER: 1630 1631 stmt.setLong(4,((Integer)value).intValue()); 1632 break; 1633 1634 case DBHelper.VALUE_TYPE_LONG: 1635 1636 stmt.setLong(4,((Long)value).longValue()); 1637 break; 1638 1639 case DBHelper.VALUE_TYPE_FLOAT: 1640 1641 Double d = (Double)value; 1642 stmt.setDouble(4,d.doubleValue()); 1643 break; 1644 1645 case DBHelper.VALUE_TYPE_BINARY: 1646 //ignore 1647 //will be handled later in processing 1648 break; 1649 1650 case DBHelper.VALUE_TYPE_STRING: 1651 1652 String s = (String)value; 1653 //does it fin into a varchar2? 1654 if (fitsInVarchar2(s)) { 1655 stmt.setString(5,s); 1656 } 1657 break; 1658 1659 default: 1660 throw new IllegalArgumentException("unsuppoeted feature type"); 1661 } 1662 1663 stmt.execute(); 1664 featID = new Long(stmt.getLong(7)); 1665 } 1666 catch(SQLException sqle) { 1667 1668 switch(sqle.getErrorCode()) { 1669 case DBHelper.X_ORACLE_INVALID_FEATURE_TYPE: 1670 throw new PersistenceException("can't create feature [step 1],"+ 1671 "[invalid feature type] in DB: ["+ sqle.getMessage()+"]"); 1672 default: 1673 throw new PersistenceException("can't create feature [step 1] in DB: ["+ 1674 sqle.getMessage()+"]"); 1675 } 1676 } 1677 finally { 1678// DBHelper.cleanup(stmt); 1679 } 1680 1681 return featID; 1682 } 1683 1684 1685 /** 1686 * creates a feature of the specified type/value/valueType/key for the specified entity 1687 * Entity is one of: LR, Annotation 1688 * Value types are: boolean, int, long, string, float, Object 1689 */ 1690 private void _createFeatureBulk(Vector features, 1691 CallableStatement stmt, 1692 ArrayDescriptor adNumber, 1693 ArrayDescriptor adString) 1694 throws PersistenceException { 1695 1696 String[] stringValues = new String[VARRAY_SIZE]; 1697 long[] numberValues = new long[VARRAY_SIZE]; 1698 double[] floatValues = new double[VARRAY_SIZE]; 1699 long[] entityIDs = new long[VARRAY_SIZE]; 1700 long[] entityTypes = new long[VARRAY_SIZE]; 1701 String[] keys = new String[VARRAY_SIZE]; 1702 long[] valueTypes = new long[VARRAY_SIZE]; 1703 1704//System.out.println("num features=["+features.size()+"]"); 1705 //1. store in DB 1706 try { 1707 1708 int ftInd = 0; 1709 int arrInd = 0; 1710 Iterator it = features.iterator(); 1711 1712 while (it.hasNext()) { 1713 1714 Feature currFeature = (Feature)it.next(); 1715 entityIDs[arrInd] = currFeature.entityID.longValue(); 1716 entityTypes[arrInd] = currFeature.entityType; 1717 keys[arrInd] = currFeature.key; 1718 valueTypes[arrInd] = currFeature.valueType; 1719//System.out.println("ftype=["+currFeature.valueType+"]"); 1720 //preconditions 1721 Assert.assertTrue(currFeature.valueType == DBHelper.VALUE_TYPE_BOOLEAN || 1722 currFeature.valueType == DBHelper.VALUE_TYPE_FLOAT || 1723 currFeature.valueType == DBHelper.VALUE_TYPE_INTEGER || 1724 currFeature.valueType == DBHelper.VALUE_TYPE_LONG || 1725 currFeature.valueType == DBHelper.VALUE_TYPE_NULL || 1726 currFeature.valueType == DBHelper.VALUE_TYPE_STRING 1727 ); 1728 1729 1730 Object value = currFeature.value; 1731 1732 switch(currFeature.valueType) { 1733 1734 case DBHelper.VALUE_TYPE_NULL: 1735 numberValues[arrInd] = 0; 1736 floatValues[arrInd] = 0; 1737 stringValues[arrInd] = ""; 1738 break; 1739 1740 case DBHelper.VALUE_TYPE_BOOLEAN: 1741 boolean b = ((Boolean)value).booleanValue(); 1742 numberValues[arrInd] = b ? this.ORACLE_TRUE : this.ORACLE_FALSE; 1743 floatValues[arrInd] = 0; 1744 stringValues[arrInd] = ""; 1745 break; 1746 1747 case DBHelper.VALUE_TYPE_INTEGER: 1748 numberValues[arrInd] = ((Integer)value).intValue(); 1749 floatValues[arrInd] = 0; 1750 stringValues[arrInd] = ""; 1751 break; 1752 1753 case DBHelper.VALUE_TYPE_LONG: 1754 numberValues[arrInd] = ((Long)value).longValue(); 1755 floatValues[arrInd] = 0; 1756 stringValues[arrInd] = ""; 1757 break; 1758 1759 case DBHelper.VALUE_TYPE_FLOAT: 1760 floatValues[arrInd] = ((Double)value).doubleValue(); 1761 numberValues[arrInd] = 0; 1762 stringValues[arrInd] = ""; 1763 break; 1764 1765 case DBHelper.VALUE_TYPE_BINARY: 1766 Assert.fail(); 1767 break; 1768 1769 case DBHelper.VALUE_TYPE_STRING: 1770 String s = (String)value; 1771 //does it fin into a varchar2? 1772 1773 if (fitsInVarchar2(s)) { 1774 stringValues[arrInd] = s; 1775 floatValues[arrInd] = 0; 1776 numberValues[arrInd] = 0; 1777 } 1778 else { 1779 Assert.fail(); 1780 } 1781 break; 1782 1783 default: 1784 throw new IllegalArgumentException("unsuppoeted feature type"); 1785 } 1786 1787 //save the features? 1788 ftInd++; 1789 arrInd++; 1790 1791 if (ftInd == features.size() || arrInd == VARRAY_SIZE) { 1792 1793 if (arrInd == VARRAY_SIZE) { 1794 arrInd = 0; 1795 } 1796//System.out.println("1"); 1797 ARRAY arrEntityIDs = new ARRAY(adNumber, this.jdbcConn,entityIDs); 1798 ARRAY arrEntityTypes = new ARRAY(adNumber, this.jdbcConn,entityTypes); 1799 ARRAY arrKeys = new ARRAY(adString, this.jdbcConn,keys); 1800 ARRAY arrValueTypes = new ARRAY(adNumber, this.jdbcConn,valueTypes); 1801 ARRAY arrNumberValues = new ARRAY(adNumber, this.jdbcConn,numberValues); 1802 ARRAY arrFloatValues = new ARRAY(adNumber, this.jdbcConn,floatValues); 1803 ARRAY arrStringValues = new ARRAY(adString, this.jdbcConn,stringValues); 1804 1805 OracleCallableStatement ostmt = (OracleCallableStatement)stmt; 1806 ostmt.setARRAY(1,arrEntityIDs); 1807 ostmt.setARRAY(2,arrEntityTypes); 1808 ostmt.setARRAY(3,arrKeys); 1809 ostmt.setARRAY(4,arrNumberValues); 1810 ostmt.setARRAY(5,arrFloatValues); 1811 ostmt.setARRAY(6,arrStringValues); 1812 ostmt.setARRAY(7,arrValueTypes); 1813 ostmt.setInt(8, arrInd == 0 ? VARRAY_SIZE : arrInd); 1814 1815 ostmt.execute(); 1816 } 1817 } 1818 } 1819 catch(SQLException sqle) { 1820 1821 switch(sqle.getErrorCode()) { 1822 1823 case DBHelper.X_ORACLE_INVALID_FEATURE_TYPE: 1824 throw new PersistenceException("can't create feature [step 1],"+ 1825 "[invalid feature type] in DB: ["+ sqle.getMessage()+"]"); 1826 default: 1827 throw new PersistenceException("can't create feature [step 1] in DB: ["+ 1828 sqle.getMessage()+"]"); 1829 } 1830 } 1831 } 1832 1833 /** 1834 * updates the value of a feature where the value is string (>4000 bytes, stored as CLOB) 1835 * or Object (stored as BLOB) 1836 */ 1837 private void _updateFeatureLOB(Long featID,Object value, int valueType) 1838 throws PersistenceException { 1839 1840 //NOTE: at this point value is never an array, 1841 // although the type may claim so 1842 1843 //0. preconditions 1844 Assert.assertTrue(valueType == DBHelper.VALUE_TYPE_BINARY || 1845 valueType == DBHelper.VALUE_TYPE_STRING); 1846 1847 1848 //1. get the row to be updated 1849 PreparedStatement stmtA = null; 1850 ResultSet rsA = null; 1851 Clob clobValue = null; 1852 Blob blobValue = null; 1853 1854 try { 1855 String sql = " select ft_long_character_value, " + 1856 " ft_binary_value " + 1857 " from "+Gate.DB_OWNER+".t_feature " + 1858 " where ft_id = ? "; 1859 1860 stmtA = this.jdbcConn.prepareStatement(sql); 1861 stmtA.setLong(1,featID.longValue()); 1862 stmtA.execute(); 1863 rsA = stmtA.getResultSet(); 1864 1865 if (false == rsA.next()) { 1866 throw new PersistenceException("Incorrect feature ID supplied ["+featID+"]"); 1867 } 1868 1869 //NOTE1: if the result set contains LOBs always read them 1870 // in the order they appear in the SQL query 1871 // otherwise data will be lost 1872 //NOTE2: access by index rather than name is usually faster 1873 clobValue = rsA.getClob(1); 1874 blobValue = rsA.getBlob(2); 1875 1876 //blob or clob? 1877 if (valueType == DBHelper.VALUE_TYPE_BINARY) { 1878 //blob 1879 writeBLOB(value,blobValue); 1880 } 1881 else if (valueType == DBHelper.VALUE_TYPE_STRING) { 1882 //clob 1883 String s = (String)value; 1884 writeCLOB(s,clobValue); 1885 } 1886 else { 1887 Assert.fail(); 1888 } 1889 } 1890 catch(SQLException sqle) { 1891 throw new PersistenceException("can't create feature [step 2] in DB: ["+ sqle.getMessage()+"]"); 1892 } 1893 catch(IOException ioe) { 1894 throw new PersistenceException("can't create feature [step 2] in DB: ["+ ioe.getMessage()+"]"); 1895 } 1896 finally { 1897 DBHelper.cleanup(rsA); 1898 DBHelper.cleanup(stmtA); 1899 } 1900 1901 } 1902 1903 1904 1905 /** 1906 * creates a feature with the specified type/key/value for the specified entity 1907 * entitties are either LRs ot Annotations 1908 * valid values are: boolean, 1909 * int, 1910 * long, 1911 * string, 1912 * float, 1913 * Object, 1914 * boolean List, 1915 * int List, 1916 * long List, 1917 * string List, 1918 * float List, 1919 * Object List 1920 * 1921 */ 1922 private void createFeature(Long entityID, int entityType,String key, Object value, CallableStatement stmt) 1923 throws PersistenceException { 1924 1925 //1. what kind of feature value is this? 1926//System.out.println("key=["+key+"], val=["+value+"]"); 1927 int valueType = findFeatureType(value); 1928 1929 //2. how many elements do we store? 1930 Vector elementsToStore = new Vector(); 1931 1932 switch(valueType) { 1933 case DBHelper.VALUE_TYPE_NULL: 1934 case DBHelper.VALUE_TYPE_BINARY: 1935 case DBHelper.VALUE_TYPE_BOOLEAN: 1936 case DBHelper.VALUE_TYPE_FLOAT: 1937 case DBHelper.VALUE_TYPE_INTEGER: 1938 case DBHelper.VALUE_TYPE_LONG: 1939 case DBHelper.VALUE_TYPE_STRING: 1940 elementsToStore.add(value); 1941 break; 1942 1943 default: 1944 //arrays 1945 List arr = (List)value; 1946 Iterator itValues = arr.iterator(); 1947 1948 while (itValues.hasNext()) { 1949 elementsToStore.add(itValues.next()); 1950 } 1951 1952 //normalize , i.e. ignore arrays 1953 if (valueType == DBHelper.VALUE_TYPE_BINARY_ARR) 1954 valueType = DBHelper.VALUE_TYPE_BINARY; 1955 else if (valueType == DBHelper.VALUE_TYPE_BOOLEAN_ARR) 1956 valueType = DBHelper.VALUE_TYPE_BOOLEAN; 1957 else if (valueType == DBHelper.VALUE_TYPE_FLOAT_ARR) 1958 valueType = DBHelper.VALUE_TYPE_FLOAT; 1959 else if (valueType == DBHelper.VALUE_TYPE_INTEGER_ARR) 1960 valueType = DBHelper.VALUE_TYPE_INTEGER; 1961 else if (valueType == DBHelper.VALUE_TYPE_LONG_ARR) 1962 valueType = DBHelper.VALUE_TYPE_LONG; 1963 else if (valueType == DBHelper.VALUE_TYPE_STRING_ARR) 1964 valueType = DBHelper.VALUE_TYPE_STRING; 1965 } 1966 1967 //3. for all elements: 1968 for (int i=0; i< elementsToStore.size(); i++) { 1969 1970 Object currValue = elementsToStore.elementAt(i); 1971 1972 //3.1. create a dummy feature [LOB hack] 1973 Long featID = _createFeature(entityID,entityType,key,currValue,valueType,stmt); 1974 1975 //3.2. update CLOBs if needed 1976 if (valueType == DBHelper.VALUE_TYPE_STRING) { 1977 //does this string fit into a varchar2 or into clob? 1978 String s = (String)currValue; 1979 if (false == this.fitsInVarchar2(s)) { 1980 // Houston, we have a problem 1981 // put the string into a clob 1982 _updateFeatureLOB(featID,value,valueType); 1983 } 1984 } 1985 else if (valueType == DBHelper.VALUE_TYPE_BINARY) { 1986 //3.3. BLOBs 1987 _updateFeatureLOB(featID,value,valueType); 1988 } 1989 } 1990 1991 1992 } 1993 1994 1995 /** 1996 * splits complex features (Lists) into a vector of Feature entries 1997 * each entry contains the entity id, 1998 * entity type, 1999 * feature key 2000 * feature value 2001 * value type 2002 * 2003 */ 2004 private Vector normalizeFeature(Long entityID, int entityType,String key, Object value) 2005 throws PersistenceException { 2006 2007 //1. what kind of feature value is this? 2008 int valueType = findFeatureType(value); 2009 2010 //2. how many elements do we store? 2011 Vector elementsToStore = new Vector(); 2012 Vector features = new Vector(); 2013 2014 switch(valueType) { 2015 case DBHelper.VALUE_TYPE_NULL: 2016 case DBHelper.VALUE_TYPE_BINARY: 2017 case DBHelper.VALUE_TYPE_BOOLEAN: 2018 case DBHelper.VALUE_TYPE_FLOAT: 2019 case DBHelper.VALUE_TYPE_INTEGER: 2020 case DBHelper.VALUE_TYPE_LONG: 2021 case DBHelper.VALUE_TYPE_STRING: 2022 elementsToStore.add(value); 2023 break; 2024 2025 default: 2026 //arrays 2027 List arr = (List)value; 2028 Iterator itValues = arr.iterator(); 2029 2030 while (itValues.hasNext()) { 2031 elementsToStore.add(itValues.next()); 2032 } 2033 2034 //normalize , i.e. ignore arrays 2035 if (valueType == DBHelper.VALUE_TYPE_BINARY_ARR) 2036 valueType = DBHelper.VALUE_TYPE_BINARY; 2037 else if (valueType == DBHelper.VALUE_TYPE_BOOLEAN_ARR) 2038 valueType = DBHelper.VALUE_TYPE_BOOLEAN; 2039 else if (valueType == DBHelper.VALUE_TYPE_FLOAT_ARR) 2040 valueType = DBHelper.VALUE_TYPE_FLOAT; 2041 else if (valueType == DBHelper.VALUE_TYPE_INTEGER_ARR) 2042 valueType = DBHelper.VALUE_TYPE_INTEGER; 2043 else if (valueType == DBHelper.VALUE_TYPE_LONG_ARR) 2044 valueType = DBHelper.VALUE_TYPE_LONG; 2045 else if (valueType == DBHelper.VALUE_TYPE_STRING_ARR) 2046 valueType = DBHelper.VALUE_TYPE_STRING; 2047 } 2048 2049 for (int i=0; i< elementsToStore.size(); i++) { 2050 2051 Object currValue = elementsToStore.elementAt(i); 2052 Feature currFeature = new Feature(entityID,entityType,key,currValue,valueType); 2053 features.add(currFeature); 2054 } 2055 2056 return features; 2057 } 2058 2059 2060 /** 2061 * checks if a String should be stores as VARCHAR2 or CLOB 2062 * because the VARCHAR2 in Oracle is limited to 4000 <b>bytes</b>, not all 2063 * the strings fit there. If a String is too long then it is store in the 2064 * database as CLOB. 2065 * Note that in the worst case 3 bytes are needed to represent a single character 2066 * in a database with UTF8 encoding, which limits the string length to 4000/3 2067 * (ORACLE_VARCHAR_LIMIT_BYTES) 2068 * @see ORACLE_VARCHAR_LIMIT_BYTES 2069 */ 2070 private boolean fitsInVarchar2(String s) { 2071 2072 return s.getBytes().length < this.ORACLE_VARCHAR_LIMIT_BYTES; 2073 } 2074 2075 2076 2077 /** 2078 * helper metod 2079 * iterates a FeatureMap and creates all its features in the database 2080 */ 2081 private void createFeatures(Long entityID, int entityType, FeatureMap features) 2082 throws PersistenceException { 2083 2084 //0. prepare statement ad use it for all features 2085 CallableStatement stmt = null; 2086 CallableStatement stmtBulk = null; 2087 ArrayDescriptor adNumber = null; 2088 ArrayDescriptor adString = null; 2089 2090 try { 2091 stmt = this.jdbcConn.prepareCall( 2092 "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} "); 2093 2094 stmtBulk = this.jdbcConn.prepareCall( 2095 "{ call "+Gate.DB_OWNER+".persist.create_feature_bulk(?,?,?,?,?,?,?,?)} "); 2096 2097 adNumber = ArrayDescriptor.createDescriptor("GATEADMIN.PERSIST.INTARRAY", this.jdbcConn); 2098 adString = ArrayDescriptor.createDescriptor("GATEADMIN.PERSIST.CHARARRAY", this.jdbcConn); 2099 } 2100 catch (SQLException sqle) { 2101 throw new PersistenceException(sqle); 2102 } 2103 2104 /* when some day Java has macros, this will be a macro */ 2105 Set entries = features.entrySet(); 2106 Iterator itFeatures = entries.iterator(); 2107 while (itFeatures.hasNext()) { 2108 Map.Entry entry = (Map.Entry)itFeatures.next(); 2109 String key = (String)entry.getKey(); 2110 Object value = entry.getValue(); 2111 createFeature(entityID,entityType,key,value,stmt); 2112 } 2113 2114 //3. cleanup 2115 DBHelper.cleanup(stmt); 2116 } 2117 2118 2119 /** 2120 * helper metod 2121 * iterates a FeatureMap and creates all its features in the database 2122 * 2123 * since it uses Oracle VARRAYs the roundtrips between the client and the server 2124 * are minimized 2125 * 2126 * make sure the two types STRING_ARRAY and INT_ARRAY have the same name in the 2127 * PL/SQL files 2128 * 2129 * also when referencing the types always use the schema owner in upper case 2130 * because the jdbc driver is buggy (see MetaLink note if u care) 2131 */ 2132 private void createFeaturesBulk(Long entityID, int entityType, FeatureMap features) 2133 throws PersistenceException { 2134 2135 //0. prepare statement ad use it for all features 2136 CallableStatement stmt = null; 2137 CallableStatement stmtBulk = null; 2138 ArrayDescriptor adNumber = null; 2139 ArrayDescriptor adString = null; 2140 2141 try { 2142 stmt = this.jdbcConn.prepareCall( 2143 "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} "); 2144 2145 stmtBulk = this.jdbcConn.prepareCall( 2146 "{ call "+Gate.DB_OWNER+".persist.create_feature_bulk(?,?,?,?,?,?,?,?)} "); 2147 2148 //ACHTUNG!!! 2149 //using toUpper for schema owner is necessary because of the dull JDBC driver 2150 //otherwise u'll end up with "invalid name pattern" Oracle error 2151 adString = ArrayDescriptor.createDescriptor(Gate.DB_OWNER.toUpperCase()+".STRING_ARRAY", this.jdbcConn); 2152 adNumber = ArrayDescriptor.createDescriptor(Gate.DB_OWNER.toUpperCase()+".INT_ARRAY", this.jdbcConn); 2153 } 2154 catch (SQLException sqle) { 2155 throw new PersistenceException(sqle); 2156 } 2157 2158 /* when some day Java has macros, this will be a macro */ 2159 Vector entityFeatures = new Vector(); 2160 2161 Set entries = features.entrySet(); 2162 Iterator itFeatures = entries.iterator(); 2163 while (itFeatures.hasNext()) { 2164 Map.Entry entry = (Map.Entry)itFeatures.next(); 2165 String key = (String)entry.getKey(); 2166 Object value = entry.getValue(); 2167 Vector normalizedFeatures = normalizeFeature(entityID,entityType,key,value); 2168 entityFeatures.addAll(normalizedFeatures); 2169 } 2170 2171 //iterate all features, store LOBs directly and other features with bulk store 2172 Iterator itEntityFeatures = entityFeatures.iterator(); 2173 2174 while (itEntityFeatures.hasNext()) { 2175 2176 Feature currFeature = (Feature)itEntityFeatures.next(); 2177 2178 if (currFeature.valueType == DBHelper.VALUE_TYPE_STRING) { 2179 //does this string fit into a varchar2 or into clob? 2180 String s = (String)currFeature.value; 2181 if (false == this.fitsInVarchar2(s)) { 2182 // Houston, we have a problem 2183 // put the string into a clob 2184 Long featID = _createFeature(currFeature.entityID, 2185 currFeature.entityType, 2186 currFeature.key, 2187 currFeature.value, 2188 currFeature.valueType, 2189 stmt); 2190 _updateFeatureLOB(featID,currFeature.value,currFeature.valueType); 2191 itEntityFeatures.remove(); 2192 } 2193 } 2194 else if (currFeature.valueType == DBHelper.VALUE_TYPE_BINARY) { 2195 //3.3. BLOBs 2196 Long featID = _createFeature(currFeature.entityID, 2197 currFeature.entityType, 2198 currFeature.key, 2199 currFeature.value, 2200 currFeature.valueType, 2201 stmt); 2202 _updateFeatureLOB(featID,currFeature.value,currFeature.valueType); 2203 itEntityFeatures.remove(); 2204 } 2205 } 2206 2207 //now we have the data for the bulk store 2208 _createFeatureBulk(entityFeatures, stmtBulk, adNumber, adString); 2209 2210 //3. cleanup 2211 DBHelper.cleanup(stmt); 2212 DBHelper.cleanup(stmtBulk); 2213 } 2214 2215 2216 /** get security information for LR . */ 2217 public SecurityInfo getSecurityInfo(LanguageResource lr) 2218 throws PersistenceException { 2219 2220 //0. preconditions 2221 Assert.assertNotNull(lr); 2222 Assert.assertNotNull(lr.getLRPersistenceId()); 2223 Assert.assertTrue(lr.getLRPersistenceId() instanceof Long); 2224 Assert.assertEquals(this,lr.getDataStore()); 2225 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 2226 lr instanceof DatabaseCorpusImpl); 2227 2228 PreparedStatement pstmt = null; 2229 ResultSet rs = null; 2230 2231 //1. read data 2232 Long userID = null; 2233 Long groupID = null; 2234 int perm; 2235 try { 2236 String sql = " select lr_owner_user_id, "+ 2237 " lr_owner_group_id, " + 2238 " lr_access_mode "+ 2239 " from "+gate.Gate.DB_OWNER+".t_lang_resource "+ 2240 " where lr_id = ?"; 2241 pstmt = this.jdbcConn.prepareStatement(sql); 2242 pstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue()); 2243 rs = pstmt.executeQuery(); 2244 2245 if (false == rs.next()) { 2246 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2247 } 2248 2249 userID = new Long(rs.getLong("lr_owner_user_id")); 2250 groupID = new Long(rs.getLong("lr_owner_group_id")); 2251 perm = rs.getInt("lr_access_mode"); 2252 2253 Assert.assertTrue(perm == SecurityInfo.ACCESS_GR_GW || 2254 perm == SecurityInfo.ACCESS_GR_OW || 2255 perm == SecurityInfo.ACCESS_OR_OW || 2256 perm == SecurityInfo.ACCESS_WR_GW); 2257 } 2258 catch(SQLException sqle) { 2259 throw new PersistenceException("Can't read document permissions from DB, error is [" + 2260 sqle.getMessage() +"]"); 2261 } 2262 finally { 2263 DBHelper.cleanup(rs); 2264 DBHelper.cleanup(pstmt); 2265 } 2266 2267 //2. get data from AccessController 2268 User usr = null; 2269 Group grp = null; 2270 try { 2271 usr = this.ac.findUser(userID); 2272 grp = this.ac.findGroup(groupID); 2273 } 2274 catch (SecurityException se) { 2275 throw new PersistenceException("Invalid security settings found in DB [" + 2276 se.getMessage() +"]"); 2277 } 2278 2279 //3. construct SecurityInfo 2280 SecurityInfo si = new SecurityInfo(perm,usr,grp); 2281 2282 2283 return si; 2284 } 2285 2286 2287 2288 /** set security information for LR . */ 2289 public void setSecurityInfo(LanguageResource lr,SecurityInfo si) 2290 throws PersistenceException, SecurityException { 2291 throw new MethodNotImplementedException(); 2292 } 2293 2294 2295 2296 /** 2297 * helper method for getLR - reads LR of type Corpus 2298 */ 2299 private DatabaseCorpusImpl readCorpus(Object lrPersistenceId) 2300 throws PersistenceException { 2301 2302 //0. preconditions 2303 Assert.assertNotNull(lrPersistenceId); 2304 2305 if (false == lrPersistenceId instanceof Long) { 2306 throw new IllegalArgumentException(); 2307 } 2308 2309 //3. read from DB 2310 PreparedStatement pstmt = null; 2311 ResultSet rs = null; 2312 DatabaseCorpusImpl result = null; 2313 2314 try { 2315 String sql = " select lr_name " + 2316 " from "+Gate.DB_OWNER+".t_lang_resource " + 2317 " where lr_id = ? "; 2318 pstmt = this.jdbcConn.prepareStatement(sql); 2319 pstmt.setLong(1,((Long)lrPersistenceId).longValue()); 2320 pstmt.execute(); 2321 rs = pstmt.getResultSet(); 2322 2323 if (false == rs.next()) { 2324 //ooops mo data found 2325 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2326 } 2327 2328 //4. fill data 2329 2330 //4.1 name 2331 String lrName = rs.getString("lr_name"); 2332 Assert.assertNotNull(lrName); 2333 2334 //4.8 features 2335 FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_CORPUS); 2336 2337 //4.9 cleanup 2338 DBHelper.cleanup(rs); 2339 DBHelper.cleanup(pstmt); 2340 2341 sql = " select lr_id ," + 2342 " lr_name " + 2343 " from "+Gate.DB_OWNER+".t_document doc, " + 2344 " "+Gate.DB_OWNER+".t_lang_resource lr, " + 2345 " "+Gate.DB_OWNER+".t_corpus_document corpdoc, " + 2346 " "+Gate.DB_OWNER+".t_corpus corp " + 2347 " where lr.lr_id = doc.doc_lr_id " + 2348 " and doc.doc_id = corpdoc.cd_doc_id " + 2349 " and corpdoc.cd_corp_id = corp.corp_id " + 2350 " and corp_lr_id = ? "; 2351 pstmt = this.jdbcConn.prepareStatement(sql); 2352 pstmt.setLong(1,((Long)lrPersistenceId).longValue()); 2353 pstmt.execute(); 2354 rs = pstmt.getResultSet(); 2355 2356 //--Vector docLRIDs = new Vector(); 2357 Vector documentData = new Vector(); 2358 while (rs.next()) { 2359 Long docLRID = new Long(rs.getLong("lr_id")); 2360 String docName = rs.getString("lr_name"); 2361 //--docLRIDs.add(docLRID); 2362 documentData.add(new DocumentData(docName, docLRID)); 2363 } 2364 DBHelper.cleanup(rs); 2365 DBHelper.cleanup(pstmt); 2366 2367/* 2368 Vector dbDocs = new Vector(); 2369 for (int i=0; i< docLRIDs.size(); i++) { 2370 Long currLRID = (Long)docLRIDs.elementAt(i); 2371 //kalina: replaced by a Factory call, so the doc gets registered 2372 //properly in GATE. Otherwise strange behaviour results in the GUI 2373 //and no events come about it 2374// Document dbDoc = (Document)getLr(DBHelper.DOCUMENT_CLASS,currLRID); 2375 FeatureMap params = Factory.newFeatureMap(); 2376 params.put(DataStore.DATASTORE_FEATURE_NAME, this); 2377 params.put(DataStore.LR_ID_FEATURE_NAME, currLRID); 2378 Document dbDoc = (Document)Factory.createResource(DBHelper.DOCUMENT_CLASS, params); 2379 2380 2381 dbDocs.add(dbDoc); 2382 } 2383*/ 2384 result = new DatabaseCorpusImpl(lrName, 2385 this, 2386 (Long)lrPersistenceId, 2387 features, 2388 // dbDocs); 2389 documentData); 2390 } 2391 catch(SQLException sqle) { 2392 throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]"); 2393 } 2394 catch(Exception e) { 2395 throw new PersistenceException(e); 2396 } 2397 finally { 2398 DBHelper.cleanup(rs); 2399 DBHelper.cleanup(pstmt); 2400 } 2401 2402 return result; 2403 } 2404 2405 2406 /** helper method for getLR - reads LR of type Document */ 2407 private DatabaseDocumentImpl readDocument(Object lrPersistenceId) 2408 throws PersistenceException { 2409 2410 //0. preconditions 2411 Assert.assertNotNull(lrPersistenceId); 2412 2413 if (false == lrPersistenceId instanceof Long) { 2414 throw new IllegalArgumentException(); 2415 } 2416 2417 // 1. dummy document to be initialized 2418 DatabaseDocumentImpl result = new DatabaseDocumentImpl(this.jdbcConn); 2419 2420 PreparedStatement pstmt = null; 2421 ResultSet rs = null; 2422 2423 //3. read from DB 2424 try { 2425 String sql = " select lr_name, " + 2426 " lrtp_type, " + 2427 " lr_id, " + 2428 " lr_parent_id, " + 2429 " doc_id, " + 2430 " doc_url, " + 2431 " doc_start, " + 2432 " doc_end, " + 2433 " doc_is_markup_aware " + 2434 " from "+Gate.DB_OWNER+".v_document " + 2435 " where lr_id = ? "; 2436 2437 pstmt = this.jdbcConn.prepareStatement(sql); 2438 pstmt.setLong(1,((Long)lrPersistenceId).longValue()); 2439 pstmt.execute(); 2440 rs = pstmt.getResultSet(); 2441 2442 if (false == rs.next()) { 2443 //ooops mo data found 2444 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2445 } 2446 2447 //4. fill data 2448 2449 //4.0 name 2450 String lrName = rs.getString("lr_name"); 2451 Assert.assertNotNull(lrName); 2452 result.setName(lrName); 2453 2454 //4.1 parent 2455 Long parentID = null; 2456 long parent_id = rs.getLong("lr_parent_id"); 2457 if (false == rs.wasNull()) { 2458 parentID = new Long(parent_id); 2459 2460 //read parent resource 2461 LanguageResource parentLR = this.getLr(DBHelper.DOCUMENT_CLASS,parentID); 2462 Assert.assertNotNull(parentLR); 2463 Assert.assertTrue(parentLR instanceof DatabaseDocumentImpl); 2464 2465 result.setParent(parentLR); 2466 } 2467 2468 2469 //4.2. markup aware 2470 long markup = rs.getLong("doc_is_markup_aware"); 2471 Assert.assertTrue(markup == this.ORACLE_FALSE || markup == this.ORACLE_TRUE); 2472 if (markup == this.ORACLE_FALSE) { 2473 result.setMarkupAware(Boolean.FALSE); 2474 } 2475 else { 2476 result.setMarkupAware(Boolean.TRUE); 2477 2478 } 2479 2480 //4.3 datastore 2481 result.setDataStore(this); 2482 2483 //4.4. persist ID 2484 Long persistID = new Long(rs.getLong("lr_id")); 2485 result.setLRPersistenceId(persistID); 2486 2487 //4.5 source url 2488 String url = rs.getString("doc_url"); 2489 result.setSourceUrl(new URL(url)); 2490 2491 //4.6. start offset 2492 Long start = null; 2493 long longVal = rs.getLong("doc_start"); 2494 //null? 2495 //if NULL is stored in the DB, Oracle returns 0 which is not what we want 2496 if (false == rs.wasNull()) { 2497 start = new Long(longVal); 2498 } 2499 result.setSourceUrlStartOffset(start); 2500// initData.put("DOC_SOURCE_URL_START",start); 2501 2502 //4.7. end offset 2503 Long end = null; 2504 longVal = rs.getLong("doc_end"); 2505 //null? 2506 //if NULL is stored in the DB, Oracle returns 0 which is not what we want 2507 if (false == rs.wasNull()) { 2508 end = new Long(longVal); 2509 } 2510 result.setSourceUrlEndOffset(end); 2511// initData.put("DOC_SOURCE_URL_END",end); 2512 2513 //4.8 features 2514 FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_DOCUMENT); 2515 result.setFeatures(features); 2516 //initData.put("DOC_FEATURES",features); 2517 2518 //4.9 set the nextAnnotationID correctly 2519 long doc_id = rs.getLong("doc_id"); 2520 2521 DBHelper.cleanup(rs); 2522 DBHelper.cleanup(pstmt); 2523 sql = " select max(ann_local_id),'ann_id'" + 2524 " from "+Gate.DB_OWNER+".t_annotation " + 2525 " where ann_doc_id = ?" + 2526 " union " + 2527 " select max(node_local_id),'node_id' " + 2528 " from "+Gate.DB_OWNER+".t_node " + 2529 " where node_doc_id = ?"; 2530 2531 pstmt = this.jdbcConn.prepareStatement(sql); 2532 pstmt.setLong(1,doc_id); 2533 pstmt.setLong(2,doc_id); 2534 pstmt.execute(); 2535 rs = pstmt.getResultSet(); 2536 2537 int maxAnnID = 0 , maxNodeID = 0; 2538 //ann id 2539 if (false == rs.next()) { 2540 //ooops no data found 2541 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2542 } 2543 if (rs.getString(2).equals("ann_id")) 2544 maxAnnID = rs.getInt(1); 2545 else 2546 maxNodeID = rs.getInt(1); 2547 2548 if (false == rs.next()) { 2549 //ooops no data found 2550 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2551 } 2552 if (rs.getString(2).equals("node_id")) 2553 maxNodeID = rs.getInt(1); 2554 else 2555 maxAnnID = rs.getInt(1); 2556 2557 result.setNextNodeId(maxNodeID+1); 2558// initData.put("DOC_NEXT_NODE_ID",new Integer(maxNodeID+1)); 2559 result.setNextAnnotationId(maxAnnID+1); 2560// initData.put("DOC_NEXT_ANN_ID",new Integer(maxAnnID+1)); 2561 2562 2563// params.put("initData__$$__", initData); 2564// try { 2565 //here we create the persistent LR via Factory, so it's registered 2566 //in GATE 2567// result = (DatabaseDocumentImpl)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params); 2568// } 2569// catch (gate.creole.ResourceInstantiationException ex) { 2570// throw new GateRuntimeException(ex.getMessage()); 2571// } 2572 } 2573 catch(SQLException sqle) { 2574 throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]"); 2575 } 2576 catch(Exception e) { 2577 throw new PersistenceException(e); 2578 } 2579 finally { 2580 DBHelper.cleanup(rs); 2581 DBHelper.cleanup(pstmt); 2582 } 2583 2584 return result; 2585 } 2586 2587 2588 2589 /** 2590 * reads the features of an entity 2591 * entities are of type LR or Annotation 2592 */ 2593 private FeatureMap readFeatures(Long entityID, int entityType) 2594 throws PersistenceException { 2595 2596 //0. preconditions 2597 Assert.assertNotNull(entityID); 2598 Assert.assertTrue(entityType == DBHelper.FEATURE_OWNER_ANNOTATION || 2599 entityType == DBHelper.FEATURE_OWNER_CORPUS || 2600 entityType == DBHelper.FEATURE_OWNER_DOCUMENT); 2601 2602 2603 PreparedStatement pstmt = null; 2604 ResultSet rs = null; 2605 FeatureMap fm = new SimpleFeatureMapImpl(); 2606 2607 //1. read from DB 2608 try { 2609 String sql = " select v2.fk_string, " + 2610 " v1.ft_value_type, " + 2611 " v1.ft_number_value, " + 2612 " v1.ft_binary_value, " + 2613 " v1.ft_character_value, " + 2614 " v1.ft_long_character_value " + 2615 " from "+Gate.DB_OWNER+".t_feature v1, " + 2616 " "+Gate.DB_OWNER+".t_feature_key v2 " + 2617 " where v1.ft_entity_id = ? " + 2618 " and v1.ft_entity_type = ? " + 2619 " and v1.ft_key_id = v2.fk_id " + 2620 " order by v2.fk_string,v1.ft_id"; 2621 2622 pstmt = this.jdbcConn.prepareStatement(sql); 2623 pstmt.setLong(1,entityID.longValue()); 2624 pstmt.setLong(2,entityType); 2625 pstmt.execute(); 2626 rs = pstmt.getResultSet(); 2627 2628 //3. fill feature map 2629 Vector arrFeatures = new Vector(); 2630 String prevKey = null; 2631 String currKey = null; 2632 Object currFeature = null; 2633 2634 2635 while (rs.next()) { 2636 //NOTE: because there are LOBs in the resulset 2637 //the columns should be read in the order they appear 2638 //in the query 2639 currKey = rs.getString(1); 2640 2641 Long valueType = new Long(rs.getLong(2)); 2642 2643 //we don't quite know what is the type of the NUMBER 2644 //stored in DB 2645 Object numberValue = null; 2646 2647 //for all numeric types + boolean -> read from DB as appropriate 2648 //Java object 2649 switch(valueType.intValue()) { 2650 2651 case DBHelper.VALUE_TYPE_BOOLEAN: 2652 numberValue = new Boolean(rs.getBoolean(3)); 2653 break; 2654 2655 case DBHelper.VALUE_TYPE_FLOAT: 2656 numberValue = new Float(rs.getFloat(3)); 2657 break; 2658 2659 case DBHelper.VALUE_TYPE_INTEGER: 2660 numberValue = new Integer(rs.getInt(3)); 2661 break; 2662 2663 case DBHelper.VALUE_TYPE_LONG: 2664 numberValue = new Long(rs.getLong(3)); 2665 break; 2666 } 2667 2668 //don't forget to read the rest of the current row 2669 Blob blobValue = rs.getBlob(4); 2670 String stringValue = rs.getString(5); 2671 Clob clobValue = rs.getClob(6); 2672 2673 switch(valueType.intValue()) { 2674 2675 case DBHelper.VALUE_TYPE_NULL: 2676 currFeature = null; 2677 break; 2678 2679 case DBHelper.VALUE_TYPE_BOOLEAN: 2680 case DBHelper.VALUE_TYPE_FLOAT: 2681 case DBHelper.VALUE_TYPE_INTEGER: 2682 case DBHelper.VALUE_TYPE_LONG: 2683 currFeature = numberValue; 2684 break; 2685 2686 case DBHelper.VALUE_TYPE_BINARY: 2687 currFeature = readBLOB(blobValue); 2688 break; 2689 2690 case DBHelper.VALUE_TYPE_STRING: 2691 //this one is tricky too 2692 //if the string is < 4000 bytes long then it's stored as varchar2 2693 //otherwise as CLOB 2694 if (null == stringValue) { 2695 //oops, we got CLOB 2696 StringBuffer temp = new StringBuffer(); 2697 readCLOB(clobValue,temp); 2698 currFeature = temp.toString(); 2699 } 2700 else { 2701 currFeature = stringValue; 2702 } 2703 break; 2704 2705 default: 2706 throw new PersistenceException("Invalid feature type found in DB, type is ["+valueType.intValue()+"]"); 2707 }//switch 2708 2709 //new feature or part of an array? 2710 if (currKey.equals(prevKey) && prevKey != null) { 2711 //part of array 2712 arrFeatures.add(currFeature); 2713 } 2714 else { 2715 //add prev feature to feature map 2716 2717 //is the prev feature an array or a single object? 2718 if (arrFeatures.size() > 1) { 2719 //put a clone, because this is a temp array that will 2720 //be cleared in few lines 2721 fm.put(prevKey, new Vector(arrFeatures)); 2722 } 2723 else if (arrFeatures.size() == 1) { 2724 fm.put(prevKey,arrFeatures.elementAt(0)); 2725 } 2726 else { 2727 //do nothing, this is the dummy feature 2728 ; 2729 }//if 2730 2731 //now clear the array from previous fesature(s) and put the new 2732 //one there 2733 arrFeatures.clear(); 2734 2735 prevKey = currKey; 2736 arrFeatures.add(currFeature); 2737 }//if 2738 }//while 2739 2740 //add the last feature 2741 if (arrFeatures.size() > 1) { 2742 fm.put(currKey,arrFeatures); 2743 } 2744 else if (arrFeatures.size() == 1) { 2745 fm.put(currKey,arrFeatures.elementAt(0)); 2746 } 2747 }//try 2748 catch(SQLException sqle) { 2749 throw new PersistenceException("can't read features from DB: ["+ sqle.getMessage()+"]"); 2750 } 2751 catch(IOException ioe) { 2752 throw new PersistenceException("can't read features from DB: ["+ ioe.getMessage()+"]"); 2753 } 2754 catch(ClassNotFoundException cnfe) { 2755 throw new PersistenceException("can't read features from DB: ["+ cnfe.getMessage()+"]"); 2756 } 2757 finally { 2758 DBHelper.cleanup(rs); 2759 DBHelper.cleanup(pstmt); 2760 } 2761 2762 return fm; 2763 } 2764 2765 2766 2767 /** 2768 * reads the ID of the database 2769 * every database should have unique string ID 2770 */ 2771 protected String readDatabaseID() throws PersistenceException{ 2772 2773 PreparedStatement pstmt = null; 2774 ResultSet rs = null; 2775 String result = null; 2776 2777 //1. read from DB 2778 try { 2779 String sql = " select par_value_string " + 2780 " from "+Gate.DB_OWNER+".t_parameter " + 2781 " where par_key = ? "; 2782 2783 pstmt = this.jdbcConn.prepareStatement(sql); 2784 pstmt.setString(1,DBHelper.DB_PARAMETER_GUID); 2785 pstmt.execute(); 2786 rs = pstmt.getResultSet(); 2787 2788 if (false == rs.next()) { 2789 throw new PersistenceException("Can't read database parameter ["+ 2790 DBHelper.DB_PARAMETER_GUID+"]"); 2791 } 2792 result = rs.getString(1); 2793 } 2794 catch(SQLException sqle) { 2795 throw new PersistenceException("Can't read database parameter ["+ 2796 sqle.getMessage()+"]"); 2797 } 2798 finally { 2799 DBHelper.cleanup(rs); 2800 DBHelper.cleanup(pstmt); 2801 } 2802 2803 if (DEBUG) { 2804 Out.println("reult=["+result+"]"); 2805 } 2806 2807 return result; 2808 } 2809 2810 2811 2812 /** 2813 * checks if two databases are identical 2814 * @see readDatabaseID() 2815 * NOTE: the same database may be represented by different OracleDataStore instances 2816 * but the IDs will be the same 2817 */ 2818 public boolean equals(Object obj) { 2819 2820 if (false == obj instanceof OracleDataStore) { 2821 return false; 2822 } 2823 2824 OracleDataStore db2 = (OracleDataStore)obj; 2825 2826 if (false == this.getDatabaseID().equals(db2.getDatabaseID())) { 2827 return false; 2828 } 2829 2830 return true; 2831 } 2832 2833 2834 2835 /** helper for sync() - saves a Document in the database */ 2836 private void syncDocument(Document doc) 2837 throws PersistenceException, SecurityException { 2838 2839 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 2840 Assert.assertTrue(doc.getLRPersistenceId() instanceof Long); 2841 2842 Long lrID = (Long)doc.getLRPersistenceId(); 2843 EventAwareLanguageResource dbDoc = (EventAwareLanguageResource)doc; 2844 //1. sync LR 2845 // only name can be changed here 2846 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_NAME)) { 2847 _syncLR(doc); 2848 } 2849 2850 //2. sync Document 2851 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_MAIN)) { 2852 _syncDocumentHeader(doc); 2853 } 2854 2855 //3. [optional] sync Content 2856 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_CONTENT)) { 2857 _syncDocumentContent(doc); 2858 } 2859 2860 //4. [optional] sync Features 2861 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) { 2862 _syncFeatures(doc); 2863 } 2864 2865 //5. [optional] delete from DB named sets that were removed from the document 2866 Collection removedSets = ((EventAwareDocument)dbDoc).getRemovedAnnotationSets(); 2867 Collection addedSets = ((EventAwareDocument)dbDoc).getAddedAnnotationSets(); 2868 if (false == removedSets.isEmpty() || false == addedSets.isEmpty()) { 2869 _syncAnnotationSets(doc,removedSets,addedSets); 2870 } 2871 2872 //6. [optional] sync Annotations 2873 _syncAnnotations(doc); 2874 } 2875 2876 2877 2878 /** 2879 * helper for sync() 2880 * NEVER call directly 2881 */ 2882 private void _syncLR(LanguageResource lr) 2883 throws PersistenceException,SecurityException { 2884 2885 //0.preconditions 2886 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 2887 lr instanceof DatabaseCorpusImpl);; 2888 Assert.assertNotNull(lr.getLRPersistenceId()); 2889 2890 CallableStatement stmt = null; 2891 2892 try { 2893 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.update_lr(?,?,?) }"); 2894 stmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue()); 2895 stmt.setString(2,lr.getName()); 2896 //do we have a parent resource? 2897 if (lr instanceof Document && 2898 null != lr.getParent()) { 2899 stmt.setLong(3,((Long)lr.getParent().getLRPersistenceId()).longValue()); 2900 } 2901 else { 2902 stmt.setNull(3,java.sql.Types.BIGINT); 2903 } 2904 2905 stmt.execute(); 2906 } 2907 catch(SQLException sqle) { 2908 2909 switch(sqle.getErrorCode()) { 2910 case DBHelper.X_ORACLE_INVALID_LR: 2911 throw new PersistenceException("can't set LR name in DB: [invalid LR ID]"); 2912 default: 2913 throw new PersistenceException( 2914 "can't set LR name in DB: ["+ sqle.getMessage()+"]"); 2915 } 2916 2917 } 2918 finally { 2919 DBHelper.cleanup(stmt); 2920 } 2921 } 2922 2923 2924 2925 /** helper for sync() - never call directly */ 2926 private void _syncDocumentHeader(Document doc) 2927 throws PersistenceException { 2928 2929 Long lrID = (Long)doc.getLRPersistenceId(); 2930 2931 CallableStatement stmt = null; 2932 2933 try { 2934 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 2935 ".persist.update_document(?,?,?,?,?) }"); 2936 stmt.setLong(1,lrID.longValue()); 2937 stmt.setString(2,doc.getSourceUrl().toString()); 2938 //do we have start offset? 2939 if (null==doc.getSourceUrlStartOffset()) { 2940 stmt.setNull(3,java.sql.Types.NUMERIC); 2941 } 2942 else { 2943 stmt.setLong(3,doc.getSourceUrlStartOffset().longValue()); 2944 } 2945 //do we have end offset? 2946 if (null==doc.getSourceUrlEndOffset()) { 2947 stmt.setNull(4,java.sql.Types.NUMERIC); 2948 } 2949 else { 2950 stmt.setLong(4,doc.getSourceUrlEndOffset().longValue()); 2951 } 2952 2953 stmt.setLong(5,true == doc.getMarkupAware().booleanValue() ? this.ORACLE_TRUE 2954 : this.ORACLE_FALSE); 2955 2956 stmt.execute(); 2957 } 2958 catch(SQLException sqle) { 2959 2960 switch(sqle.getErrorCode()) { 2961 case DBHelper.X_ORACLE_INVALID_LR : 2962 throw new PersistenceException("invalid LR supplied: no such document: ["+ 2963 sqle.getMessage()+"]"); 2964 default: 2965 throw new PersistenceException("can't change document data: ["+ 2966 sqle.getMessage()+"]"); 2967 } 2968 } 2969 finally { 2970 DBHelper.cleanup(stmt); 2971 } 2972 2973 } 2974 2975 2976 2977 /** helper for sync() - never call directly */ 2978 private void _syncDocumentContent(Document doc) 2979 throws PersistenceException { 2980 2981 PreparedStatement pstmt = null; 2982 ResultSet rs = null; 2983 Long docContID = null; 2984 2985 //1. read from DB 2986 try { 2987 String sql = " select dc_id " + 2988 " from "+Gate.DB_OWNER+".v_content " + 2989 " where lr_id = ? "; 2990 2991 pstmt = this.jdbcConn.prepareStatement(sql); 2992 pstmt.setLong(1,((Long)doc.getLRPersistenceId()).longValue()); 2993 pstmt.execute(); 2994 rs = pstmt.getResultSet(); 2995 2996 if (false == rs.next()) { 2997 throw new PersistenceException("invalid LR ID supplied"); 2998 } 2999 3000 //1, get DC_ID 3001 docContID = new Long(rs.getLong(1)); 3002 3003 //2, update LOBs 3004 updateDocumentContent(docContID,doc.getContent()); 3005 3006 } 3007 catch(SQLException sqle) { 3008 throw new PersistenceException("Cannot update document content ["+ 3009 sqle.getMessage()+"]"); 3010 } 3011 finally { 3012 DBHelper.cleanup(rs); 3013 DBHelper.cleanup(pstmt); 3014 } 3015 } 3016 3017 3018 3019 /** helper for sync() - never call directly */ 3020 private void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes) 3021 throws PersistenceException { 3022 3023 //0.preconditions 3024 Assert.assertNotNull(doc); 3025 Assert.assertNotNull(as); 3026 Assert.assertNotNull(changes); 3027 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 3028 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl); 3029 Assert.assertTrue(changes.size() > 0); 3030 3031 3032 PreparedStatement pstmt = null; 3033 ResultSet rs = null; 3034 CallableStatement cstmt = null; 3035 Long lrID = (Long)doc.getLRPersistenceId(); 3036// Long docID = null; 3037 Long asetID = null; 3038 3039 try { 3040 //1. get the a-set ID in the database 3041 String sql = " select as_id " + 3042// " as_doc_id " + 3043 " from "+Gate.DB_OWNER+".v_annotation_set " + 3044 " where lr_id = ? "; 3045 //do we have aset name? 3046 String clause = null; 3047 String name = as.getName(); 3048 if (null != name) { 3049 clause = " and as_name = ? "; 3050 } 3051 else { 3052 clause = " and as_name is null "; 3053 } 3054 sql = sql + clause; 3055 3056 pstmt = this.jdbcConn.prepareStatement(sql); 3057 pstmt.setLong(1,lrID.longValue()); 3058 if (null != name) { 3059 pstmt.setString(2,name); 3060 } 3061 pstmt.execute(); 3062 rs = pstmt.getResultSet(); 3063 3064 if (rs.next()) { 3065 asetID = new Long(rs.getLong("as_id")); 3066// docID = new Long(rs.getLong("as_doc_id")); 3067//System.out.println("syncing annots, lr_id=["+lrID+"],doc_id=["+docID+"], set_id=["+asetID+"]"); 3068 } 3069 else { 3070 throw new PersistenceException("cannot find annotation set with" + 3071 " name=["+name+"] , LRID=["+lrID+"] in database"); 3072 } 3073 3074 //3. insert the new annotations from this set 3075 3076 //3.1. prepare call 3077 cstmt = this.jdbcConn.prepareCall( 3078 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }"); 3079 3080 Long annGlobalID = null; 3081 Iterator it = changes.iterator(); 3082 3083 while (it.hasNext()) { 3084 3085 //3.2. insert annotation 3086 Annotation ann = (Annotation)it.next(); 3087 3088 Node start = (Node)ann.getStartNode(); 3089 Node end = (Node)ann.getEndNode(); 3090 String type = ann.getType(); 3091 3092 cstmt.setLong(1,lrID.longValue()); 3093 cstmt.setLong(2,ann.getId().longValue()); 3094 cstmt.setLong(3,asetID.longValue()); 3095 cstmt.setLong(4,start.getId().longValue()); 3096 cstmt.setLong(5,start.getOffset().longValue()); 3097 cstmt.setLong(6,end.getId().longValue()); 3098 cstmt.setLong(7,end.getOffset().longValue()); 3099 cstmt.setString(8,type); 3100 cstmt.registerOutParameter(9,java.sql.Types.BIGINT); 3101 3102 cstmt.execute(); 3103 annGlobalID = new Long(cstmt.getLong(9)); 3104 3105 //3.3. set annotation features 3106 FeatureMap features = ann.getFeatures(); 3107 Assert.assertNotNull(features); 3108// createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features); 3109 createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features); 3110 } 3111 } 3112 catch(SQLException sqle) { 3113 throw new PersistenceException("can't add annotations in DB : ["+ 3114 sqle.getMessage()+"]"); 3115 } 3116 finally { 3117 DBHelper.cleanup(rs); 3118 DBHelper.cleanup(pstmt); 3119 DBHelper.cleanup(cstmt); 3120 } 3121 } 3122 3123 3124 3125 /** helper for sync() - never call directly */ 3126 private void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes) 3127 throws PersistenceException { 3128 3129 //technically this approach sux 3130 //at least it works 3131 3132 //1. delete 3133 _syncRemovedAnnotations(doc,as,changes); 3134 //2. recreate 3135 _syncAddedAnnotations(doc,as,changes); 3136 } 3137 3138 3139 /** helper for sync() - never call directly */ 3140 private void _syncRemovedDocumentsFromCorpus(List docLRIDs, Long corpLRID) 3141 throws PersistenceException { 3142 3143 //0.preconditions 3144 Assert.assertNotNull(docLRIDs); 3145 Assert.assertNotNull(corpLRID); 3146 Assert.assertTrue(docLRIDs.size() > 0); 3147 3148 CallableStatement cstmt = null; 3149 3150 try { 3151 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 3152 ".persist.remove_document_from_corpus(?,?) }"); 3153 3154 Iterator it = docLRIDs.iterator(); 3155 while (it.hasNext()) { 3156 Long currLRID = (Long)it.next(); 3157 cstmt.setLong(1,currLRID.longValue()); 3158 cstmt.setLong(2,corpLRID.longValue()); 3159 cstmt.execute(); 3160 } 3161 } 3162 catch(SQLException sqle) { 3163 3164 switch(sqle.getErrorCode()) { 3165 case DBHelper.X_ORACLE_INVALID_LR : 3166 throw new PersistenceException("invalid LR supplied: no such document: ["+ 3167 sqle.getMessage()+"]"); 3168 default: 3169 throw new PersistenceException("can't change document data: ["+ 3170 sqle.getMessage()+"]"); 3171 } 3172 } 3173 finally { 3174 DBHelper.cleanup(cstmt); 3175 } 3176 3177 } 3178 3179 3180 /** helper for sync() - never call directly */ 3181 private void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes) 3182 throws PersistenceException { 3183 //0.preconditions 3184 Assert.assertNotNull(doc); 3185 Assert.assertNotNull(as); 3186 Assert.assertNotNull(changes); 3187 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 3188 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl); 3189 Assert.assertTrue(changes.size() > 0); 3190 3191 3192 PreparedStatement pstmt = null; 3193 ResultSet rs = null; 3194 CallableStatement cstmt = null; 3195 Long lrID = (Long)doc.getLRPersistenceId(); 3196 Long docID = null; 3197 Long asetID = null; 3198 3199 try { 3200 //1. get the a-set ID in the database 3201 String sql = " select as_id, " + 3202 " as_doc_id " + 3203 " from "+Gate.DB_OWNER+".v_annotation_set " + 3204 " where lr_id = ? "; 3205 //do we have aset name? 3206 String clause = null; 3207 String name = as.getName(); 3208 if (null != name) { 3209 clause = " and as_name = ? "; 3210 } 3211 else { 3212 clause = " and as_name is null "; 3213 } 3214 sql = sql + clause; 3215 3216 pstmt = this.jdbcConn.prepareStatement(sql); 3217 pstmt.setLong(1,lrID.longValue()); 3218 if (null != name) { 3219 pstmt.setString(2,name); 3220 } 3221 pstmt.execute(); 3222 rs = pstmt.getResultSet(); 3223 3224 if (rs.next()) { 3225 asetID = new Long(rs.getLong("as_id")); 3226 docID = new Long(rs.getLong("as_doc_id")); 3227 } 3228 else { 3229 throw new PersistenceException("cannot find annotation set with" + 3230 " name=["+name+"] , LRID=["+lrID+"] in database"); 3231 } 3232 3233 //3. delete the removed annotations from this set 3234 3235 //3.1. prepare call 3236 cstmt = this.jdbcConn.prepareCall( 3237 "{ call "+Gate.DB_OWNER+".persist.delete_annotation(?,?) }"); 3238 3239 3240 Iterator it = changes.iterator(); 3241 3242 while (it.hasNext()) { 3243 3244 //3.2. insert annotation 3245 Annotation ann = (Annotation)it.next(); 3246 3247 cstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs! 3248 cstmt.setLong(2,ann.getId().longValue()); 3249 cstmt.execute(); 3250 } 3251 } 3252 catch(SQLException sqle) { 3253 throw new PersistenceException("can't delete annotations in DB : ["+ 3254 sqle.getMessage()+"]"); 3255 } 3256 finally { 3257 DBHelper.cleanup(rs); 3258 DBHelper.cleanup(pstmt); 3259 DBHelper.cleanup(cstmt); 3260 } 3261 } 3262 3263 3264 3265 /** helper for sync() - never call directly */ 3266 private void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets) 3267 throws PersistenceException { 3268 3269 //0. preconditions 3270 Assert.assertNotNull(doc); 3271 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 3272 Assert.assertNotNull(doc.getLRPersistenceId()); 3273 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(), 3274 this.getDatabaseID()); 3275 Assert.assertNotNull(removedSets); 3276 Assert.assertNotNull(addedSets); 3277 3278 Long lrID = (Long)doc.getLRPersistenceId(); 3279 3280 //1. delete from DB removed a-sets 3281 CallableStatement cstmt = null; 3282 3283 try { 3284 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 3285 ".persist.delete_annotation_set(?,?) }"); 3286 3287 Iterator it = removedSets.iterator(); 3288 while (it.hasNext()) { 3289 String setName = (String)it.next(); 3290 cstmt.setLong(1,lrID.longValue()); 3291 cstmt.setString(2,setName); 3292 cstmt.execute(); 3293 } 3294 } 3295 catch(SQLException sqle) { 3296 throw new PersistenceException("can't remove annotation set from DB: ["+ sqle.getMessage()+"]"); 3297 } 3298 finally { 3299 DBHelper.cleanup(cstmt); 3300 } 3301 3302 //2. create in DB new a-sets 3303 Iterator it = addedSets.iterator(); 3304 while (it.hasNext()) { 3305 String setName = (String)it.next(); 3306 AnnotationSet aset = doc.getAnnotations(setName); 3307 3308 Assert.assertNotNull(aset); 3309 Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl); 3310 3311 createAnnotationSet(lrID,aset); 3312 } 3313 } 3314 3315 3316 3317 /** helper for sync() - never call directly */ 3318 private void _syncAnnotations(Document doc) 3319 throws PersistenceException { 3320 3321 //0. preconditions 3322 Assert.assertNotNull(doc); 3323 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 3324 Assert.assertNotNull(doc.getLRPersistenceId()); 3325 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(), 3326 this.getDatabaseID()); 3327 3328 3329 EventAwareDocument ead = (EventAwareDocument)doc; 3330 //1. get the sets read from the DB for this document 3331 //chnaged annotations can occur only in such sets 3332 Collection loadedSets = ead.getLoadedAnnotationSets(); 3333 3334 Iterator it = loadedSets.iterator(); 3335 while (it.hasNext()) { 3336 AnnotationSet as = (AnnotationSet)it.next(); 3337 //check that this set is neither NEW nor DELETED 3338 //they should be already synced 3339 if (ead.getAddedAnnotationSets().contains(as.getName()) || 3340 ead.getRemovedAnnotationSets().contains(as.getName())) { 3341 //oops, ignore it 3342 continue; 3343 } 3344 3345 EventAwareAnnotationSet eas = (EventAwareAnnotationSet)as; 3346 Assert.assertNotNull(as); 3347 3348 Collection anns = null; 3349 anns = eas.getAddedAnnotations(); 3350 Assert.assertNotNull(anns); 3351 if (anns.size()>0) { 3352 _syncAddedAnnotations(doc,as,anns); 3353 } 3354 3355 anns = eas.getRemovedAnnotations(); 3356 Assert.assertNotNull(anns); 3357 if (anns.size()>0) { 3358 _syncRemovedAnnotations(doc,as,anns); 3359 } 3360 3361 anns = eas.getChangedAnnotations(); 3362 Assert.assertNotNull(anns); 3363 if (anns.size()>0) { 3364 _syncChangedAnnotations(doc,as,anns); 3365 } 3366 } 3367 } 3368 3369 3370 3371 /** helper for sync() - never call directly */ 3372 private void _syncFeatures(LanguageResource lr) 3373 throws PersistenceException { 3374 3375 //0. preconditions 3376 Assert.assertNotNull(lr); 3377 Assert.assertNotNull(lr.getLRPersistenceId()); 3378 Assert.assertEquals(((DatabaseDataStore)lr.getDataStore()).getDatabaseID(), 3379 this.getDatabaseID()); 3380 Assert.assertTrue(lr instanceof Document || lr instanceof Corpus); 3381 //we have to be in the context of transaction 3382 3383 //1, get ID in the DB 3384 Long lrID = (Long)lr.getLRPersistenceId(); 3385 int entityType; 3386 3387 //2. delete features 3388 CallableStatement stmt = null; 3389 try { 3390 Assert.assertTrue(false == this.jdbcConn.getAutoCommit()); 3391 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 3392 ".persist.delete_features(?,?) }"); 3393 stmt.setLong(1,lrID.longValue()); 3394 3395 if (lr instanceof Document) { 3396 entityType = DBHelper.FEATURE_OWNER_DOCUMENT; 3397 } 3398 else if (lr instanceof Corpus) { 3399 entityType = DBHelper.FEATURE_OWNER_CORPUS; 3400 } 3401 else { 3402 throw new IllegalArgumentException(); 3403 } 3404 3405 stmt.setInt(2,entityType); 3406 stmt.execute(); 3407 } 3408 catch(SQLException sqle) { 3409 throw new PersistenceException("can't delete features in DB: ["+ sqle.getMessage()+"]"); 3410 } 3411 finally { 3412 DBHelper.cleanup(stmt); 3413 } 3414 3415 //3. recreate them 3416 //createFeatures(lrID,entityType, lr.getFeatures()); 3417 createFeaturesBulk(lrID,entityType, lr.getFeatures()); 3418 3419 } 3420 3421 3422 3423 /** helper for sync() - saves a Corpus in the database */ 3424 private void syncCorpus(Corpus corp) 3425 throws PersistenceException,SecurityException { 3426 3427 //0. preconditions 3428 Assert.assertNotNull(corp); 3429 Assert.assertTrue(corp instanceof DatabaseCorpusImpl); 3430 Assert.assertEquals(this,corp.getDataStore()); 3431 Assert.assertNotNull(corp.getLRPersistenceId()); 3432 3433 EventAwareCorpus dbCorpus = (EventAwareCorpus)corp; 3434 3435 //1. sync the corpus name? 3436 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_NAME)) { 3437 _syncLR(corp); 3438 } 3439 3440 //2. sync the corpus features? 3441 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) { 3442 _syncFeatures(corp); 3443 } 3444 3445 //2.5 get removed documents and detach (not remove) them from the corpus in the 3446 //database 3447 List removedDocLRIDs = dbCorpus.getRemovedDocuments(); 3448 if (removedDocLRIDs.size() > 0) { 3449 _syncRemovedDocumentsFromCorpus(removedDocLRIDs,(Long)corp.getLRPersistenceId()); 3450 } 3451 3452 //3. get all documents 3453 //--Iterator it = corp.iterator(); 3454 Iterator it = dbCorpus.getLoadedDocuments().iterator(); 3455 3456 while (it.hasNext()) { 3457 Document dbDoc = (Document)it.next(); 3458 //note - document may be NULL which means it was not loaded (load on demand) 3459 //just ignore it then 3460 if (null == dbDoc) { 3461 continue; 3462 } 3463 3464 //adopt/sync? 3465 if (null == dbDoc.getLRPersistenceId()) { 3466 //doc was never adopted, adopt it 3467 3468 //3.1 remove the transient doc from the corpus 3469 it.remove(); 3470 3471 //3.2 get the security info for the corpus 3472 SecurityInfo si = getSecurityInfo(corp); 3473 3474 3475 Document adoptedDoc = null; 3476 try { 3477 //3.3. adopt the doc with the sec info 3478//System.out.println("adopting ["+dbDoc.getName()+"] ..."); 3479 //don't open a new transaction, since sync() already has opended one 3480 adoptedDoc = (Document)_adopt(dbDoc,si,true); 3481 3482 //3.4. add doc to corpus in DB 3483 addDocumentToCorpus((Long)adoptedDoc.getLRPersistenceId(), 3484 (Long)corp.getLRPersistenceId()); 3485 } 3486 catch(SecurityException se) { 3487 throw new PersistenceException(se); 3488 } 3489 3490 //3.5 add back to corpus the new DatabaseDocument 3491 corp.add(adoptedDoc); 3492 } 3493 else { 3494 //don't open a new transaction, the sync() called for corpus has already 3495 //opened one 3496 try { 3497 _sync(dbDoc,true); 3498 3499 // let the world know about it 3500 fireResourceWritten( new DatastoreEvent(this, 3501 DatastoreEvent.RESOURCE_WRITTEN, 3502 dbDoc, 3503 dbDoc.getLRPersistenceId() 3504 ) 3505 ); 3506 3507 //if the document is form the same DS but did not belong to the corpus add it now 3508 //NOTE: if the document already belongs to the corpus then nothing will be changed 3509 //in the DB 3510 addDocumentToCorpus((Long)dbDoc.getLRPersistenceId(), 3511 (Long)corp.getLRPersistenceId()); 3512 } 3513 catch(SecurityException se) { 3514 gate.util.Err.prln("document cannot be synced: ["+se.getMessage()+"]"); 3515 } 3516 } 3517 } 3518 } 3519 3520 3521 3522 /** 3523 * Try to acquire exlusive lock on a resource from the persistent store. 3524 * Always call unlockLR() when the lock is no longer needed 3525 */ 3526 public boolean lockLr(LanguageResource lr) 3527 throws PersistenceException,SecurityException { 3528 3529 //0. preconditions 3530 Assert.assertNotNull(lr); 3531 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 3532 lr instanceof DatabaseCorpusImpl); 3533 Assert.assertNotNull(lr.getLRPersistenceId()); 3534 Assert.assertEquals(lr.getDataStore(),this); 3535 3536 //1. delegate 3537 return _lockLr((Long)lr.getLRPersistenceId()); 3538 } 3539 3540 3541 3542 /** 3543 * helper for lockLR() 3544 * never call directly 3545 */ 3546 private boolean _lockLr(Long lrID) 3547 throws PersistenceException,SecurityException { 3548 3549 //0. preconditions 3550 Assert.assertNotNull(lrID); 3551 3552 //1. check session 3553 if (null == this.session) { 3554 throw new SecurityException("session not set"); 3555 } 3556 3557 if (false == this.ac.isValidSession(this.session)) { 3558 throw new SecurityException("invalid session supplied"); 3559 } 3560 3561 //2. check permissions 3562 if (false == canWriteLR(lrID)) { 3563 throw new SecurityException("no write access granted to the user"); 3564 } 3565 3566 //3. try to lock 3567 CallableStatement cstmt = null; 3568 boolean lockSucceeded = false; 3569 3570 try { 3571 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.lock_lr(?,?,?,?) }"); 3572 cstmt.setLong(1,lrID.longValue()); 3573 cstmt.setLong(2,this.session.getUser().getID().longValue()); 3574 cstmt.setLong(3,this.session.getGroup().getID().longValue()); 3575 cstmt.registerOutParameter(4,java.sql.Types.NUMERIC); 3576 cstmt.execute(); 3577 3578 lockSucceeded = cstmt.getLong(4) == this.ORACLE_TRUE 3579 ? true 3580 : false; 3581 } 3582 catch(SQLException sqle) { 3583 3584 switch(sqle.getErrorCode()) { 3585 case DBHelper.X_ORACLE_INVALID_LR: 3586 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]"); 3587 default: 3588 throw new PersistenceException( 3589 "can't lock LR in DB : ["+ sqle.getMessage()+"]"); 3590 } 3591 } 3592 finally { 3593 DBHelper.cleanup(cstmt); 3594 } 3595 3596 return lockSucceeded; 3597 } 3598 3599 3600 3601 /** 3602 * Releases the exlusive lock on a resource from the persistent store. 3603 */ 3604 public void unlockLr(LanguageResource lr) 3605 throws PersistenceException,SecurityException { 3606 3607 //0. preconditions 3608 Assert.assertNotNull(lr); 3609 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 3610 lr instanceof DatabaseCorpusImpl); 3611 Assert.assertNotNull(lr.getLRPersistenceId()); 3612 Assert.assertEquals(lr.getDataStore(),this); 3613 3614 //1. check session 3615 if (null == this.session) { 3616 throw new SecurityException("session not set"); 3617 } 3618 3619 if (false == this.ac.isValidSession(this.session)) { 3620 throw new SecurityException("invalid session supplied"); 3621 } 3622 3623 //2. check permissions 3624 if (false == canWriteLR(lr.getLRPersistenceId())) { 3625 throw new SecurityException("no write access granted to the user"); 3626 } 3627 3628 //3. try to unlock 3629 CallableStatement cstmt = null; 3630 boolean lockSucceeded = false; 3631 3632 try { 3633 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.unlock_lr(?,?) }"); 3634 cstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue()); 3635 cstmt.setLong(2,this.session.getUser().getID().longValue()); 3636 cstmt.execute(); 3637 } 3638 catch(SQLException sqle) { 3639 3640 switch(sqle.getErrorCode()) { 3641 case DBHelper.X_ORACLE_INVALID_LR: 3642 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]"); 3643 default: 3644 throw new PersistenceException( 3645 "can't unlock LR in DB : ["+ sqle.getMessage()+"]"); 3646 } 3647 } 3648 finally { 3649 DBHelper.cleanup(cstmt); 3650 } 3651 } 3652 3653 3654 3655 /** 3656 * Releases the exlusive lock on a resource from the persistent store. 3657 */ 3658 private User getLockingUser(LanguageResource lr) 3659 throws PersistenceException,SecurityException { 3660 3661 //0. preconditions 3662 Assert.assertNotNull(lr); 3663 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 3664 lr instanceof DatabaseCorpusImpl); 3665 Assert.assertNotNull(lr.getLRPersistenceId()); 3666 Assert.assertEquals(lr.getDataStore(),this); 3667 3668 //delegate 3669 return getLockingUser((Long)lr.getLRPersistenceId()); 3670 } 3671 3672 3673 3674 /** 3675 * Releases the exlusive lock on a resource from the persistent store. 3676 */ 3677 private User getLockingUser(Long lrID) 3678 throws PersistenceException,SecurityException { 3679 3680 //1. check session 3681 if (null == this.session) { 3682 throw new SecurityException("session not set"); 3683 } 3684 3685 if (false == this.ac.isValidSession(this.session)) { 3686 throw new SecurityException("invalid session supplied"); 3687 } 3688 3689 //3. read from DB 3690 PreparedStatement pstmt = null; 3691 Long userID = null; 3692 ResultSet rs = null; 3693 3694 try { 3695 String sql = " select nvl(lr_locking_user_id,0) as user_id" + 3696 " from "+Gate.DB_OWNER+".t_lang_resource " + 3697 " where lr_id = ?"; 3698 3699 pstmt = this.jdbcConn.prepareStatement(sql); 3700 pstmt.setLong(1,lrID.longValue()); 3701 pstmt.execute(); 3702 rs = pstmt.getResultSet(); 3703 3704 if (false == rs.next()) { 3705 throw new PersistenceException("LR not found in DB"); 3706 } 3707 3708 long result = rs.getLong("user_id"); 3709 3710 return result == 0 ? null 3711 : this.ac.findUser(new Long(result)); 3712 } 3713 catch(SQLException sqle) { 3714 throw new PersistenceException("can't get locking user from DB : ["+ sqle.getMessage()+"]"); 3715 } 3716 finally { 3717 DBHelper.cleanup(rs); 3718 DBHelper.cleanup(pstmt); 3719 } 3720 } 3721 3722 3723 3724 /** 3725 * adds document to corpus in the database 3726 * if the document is already part of the corpus nothing 3727 * changes 3728 */ 3729 private void addDocumentToCorpus(Long docID,Long corpID) 3730 throws PersistenceException,SecurityException { 3731 3732 //0. preconditions 3733 Assert.assertNotNull(docID); 3734 Assert.assertNotNull(corpID); 3735 3736 //1. check session 3737 if (null == this.session) { 3738 throw new SecurityException("session not set"); 3739 } 3740 3741 if (false == this.ac.isValidSession(this.session)) { 3742 throw new SecurityException("invalid session supplied"); 3743 } 3744 3745 //2. check permissions 3746 if (false == canWriteLR(corpID)) { 3747 throw new SecurityException("no write access granted to the user"); 3748 } 3749 3750 if (false == canWriteLR(docID)) { 3751 throw new SecurityException("no write access granted to the user"); 3752 } 3753 3754 //3. database 3755 CallableStatement cstmt = null; 3756 3757 try { 3758 cstmt = this.jdbcConn.prepareCall("{ call "+ 3759 Gate.DB_OWNER+".persist.add_document_to_corpus(?,?) }"); 3760 cstmt.setLong(1,docID.longValue()); 3761 cstmt.setLong(2,corpID.longValue()); 3762 cstmt.execute(); 3763 } 3764 catch(SQLException sqle) { 3765 3766 switch(sqle.getErrorCode()) { 3767 case DBHelper.X_ORACLE_INVALID_LR: 3768 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]"); 3769 default: 3770 throw new PersistenceException( 3771 "can't add document to corpus : ["+ sqle.getMessage()+"]"); 3772 } 3773 } 3774 finally { 3775 DBHelper.cleanup(cstmt); 3776 } 3777 } 3778 3779 3780 3781 /** 3782 * unloads a LR from the GUI 3783 */ 3784 private void unloadLR(Long lrID) 3785 throws GateException{ 3786 3787 //0. preconfitions 3788 Assert.assertNotNull(lrID); 3789 3790 //1. get all LRs in the system 3791 List resources = Gate.getCreoleRegister().getAllInstances("gate.LanguageResource"); 3792 3793 Iterator it = resources.iterator(); 3794 while (it.hasNext()) { 3795 LanguageResource lr = (LanguageResource)it.next(); 3796 if (lrID.equals(lr.getLRPersistenceId()) && 3797 this.equals(lr.getDataStore())) { 3798 //found it - unload it 3799 Factory.deleteResource(lr); 3800 break; 3801 } 3802 } 3803 } 3804 3805 /** Get a list of LRs that satisfy some set or restrictions 3806 * 3807 * @param constraints list of Restriction objects 3808 */ 3809 public List findLrIds(List constraints) throws PersistenceException { 3810 return findLrIds(constraints,null); 3811 } 3812 3813 /** 3814 * Get a list of LRs IDs that satisfy some set or restrictions and are 3815 * of a particular type 3816 * 3817 * @param constraints list of Restriction objects 3818 * @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS 3819 */ 3820 public List findLrIds(List constraints, String lrType) throws PersistenceException { 3821 return findLrIds(constraints, lrType, null, -1); 3822 } 3823 3824 /** 3825 * Get a list of LRs IDs that satisfy some set or restrictions and are 3826 * of a particular type 3827 * 3828 * @param constraints list of Restriction objects 3829 * @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS 3830 * @param orderByConstraints liat of OrderByRestriction objects 3831 * @param limitcount limit returning objects -1 for unlimited 3832 */ 3833 public List findLrIds(List constraints, String lrType, 3834 List orderByConstraints, int limitcount) throws PersistenceException { 3835 Vector lrsIDs = new Vector(); 3836 CallableStatement stmt = null; 3837 ResultSet rs = null; 3838 Connection conn = null; 3839 3840 try { 3841 Vector sqlValues = new Vector(); 3842 String sql = getSQLQuery(constraints, lrType, false, orderByConstraints, limitcount, sqlValues); 3843 conn = DBHelper.connect(this.getStorageUrl(), true); 3844 stmt = conn.prepareCall(sql); 3845 //System.out.println(sql); 3846 for (int i = 0; i<sqlValues.size(); i++){ 3847 if (sqlValues.elementAt(i) instanceof String){ 3848 stmt.setString(i+1,sqlValues.elementAt(i).toString()); 3849 } 3850 else if (sqlValues.elementAt(i) instanceof Long){ 3851 stmt.setLong(i+1,((Long) sqlValues.elementAt(i)).longValue()); 3852 } 3853 else if (sqlValues.elementAt(i) instanceof Integer){ 3854 stmt.setLong(i+1,((Integer) sqlValues.elementAt(i)).intValue()); 3855 } 3856 } 3857 stmt.execute(); 3858 rs = stmt.getResultSet(); 3859 3860 while (rs.next()) { 3861 long lr_ID = rs.getLong(1); 3862 lrsIDs.addElement(new Long(lr_ID)); 3863 } 3864 return lrsIDs; 3865 } 3866 catch(SQLException sqle) { 3867 throw new PersistenceException("can't get LRs from DB: ["+ sqle+"]"); 3868 } 3869 catch (ClassNotFoundException cnfe){ 3870 throw new PersistenceException("can't not find driver: ["+ cnfe +"]"); 3871 } 3872 finally { 3873 DBHelper.cleanup(rs); 3874 DBHelper.cleanup(stmt); 3875 DBHelper.disconnect(conn, true); 3876 } 3877 } 3878 /** 3879 * Return count of LRs which matches the constraints. 3880 * 3881 * @param constraints list of Restriction objects 3882 * @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS 3883 */ 3884 public long getLrsCount(List constraints, String lrType) throws PersistenceException { 3885 Vector lrs = new Vector(); 3886 CallableStatement stmt = null; 3887 ResultSet rs = null; 3888 Connection conn = null; 3889 3890 try { 3891 Vector sqlValues = new Vector(); 3892 String sql = getSQLQuery(constraints,lrType, true, null, -1, sqlValues); 3893 conn = DBHelper.connect(this.getStorageUrl(), true); 3894 stmt = conn.prepareCall(sql); 3895 for (int i = 0; i<sqlValues.size(); i++){ 3896 if (sqlValues.elementAt(i) instanceof String){ 3897 stmt.setString(i+1,sqlValues.elementAt(i).toString()); 3898 } 3899 else if (sqlValues.elementAt(i) instanceof Long){ 3900 stmt.setLong(i+1,((Long) sqlValues.elementAt(i)).longValue()); 3901 } 3902 else if (sqlValues.elementAt(i) instanceof Integer){ 3903 stmt.setLong(i+1,((Integer) sqlValues.elementAt(i)).intValue()); 3904 } 3905 } 3906 3907 stmt.execute(); 3908 rs = stmt.getResultSet(); 3909 rs.next(); 3910 return rs.getLong(1); 3911 } 3912 catch(SQLException sqle) { 3913 throw new PersistenceException("can't get LRs Count from DB: ["+ sqle+"]"); 3914 } 3915 catch (ClassNotFoundException cnfe){ 3916 throw new PersistenceException("can't not find driver: ["+ cnfe +"]"); 3917 } 3918 finally { 3919 DBHelper.cleanup(rs); 3920 DBHelper.cleanup(stmt); 3921 DBHelper.disconnect(conn, true); 3922 } 3923 } 3924 3925 private String getSQLQuery(List filter, String lrType, boolean count, 3926 List orderByFilter, int limitcount, Vector sqlValues){ 3927 StringBuffer query = new StringBuffer(""); 3928 String join = getJoinQuery(orderByFilter, sqlValues); 3929 String select = "lr_id"; 3930 if (count){ 3931 select = "count(*)"; 3932 } 3933 3934 query = query.append(" SELECT " + select + " " + 3935 " FROM "+Gate.DB_OWNER+".t_lang_resource LR " + join + 3936 " WHERE "); 3937 3938 if (lrType != null){ 3939 query = query.append(" LR.lr_type_id = ? "); 3940 if (lrType.equals(DBHelper.CORPUS_CLASS)) { 3941 sqlValues.addElement(new Long(2)); 3942 }// if DBHelper.CORPUS_CLASS 3943 if (lrType.equals(DBHelper.DOCUMENT_CLASS)) { 3944 sqlValues.addElement(new Long(1)); 3945 }// if DBHelper.DOCUMENT_CLASS 3946 } 3947 3948 if (filter!=null && filter.size()>0){ 3949 if (lrType!=null){ 3950 query = query.append(" AND "); 3951 } 3952 for (int i=0; i<filter.size(); i++){ 3953 query = query.append(getRestrictionPartOfQuery((Restriction) filter.get(i),sqlValues)); 3954 if (i<filter.size()-1) { 3955 query = query.append(" AND "); 3956 } 3957 } 3958 } 3959 3960 String endPartOfJoin = getEndPartOfJoin(orderByFilter,sqlValues); 3961 query = query.append(endPartOfJoin); 3962 3963 if (limitcount>0){ 3964 query = query.insert(0,"select lr_id from ( "); 3965 query = query.append( ") where rownum<"+limitcount); 3966 } 3967 3968 return query.toString(); 3969 } 3970 3971 private String getRestrictionPartOfQuery(Restriction restr, Vector sqlValues){ 3972 StringBuffer expresion = new StringBuffer( 3973 " EXISTS ("+ 3974 " SELECT ft_id " + 3975 " FROM "+Gate.DB_OWNER+".t_feature FEATURE, " + 3976 Gate.DB_OWNER + ".t_feature_key FTK" + 3977 " WHERE FEATURE.ft_entity_id = LR.lr_id "); 3978 3979 if (restr.getKey() != null){ 3980 expresion = expresion.append(" AND FTK.fk_id = FEATURE.ft_key_id "); 3981 expresion = expresion.append(" AND FTK.fk_string = ? "); 3982 sqlValues.addElement(restr.getKey()); 3983 } 3984 3985 if (restr.getValue() != null){ 3986 expresion = expresion.append(" AND "); 3987 switch (this.findFeatureType(restr.getValue())){ 3988 case DBHelper.VALUE_TYPE_INTEGER: 3989 expresion = expresion.append(getNumberExpresion(restr, sqlValues)); 3990 break; 3991 case DBHelper.VALUE_TYPE_LONG: 3992 expresion = expresion.append(getNumberExpresion(restr, sqlValues)); 3993 break; 3994 default: 3995 if (restr.getOperator()==Restriction.OPERATOR_EQUATION){ 3996 expresion = expresion.append(" FEATURE.ft_character_value = ? "); 3997 sqlValues.addElement(restr.getStringValue()); 3998 } 3999 if (restr.getOperator()==Restriction.OPERATOR_LIKE){ 4000 expresion = expresion.append(" upper(FEATURE.ft_character_value) like ? "); 4001 sqlValues.addElement("%"+restr.getStringValue().toUpperCase()+"%"); 4002 } 4003 break; 4004 } 4005 } 4006 4007 expresion = expresion.append(" )"); 4008 4009 return expresion.toString(); 4010 } 4011 4012 private String getNumberExpresion(Restriction restr, Vector sqlValues){ 4013 StringBuffer expr = new StringBuffer("FEATURE.ft_number_value "); 4014 4015 switch (restr.getOperator()){ 4016 case Restriction.OPERATOR_EQUATION: 4017 expr = expr.append(" = "); 4018 break; 4019 case Restriction.OPERATOR_BIGGER: 4020 expr = expr.append(" > "); 4021 break; 4022 case Restriction.OPERATOR_LESS: 4023 expr = expr.append(" < "); 4024 break; 4025 case Restriction.OPERATOR_EQUATION_OR_BIGGER: 4026 expr = expr.append(" >= "); 4027 break; 4028 case Restriction.OPERATOR_EQUATION_OR_LESS: 4029 expr = expr.append(" <= "); 4030 break; 4031 default: 4032 return " 0 = 0 "; 4033 } 4034 4035 expr.append(" ? "); 4036 sqlValues.addElement(restr.getValue()); 4037 4038 return expr.toString(); 4039 } 4040 4041 private String getJoinQuery(List orderByFilter, Vector sqlValues){ 4042 StringBuffer join = new StringBuffer(""); 4043 if (orderByFilter!=null){ 4044 for (int i = 0; i<orderByFilter.size(); i++){ 4045 join = join.append(" , "+Gate.DB_OWNER+".t_feature FT"+i); 4046 join = join.append(" , "+Gate.DB_OWNER+".t_feature_key FTK"+i); 4047 } 4048 } 4049 return join.toString(); 4050 } 4051 4052 private String getEndPartOfJoin(List orderByFilter, Vector sqlValues){ 4053 StringBuffer endJoin = new StringBuffer(""); 4054 if (orderByFilter!=null && orderByFilter.size()>0){ 4055 for (int i=0; i<orderByFilter.size(); i++){ 4056 endJoin = endJoin.append(" and lr_id=FT"+i+".ft_entity_id "); 4057 endJoin = endJoin.append(" and FT"+i+".ft_key_id = FTK"+i+".fk_id "); 4058 endJoin = endJoin.append(" and FTK"+i+".fk_string= ? "); 4059 OrderByRestriction restr = (OrderByRestriction) orderByFilter.get(i); 4060 sqlValues.addElement(restr.getKey()); 4061 } 4062 endJoin = endJoin.append(" order by "); 4063 for (int i=0; i<orderByFilter.size(); i++){ 4064 OrderByRestriction restr = (OrderByRestriction) orderByFilter.get(i); 4065 endJoin = endJoin.append(" FT"+i+".ft_number_value "); 4066 if (restr.getOperator()==OrderByRestriction.OPERATOR_ASCENDING){ 4067 endJoin = endJoin.append(" asc, "); 4068 } else { 4069 endJoin = endJoin.append(" desc, "); 4070 } 4071 endJoin = endJoin.append(" FT"+i+".ft_character_value "); 4072 if (restr.getOperator()==OrderByRestriction.OPERATOR_ASCENDING){ 4073 endJoin = endJoin.append(" asc "); 4074 } else { 4075 endJoin = endJoin.append(" desc "); 4076 } 4077 if (i<orderByFilter.size()-1){ 4078 endJoin = endJoin.append(" , "); 4079 } 4080 } 4081 } 4082 return endJoin.toString(); 4083 } 4084 4085 4086 private class Feature { 4087 4088 Long entityID; 4089 int entityType; 4090 String key; 4091 Object value; 4092 int valueType; 4093 4094 public Feature(Long eid, int eType, String key, Object value, int vType) { 4095 4096 this.entityID = eid; 4097 this.entityType = eType; 4098 this.key = key; 4099 this.value = value; 4100 this.valueType = vType; 4101 } 4102 } 4103 4104} 4105 4106
|
OracleDataStore |
|