|
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.114 2001/11/30 12:54:13 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.CLOB; 24 import oracle.sql.BLOB; 25 26 import junit.framework.*; 27 28 import gate.*; 29 import gate.util.*; 30 import gate.event.*; 31 import gate.security.*; 32 import gate.security.SecurityException; //hide the more general exception 33 import gate.corpora.*; 34 import gate.annotation.*; 35 import gate.creole.ResourceData; 36 37 public class OracleDataStore extends JDBCDataStore { 38 39 /** Name of this resource */ 40 public static final String DS_COMMENT = "GATE Oracle datastore"; 41 42 /** the icon for this resource */ 43 public static final String DS_ICON_NAME = "ds.gif"; 44 45 /** Debug flag */ 46 private static final boolean DEBUG = false; 47 48 /** "true" value for Oracle (supports no boolean type) */ 49 private static final int ORACLE_TRUE = 1; 50 /** "false" value for Oracle (supports no boolean type) */ 51 private static final int ORACLE_FALSE = 0; 52 53 /** used internaly, may change in the future */ 54 private static final int READ_ACCESS = 0; 55 /** used internaly, may change in the future */ 56 private static final int WRITE_ACCESS = 1; 57 58 /** the size in bytes if varchar2 column in Oracle 59 * when a String is stored in Oracle it may be too long 60 * for a varchar2 value, and then CLOB will be used 61 * Note that the limit is in bytes, not in characters, so 62 * in the worst case this will limit the string to 4000/3 characters 63 * */ 64 private static final int ORACLE_VARCHAR_LIMIT_BYTES = 4000; 65 66 /** maximum number of bytes that represent a char in UTF8 database */ 67 private static final int UTF_BYTES_PER_CHAR_MAX = 3; 68 69 /** maximum number of characters per string stored as varchar2 70 * if longer then stored as CLOB 71 * */ 72 private static final int ORACLE_VARCHAR_MAX_SYMBOLS = 73 ORACLE_VARCHAR_LIMIT_BYTES/UTF_BYTES_PER_CHAR_MAX; 74 75 /** read buffer size (for reading CLOBs) */ 76 private static final int INTERNAL_BUFFER_SIZE = 16*1024; 77 78 /** default constructor - just call the super constructor 79 * (may change in the future) 80 * */ 81 public OracleDataStore() { 82 83 super(); 84 } 85 86 /** 87 * Save: synchonise the in-memory image of the LR with the persistent 88 * image. 89 */ 90 public String getComment() { 91 return OracleDataStore.DS_COMMENT; 92 } 93 94 /** 95 * Returns the name of the icon to be used when this datastore is displayed 96 * in the GUI 97 */ 98 public String getIconName() { 99 return OracleDataStore.DS_ICON_NAME; 100 } 101 102 103 104 /** Get the name of an LR from its ID. */ 105 public String getLrName(Object lrId) 106 throws PersistenceException { 107 108 if (false == lrId instanceof Long) { 109 throw new IllegalArgumentException(); 110 } 111 112 Long ID = (Long)lrId; 113 114 CallableStatement stmt = null; 115 116 try { 117 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.get_lr_name(?,?) }"); 118 stmt.setLong(1,ID.longValue()); 119 stmt.registerOutParameter(2,java.sql.Types.VARCHAR); 120 stmt.execute(); 121 String result = stmt.getString(2); 122 123 return result; 124 } 125 catch(SQLException sqle) { 126 throw new PersistenceException("can't get LR name from DB: ["+ sqle.getMessage()+"]"); 127 } 128 finally { 129 DBHelper.cleanup(stmt); 130 } 131 } 132 133 134 135 /** Set the URL for the underlying storage mechanism. */ 136 public void setStorageUrl(String storageUrl) throws PersistenceException { 137 138 super.setStorageUrl(storageUrl); 139 140 } 141 142 143 144 /** Get the URL for the underlying storage mechanism. */ 145 public String getStorageUrl() { 146 147 return super.getStorageUrl(); 148 } 149 150 151 152 /** 153 * Create a new data store. <B>NOTE:</B> for some data stores 154 * creation is an system administrator task; in such cases this 155 * method will throw an UnsupportedOperationException. 156 */ 157 public void create() 158 throws PersistenceException, UnsupportedOperationException { 159 160 super.create(); 161 } 162 163 164 165 /** Open a connection to the data store. */ 166 public void open() throws PersistenceException { 167 168 super.open(); 169 } 170 171 172 173 /** Close the data store. */ 174 public void close() throws PersistenceException { 175 176 super.close(); 177 } 178 179 180 181 /** 182 * Delete the data store. <B>NOTE:</B> for some data stores 183 * deletion is an system administrator task; in such cases this 184 * method will throw an UnsupportedOperationException. 185 */ 186 public void delete() 187 throws PersistenceException, UnsupportedOperationException { 188 189 //0. user session should be set 190 /* if (null == this.session) { 191 throw new SecurityException("user session not set"); 192 } 193 */ 194 super.delete(); 195 } 196 197 198 199 /** 200 * Delete a resource from the data store. 201 * @param lrId a data-store specific unique identifier for the resource 202 * @param lrClassName class name of the type of resource 203 */ 204 public void delete(String lrClassName, Object lrId) 205 throws PersistenceException,SecurityException { 206 207 //0. preconditions 208 if (false == lrId instanceof Long) { 209 throw new IllegalArgumentException(); 210 } 211 212 if (!lrClassName.equals(DBHelper.DOCUMENT_CLASS) && 213 !lrClassName.equals(DBHelper.CORPUS_CLASS)) { 214 throw new IllegalArgumentException("Only Corpus and Document classes are supported" + 215 " by Database data store"); 216 } 217 218 //1. check session 219 if (null == this.session) { 220 throw new SecurityException("session not set"); 221 } 222 223 if (false == this.ac.isValidSession(this.session)) { 224 throw new SecurityException("invalid session supplied"); 225 } 226 227 //2. check permissions 228 if (false == canWriteLR(lrId)) { 229 throw new SecurityException("insufficient privileges"); 230 } 231 232 //3. try to lock document, so that we'll be sure no one is editing it 233 //NOTE: use the private method 234 User lockingUser = this.getLockingUser((Long)lrId); 235 User currUser = this.session.getUser(); 236 237 if (null != lockingUser && false == lockingUser.equals(currUser)) { 238 //oops, someone is editing now 239 throw new PersistenceException("LR locked by another user"); 240 } 241 242 boolean transFailed = false; 243 try { 244 //4. autocommit should be FALSE because of LOBs 245 this.jdbcConn.setAutoCommit(false); 246 247 //5. perform changes, if anything goes wrong, rollback 248 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) { 249 deleteDocument((Long)lrId); 250 } 251 else { 252 deleteCorpus((Long)lrId); 253 } 254 255 //6. done, commit 256 this.jdbcConn.commit(); 257 } 258 catch(SQLException sqle) { 259 transFailed = true; 260 throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]"); 261 } 262 catch(PersistenceException pe) { 263 transFailed = true; 264 throw(pe); 265 } 266 finally { 267 //problems? 268 if (transFailed) { 269 try { 270 this.jdbcConn.rollback(); 271 } 272 catch(SQLException sqle) { 273 throw new PersistenceException(sqle); 274 } 275 } 276 } 277 278 //7, unlock 279 //do nothing - the resource does not exist anymore 280 281 //8. delete from the list of dependent resources 282 boolean resourceFound = false; 283 Iterator it = this.dependentResources.iterator(); 284 while (it.hasNext()) { 285 LanguageResource lr = (LanguageResource)it.next(); 286 if (lr.getLRPersistenceId().equals(lrId)) { 287 resourceFound = true; 288 it.remove(); 289 break; 290 } 291 } 292 293 //Assert.assertTrue(resourceFound); 294 295 //9. let the world know about it 296 fireResourceDeleted( 297 new DatastoreEvent(this, DatastoreEvent.RESOURCE_DELETED, null, lrId)); 298 299 //10. unload the resource form the GUI 300 try { 301 unloadLR((Long)lrId); 302 } 303 catch(GateException ge) { 304 Err.prln("can't unload resource from GUI..."); 305 } 306 } 307 308 309 310 /** 311 * helper method for delete() 312 * never call it directly beause proper events will not be fired 313 */ 314 private void deleteDocument(Long lrId) 315 throws PersistenceException { 316 317 //0. preconditions 318 Assert.assertNotNull(lrId); 319 320 CallableStatement stmt = null; 321 322 //1. delete from DB 323 try { 324 stmt = this.jdbcConn.prepareCall( 325 "{ call "+Gate.DB_OWNER+".persist.delete_document(?) }"); 326 stmt.setLong(1,lrId.longValue()); 327 stmt.execute(); 328 } 329 catch(SQLException sqle) { 330 throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]"); 331 } 332 finally { 333 DBHelper.cleanup(stmt); 334 } 335 } 336 337 338 339 /** 340 * helper method for delete() 341 * never call it directly beause proper events will not be fired 342 */ 343 private void deleteCorpus(Long lrId) 344 throws PersistenceException { 345 346 Long ID = (Long)lrId; 347 348 CallableStatement stmt = null; 349 350 try { 351 stmt = this.jdbcConn.prepareCall( 352 "{ call "+Gate.DB_OWNER+".persist.delete_corpus(?) }"); 353 stmt.setLong(1,ID.longValue()); 354 stmt.execute(); 355 } 356 catch(SQLException sqle) { 357 throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]"); 358 } 359 finally { 360 DBHelper.cleanup(stmt); 361 } 362 } 363 364 365 366 /** 367 * Save: synchonise the in-memory image of the LR with the persistent 368 * image. 369 */ 370 public void sync(LanguageResource lr) 371 throws PersistenceException,SecurityException { 372 373 //4.delegate (open a new transaction) 374 _sync(lr,true); 375 } 376 377 378 379 /** 380 * Save: synchonise the in-memory image of the LR with the persistent 381 * image. 382 */ 383 private void _sync(LanguageResource lr, boolean openNewTrans) 384 throws PersistenceException,SecurityException { 385 386 //0.preconditions 387 Assert.assertNotNull(lr); 388 Long lrID = (Long)lr.getLRPersistenceId(); 389 390 if (false == lr instanceof Document && 391 false == lr instanceof Corpus) { 392 //only documents and corpuses could be serialized in DB 393 throw new IllegalArgumentException("only Documents and Corpuses could "+ 394 "be serialized in DB"); 395 } 396 397 // check that this LR is one of ours (i.e. has been adopted) 398 if( null == lr.getDataStore() || false == lr.getDataStore().equals(this)) 399 throw new PersistenceException( 400 "This LR is not stored in this DataStore" 401 ); 402 403 404 //1. check session 405 if (null == this.session) { 406 throw new SecurityException("session not set"); 407 } 408 409 if (false == this.ac.isValidSession(this.session)) { 410 throw new SecurityException("invalid session supplied"); 411 } 412 413 //2. check permissions 414 if (false == canWriteLR(lrID)) { 415 throw new SecurityException("insufficient privileges"); 416 } 417 418 //3. is the resource locked? 419 User lockingUser = getLockingUser(lr); 420 User currUser = this.session.getUser(); 421 422 if (lockingUser != null && false == lockingUser.equals(currUser)) { 423 throw new PersistenceException("document is locked by another user and cannot be synced"); 424 } 425 426 427 boolean transFailed = false; 428 try { 429 //2. autocommit should be FALSE because of LOBs 430 if (openNewTrans) { 431 this.jdbcConn.setAutoCommit(false); 432 } 433 434 //3. perform changes, if anything goes wrong, rollback 435 if (lr instanceof Document) { 436 syncDocument((Document)lr); 437 } 438 else { 439 syncCorpus((Corpus)lr); 440 } 441 442 //4. done, commit 443 if (openNewTrans) { 444 this.jdbcConn.commit(); 445 } 446 } 447 catch(SQLException sqle) { 448 transFailed = true; 449 throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]"); 450 } 451 catch(PersistenceException pe) { 452 transFailed = true; 453 throw(pe); 454 } 455 finally { 456 //problems? 457 if (transFailed) { 458 try { 459 this.jdbcConn.rollback(); 460 } 461 catch(SQLException sqle) { 462 throw new PersistenceException(sqle); 463 } 464 } 465 } 466 467 // let the world know about it 468 fireResourceWritten( 469 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, lr, lr.getLRPersistenceId())); 470 471 } 472 473 474 475 /** 476 * Set method for the autosaving behaviour of the data store. 477 * <B>NOTE:</B> many types of datastore have no auto-save function, 478 * in which case this will throw an UnsupportedOperationException. 479 */ 480 public void setAutoSaving(boolean autoSaving) 481 throws UnsupportedOperationException,PersistenceException { 482 483 super.setAutoSaving(autoSaving); 484 } 485 486 487 488 /** Get the autosaving behaviour of the LR. */ 489 public boolean isAutoSaving() { 490 throw new MethodNotImplementedException(); 491 } 492 493 494 /** Adopt a resource for persistence. */ 495 public LanguageResource adopt(LanguageResource lr, SecurityInfo secInfo) 496 throws PersistenceException,SecurityException { 497 //open a new transaction 498 return _adopt(lr,secInfo,true); 499 } 500 501 /** helper for adopt() 502 * @param openNewTrans shows if a new transaction should be started or the adopt 503 * is performed in the context of an already opened transaction 504 */ 505 private LanguageResource _adopt(LanguageResource lr, 506 SecurityInfo secInfo, 507 boolean openNewTrans) 508 throws PersistenceException,SecurityException { 509 510 LanguageResource result = null; 511 512 //-1. preconditions 513 Assert.assertNotNull(lr); 514 Assert.assertNotNull(secInfo); 515 if (false == lr instanceof Document && 516 false == lr instanceof Corpus) { 517 //only documents and corpuses could be serialized in DB 518 throw new IllegalArgumentException("only Documents and Corpuses could "+ 519 "be serialized in DB"); 520 } 521 522 //0. check SecurityInfo 523 if (false == this.ac.isValidSecurityInfo(secInfo)) { 524 throw new SecurityException("Invalid security settings supplied"); 525 } 526 527 //1. user session should be set 528 if (null == this.session) { 529 throw new SecurityException("user session not set"); 530 } 531 532 //2. check the LR's current DS 533 DataStore currentDS = lr.getDataStore(); 534 if(currentDS == null) { // an orphan - do the adoption 535 //do not set the datastore on the lr, because actually it should 536 //remain transient. The DS should only be set on the one that is 537 //saved in Oracle 538 // lr.setDataStore(this); 539 // let the world know 540 //fireResourceAdopted( 541 // new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, lr, null) 542 //); 543 } 544 else if(currentDS.equals(this)){ // adopted already 545 return lr; 546 } 547 else { // someone else's child 548 throw new PersistenceException( 549 "Can't adopt a resource which is already in a different datastore"); 550 } 551 552 553 //3. is the LR one of Document or Corpus? 554 if (false == lr instanceof Document && 555 false == lr instanceof Corpus) { 556 557 throw new IllegalArgumentException("Database datastore is implemented only for "+ 558 "Documents and Corpora"); 559 } 560 561 //4.is the document already stored in this storage? 562 Object persistID = lr.getLRPersistenceId(); 563 if (persistID != null) { 564 throw new PersistenceException("This LR is already stored in the " + 565 " database (persistance ID is =["+(Long)persistID+"] )"); 566 } 567 568 boolean transFailed = false; 569 try { 570 //5 autocommit should be FALSE because of LOBs 571 if (openNewTrans) { 572 this.jdbcConn.setAutoCommit(false); 573 } 574 575 //6. perform changes, if anything goes wrong, rollback 576 if (lr instanceof Document) { 577 result = createDocument((Document)lr,secInfo); 578 //System.out.println("result ID=["+result.getLRPersistenceId()+"]"); 579 } 580 else { 581 result = createCorpus((Corpus)lr,secInfo); 582 } 583 584 //7. done, commit 585 if (openNewTrans) { 586 this.jdbcConn.commit(); 587 } 588 } 589 catch(SQLException sqle) { 590 transFailed = true; 591 throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]"); 592 } 593 catch(PersistenceException pe) { 594 transFailed = true; 595 throw(pe); 596 } 597 catch(SecurityException se) { 598 transFailed = true; 599 throw(se); 600 } 601 finally { 602 //problems? 603 if (transFailed) { 604 try { 605 this.jdbcConn.rollback(); 606 } 607 catch(SQLException sqle) { 608 throw new PersistenceException(sqle); 609 } 610 } 611 } 612 613 //8. let the world know 614 fireResourceAdopted( 615 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, 616 result, 617 result.getLRPersistenceId()) 618 ); 619 620 //9. fire also resource written event because it's now saved 621 fireResourceWritten( 622 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, 623 result, 624 result.getLRPersistenceId() 625 ) 626 ); 627 628 //10. add the resource to the list of dependent resources - i.e. the ones that the 629 //data store should take care upon closing [and call sync()] 630 this.dependentResources.add(result); 631 632 return result; 633 } 634 635 636 637 /** 638 * helper for adopt() 639 * never call directly 640 */ 641 private Long createLR(String lrType, 642 String lrName, 643 SecurityInfo si, 644 Long lrParentID) 645 throws PersistenceException,SecurityException { 646 647 //0. preconditions 648 Assert.assertNotNull(lrName); 649 650 //1. check the session 651 // if (this.ac.isValidSession(s) == false) { 652 // throw new SecurityException("invalid session provided"); 653 // } 654 655 //2. create a record in DB 656 CallableStatement stmt = null; 657 658 try { 659 stmt = this.jdbcConn.prepareCall( 660 "{ call "+Gate.DB_OWNER+".persist.create_lr(?,?,?,?,?,?,?) }"); 661 stmt.setLong(1,si.getUser().getID().longValue()); 662 stmt.setLong(2,si.getGroup().getID().longValue()); 663 stmt.setString(3,lrType); 664 stmt.setString(4,lrName); 665 stmt.setInt(5,si.getAccessMode()); 666 if (null == lrParentID) { 667 stmt.setNull(6,java.sql.Types.BIGINT); 668 } 669 else { 670 stmt.setLong(6,lrParentID.longValue()); 671 } 672 //Oracle numbers are BIGNINT 673 stmt.registerOutParameter(7,java.sql.Types.BIGINT); 674 stmt.execute(); 675 676 Long result = new Long(stmt.getLong(7)); 677 return result; 678 } 679 catch(SQLException sqle) { 680 681 switch(sqle.getErrorCode()) { 682 case DBHelper.X_ORACLE_INVALID_LR_TYPE: 683 throw new PersistenceException("can't create LR [step 3] in DB, invalid LR Type"); 684 default: 685 throw new PersistenceException( 686 "can't create LR [step 3] in DB : ["+ sqle.getMessage()+"]"); 687 } 688 } 689 finally { 690 DBHelper.cleanup(stmt); 691 } 692 } 693 694 695 696 /** 697 * updates the content of the document if it is binary or a long string 698 * (that does not fit into VARCHAR2) 699 */ 700 private void updateDocumentContent(Long docContentID,DocumentContent content) 701 throws PersistenceException { 702 703 //1. get LOB locators from DB 704 PreparedStatement pstmt = null; 705 ResultSet rs = null; 706 CallableStatement cstmt = null; 707 try { 708 String sql = "select dc_content_type, " + 709 " dc_character_content, " + 710 " dc_binary_content " + 711 "from "+gate.Gate.DB_OWNER+".t_doc_content " + 712 "where dc_id = ? " + 713 "for update "; 714 pstmt = this.jdbcConn.prepareStatement(sql); 715 pstmt.setLong(1,docContentID.longValue()); 716 rs = pstmt.executeQuery(); 717 718 //rs = pstmt.getResultSet(); 719 720 rs.next(); 721 //important: read the objects in the order they appear in 722 //the ResultSet, otherwise data may be lost 723 long contentType = rs.getLong("DC_CONTENT_TYPE"); 724 Clob clob = (Clob)rs.getClob("dc_character_content"); 725 Blob blob = (Blob)rs.getBlob("dc_binary_content"); 726 727 Assert.assertTrue(contentType == DBHelper.CHARACTER_CONTENT || 728 contentType == DBHelper.BINARY_CONTENT || 729 contentType == DBHelper.EMPTY_CONTENT); 730 731 732 //2. write data using the LOB locators 733 //NOTE: so far only character content is supported 734 writeCLOB(content.toString(),clob); 735 long newContentType = DBHelper.CHARACTER_CONTENT; 736 737 //3. update content type 738 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.change_content_type(?,?) }"); 739 cstmt.setLong(1,docContentID.longValue()); 740 cstmt.setLong(2,newContentType); 741 cstmt.execute(); 742 } 743 catch(IOException ioe) { 744 throw new PersistenceException("can't update document content in DB : ["+ 745 ioe.getMessage()+"]"); 746 } 747 catch(SQLException sqle) { 748 throw new PersistenceException("can't update document content in DB : ["+ 749 sqle.getMessage()+"]"); 750 } 751 finally { 752 DBHelper.cleanup(rs); 753 DBHelper.cleanup(pstmt); 754 DBHelper.cleanup(cstmt); 755 } 756 757 } 758 759 760 761 /** 762 * helper for adopt 763 * creates a LR of type Document 764 */ 765 private Document createDocument(Document doc,SecurityInfo secInfo) 766 throws PersistenceException,SecurityException { 767 768 //delegate, set to Null 769 return createDocument(doc,null,secInfo); 770 } 771 772 773 774 /** 775 * helper for adopt 776 * creates a LR of type Document 777 */ 778 private Document createDocument(Document doc, Long corpusID,SecurityInfo secInfo) 779 throws PersistenceException,SecurityException { 780 781 //-1. preconditions 782 Assert.assertNotNull(doc); 783 Assert.assertNotNull(secInfo); 784 785 //0. check securoity settings 786 if (false == this.ac.isValidSecurityInfo(secInfo)) { 787 throw new SecurityException("Invalid security settings"); 788 } 789 790 //1. get the data to be stored 791 AnnotationSet defaultAnnotations = doc.getAnnotations(); 792 DocumentContent docContent = doc.getContent(); 793 FeatureMap docFeatures = doc.getFeatures(); 794 String docName = doc.getName(); 795 URL docURL = doc.getSourceUrl(); 796 Boolean docIsMarkupAware = doc.getMarkupAware(); 797 Long docStartOffset = doc.getSourceUrlStartOffset(); 798 Long docEndOffset = doc.getSourceUrlEndOffset(); 799 String docEncoding = null; 800 try { 801 docEncoding = (String)doc.getParameterValue("encoding"); 802 } 803 catch(gate.creole.ResourceInstantiationException re) { 804 throw new PersistenceException("cannot create document: error getting " + 805 " document encoding ["+re.getMessage()+"]"); 806 } 807 808 809 //3. create a Language Resource (an entry in T_LANG_RESOURCE) for this document 810 Long lrID = createLR(DBHelper.DOCUMENT_CLASS,docName,secInfo,null); 811 812 //4. create a record in T_DOCUMENT for this document 813 CallableStatement stmt = null; 814 Long docID = null; 815 Long docContentID = null; 816 817 try { 818 stmt = this.jdbcConn.prepareCall( 819 "{ call "+Gate.DB_OWNER+".persist.create_document(?,?,?,?,?,?,?,?,?) }"); 820 stmt.setLong(1,lrID.longValue()); 821 stmt.setString(2,docURL.toString()); 822 //do we have doc encoding? 823 if (null == docEncoding) { 824 stmt.setNull(3,java.sql.Types.VARCHAR); 825 } 826 else { 827 stmt.setString(3,docEncoding); 828 } 829 //do we have start offset? 830 if (null==docStartOffset) { 831 stmt.setNull(4,java.sql.Types.NUMERIC); 832 } 833 else { 834 stmt.setLong(4,docStartOffset.longValue()); 835 } 836 //do we have end offset? 837 if (null==docEndOffset) { 838 stmt.setNull(5,java.sql.Types.NUMERIC); 839 } 840 else { 841 stmt.setLong(5,docEndOffset.longValue()); 842 } 843 844 stmt.setBoolean(6,docIsMarkupAware.booleanValue()); 845 846 //is the document part of a corpus? 847 if (null == corpusID) { 848 stmt.setNull(7,java.sql.Types.BIGINT); 849 } 850 else { 851 stmt.setLong(7,corpusID.longValue()); 852 } 853 854 //results 855 stmt.registerOutParameter(8,java.sql.Types.BIGINT); 856 stmt.registerOutParameter(9,java.sql.Types.BIGINT); 857 858 stmt.execute(); 859 docID = new Long(stmt.getLong(8)); 860 docContentID = new Long(stmt.getLong(9)); 861 } 862 catch(SQLException sqle) { 863 throw new PersistenceException("can't create document [step 4] in DB: ["+ sqle.getMessage()+"]"); 864 } 865 finally { 866 DBHelper.cleanup(stmt); 867 } 868 869 //5. fill document content (record[s] in T_DOC_CONTENT) 870 871 //do we have content at all? 872 if (docContent.size().longValue() > 0) { 873 updateDocumentContent(docContentID,docContent); 874 } 875 876 //6. insert annotations, etc 877 878 //6.1. create default annotation set 879 createAnnotationSet(lrID,defaultAnnotations); 880 881 //6.2. create named annotation sets 882 Map namedAnns = doc.getNamedAnnotationSets(); 883 //the map may be null 884 if (null != namedAnns) { 885 Set setAnns = namedAnns.entrySet(); 886 Iterator itAnns = setAnns.iterator(); 887 888 while (itAnns.hasNext()) { 889 Map.Entry mapEntry = (Map.Entry)itAnns.next(); 890 //String currAnnName = (String)mapEntry.getKey(); 891 AnnotationSet currAnnSet = (AnnotationSet)mapEntry.getValue(); 892 893 //create a-sets 894 createAnnotationSet(lrID,currAnnSet); 895 } 896 } 897 898 //7. create features 899 createFeatures(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures); 900 901 //9. create a DatabaseDocument wrapper and return it 902 903 Document dbDoc = new DatabaseDocumentImpl(this.jdbcConn, 904 doc.getName(), 905 this, 906 lrID, 907 doc.getContent(), 908 doc.getFeatures(), 909 doc.getMarkupAware(), 910 doc.getSourceUrl(), 911 doc.getSourceUrlStartOffset(), 912 doc.getSourceUrlEndOffset(), 913 doc.getAnnotations(), 914 doc.getNamedAnnotationSets()); 915 916 return dbDoc; 917 } 918 919 920 921 /** creates an entry for annotation set in the database */ 922 private void createAnnotationSet(Long lrID, AnnotationSet aset) 923 throws PersistenceException { 924 925 //1. create a-set 926 String asetName = aset.getName(); 927 Long asetID = null; 928 929 //DB stuff 930 CallableStatement stmt = null; 931 try { 932 stmt = this.jdbcConn.prepareCall( 933 "{ call "+Gate.DB_OWNER+".persist.create_annotation_set(?,?,?) }"); 934 stmt.setLong(1,lrID.longValue()); 935 936 if (null == asetName) { 937 stmt.setNull(2,java.sql.Types.VARCHAR); 938 } 939 else { 940 stmt.setString(2,asetName); 941 } 942 stmt.registerOutParameter(3,java.sql.Types.BIGINT); 943 stmt.execute(); 944 945 asetID = new Long(stmt.getLong(3)); 946 } 947 catch(SQLException sqle) { 948 throw new PersistenceException("can't create a-set [step 1] in DB: ["+ sqle.getMessage()+"]"); 949 } 950 finally { 951 DBHelper.cleanup(stmt); 952 } 953 954 955 //2. insert annotations/nodes for DEFAULT a-set 956 //for now use a stupid cycle 957 //TODO: pass all the data with one DB call (?) 958 Iterator itAnnotations = aset.iterator(); 959 while (itAnnotations.hasNext()) { 960 Annotation ann = (Annotation)itAnnotations.next(); 961 Node start = (Node)ann.getStartNode(); 962 Node end = (Node)ann.getEndNode(); 963 String type = ann.getType(); 964 965 //DB stuff 966 Long annGlobalID = null; 967 try { 968 stmt = this.jdbcConn.prepareCall( 969 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }"); 970 stmt.setLong(1,lrID.longValue()); 971 stmt.setLong(2,ann.getId().longValue()); 972 stmt.setLong(3,asetID.longValue()); 973 stmt.setLong(4,start.getId().longValue()); 974 stmt.setLong(5,start.getOffset().longValue()); 975 stmt.setLong(6,end.getId().longValue()); 976 stmt.setLong(7,end.getOffset().longValue()); 977 stmt.setString(8,type); 978 stmt.registerOutParameter(9,java.sql.Types.BIGINT); 979 980 stmt.execute(); 981 982 annGlobalID = new Long(stmt.getLong(9)); 983 } 984 catch(SQLException sqle) { 985 switch(sqle.getErrorCode()) { 986 case DBHelper.X_ORACLE_INVALID_ANNOTATION_TYPE: 987 throw new PersistenceException( 988 "can't create annotation in DB, [invalid annotation type]"); 989 default: 990 throw new PersistenceException( 991 "can't create annotation in DB: ["+ sqle.getMessage()+"]"); 992 } 993 994 } 995 finally { 996 DBHelper.cleanup(stmt); 997 } 998 999 //2.1. set annotation features 1000 FeatureMap features = ann.getFeatures(); 1001 Assert.assertNotNull(features); 1002 createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features); 1003 } 1004 } 1005 1006 1007 1008 /** creates a LR of type Corpus */ 1009 private Corpus createCorpus(Corpus corp,SecurityInfo secInfo) 1010 throws PersistenceException,SecurityException { 1011 1012 //1. create an LR entry for the corpus (T_LANG_RESOURCE table) 1013 Long lrID = createLR(DBHelper.CORPUS_CLASS,corp.getName(),secInfo,null); 1014 1015 //2.create am entry in the T_COPRUS table 1016 Long corpusID = null; 1017 //DB stuff 1018 CallableStatement stmt = null; 1019 try { 1020 stmt = this.jdbcConn.prepareCall( 1021 "{ call "+Gate.DB_OWNER+".persist.create_corpus(?,?) }"); 1022 stmt.setLong(1,lrID.longValue()); 1023 stmt.registerOutParameter(2,java.sql.Types.BIGINT); 1024 stmt.execute(); 1025 corpusID = new Long(stmt.getLong(2)); 1026 } 1027 catch(SQLException sqle) { 1028 throw new PersistenceException("can't create corpus [step 2] in DB: ["+ sqle.getMessage()+"]"); 1029 } 1030 finally { 1031 DBHelper.cleanup(stmt); 1032 } 1033 1034 //3. for each document in the corpus call createDocument() 1035 Iterator itDocuments = corp.iterator(); 1036 Vector dbDocs = new Vector(); 1037 while (itDocuments.hasNext()) { 1038 Document doc = (Document)itDocuments.next(); 1039 1040 //3.1. ensure that the document is either transient or is from the ... 1041 // same DataStore 1042 if (doc.getLRPersistenceId() == null) { 1043 //transient document 1044 Document dbDoc = createDocument(doc,corpusID,secInfo); 1045 dbDocs.add(dbDoc); 1046 } 1047 else if (doc.getDataStore().equals(this)) { 1048 //persistent doc from the same DataStore 1049 fireResourceAdopted( 1050 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, 1051 doc, 1052 doc.getLRPersistenceId())); 1053 1054 //6. fire also resource written event because it's now saved 1055 fireResourceWritten( 1056 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, 1057 doc, 1058 doc.getLRPersistenceId())); 1059 } 1060 else { 1061 //persistent doc from other datastore 1062 //skip 1063 gate.util.Err.prln("document ["+doc.getLRPersistenceId()+"] is adopted from another "+ 1064 " datastore. Skipped."); 1065 } 1066 } 1067 1068 //4. create features 1069 createFeatures(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures()); 1070 1071 //5. create a DatabaseCorpusImpl and return it 1072 Corpus dbCorpus = new DatabaseCorpusImpl(corp.getName(), 1073 this, 1074 lrID, 1075 corp.getFeatures(), 1076 dbDocs); 1077 1078 //6. done 1079 return dbCorpus; 1080 } 1081 1082 1083 1084 /** 1085 * Get a resource from the persistent store. 1086 * <B>Don't use this method - use Factory.createResource with 1087 * DataStore and DataStoreInstanceId parameters set instead.</B> 1088 */ 1089 public LanguageResource getLr(String lrClassName, Object lrPersistenceId) 1090 throws PersistenceException,SecurityException { 1091 1092 LanguageResource result = null; 1093 1094 //0. preconditions 1095 Assert.assertNotNull(lrPersistenceId); 1096 1097 //1. check session 1098 if (null == this.session) { 1099 throw new SecurityException("session not set"); 1100 } 1101 1102 if (false == this.ac.isValidSession(this.session)) { 1103 throw new SecurityException("invalid session supplied"); 1104 } 1105 1106 //2. check permissions 1107 if (false == canReadLR(lrPersistenceId)) { 1108 throw new SecurityException("insufficient privileges"); 1109 } 1110 1111 //3. get resource from DB 1112 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) { 1113 result = readDocument(lrPersistenceId); 1114 Assert.assertTrue(result instanceof DatabaseDocumentImpl); 1115 } 1116 else if (lrClassName.equals(DBHelper.CORPUS_CLASS)) { 1117 result = readCorpus(lrPersistenceId); 1118 Assert.assertTrue(result instanceof DatabaseCorpusImpl); 1119 } 1120 else { 1121 throw new IllegalArgumentException("resource class should be either Document or Corpus"); 1122 } 1123 1124 //4. postconditions 1125 Assert.assertNotNull(result.getDataStore()); 1126 Assert.assertTrue(result.getDataStore() instanceof DatabaseDataStore); 1127 Assert.assertNotNull(result.getLRPersistenceId()); 1128 1129 //5. register the read doc as listener for sync events 1130 addDatastoreListener((DatastoreListener)result); 1131 1132 //6. add the resource to the list of dependent resources - i.e. the ones that the 1133 //data store should take care upon closing [and call sync()] 1134 this.dependentResources.add(result); 1135 1136 //7. done 1137 return result; 1138 } 1139 1140 1141 1142 /** Get a list of the types of LR that are present in the data store. */ 1143 public List getLrTypes() throws PersistenceException { 1144 1145 Vector lrTypes = new Vector(); 1146 Statement stmt = null; 1147 ResultSet rs = null; 1148 1149 try { 1150 stmt = this.jdbcConn.createStatement(); 1151 rs = stmt.executeQuery(" SELECT lrtp_type " + 1152 " FROM "+Gate.DB_OWNER+".t_lr_type LRTYPE "); 1153 1154 while (rs.next()) { 1155 //access by index is faster 1156 String lrType = rs.getString(1); 1157 lrTypes.add(lrType); 1158 } 1159 1160 return lrTypes; 1161 } 1162 catch(SQLException sqle) { 1163 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]"); 1164 } 1165 finally { 1166 DBHelper.cleanup(rs); 1167 DBHelper.cleanup(stmt); 1168 } 1169 } 1170 1171 1172 1173 /** Get a list of the IDs of LRs of a particular type that are present. */ 1174 public List getLrIds(String lrType) throws PersistenceException { 1175 1176 Vector lrIDs = new Vector(); 1177 PreparedStatement stmt = null; 1178 ResultSet rs = null; 1179 1180 try { 1181 stmt = this.jdbcConn.prepareStatement( 1182 " SELECT lr_id " + 1183 " FROM "+Gate.DB_OWNER+".t_lang_resource LR, " + 1184 " "+Gate.DB_OWNER+".t_lr_type LRTYPE " + 1185 " WHERE LR.lr_type_id = LRTYPE.lrtp_id " + 1186 " AND LRTYPE.lrtp_type = ? " + 1187 " ORDER BY lr_name" 1188 ); 1189 stmt.setString(1,lrType); 1190 stmt.execute(); 1191 rs = stmt.getResultSet(); 1192 1193 while (rs.next()) { 1194 //access by index is faster 1195 Long lrID = new Long(rs.getLong(1)); 1196 lrIDs.add(lrID); 1197 } 1198 1199 return lrIDs; 1200 } 1201 catch(SQLException sqle) { 1202 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]"); 1203 } 1204 finally { 1205 DBHelper.cleanup(rs); 1206 DBHelper.cleanup(stmt); 1207 } 1208 1209 } 1210 1211 1212 1213 /** Get a list of the names of LRs of a particular type that are present. */ 1214 public List getLrNames(String lrType) throws PersistenceException { 1215 1216 Vector lrNames = new Vector(); 1217 PreparedStatement stmt = null; 1218 ResultSet rs = null; 1219 1220 try { 1221 stmt = this.jdbcConn.prepareStatement( 1222 " SELECT lr_name " + 1223 " FROM "+Gate.DB_OWNER+".t_lang_resource LR, " + 1224 " t_lr_type LRTYPE " + 1225 " WHERE LR.lr_type_id = LRTYPE.lrtp_id " + 1226 " AND LRTYPE.lrtp_type = ? " 1227 ); 1228 stmt.setString(1,lrType); 1229 stmt.execute(); 1230 rs = stmt.getResultSet(); 1231 1232 while (rs.next()) { 1233 //access by index is faster 1234 String lrName = rs.getString(1); 1235 lrNames.add(lrName); 1236 } 1237 1238 return lrNames; 1239 } 1240 catch(SQLException sqle) { 1241 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]"); 1242 } 1243 finally { 1244 DBHelper.cleanup(rs); 1245 DBHelper.cleanup(stmt); 1246 } 1247 1248 } 1249 1250 1251 1252 /** Gets a timestamp marker that will be used for all changes made in 1253 * the database so that subsequent calls to deleteSince() could restore (partly) 1254 * the database state as it was before the update. <B>NOTE:</B> Restoring the previous 1255 * state may not be possible at all (i.e. if DELETE is performed) 1256 * */ 1257 public Long timestamp() 1258 throws PersistenceException{ 1259 1260 CallableStatement stmt = null; 1261 1262 try { 1263 stmt = this.jdbcConn.prepareCall( 1264 "{ call "+Gate.DB_OWNER+".persist.get_timestamp(?)} "); 1265 //numbers generated from Oracle sequences are BIGINT 1266 stmt.registerOutParameter(1,java.sql.Types.BIGINT); 1267 stmt.execute(); 1268 long result = stmt.getLong(1); 1269 1270 return new Long(result); 1271 } 1272 catch(SQLException sqle) { 1273 throw new PersistenceException("can't get a timestamp from DB: ["+ sqle.getMessage()+"]"); 1274 } 1275 1276 } 1277 1278 1279 1280 /** 1281 * Checks if the user (identified by the sessionID) 1282 * has read access to the LR 1283 */ 1284 public boolean canReadLR(Object lrID) 1285 throws PersistenceException, SecurityException{ 1286 1287 return canAccessLR((Long) lrID,READ_ACCESS); 1288 } 1289 1290 1291 1292 /** 1293 * Checks if the user (identified by the sessionID) 1294 * has write access to the LR 1295 */ 1296 public boolean canWriteLR(Object lrID) 1297 throws PersistenceException, SecurityException{ 1298 1299 return canAccessLR((Long) lrID,WRITE_ACCESS); 1300 } 1301 1302 1303 1304 /** 1305 * Checks if the user (identified by the sessionID) 1306 * has some access (read/write) to the LR 1307 */ 1308 private boolean canAccessLR(Long lrID,int mode) 1309 throws PersistenceException, SecurityException{ 1310 1311 //0. preconditions 1312 Assert.assertTrue(READ_ACCESS == mode || WRITE_ACCESS == mode); 1313 1314 //1. is session initialised? 1315 if (null == this.session) { 1316 throw new SecurityException("user session not set"); 1317 } 1318 1319 //2.first check the session and then check whether the user is member of the group 1320 if (this.ac.isValidSession(this.session) == false) { 1321 throw new SecurityException("invalid session supplied"); 1322 } 1323 1324 CallableStatement stmt = null; 1325 1326 try { 1327 stmt = this.jdbcConn.prepareCall( 1328 "{ call "+Gate.DB_OWNER+".security.has_access_to_lr(?,?,?,?,?)} "); 1329 stmt.setLong(1,lrID.longValue()); 1330 stmt.setLong(2,this.session.getUser().getID().longValue()); 1331 stmt.setLong(3,this.session.getGroup().getID().longValue()); 1332 stmt.setLong(4,mode); 1333 1334 stmt.registerOutParameter(5,java.sql.Types.NUMERIC); 1335 stmt.execute(); 1336 int result = stmt.getInt(5); 1337 1338 return (ORACLE_TRUE == result); 1339 } 1340 catch(SQLException sqle) { 1341 throw new PersistenceException("can't check permissions in DB: ["+ sqle.getMessage()+"]"); 1342 } 1343 finally { 1344 DBHelper.cleanup(stmt); 1345 } 1346 } 1347 1348 1349 1350 /** reads the content of a CLOB into the specified StringBuffer */ 1351 public static void readCLOB(java.sql.Clob src, StringBuffer dest) 1352 throws SQLException, IOException { 1353 1354 int readLength = 0; 1355 1356 //1. empty the dest buffer 1357 dest.delete(0,dest.length()); 1358 1359 //2. get Oracle CLOB 1360 CLOB clo = (CLOB)src; 1361 1362 //3. create temp buffer 1363 int buffSize = Math.max(INTERNAL_BUFFER_SIZE,clo.getBufferSize()); 1364 char[] readBuffer = new char[buffSize]; 1365 1366 //3. get Unicode stream 1367 Reader input = clo.getCharacterStream(); 1368 1369 //4. read 1370 BufferedReader buffInput = new BufferedReader(input,INTERNAL_BUFFER_SIZE); 1371 1372 while ((readLength = buffInput.read(readBuffer, 0, INTERNAL_BUFFER_SIZE)) != -1) { 1373 dest.append(readBuffer, 0, readLength); 1374 } 1375 1376 //5.close streams 1377 buffInput.close(); 1378 input.close(); 1379 1380 } 1381 1382 1383 1384 /** writes the content of a String into the specified CLOB object */ 1385 public static void writeCLOB(String src,java.sql.Clob dest) 1386 throws SQLException, IOException { 1387 1388 //preconditions 1389 Assert.assertNotNull(src); 1390 1391 //1. get Oracle CLOB 1392 CLOB clo = (CLOB)dest; 1393 1394 //2. get Unicode stream 1395 Writer output = clo.getCharacterOutputStream(); 1396 1397 //3. write 1398 BufferedWriter buffOutput = new BufferedWriter(output,INTERNAL_BUFFER_SIZE); 1399 buffOutput.write(src.toString()); 1400 1401 //4. flushing is a good idea [although BufferedWriter::close() calls it this is 1402 //implementation specific] 1403 buffOutput.flush(); 1404 output.flush(); 1405 1406 //5.close streams 1407 buffOutput.close(); 1408 output.close(); 1409 } 1410 1411 1412 1413 /** writes the content of a StringBuffer into the specified CLOB object */ 1414 public static void writeCLOB(StringBuffer src,java.sql.Clob dest) 1415 throws SQLException, IOException { 1416 1417 //delegate 1418 writeCLOB(src.toString(),dest); 1419 } 1420 1421 1422 1423 /** 1424 * reads the content of the specified BLOB object and returns the object 1425 * contained. 1426 * NOTE: the BLOB is expected to contain serializable objects, not just any 1427 * binary stream 1428 */ 1429 public static Object readBLOB(java.sql.Blob src) 1430 throws SQLException, IOException,ClassNotFoundException { 1431 1432 int readLength = 0; 1433 Object result = null; 1434 1435 //0. preconditions 1436 Assert.assertNotNull(src); 1437 1438 //2. get Oracle BLOB 1439 BLOB blo = (BLOB)src; 1440 1441 //3. get binary stream 1442 InputStream input = blo.getBinaryStream(); 1443 Assert.assertNotNull(input); 1444 1445 //4. read 1446 ObjectInputStream ois = new ObjectInputStream(input); 1447 result = ois.readObject(); 1448 1449 //5.close streams 1450 ois.close(); 1451 input.close(); 1452 1453 return result; 1454 } 1455 1456 1457 1458 /** 1459 * writes the specified object into the BLOB 1460 * NOTE: the object should be serializable 1461 */ 1462 public static void writeBLOB(Object src,java.sql.Blob dest) 1463 throws SQLException, IOException { 1464 1465 //preconditions 1466 Assert.assertNotNull(src); 1467 1468 //1. get Oracle CLOB 1469 BLOB blo = (BLOB)dest; 1470 1471 //2. get Unicode stream 1472 OutputStream output = blo.getBinaryOutputStream(); 1473 1474 //3. write 1475 ObjectOutputStream oos = new ObjectOutputStream(output); 1476 oos.writeObject(src); 1477 1478 //4. flushing is a good idea 1479 //[although ::close() calls it this is implementation specific] 1480 oos.flush(); 1481 output.flush(); 1482 1483 //5.close streams 1484 oos.close(); 1485 output.close(); 1486 } 1487 1488 1489 1490 /** 1491 * creates a feature of the specified type/value/valueType/key for the specified entity 1492 * Entity is one of: LR, Annotation 1493 * Value types are: boolean, int, long, string, float, Object 1494 */ 1495 private Long _createFeature(Long entityID, 1496 int entityType, 1497 String key, 1498 Object value, 1499 int valueType) 1500 throws PersistenceException { 1501 1502 //1. store in DB 1503 Long featID = null; 1504 CallableStatement stmt = null; 1505 1506 try { 1507 stmt = this.jdbcConn.prepareCall( 1508 "{ call "+Gate.DB_OWNER+".persist.create_feature(?,?,?,?,?,?,?)} "); 1509 1510 //1.1 set known values + NULLs 1511 stmt.setLong(1,entityID.longValue()); 1512 stmt.setLong(2,entityType); 1513 stmt.setString(3,key); 1514 stmt.setNull(4,java.sql.Types.NUMERIC); 1515 stmt.setNull(5,java.sql.Types.VARCHAR); 1516 stmt.setLong(6,valueType); 1517 stmt.registerOutParameter(7,java.sql.Types.BIGINT); 1518 1519 //1.2 set proper data 1520 switch(valueType) { 1521 1522 case DBHelper.VALUE_TYPE_NULL: 1523 break; 1524 1525 case DBHelper.VALUE_TYPE_BOOLEAN: 1526 1527 boolean b = ((Boolean)value).booleanValue(); 1528 stmt.setLong(4, b ? this.ORACLE_TRUE : this.ORACLE_FALSE); 1529 break; 1530 1531 case DBHelper.VALUE_TYPE_INTEGER: 1532 1533 stmt.setLong(4,((Integer)value).intValue()); 1534 break; 1535 1536 case DBHelper.VALUE_TYPE_LONG: 1537 1538 stmt.setLong(4,((Long)value).longValue()); 1539 break; 1540 1541 case DBHelper.VALUE_TYPE_FLOAT: 1542 1543 Double d = (Double)value; 1544 stmt.setDouble(4,d.doubleValue()); 1545 break; 1546 1547 case DBHelper.VALUE_TYPE_BINARY: 1548 //ignore 1549 //will be handled later in processing 1550 break; 1551 1552 case DBHelper.VALUE_TYPE_STRING: 1553 1554 String s = (String)value; 1555 //does it fin into a varchar2? 1556 if (fitsInVarchar2(s)) { 1557 stmt.setString(5,s); 1558 } 1559 break; 1560 1561 default: 1562 throw new IllegalArgumentException("unsuppoeted feature type"); 1563 } 1564 1565 stmt.execute(); 1566 featID = new Long(stmt.getLong(7)); 1567 } 1568 catch(SQLException sqle) { 1569 1570 switch(sqle.getErrorCode()) { 1571 case DBHelper.X_ORACLE_INVALID_FEATURE_TYPE: 1572 throw new PersistenceException("can't create feature [step 1],"+ 1573 "[invalid feature type] in DB: ["+ sqle.getMessage()+"]"); 1574 default: 1575 throw new PersistenceException("can't create feature [step 1] in DB: ["+ 1576 sqle.getMessage()+"]"); 1577 } 1578 } 1579 finally { 1580 DBHelper.cleanup(stmt); 1581 } 1582 1583 return featID; 1584 } 1585 1586 1587 1588 /** 1589 * updates the value of a feature where the value is string (>4000 bytes, stored as CLOB) 1590 * or Object (stored as BLOB) 1591 */ 1592 private void _updateFeatureLOB(Long featID,Object value, int valueType) 1593 throws PersistenceException { 1594 1595 //NOTE: at this point value is never an array, 1596 // although the type may claim so 1597 1598 //0. preconditions 1599 Assert.assertTrue(valueType == DBHelper.VALUE_TYPE_BINARY || 1600 valueType == DBHelper.VALUE_TYPE_STRING); 1601 1602 1603 //1. get the row to be updated 1604 PreparedStatement stmtA = null; 1605 ResultSet rsA = null; 1606 Clob clobValue = null; 1607 Blob blobValue = null; 1608 1609 try { 1610 String sql = " select ft_long_character_value, " + 1611 " ft_binary_value " + 1612 " from "+Gate.DB_OWNER+".t_feature " + 1613 " where ft_id = ? "; 1614 1615 stmtA = this.jdbcConn.prepareStatement(sql); 1616 stmtA.setLong(1,featID.longValue()); 1617 stmtA.execute(); 1618 rsA = stmtA.getResultSet(); 1619 1620 if (false == rsA.next()) { 1621 throw new PersistenceException("Incorrect feature ID supplied ["+featID+"]"); 1622 } 1623 1624 //NOTE1: if the result set contains LOBs always read them 1625 // in the order they appear in the SQL query 1626 // otherwise data will be lost 1627 //NOTE2: access by index rather than name is usually faster 1628 clobValue = rsA.getClob(1); 1629 blobValue = rsA.getBlob(2); 1630 1631 //blob or clob? 1632 if (valueType == DBHelper.VALUE_TYPE_BINARY) { 1633 //blob 1634 writeBLOB(value,blobValue); 1635 } 1636 else if (valueType == DBHelper.VALUE_TYPE_STRING) { 1637 //clob 1638 String s = (String)value; 1639 writeCLOB(s,clobValue); 1640 } 1641 else { 1642 Assert.fail(); 1643 } 1644 } 1645 catch(SQLException sqle) { 1646 throw new PersistenceException("can't create feature [step 2] in DB: ["+ sqle.getMessage()+"]"); 1647 } 1648 catch(IOException ioe) { 1649 throw new PersistenceException("can't create feature [step 2] in DB: ["+ ioe.getMessage()+"]"); 1650 } 1651 finally { 1652 DBHelper.cleanup(rsA); 1653 DBHelper.cleanup(stmtA); 1654 } 1655 1656 } 1657 1658 1659 1660 /** 1661 * creates a feature with the specified type/key/value for the specified entity 1662 * entitties are either LRs ot Annotations 1663 * valid values are: boolean, 1664 * int, 1665 * long, 1666 * string, 1667 * float, 1668 * Object, 1669 * boolean List, 1670 * int List, 1671 * long List, 1672 * string List, 1673 * float List, 1674 * Object List 1675 * 1676 */ 1677 private void createFeature(Long entityID, int entityType,String key, Object value) 1678 throws PersistenceException { 1679 1680 //1. what kind of feature value is this? 1681//System.out.println("key=["+key+"], val=["+value+"]"); 1682 int valueType = findFeatureType(value); 1683 1684 //2. how many elements do we store? 1685 Vector elementsToStore = new Vector(); 1686 1687 switch(valueType) { 1688 case DBHelper.VALUE_TYPE_NULL: 1689 case DBHelper.VALUE_TYPE_BINARY: 1690 case DBHelper.VALUE_TYPE_BOOLEAN: 1691 case DBHelper.VALUE_TYPE_FLOAT: 1692 case DBHelper.VALUE_TYPE_INTEGER: 1693 case DBHelper.VALUE_TYPE_LONG: 1694 case DBHelper.VALUE_TYPE_STRING: 1695 elementsToStore.add(value); 1696 break; 1697 1698 default: 1699 //arrays 1700 List arr = (List)value; 1701 Iterator itValues = arr.iterator(); 1702 1703 while (itValues.hasNext()) { 1704 elementsToStore.add(itValues.next()); 1705 } 1706 1707 //normalize , i.e. ignore arrays 1708 if (valueType == DBHelper.VALUE_TYPE_BINARY_ARR) 1709 valueType = DBHelper.VALUE_TYPE_BINARY; 1710 else if (valueType == DBHelper.VALUE_TYPE_BOOLEAN_ARR) 1711 valueType = DBHelper.VALUE_TYPE_BOOLEAN; 1712 else if (valueType == DBHelper.VALUE_TYPE_FLOAT_ARR) 1713 valueType = DBHelper.VALUE_TYPE_FLOAT; 1714 else if (valueType == DBHelper.VALUE_TYPE_INTEGER_ARR) 1715 valueType = DBHelper.VALUE_TYPE_INTEGER; 1716 else if (valueType == DBHelper.VALUE_TYPE_LONG_ARR) 1717 valueType = DBHelper.VALUE_TYPE_LONG; 1718 else if (valueType == DBHelper.VALUE_TYPE_STRING_ARR) 1719 valueType = DBHelper.VALUE_TYPE_STRING; 1720 } 1721 1722 //3. for all elements: 1723 for (int i=0; i< elementsToStore.size(); i++) { 1724 1725 Object currValue = elementsToStore.elementAt(i); 1726 1727 //3.1. create a dummy feature [LOB hack] 1728 Long featID = _createFeature(entityID,entityType,key,currValue,valueType); 1729 1730 //3.2. update CLOBs if needed 1731 if (valueType == DBHelper.VALUE_TYPE_STRING) { 1732 //does this string fit into a varchar2 or into clob? 1733 String s = (String)currValue; 1734 if (false == this.fitsInVarchar2(s)) { 1735 // Houston, we have a problem 1736 // put the string into a clob 1737 _updateFeatureLOB(featID,value,valueType); 1738 } 1739 } 1740 else if (valueType == DBHelper.VALUE_TYPE_BINARY) { 1741 //3.3. BLOBs 1742 _updateFeatureLOB(featID,value,valueType); 1743 } 1744 } 1745 1746 1747 } 1748 1749 1750 1751 /** 1752 * checks if a String should be stores as VARCHAR2 or CLOB 1753 * because the VARCHAR2 in Oracle is limited to 4000 <b>bytes</b>, not all 1754 * the strings fit there. If a String is too long then it is store in the 1755 * database as CLOB. 1756 * Note that in the worst case 3 bytes are needed to represent a single character 1757 * in a database with UTF8 encoding, which limits the string length to 4000/3 1758 * (ORACLE_VARCHAR_LIMIT_BYTES) 1759 * @see ORACLE_VARCHAR_LIMIT_BYTES 1760 */ 1761 private boolean fitsInVarchar2(String s) { 1762 1763 return s.getBytes().length < this.ORACLE_VARCHAR_LIMIT_BYTES; 1764 } 1765 1766 1767 1768 /** 1769 * helper metod 1770 * iterates a FeatureMap and creates all its features in the database 1771 * */ 1772 private void createFeatures(Long entityID, int entityType, FeatureMap features) 1773 throws PersistenceException { 1774 1775 /* when some day Java has macros, this will be a macro */ 1776 Set entries = features.entrySet(); 1777 Iterator itFeatures = entries.iterator(); 1778 while (itFeatures.hasNext()) { 1779 Map.Entry entry = (Map.Entry)itFeatures.next(); 1780 String key = (String)entry.getKey(); 1781 Object value = entry.getValue(); 1782 createFeature(entityID,entityType,key,value); 1783 } 1784 } 1785 1786 1787 1788 /** get security information for LR . */ 1789 public SecurityInfo getSecurityInfo(LanguageResource lr) 1790 throws PersistenceException { 1791 1792 //0. preconditions 1793 Assert.assertNotNull(lr); 1794 Assert.assertNotNull(lr.getLRPersistenceId()); 1795 Assert.assertTrue(lr.getLRPersistenceId() instanceof Long); 1796 Assert.assertEquals(this,lr.getDataStore()); 1797 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 1798 lr instanceof DatabaseCorpusImpl); 1799 1800 PreparedStatement pstmt = null; 1801 ResultSet rs = null; 1802 1803 //1. read data 1804 Long userID = null; 1805 Long groupID = null; 1806 int perm; 1807 try { 1808 String sql = " select lr_owner_user_id, "+ 1809 " lr_owner_group_id, " + 1810 " lr_access_mode "+ 1811 " from "+gate.Gate.DB_OWNER+".t_lang_resource "+ 1812 " where lr_id = ?"; 1813 pstmt = this.jdbcConn.prepareStatement(sql); 1814 pstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue()); 1815 rs = pstmt.executeQuery(); 1816 1817 if (false == rs.next()) { 1818 throw new PersistenceException("Invalid LR ID supplied - no data found"); 1819 } 1820 1821 userID = new Long(rs.getLong("lr_owner_user_id")); 1822 groupID = new Long(rs.getLong("lr_owner_group_id")); 1823 perm = rs.getInt("lr_access_mode"); 1824 1825 Assert.assertTrue(perm == SecurityInfo.ACCESS_GR_GW || 1826 perm == SecurityInfo.ACCESS_GR_OW || 1827 perm == SecurityInfo.ACCESS_OR_OW || 1828 perm == SecurityInfo.ACCESS_WR_GW); 1829 } 1830 catch(SQLException sqle) { 1831 throw new PersistenceException("Can't read document permissions from DB, error is [" + 1832 sqle.getMessage() +"]"); 1833 } 1834 finally { 1835 DBHelper.cleanup(rs); 1836 DBHelper.cleanup(pstmt); 1837 } 1838 1839 //2. get data from AccessController 1840 User usr = null; 1841 Group grp = null; 1842 try { 1843 usr = this.ac.findUser(userID); 1844 grp = this.ac.findGroup(groupID); 1845 } 1846 catch (SecurityException se) { 1847 throw new PersistenceException("Invalid security settings found in DB [" + 1848 se.getMessage() +"]"); 1849 } 1850 1851 //3. construct SecurityInfo 1852 SecurityInfo si = new SecurityInfo(perm,usr,grp); 1853 1854 1855 return si; 1856 } 1857 1858 1859 1860 /** set security information for LR . */ 1861 public void setSecurityInfo(LanguageResource lr,SecurityInfo si) 1862 throws PersistenceException, SecurityException { 1863 throw new MethodNotImplementedException(); 1864 } 1865 1866 1867 1868 /** 1869 * helper method for getLR - reads LR of type Corpus 1870 */ 1871 private DatabaseCorpusImpl readCorpus(Object lrPersistenceId) 1872 throws PersistenceException { 1873 1874 //0. preconditions 1875 Assert.assertNotNull(lrPersistenceId); 1876 1877 if (false == lrPersistenceId instanceof Long) { 1878 throw new IllegalArgumentException(); 1879 } 1880 1881 //3. read from DB 1882 PreparedStatement pstmt = null; 1883 ResultSet rs = null; 1884 DatabaseCorpusImpl result = null; 1885 1886 try { 1887 String sql = " select lr_name " + 1888 " from "+Gate.DB_OWNER+".t_lang_resource " + 1889 " where lr_id = ? "; 1890 pstmt = this.jdbcConn.prepareStatement(sql); 1891 pstmt.setLong(1,((Long)lrPersistenceId).longValue()); 1892 pstmt.execute(); 1893 rs = pstmt.getResultSet(); 1894 1895 if (false == rs.next()) { 1896 //ooops mo data found 1897 throw new PersistenceException("Invalid LR ID supplied - no data found"); 1898 } 1899 1900 //4. fill data 1901 1902 //4.1 name 1903 String lrName = rs.getString("lr_name"); 1904 Assert.assertNotNull(lrName); 1905 1906 //4.8 features 1907 FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_CORPUS); 1908 1909 DBHelper.cleanup(rs); 1910 DBHelper.cleanup(pstmt); 1911 1912 sql = " select doc_lr_id " + 1913 " from "+Gate.DB_OWNER+".t_document doc, " + 1914 " "+Gate.DB_OWNER+".t_corpus_document corpdoc, " + 1915 " "+Gate.DB_OWNER+".t_corpus corp " + 1916 " where doc.doc_id = corpdoc.cd_doc_id " + 1917 " and corpdoc.cd_corp_id = corp.corp_id " + 1918 " and corp_lr_id = ? "; 1919 pstmt = this.jdbcConn.prepareStatement(sql); 1920 pstmt.setLong(1,((Long)lrPersistenceId).longValue()); 1921 pstmt.execute(); 1922 rs = pstmt.getResultSet(); 1923 1924 Vector docLRIDs = new Vector(); 1925 while (rs.next()) { 1926 Long docLRID = new Long(rs.getLong("doc_lr_id")); 1927 docLRIDs.add(docLRID); 1928 } 1929 DBHelper.cleanup(rs); 1930 DBHelper.cleanup(pstmt); 1931 1932 1933 Vector dbDocs = new Vector(); 1934 for (int i=0; i< docLRIDs.size(); i++) { 1935 Long currLRID = (Long)docLRIDs.elementAt(i); 1936 //kalina: replaced by a Factory call, so the doc gets registered 1937 //properly in GATE. Otherwise strange behaviour results in the GUI 1938 //and no events come about it 1939// Document dbDoc = (Document)getLr(DBHelper.DOCUMENT_CLASS,currLRID); 1940 FeatureMap params = Factory.newFeatureMap(); 1941 params.put(DataStore.DATASTORE_FEATURE_NAME, this); 1942 params.put(DataStore.LR_ID_FEATURE_NAME, currLRID); 1943 Document dbDoc = (Document) 1944 Factory.createResource(DBHelper.DOCUMENT_CLASS, params); 1945 1946 1947 dbDocs.add(dbDoc); 1948 } 1949 1950 result = new DatabaseCorpusImpl(lrName, 1951 this, 1952 (Long)lrPersistenceId, 1953 features, 1954 dbDocs); 1955 } 1956 catch(SQLException sqle) { 1957 throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]"); 1958 } 1959 catch(Exception e) { 1960 throw new PersistenceException(e); 1961 } 1962 finally { 1963 DBHelper.cleanup(rs); 1964 DBHelper.cleanup(pstmt); 1965 } 1966 1967 return result; 1968 } 1969 1970 1971 /** helper method for getLR - reads LR of type Document */ 1972 private DatabaseDocumentImpl readDocument(Object lrPersistenceId) 1973 throws PersistenceException { 1974 1975 //0. preconditions 1976 Assert.assertNotNull(lrPersistenceId); 1977 1978 if (false == lrPersistenceId instanceof Long) { 1979 throw new IllegalArgumentException(); 1980 } 1981 1982 // 1. dummy document to be initialized 1983 DatabaseDocumentImpl result = new DatabaseDocumentImpl(this.jdbcConn); 1984 1985 PreparedStatement pstmt = null; 1986 ResultSet rs = null; 1987 1988 //3. read from DB 1989 try { 1990 String sql = " select lr_name, " + 1991 " lrtp_type, " + 1992 " lr_id, " + 1993 " doc_id, " + 1994 " doc_url, " + 1995 " doc_start, " + 1996 " doc_end, " + 1997 " doc_is_markup_aware " + 1998 " from "+Gate.DB_OWNER+".v_document " + 1999 " where lr_id = ? "; 2000 2001 pstmt = this.jdbcConn.prepareStatement(sql); 2002 pstmt.setLong(1,((Long)lrPersistenceId).longValue()); 2003 pstmt.execute(); 2004 rs = pstmt.getResultSet(); 2005 2006 if (false == rs.next()) { 2007 //ooops mo data found 2008 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2009 } 2010 2011 //4. fill data 2012 2013 //4.1 name 2014 String lrName = rs.getString("lr_name"); 2015 Assert.assertNotNull(lrName); 2016 result.setName(lrName); 2017 2018 //4.2. markup aware 2019 long markup = rs.getLong("doc_is_markup_aware"); 2020 Assert.assertTrue(markup == this.ORACLE_FALSE || markup == this.ORACLE_TRUE); 2021 if (markup == this.ORACLE_FALSE) { 2022 result.setMarkupAware(Boolean.FALSE); 2023 } 2024 else { 2025 result.setMarkupAware(Boolean.TRUE); 2026 } 2027 2028 //4.3 datastore 2029 result.setDataStore(this); 2030 2031 //4.4. persist ID 2032 Long persistID = new Long(rs.getLong("lr_id")); 2033 result.setLRPersistenceId(persistID); 2034 2035 //4.5 source url 2036 String url = rs.getString("doc_url"); 2037 result.setSourceUrl(new URL(url)); 2038 2039 //4.6. start offset 2040 Long start = null; 2041 long longVal = rs.getLong("doc_start"); 2042 //null? 2043 //if NULL is stored in the DB, Oracle returns 0 which is not what we want 2044 if (false == rs.wasNull()) { 2045 start = new Long(longVal); 2046 } 2047 result.setSourceUrlStartOffset(start); 2048 2049 //4.7. end offset 2050 Long end = null; 2051 longVal = rs.getLong("doc_end"); 2052 //null? 2053 //if NULL is stored in the DB, Oracle returns 0 which is not what we want 2054 if (false == rs.wasNull()) { 2055 end = new Long(longVal); 2056 } 2057 result.setSourceUrlEndOffset(end); 2058 2059 //4.8 features 2060 FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_DOCUMENT); 2061 result.setFeatures(features); 2062 2063 //4.9 set the nextAnnotationID correctly 2064 long doc_id = rs.getLong("doc_id"); 2065 2066 DBHelper.cleanup(rs); 2067 DBHelper.cleanup(pstmt); 2068 sql = " select max(ann_local_id) as max_ann_id" + 2069// " max(node_local_id) as max_node_id " + 2070 " from "+Gate.DB_OWNER+".t_annotation " + 2071 " where ann_doc_id = ?"; 2072 pstmt = this.jdbcConn.prepareStatement(sql); 2073 pstmt.setLong(1,doc_id); 2074 pstmt.execute(); 2075 rs = pstmt.getResultSet(); 2076 2077 if (false == rs.next()) { 2078 //ooops mo data found 2079 throw new PersistenceException("Invalid LR ID supplied - no data found"); 2080 } 2081 2082 int maxAnnID = rs.getInt("max_ann_id"); 2083 result.setNextAnnotationId(maxAnnID+1); 2084 //node? 2085 } 2086 catch(SQLException sqle) { 2087 throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]"); 2088 } 2089 catch(Exception e) { 2090 throw new PersistenceException(e); 2091 } 2092 finally { 2093 DBHelper.cleanup(rs); 2094 DBHelper.cleanup(pstmt); 2095 } 2096 2097 return result; 2098 } 2099 2100 2101 2102 /** 2103 * reads the features of an entity 2104 * entities are of type LR or Annotation 2105 */ 2106 private FeatureMap readFeatures(Long entityID, int entityType) 2107 throws PersistenceException { 2108 2109 //0. preconditions 2110 Assert.assertNotNull(entityID); 2111 Assert.assertTrue(entityType == DBHelper.FEATURE_OWNER_ANNOTATION || 2112 entityType == DBHelper.FEATURE_OWNER_CORPUS || 2113 entityType == DBHelper.FEATURE_OWNER_DOCUMENT); 2114 2115 2116 PreparedStatement pstmt = null; 2117 ResultSet rs = null; 2118 FeatureMap fm = new SimpleFeatureMapImpl(); 2119 2120 //1. read from DB 2121 try { 2122 String sql = " select v1.ft_key, " + 2123 " v1.ft_value_type, " + 2124 " v1.ft_number_value, " + 2125 " v1.ft_binary_value, " + 2126 " v1.ft_character_value, " + 2127 " v1.ft_long_character_value " + 2128 " from "+Gate.DB_OWNER+".t_feature v1 " + 2129 " where v1.ft_entity_id = ? " + 2130 " and v1.ft_entity_type = ? " + 2131 " order by v1.ft_key,v1.ft_id"; 2132 2133 pstmt = this.jdbcConn.prepareStatement(sql); 2134 pstmt.setLong(1,entityID.longValue()); 2135 pstmt.setLong(2,entityType); 2136 pstmt.execute(); 2137 rs = pstmt.getResultSet(); 2138 2139 //3. fill feature map 2140 Vector arrFeatures = new Vector(); 2141 String prevKey = null; 2142 String currKey = null; 2143 Object currFeature = null; 2144 2145 2146 while (rs.next()) { 2147 //NOTE: because there are LOBs in the resulset 2148 //the columns should be read in the order they appear 2149 //in the query 2150 currKey = rs.getString(1); 2151 2152 Long valueType = new Long(rs.getLong(2)); 2153 2154 //we don't quite know what is the type of the NUMBER 2155 //stored in DB 2156 Object numberValue = null; 2157 2158 //for all numeric types + boolean -> read from DB as appropriate 2159 //Java object 2160 switch(valueType.intValue()) { 2161 2162 case DBHelper.VALUE_TYPE_BOOLEAN: 2163 numberValue = new Boolean(rs.getBoolean(3)); 2164 break; 2165 2166 case DBHelper.VALUE_TYPE_FLOAT: 2167 numberValue = new Float(rs.getFloat(3)); 2168 break; 2169 2170 case DBHelper.VALUE_TYPE_INTEGER: 2171 numberValue = new Integer(rs.getInt(3)); 2172 break; 2173 2174 case DBHelper.VALUE_TYPE_LONG: 2175 numberValue = new Long(rs.getLong(3)); 2176 break; 2177 } 2178 2179 //don't forget to read the rest of the current row 2180 Blob blobValue = rs.getBlob(4); 2181 String stringValue = rs.getString(5); 2182 Clob clobValue = rs.getClob(6); 2183 2184 switch(valueType.intValue()) { 2185 2186 case DBHelper.VALUE_TYPE_NULL: 2187 currFeature = null; 2188 break; 2189 2190 case DBHelper.VALUE_TYPE_BOOLEAN: 2191 case DBHelper.VALUE_TYPE_FLOAT: 2192 case DBHelper.VALUE_TYPE_INTEGER: 2193 case DBHelper.VALUE_TYPE_LONG: 2194 currFeature = numberValue; 2195 break; 2196 2197 case DBHelper.VALUE_TYPE_BINARY: 2198 currFeature = readBLOB(blobValue); 2199 break; 2200 2201 case DBHelper.VALUE_TYPE_STRING: 2202 //this one is tricky too 2203 //if the string is < 4000 bytes long then it's stored as varchar2 2204 //otherwise as CLOB 2205 if (null == stringValue) { 2206 //oops, we got CLOB 2207 StringBuffer temp = new StringBuffer(); 2208 readCLOB(clobValue,temp); 2209 currFeature = temp.toString(); 2210 } 2211 else { 2212 currFeature = stringValue; 2213 } 2214 break; 2215 2216 default: 2217 throw new PersistenceException("Invalid feature type found in DB"); 2218 }//switch 2219 2220 //new feature or part of an array? 2221 if (currKey.equals(prevKey) && prevKey != null) { 2222 //part of array 2223 arrFeatures.add(currFeature); 2224 } 2225 else { 2226 //add prev feature to feature map 2227 2228 //is the prev feature an array or a single object? 2229 if (arrFeatures.size() > 1) { 2230 //put a clone, because this is a temp array that will 2231 //be cleared in few lines 2232 fm.put(prevKey, new Vector(arrFeatures)); 2233 } 2234 else if (arrFeatures.size() == 1) { 2235 fm.put(prevKey,arrFeatures.elementAt(0)); 2236 } 2237 else { 2238 //do nothing, this is the dummy feature 2239 ; 2240 }//if 2241 2242 //now clear the array from previous fesature(s) and put the new 2243 //one there 2244 arrFeatures.clear(); 2245 2246 prevKey = currKey; 2247 arrFeatures.add(currFeature); 2248 }//if 2249 }//while 2250 2251 //add the last feature 2252 if (arrFeatures.size() > 1) { 2253 fm.put(currKey,arrFeatures); 2254 } 2255 else if (arrFeatures.size() == 1) { 2256 fm.put(currKey,arrFeatures.elementAt(0)); 2257 } 2258 }//try 2259 catch(SQLException sqle) { 2260 throw new PersistenceException("can't read features from DB: ["+ sqle.getMessage()+"]"); 2261 } 2262 catch(IOException ioe) { 2263 throw new PersistenceException("can't read features from DB: ["+ ioe.getMessage()+"]"); 2264 } 2265 catch(ClassNotFoundException cnfe) { 2266 throw new PersistenceException("can't read features from DB: ["+ cnfe.getMessage()+"]"); 2267 } 2268 finally { 2269 DBHelper.cleanup(rs); 2270 DBHelper.cleanup(pstmt); 2271 } 2272 2273 return fm; 2274 } 2275 2276 2277 2278 /** 2279 * reads the ID of the database 2280 * every database should have unique string ID 2281 */ 2282 public String readDatabaseID() throws PersistenceException{ 2283 2284 PreparedStatement pstmt = null; 2285 ResultSet rs = null; 2286 String result = null; 2287 2288 //1. read from DB 2289 try { 2290 String sql = " select par_value_string " + 2291 " from "+Gate.DB_OWNER+".t_parameter " + 2292 " where par_key = ? "; 2293 2294 pstmt = this.jdbcConn.prepareStatement(sql); 2295 pstmt.setString(1,DBHelper.DB_PARAMETER_GUID); 2296 pstmt.execute(); 2297 rs = pstmt.getResultSet(); 2298 2299 if (false == rs.next()) { 2300 throw new PersistenceException("Can't read database parameter ["+ 2301 DBHelper.DB_PARAMETER_GUID+"]"); 2302 } 2303 result = rs.getString(1); 2304 } 2305 catch(SQLException sqle) { 2306 throw new PersistenceException("Can't read database parameter ["+ 2307 sqle.getMessage()+"]"); 2308 } 2309 finally { 2310 DBHelper.cleanup(rs); 2311 DBHelper.cleanup(pstmt); 2312 } 2313 2314 if (DEBUG) { 2315 Out.println("reult=["+result+"]"); 2316 } 2317 2318 return result; 2319 } 2320 2321 2322 2323 /** 2324 * checks if two databases are identical 2325 * @see readDatabaseID() 2326 * NOTE: the same database may be represented by different OracleDataStore instances 2327 * but the IDs will be the same 2328 */ 2329 public boolean equals(Object obj) { 2330 2331 if (false == obj instanceof OracleDataStore) { 2332 return false; 2333 } 2334 2335 OracleDataStore db2 = (OracleDataStore)obj; 2336 2337 if (false == this.getDatabaseID().equals(db2.getDatabaseID())) { 2338 return false; 2339 } 2340 2341 return true; 2342 } 2343 2344 2345 2346 /** helper for sync() - saves a Document in the database */ 2347 private void syncDocument(Document doc) throws PersistenceException { 2348 2349 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 2350 Assert.assertTrue(doc.getLRPersistenceId() instanceof Long); 2351 2352 Long lrID = (Long)doc.getLRPersistenceId(); 2353 EventAwareLanguageResource dbDoc = (EventAwareLanguageResource)doc; 2354 //1. sync LR 2355 // only name can be changed here 2356 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_NAME)) { 2357 _syncLR(lrID,doc.getName()); 2358 } 2359 2360 //2. sync Document 2361 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_MAIN)) { 2362 _syncDocument(doc); 2363 } 2364 2365 //3. [optional] sync Content 2366 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_CONTENT)) { 2367 _syncDocumentContent(doc); 2368 } 2369 2370 //4. [optional] sync Features 2371 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) { 2372 _syncFeatures(doc); 2373 } 2374 2375 //5. [optional] delete from DB named sets that were removed from the document 2376 Collection removedSets = ((EventAwareDocument)dbDoc).getRemovedAnnotationSets(); 2377 Collection addedSets = ((EventAwareDocument)dbDoc).getAddedAnnotationSets(); 2378 if (false == removedSets.isEmpty() || false == addedSets.isEmpty()) { 2379 _syncAnnotationSets(doc,removedSets,addedSets); 2380 } 2381 2382 //6. [optional] sync Annotations 2383 _syncAnnotations(doc); 2384 } 2385 2386 2387 2388 /** 2389 * helper for sync() 2390 * NEVER call directly 2391 */ 2392 private void _syncLR(Long lrID, String newName) 2393 throws PersistenceException { 2394 2395 CallableStatement stmt = null; 2396 2397 try { 2398 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.set_lr_name(?,?) }"); 2399 stmt.setLong(1,lrID.longValue()); 2400 stmt.setString(2,newName); 2401 stmt.execute(); 2402 } 2403 catch(SQLException sqle) { 2404 2405 switch(sqle.getErrorCode()) { 2406 case DBHelper.X_ORACLE_INVALID_LR: 2407 throw new PersistenceException("can't set LR name in DB: [invalid LR ID]"); 2408 default: 2409 throw new PersistenceException( 2410 "can't set LR name in DB: ["+ sqle.getMessage()+"]"); 2411 } 2412 2413 } 2414 finally { 2415 DBHelper.cleanup(stmt); 2416 } 2417 } 2418 2419 2420 2421 /** helper for sync() - never call directly */ 2422 private void _syncDocument(Document doc) 2423 throws PersistenceException { 2424 2425 Long lrID = (Long)doc.getLRPersistenceId(); 2426 2427 CallableStatement stmt = null; 2428 2429 try { 2430 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 2431 ".persist.update_document(?,?,?,?,?) }"); 2432 stmt.setLong(1,lrID.longValue()); 2433 stmt.setString(2,doc.getSourceUrl().toString()); 2434 //do we have start offset? 2435 if (null==doc.getSourceUrlStartOffset()) { 2436 stmt.setNull(3,java.sql.Types.NUMERIC); 2437 } 2438 else { 2439 stmt.setLong(3,doc.getSourceUrlStartOffset().longValue()); 2440 } 2441 //do we have end offset? 2442 if (null==doc.getSourceUrlEndOffset()) { 2443 stmt.setNull(4,java.sql.Types.NUMERIC); 2444 } 2445 else { 2446 stmt.setLong(4,doc.getSourceUrlEndOffset().longValue()); 2447 } 2448 2449 stmt.setLong(5,true == doc.getMarkupAware().booleanValue() ? this.ORACLE_TRUE 2450 : this.ORACLE_FALSE); 2451 2452 stmt.execute(); 2453 } 2454 catch(SQLException sqle) { 2455 2456 switch(sqle.getErrorCode()) { 2457 case DBHelper.X_ORACLE_INVALID_LR : 2458 throw new PersistenceException("invalid LR supplied: no such document: ["+ 2459 sqle.getMessage()+"]"); 2460 default: 2461 throw new PersistenceException("can't change document data: ["+ 2462 sqle.getMessage()+"]"); 2463 } 2464 } 2465 finally { 2466 DBHelper.cleanup(stmt); 2467 } 2468 2469 } 2470 2471 2472 2473 /** helper for sync() - never call directly */ 2474 private void _syncDocumentContent(Document doc) 2475 throws PersistenceException { 2476 2477 PreparedStatement pstmt = null; 2478 ResultSet rs = null; 2479 Long docContID = null; 2480 2481 //1. read from DB 2482 try { 2483 String sql = " select dc_id " + 2484 " from "+Gate.DB_OWNER+".v_content " + 2485 " where lr_id = ? "; 2486 2487 pstmt = this.jdbcConn.prepareStatement(sql); 2488 pstmt.setLong(1,((Long)doc.getLRPersistenceId()).longValue()); 2489 pstmt.execute(); 2490 rs = pstmt.getResultSet(); 2491 2492 if (false == rs.next()) { 2493 throw new PersistenceException("invalid LR ID supplied"); 2494 } 2495 2496 //1, get DC_ID 2497 docContID = new Long(rs.getLong(1)); 2498 2499 //2, update LOBs 2500 updateDocumentContent(docContID,doc.getContent()); 2501 2502 } 2503 catch(SQLException sqle) { 2504 throw new PersistenceException("Cannot update document content ["+ 2505 sqle.getMessage()+"]"); 2506 } 2507 finally { 2508 DBHelper.cleanup(pstmt); 2509 } 2510 } 2511 2512 2513 2514 /** helper for sync() - never call directly */ 2515 private void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes) 2516 throws PersistenceException { 2517 2518 //0.preconditions 2519 Assert.assertNotNull(doc); 2520 Assert.assertNotNull(as); 2521 Assert.assertNotNull(changes); 2522 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 2523 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl); 2524 Assert.assertTrue(changes.size() > 0); 2525 2526 2527 PreparedStatement pstmt = null; 2528 ResultSet rs = null; 2529 CallableStatement cstmt = null; 2530 Long lrID = (Long)doc.getLRPersistenceId(); 2531 Long docID = null; 2532 Long asetID = null; 2533 2534 try { 2535 //1. get the a-set ID in the database 2536 String sql = " select as_id, " + 2537 " as_doc_id " + 2538 " from "+Gate.DB_OWNER+".v_annotation_set " + 2539 " where lr_id = ? "; 2540 //do we have aset name? 2541 String clause = null; 2542 String name = as.getName(); 2543 if (null != name) { 2544 clause = " and as_name = ? "; 2545 } 2546 else { 2547 clause = " and as_name is null "; 2548 } 2549 sql = sql + clause; 2550 2551 pstmt = this.jdbcConn.prepareStatement(sql); 2552 pstmt.setLong(1,lrID.longValue()); 2553 if (null != name) { 2554 pstmt.setString(2,name); 2555 } 2556 pstmt.execute(); 2557 rs = pstmt.getResultSet(); 2558 2559 if (rs.next()) { 2560 asetID = new Long(rs.getLong("as_id")); 2561 docID = new Long(rs.getLong("as_doc_id")); 2562 } 2563 else { 2564 throw new PersistenceException("cannot find annotation set with" + 2565 " name=["+name+"] , LRID=["+lrID+"] in database"); 2566 } 2567 2568 //3. insert the new annotations from this set 2569 2570 //3.1. prepare call 2571 cstmt = this.jdbcConn.prepareCall( 2572 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }"); 2573 2574 Long annGlobalID = null; 2575 Iterator it = changes.iterator(); 2576 2577 while (it.hasNext()) { 2578 2579 //3.2. insert annotation 2580 Annotation ann = (Annotation)it.next(); 2581 2582 Node start = (Node)ann.getStartNode(); 2583 Node end = (Node)ann.getEndNode(); 2584 String type = ann.getType(); 2585 2586 cstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs! 2587 cstmt.setLong(2,ann.getId().longValue()); 2588 cstmt.setLong(3,asetID.longValue()); 2589 cstmt.setLong(4,start.getId().longValue()); 2590 cstmt.setLong(5,start.getOffset().longValue()); 2591 cstmt.setLong(6,end.getId().longValue()); 2592 cstmt.setLong(7,end.getOffset().longValue()); 2593 cstmt.setString(8,type); 2594 cstmt.registerOutParameter(9,java.sql.Types.BIGINT); 2595 2596 cstmt.execute(); 2597 annGlobalID = new Long(cstmt.getLong(9)); 2598 2599 //3.3. set annotation features 2600 FeatureMap features = ann.getFeatures(); 2601 Assert.assertNotNull(features); 2602 createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features); 2603 } 2604 } 2605 catch(SQLException sqle) { 2606 throw new PersistenceException("can't add annotations in DB : ["+ 2607 sqle.getMessage()+"]"); 2608 } 2609 finally { 2610 DBHelper.cleanup(rs); 2611 DBHelper.cleanup(pstmt); 2612 DBHelper.cleanup(cstmt); 2613 } 2614 } 2615 2616 2617 2618 /** helper for sync() - never call directly */ 2619 private void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes) 2620 throws PersistenceException { 2621 2622 //technically this approach sux 2623 //at least it works 2624 2625 //1. delete 2626 _syncRemovedAnnotations(doc,as,changes); 2627 //2. recreate 2628 _syncAddedAnnotations(doc,as,changes); 2629 } 2630 2631 2632 2633 /** helper for sync() - never call directly */ 2634 private void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes) 2635 throws PersistenceException { 2636 //0.preconditions 2637 Assert.assertNotNull(doc); 2638 Assert.assertNotNull(as); 2639 Assert.assertNotNull(changes); 2640 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 2641 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl); 2642 Assert.assertTrue(changes.size() > 0); 2643 2644 2645 PreparedStatement pstmt = null; 2646 ResultSet rs = null; 2647 CallableStatement cstmt = null; 2648 Long lrID = (Long)doc.getLRPersistenceId(); 2649 Long docID = null; 2650 Long asetID = null; 2651 2652 try { 2653 //1. get the a-set ID in the database 2654 String sql = " select as_id, " + 2655 " as_doc_id " + 2656 " from "+Gate.DB_OWNER+".v_annotation_set " + 2657 " where lr_id = ? "; 2658 //do we have aset name? 2659 String clause = null; 2660 String name = as.getName(); 2661 if (null != name) { 2662 clause = " and as_name = ? "; 2663 } 2664 else { 2665 clause = " and as_name is null "; 2666 } 2667 sql = sql + clause; 2668 2669 pstmt = this.jdbcConn.prepareStatement(sql); 2670 pstmt.setLong(1,lrID.longValue()); 2671 if (null != name) { 2672 pstmt.setString(2,name); 2673 } 2674 pstmt.execute(); 2675 rs = pstmt.getResultSet(); 2676 2677 if (rs.next()) { 2678 asetID = new Long(rs.getLong("as_id")); 2679 docID = new Long(rs.getLong("as_doc_id")); 2680 } 2681 else { 2682 throw new PersistenceException("cannot find annotation set with" + 2683 " name=["+name+"] , LRID=["+lrID+"] in database"); 2684 } 2685 2686 //3. delete the removed annotations from this set 2687 2688 //3.1. prepare call 2689 cstmt = this.jdbcConn.prepareCall( 2690 "{ call "+Gate.DB_OWNER+".persist.delete_annotation(?,?) }"); 2691 2692 2693 Iterator it = changes.iterator(); 2694 2695 while (it.hasNext()) { 2696 2697 //3.2. insert annotation 2698 Annotation ann = (Annotation)it.next(); 2699 2700 cstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs! 2701 cstmt.setLong(2,ann.getId().longValue()); 2702 cstmt.execute(); 2703 } 2704 } 2705 catch(SQLException sqle) { 2706 throw new PersistenceException("can't delete annotations in DB : ["+ 2707 sqle.getMessage()+"]"); 2708 } 2709 finally { 2710 DBHelper.cleanup(rs); 2711 DBHelper.cleanup(pstmt); 2712 DBHelper.cleanup(cstmt); 2713 } 2714 } 2715 2716 2717 2718 /** helper for sync() - never call directly */ 2719 private void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets) 2720 throws PersistenceException { 2721 2722 //0. preconditions 2723 Assert.assertNotNull(doc); 2724 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 2725 Assert.assertNotNull(doc.getLRPersistenceId()); 2726 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(), 2727 this.getDatabaseID()); 2728 Assert.assertNotNull(removedSets); 2729 Assert.assertNotNull(addedSets); 2730 2731 Long lrID = (Long)doc.getLRPersistenceId(); 2732 2733 //1. delete from DB removed a-sets 2734 CallableStatement cstmt = null; 2735 2736 try { 2737 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 2738 ".persist.delete_annotation_set(?,?) }"); 2739 2740 Iterator it = removedSets.iterator(); 2741 while (it.hasNext()) { 2742 String setName = (String)it.next(); 2743 2744 cstmt.setLong(1,lrID.longValue()); 2745 cstmt.setString(2,setName); 2746 cstmt.execute(); 2747 } 2748 } 2749 catch(SQLException sqle) { 2750 throw new PersistenceException("can't remove annotation set from DB: ["+ sqle.getMessage()+"]"); 2751 } 2752 finally { 2753 DBHelper.cleanup(cstmt); 2754 } 2755 2756 //2. create in DB new a-sets 2757 Iterator it = addedSets.iterator(); 2758 while (it.hasNext()) { 2759 String setName = (String)it.next(); 2760 AnnotationSet aset = doc.getAnnotations(setName); 2761 2762 Assert.assertNotNull(aset); 2763 Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl); 2764 2765 createAnnotationSet(lrID,aset); 2766 } 2767 } 2768 2769 2770 2771 /** helper for sync() - never call directly */ 2772 private void _syncAnnotations(Document doc) 2773 throws PersistenceException { 2774 2775 //0. preconditions 2776 Assert.assertNotNull(doc); 2777 Assert.assertTrue(doc instanceof DatabaseDocumentImpl); 2778 Assert.assertNotNull(doc.getLRPersistenceId()); 2779 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(), 2780 this.getDatabaseID()); 2781 2782 2783 //1. get the sets read from the DB for this document 2784 //chnaged annotations can occur only in such sets 2785 Collection loadedSets = ((EventAwareDocument)doc).getLoadedAnnotationSets(); 2786 2787 Iterator it = loadedSets.iterator(); 2788 while (it.hasNext()) { 2789 2790 AnnotationSet as = (AnnotationSet)it.next(); 2791 EventAwareAnnotationSet eas = (EventAwareAnnotationSet)as; 2792 Assert.assertNotNull(as); 2793 2794 Collection anns = null; 2795 anns = eas.getAddedAnnotations(); 2796 Assert.assertNotNull(anns); 2797 if (anns.size()>0) { 2798 _syncAddedAnnotations(doc,as,anns); 2799 } 2800 2801 anns = eas.getRemovedAnnotations(); 2802 Assert.assertNotNull(anns); 2803 if (anns.size()>0) { 2804 _syncRemovedAnnotations(doc,as,anns); 2805 } 2806 2807 anns = eas.getChangedAnnotations(); 2808 Assert.assertNotNull(anns); 2809 if (anns.size()>0) { 2810 _syncChangedAnnotations(doc,as,anns); 2811 } 2812 } 2813 } 2814 2815 2816 2817 /** helper for sync() - never call directly */ 2818 private void _syncFeatures(LanguageResource lr) 2819 throws PersistenceException { 2820 2821 //0. preconditions 2822 Assert.assertNotNull(lr); 2823 Assert.assertNotNull(lr.getLRPersistenceId()); 2824 Assert.assertEquals(((DatabaseDataStore)lr.getDataStore()).getDatabaseID(), 2825 this.getDatabaseID()); 2826 Assert.assertTrue(lr instanceof Document || lr instanceof Corpus); 2827 //we have to be in the context of transaction 2828 2829 //1, get ID in the DB 2830 Long lrID = (Long)lr.getLRPersistenceId(); 2831 int entityType; 2832 2833 //2. delete features 2834 CallableStatement stmt = null; 2835 try { 2836 Assert.assertTrue(false == this.jdbcConn.getAutoCommit()); 2837 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+ 2838 ".persist.delete_features(?,?) }"); 2839 stmt.setLong(1,lrID.longValue()); 2840 2841 if (lr instanceof Document) { 2842 entityType = DBHelper.FEATURE_OWNER_DOCUMENT; 2843 } 2844 else if (lr instanceof Corpus) { 2845 entityType = DBHelper.FEATURE_OWNER_CORPUS; 2846 } 2847 else { 2848 throw new IllegalArgumentException(); 2849 } 2850 2851 stmt.setInt(2,entityType); 2852 stmt.execute(); 2853 } 2854 catch(SQLException sqle) { 2855 throw new PersistenceException("can't delete features in DB: ["+ sqle.getMessage()+"]"); 2856 } 2857 finally { 2858 DBHelper.cleanup(stmt); 2859 } 2860 2861 //3. recreate them 2862 createFeatures(lrID,entityType, lr.getFeatures()); 2863 2864 } 2865 2866 2867 2868 /** helper for sync() - saves a Corpus in the database */ 2869 private void syncCorpus(Corpus corp) throws PersistenceException { 2870 2871 //0. preconditions 2872 Assert.assertNotNull(corp); 2873 Assert.assertTrue(corp instanceof DatabaseCorpusImpl); 2874 Assert.assertEquals(this,corp.getDataStore()); 2875 Assert.assertNotNull(corp.getLRPersistenceId()); 2876 2877 EventAwareLanguageResource dbCorpus = (EventAwareLanguageResource)corp; 2878 2879 //1. sync the corpus name? 2880 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_NAME)) { 2881 _syncLR((Long)corp.getLRPersistenceId(),corp.getName()); 2882 } 2883 2884 //2. sync the corpus features? 2885 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) { 2886 _syncFeatures(corp); 2887 } 2888 2889 //3. get all documents 2890 Iterator it = corp.iterator(); 2891 while (it.hasNext()) { 2892 Document dbDoc = (Document)it.next(); 2893//System.out.println("found doc ["+dbDoc.getName()+"]"); 2894 //adopt/sync? 2895 if (null == dbDoc.getLRPersistenceId()) { 2896 //doc was never adopted, adopt it 2897 2898 //3.1 remove the transient doc from the corpus 2899 it.remove(); 2900 2901 //3.2 get the security info for the corpus 2902 SecurityInfo si = getSecurityInfo(corp); 2903 2904 2905 Document adoptedDoc = null; 2906 try { 2907 //3.3. adopt the doc with the sec info 2908//System.out.println("adopting ["+dbDoc.getName()+"] ..."); 2909 //don't open a new transaction, since sync() already has opended one 2910 adoptedDoc = (Document)_adopt(dbDoc,si,false); 2911 2912 //3.4. add doc to corpus in DB 2913 addDocumentToCorpus((Long)adoptedDoc.getLRPersistenceId(), 2914 (Long)corp.getLRPersistenceId()); 2915 } 2916 catch(SecurityException se) { 2917 throw new PersistenceException(se); 2918 } 2919 2920 //3.5 add back to corpus the new DatabaseDocument 2921 corp.add(adoptedDoc); 2922 } 2923 else { 2924 //don't open a new transaction, the sync() called for corpus has already 2925 //opened one 2926 try { 2927 _sync(dbDoc,false); 2928 2929 //if the document is form the same DS but did not belong to the corpus add it now 2930 //NOTE: if the document already belongs to the corpus then nothing will be changed 2931 //in the DB 2932 addDocumentToCorpus((Long)dbDoc.getLRPersistenceId(), 2933 (Long)corp.getLRPersistenceId()); 2934 } 2935 catch(SecurityException se) { 2936 gate.util.Err.prln("document cannot be synced: ["+se.getMessage()+"]"); 2937 } 2938 } 2939 } 2940 } 2941 2942 2943 2944 /** 2945 * Try to acquire exlusive lock on a resource from the persistent store. 2946 * Always call unlockLR() when the lock is no longer needed 2947 */ 2948 public boolean lockLr(LanguageResource lr) 2949 throws PersistenceException,SecurityException { 2950 2951 //0. preconditions 2952 Assert.assertNotNull(lr); 2953 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 2954 lr instanceof DatabaseCorpusImpl); 2955 Assert.assertNotNull(lr.getLRPersistenceId()); 2956 Assert.assertEquals(lr.getDataStore(),this); 2957 2958 //1. delegate 2959 return _lockLr((Long)lr.getLRPersistenceId()); 2960 } 2961 2962 2963 2964 /** 2965 * helper for lockLR() 2966 * never call directly 2967 */ 2968 private boolean _lockLr(Long lrID) 2969 throws PersistenceException,SecurityException { 2970 2971 //0. preconditions 2972 Assert.assertNotNull(lrID); 2973 2974 //1. check session 2975 if (null == this.session) { 2976 throw new SecurityException("session not set"); 2977 } 2978 2979 if (false == this.ac.isValidSession(this.session)) { 2980 throw new SecurityException("invalid session supplied"); 2981 } 2982 2983 //2. check permissions 2984 if (false == canWriteLR(lrID)) { 2985 throw new SecurityException("no write access granted to the user"); 2986 } 2987 2988 //3. try to lock 2989 CallableStatement cstmt = null; 2990 boolean lockSucceeded = false; 2991 2992 try { 2993 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.lock_lr(?,?,?,?) }"); 2994 cstmt.setLong(1,lrID.longValue()); 2995 cstmt.setLong(2,this.session.getUser().getID().longValue()); 2996 cstmt.setLong(3,this.session.getGroup().getID().longValue()); 2997 cstmt.registerOutParameter(4,java.sql.Types.NUMERIC); 2998 cstmt.execute(); 2999 3000 lockSucceeded = cstmt.getLong(4) == this.ORACLE_TRUE 3001 ? true 3002 : false; 3003 } 3004 catch(SQLException sqle) { 3005 3006 switch(sqle.getErrorCode()) { 3007 case DBHelper.X_ORACLE_INVALID_LR: 3008 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]"); 3009 default: 3010 throw new PersistenceException( 3011 "can't lock LR in DB : ["+ sqle.getMessage()+"]"); 3012 } 3013 } 3014 finally { 3015 DBHelper.cleanup(cstmt); 3016 } 3017 3018 return lockSucceeded; 3019 } 3020 3021 3022 3023 /** 3024 * Releases the exlusive lock on a resource from the persistent store. 3025 */ 3026 public void unlockLr(LanguageResource lr) 3027 throws PersistenceException,SecurityException { 3028 3029 //0. preconditions 3030 Assert.assertNotNull(lr); 3031 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 3032 lr instanceof DatabaseCorpusImpl); 3033 Assert.assertNotNull(lr.getLRPersistenceId()); 3034 Assert.assertEquals(lr.getDataStore(),this); 3035 3036 //1. check session 3037 if (null == this.session) { 3038 throw new SecurityException("session not set"); 3039 } 3040 3041 if (false == this.ac.isValidSession(this.session)) { 3042 throw new SecurityException("invalid session supplied"); 3043 } 3044 3045 //2. check permissions 3046 if (false == canWriteLR(lr.getLRPersistenceId())) { 3047 throw new SecurityException("no write access granted to the user"); 3048 } 3049 3050 //3. try to unlock 3051 CallableStatement cstmt = null; 3052 boolean lockSucceeded = false; 3053 3054 try { 3055 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.unlock_lr(?,?) }"); 3056 cstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue()); 3057 cstmt.setLong(2,this.session.getUser().getID().longValue()); 3058 cstmt.execute(); 3059 } 3060 catch(SQLException sqle) { 3061 3062 switch(sqle.getErrorCode()) { 3063 case DBHelper.X_ORACLE_INVALID_LR: 3064 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]"); 3065 default: 3066 throw new PersistenceException( 3067 "can't unlock LR in DB : ["+ sqle.getMessage()+"]"); 3068 } 3069 } 3070 finally { 3071 DBHelper.cleanup(cstmt); 3072 } 3073 } 3074 3075 3076 3077 /** 3078 * Releases the exlusive lock on a resource from the persistent store. 3079 */ 3080 private User getLockingUser(LanguageResource lr) 3081 throws PersistenceException,SecurityException { 3082 3083 //0. preconditions 3084 Assert.assertNotNull(lr); 3085 Assert.assertTrue(lr instanceof DatabaseDocumentImpl || 3086 lr instanceof DatabaseCorpusImpl); 3087 Assert.assertNotNull(lr.getLRPersistenceId()); 3088 Assert.assertEquals(lr.getDataStore(),this); 3089 3090 //delegate 3091 return getLockingUser((Long)lr.getLRPersistenceId()); 3092 } 3093 3094 3095 3096 /** 3097 * Releases the exlusive lock on a resource from the persistent store. 3098 */ 3099 private User getLockingUser(Long lrID) 3100 throws PersistenceException,SecurityException { 3101 3102 //1. check session 3103 if (null == this.session) { 3104 throw new SecurityException("session not set"); 3105 } 3106 3107 if (false == this.ac.isValidSession(this.session)) { 3108 throw new SecurityException("invalid session supplied"); 3109 } 3110 3111 //3. read from DB 3112 PreparedStatement pstmt = null; 3113 Long userID = null; 3114 ResultSet rs = null; 3115 3116 try { 3117 String sql = " select nvl(lr_locking_user_id,0) as user_id" + 3118 " from "+Gate.DB_OWNER+".t_lang_resource " + 3119 " where lr_id = ?"; 3120 3121 pstmt = this.jdbcConn.prepareStatement(sql); 3122 pstmt.setLong(1,lrID.longValue()); 3123 pstmt.execute(); 3124 rs = pstmt.getResultSet(); 3125 3126 if (false == rs.next()) { 3127 throw new PersistenceException("LR not found in DB"); 3128 } 3129 3130 long result = rs.getLong("user_id"); 3131 3132 return result == 0 ? null 3133 : this.ac.findUser(new Long(result)); 3134 } 3135 catch(SQLException sqle) { 3136 throw new PersistenceException("can't get locking user from DB : ["+ sqle.getMessage()+"]"); 3137 } 3138 finally { 3139 DBHelper.cleanup(rs); 3140 DBHelper.cleanup(pstmt); 3141 } 3142 } 3143 3144 3145 3146 /** 3147 * adds document to corpus in the database 3148 * if the document is already part of the corpus nothing 3149 * changes 3150 */ 3151 private void addDocumentToCorpus(Long docID,Long corpID) 3152 throws PersistenceException,SecurityException { 3153 3154 //0. preconditions 3155 Assert.assertNotNull(docID); 3156 Assert.assertNotNull(corpID); 3157 3158 //1. check session 3159 if (null == this.session) { 3160 throw new SecurityException("session not set"); 3161 } 3162 3163 if (false == this.ac.isValidSession(this.session)) { 3164 throw new SecurityException("invalid session supplied"); 3165 } 3166 3167 //2. check permissions 3168 if (false == canWriteLR(corpID)) { 3169 throw new SecurityException("no write access granted to the user"); 3170 } 3171 3172 if (false == canWriteLR(docID)) { 3173 throw new SecurityException("no write access granted to the user"); 3174 } 3175 3176 //3. database 3177 CallableStatement cstmt = null; 3178 3179 try { 3180 cstmt = this.jdbcConn.prepareCall("{ call "+ 3181 Gate.DB_OWNER+".persist.add_document_to_corpus(?,?) }"); 3182 cstmt.setLong(1,docID.longValue()); 3183 cstmt.setLong(2,corpID.longValue()); 3184 cstmt.execute(); 3185 } 3186 catch(SQLException sqle) { 3187 3188 switch(sqle.getErrorCode()) { 3189 case DBHelper.X_ORACLE_INVALID_LR: 3190 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]"); 3191 default: 3192 throw new PersistenceException( 3193 "can't add document to corpus : ["+ sqle.getMessage()+"]"); 3194 } 3195 } 3196 finally { 3197 DBHelper.cleanup(cstmt); 3198 } 3199 } 3200 3201 3202 3203 /** 3204 * unloads a LR from the GUI 3205 */ 3206 private void unloadLR(Long lrID) 3207 throws GateException{ 3208 3209 //0. preconfitions 3210 Assert.assertNotNull(lrID); 3211 3212 //1. get all LRs in the system 3213 List resources = Gate.getCreoleRegister().getAllInstances("gate.LanguageResource"); 3214 3215 Iterator it = resources.iterator(); 3216 while (it.hasNext()) { 3217 LanguageResource lr = (LanguageResource)it.next(); 3218 if (lrID.equals(lr.getLRPersistenceId()) && 3219 this.equals(lr.getDataStore())) { 3220 //found it - unload it 3221 Factory.deleteResource(lr); 3222 break; 3223 } 3224 } 3225 } 3226 3227} 3228
|
OracleDataStore |
|