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