| OracleDataStore.java |
1 /*
2 * OracleDataStore.java
3 *
4 * Copyright (c) 1998-2004, 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.179 2004/07/21 17:10:08 akshay Exp $
14 */
15
16 package gate.persist;
17
18 import java.io.*;
19 import java.net.URL;
20 import java.sql.*;
21 import java.util.*;
22
23 import junit.framework.Assert;
24 import oracle.jdbc.driver.OracleCallableStatement;
25 import oracle.sql.*;
26
27 import gate.*;
28 import gate.corpora.DatabaseCorpusImpl;
29 import gate.corpora.DatabaseDocumentImpl;
30 import gate.security.SecurityException;
31 import gate.security.SecurityInfo;
32 import gate.util.*;
33
34 public class OracleDataStore extends JDBCDataStore {
35
36 /** Name of this resource */
37 private static final String DS_COMMENT = "GATE Oracle datastore";
38
39 /** the icon for this resource */
40 private static final String DS_ICON_NAME = "ora_ds.gif";
41
42 /** Debug flag */
43 private static final boolean DEBUG = false;
44
45 /** "true" value for Oracle (supports no boolean type) */
46 private static final int ORACLE_TRUE = 1;
47 /** "false" value for Oracle (supports no boolean type) */
48 private static final int ORACLE_FALSE = 0;
49
50 /** size of the Oracle varrays used for bulc inserts */
51 private static final int VARRAY_SIZE = 10;
52
53 /** the size in bytes if varchar2 column in Oracle
54 * when a String is stored in Oracle it may be too long
55 * for a varchar2 value, and then CLOB will be used
56 * Note that the limit is in bytes, not in characters, so
57 * in the worst case this will limit the string to 4000/3 characters
58 * */
59 private static final int ORACLE_VARCHAR_LIMIT_BYTES = 4000;
60
61 /** maximum number of bytes that represent a char in UTF8 database */
62 private static final int UTF_BYTES_PER_CHAR_MAX = 3;
63
64 /** maximum number of characters per string stored as varchar2
65 * if longer then stored as CLOB
66 * */
67 private static final int ORACLE_VARCHAR_MAX_SYMBOLS =
68 ORACLE_VARCHAR_LIMIT_BYTES/UTF_BYTES_PER_CHAR_MAX;
69
70 /** read buffer size (for reading CLOBs) */
71 private static final int INTERNAL_BUFFER_SIZE = 16*1024;
72
73 /** default constructor - just call the super constructor
74 * (may change in the future)
75 * */
76 public OracleDataStore() {
77
78 super();
79
80 this.datastoreComment = DS_COMMENT;
81 this.iconName = DS_ICON_NAME;
82 }
83
84
85
86 /** Set the URL for the underlying storage mechanism. */
87 public void setStorageUrl(String storageUrl) throws PersistenceException {
88
89 super.setStorageUrl(storageUrl);
90
91 }
92
93
94
95 /** Get the URL for the underlying storage mechanism. */
96 public String getStorageUrl() {
97
98 return super.getStorageUrl();
99 }
100
101
102
103 /**
104 * Create a new data store. <B>NOTE:</B> for some data stores
105 * creation is an system administrator task; in such cases this
106 * method will throw an UnsupportedOperationException.
107 */
108 public void create()
109 throws PersistenceException, UnsupportedOperationException {
110
111 super.create();
112 }
113
114
115
116 /** Open a connection to the data store. */
117 public void open() throws PersistenceException {
118
119 super.open();
120
121 /*try {
122 //set statement caching for Oracle
123 ((OracleConnection)this.jdbcConn).setStmtCacheSize(50);
124 }
125 catch(SQLException sqle) {
126 throw new PersistenceException(sqle);
127 }*/
128 }
129
130
131
132 /** Close the data store. */
133 public void close() throws PersistenceException {
134
135 super.close();
136 }
137
138
139
140 /**
141 * Delete a resource from the data store.
142 * @param lrId a data-store specific unique identifier for the resource
143 * @param lrClassName class name of the type of resource
144 */
145 /*
146 public void delete(String lrClassName, Object lrId)
147 throws PersistenceException,SecurityException {
148 //0. preconditions
149 if (false == lrId instanceof Long) {
150 throw new IllegalArgumentException();
151 }
152
153 if (!lrClassName.equals(DBHelper.DOCUMENT_CLASS) &&
154 !lrClassName.equals(DBHelper.CORPUS_CLASS)) {
155 throw new IllegalArgumentException("Only Corpus and Document classes are supported" +
156 " by Database data store");
157 }
158
159 //1. check session
160 if (null == this.session) {
161 throw new SecurityException("session not set");
162 }
163
164 if (false == this.ac.isValidSession(this.session)) {
165 throw new SecurityException("invalid session supplied");
166 }
167
168 //2. check permissions
169 if (false == canWriteLR(lrId)) {
170 throw new SecurityException("insufficient privileges");
171 }
172
173 //3. try to lock document, so that we'll be sure no one is editing it
174 //NOTE: use the private method
175 User lockingUser = this.getLockingUser((Long)lrId);
176 User currUser = this.session.getUser();
177
178 if (null != lockingUser && false == lockingUser.equals(currUser)) {
179 //oops, someone is editing now
180 throw new PersistenceException("LR locked by another user");
181 }
182
183 boolean transFailed = false;
184 try {
185 //4. autocommit should be FALSE because of LOBs
186 beginTrans();
187
188 //5. perform changes, if anything goes wrong, rollback
189 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
190 deleteDocument((Long)lrId);
191 }
192 else {
193 deleteCorpus((Long)lrId);
194 }
195
196 //6. done, commit
197 commitTrans();
198 }
199 catch(PersistenceException pe) {
200 transFailed = true;
201 throw(pe);
202 }
203 finally {
204 //problems?
205 if (transFailed) {
206 rollbackTrans();
207 }
208 }
209
210 //7, unlock
211 //do nothing - the resource does not exist anymore
212
213 //8. delete from the list of dependent resources
214 boolean resourceFound = false;
215 Iterator it = this.dependentResources.iterator();
216 while (it.hasNext()) {
217 LanguageResource lr = (LanguageResource)it.next();
218 if (lr.getLRPersistenceId().equals(lrId)) {
219 resourceFound = true;
220 it.remove();
221 break;
222 }
223 }
224
225 //Assert.assertTrue(resourceFound);
226
227 //9. let the world know about it
228 fireResourceDeleted(
229 new DatastoreEvent(this, DatastoreEvent.RESOURCE_DELETED, null, lrId));
230
231 //10. unload the resource form the GUI
232 try {
233 unloadLR((Long)lrId);
234 }
235 catch(GateException ge) {
236 Err.prln("can't unload resource from GUI...");
237 }
238 }
239 */
240
241
242 /**
243 * helper method for delete()
244 * never call it directly beause proper events will not be fired
245 */
246 protected void deleteDocument(Long lrId)
247 throws PersistenceException {
248
249 //0. preconditions
250 Assert.assertNotNull(lrId);
251
252 CallableStatement stmt = null;
253
254 //1. delete from DB
255 try {
256 stmt = this.jdbcConn.prepareCall(
257 "{ call "+Gate.DB_OWNER+".persist.delete_document(?) }");
258 stmt.setLong(1,lrId.longValue());
259 stmt.execute();
260 }
261 catch(SQLException sqle) {
262 throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]");
263 }
264 finally {
265 DBHelper.cleanup(stmt);
266 }
267 }
268
269
270
271 /**
272 * helper method for delete()
273 * never call it directly beause proper events will not be fired
274 */
275 protected void deleteCorpus(Long lrId)
276 throws PersistenceException {
277
278 Long ID = (Long)lrId;
279
280 CallableStatement stmt = null;
281
282 try {
283 stmt = this.jdbcConn.prepareCall(
284 "{ call "+Gate.DB_OWNER+".persist.delete_corpus(?) }");
285 stmt.setLong(1,ID.longValue());
286 stmt.execute();
287 }
288 catch(SQLException sqle) {
289 throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]");
290 }
291 finally {
292 DBHelper.cleanup(stmt);
293 }
294 }
295
296
297
298
299
300 /**
301 * Set method for the autosaving behaviour of the data store.
302 * <B>NOTE:</B> many types of datastore have no auto-save function,
303 * in which case this will throw an UnsupportedOperationException.
304 */
305 public void setAutoSaving(boolean autoSaving)
306 throws UnsupportedOperationException,PersistenceException {
307
308 super.setAutoSaving(autoSaving);
309 }
310
311
312
313 /** Get the autosaving behaviour of the LR. */
314 public boolean isAutoSaving() {
315 throw new MethodNotImplementedException();
316 }
317
318
319 /**
320 * helper for adopt()
321 * never call directly
322 */
323 protected Long createLR(String lrType,
324 String lrName,
325 SecurityInfo si,
326 Long lrParentID)
327 throws PersistenceException,SecurityException {
328
329 //0. preconditions
330 Assert.assertNotNull(lrName);
331
332 //1. check the session
333 // if (this.ac.isValidSession(s) == false) {
334 // throw new SecurityException("invalid session provided");
335 // }
336
337 //2. create a record in DB
338 CallableStatement stmt = null;
339
340 try {
341 stmt = this.jdbcConn.prepareCall(
342 "{ call "+Gate.DB_OWNER+".persist.create_lr(?,?,?,?,?,?,?) }");
343 stmt.setLong(1,si.getUser().getID().longValue());
344 stmt.setLong(2,si.getGroup().getID().longValue());
345 stmt.setString(3,lrType);
346 stmt.setString(4,lrName);
347 stmt.setInt(5,si.getAccessMode());
348 if (null == lrParentID) {
349 stmt.setNull(6,java.sql.Types.BIGINT);
350 }
351 else {
352 stmt.setLong(6,lrParentID.longValue());
353 }
354 //Oracle numbers are BIGNINT
355 stmt.registerOutParameter(7,java.sql.Types.BIGINT);
356 stmt.execute();
357
358 Long result = new Long(stmt.getLong(7));
359 return result;
360 }
361 catch(SQLException sqle) {
362
363 switch(sqle.getErrorCode()) {
364 case DBHelper.X_ORACLE_INVALID_LR_TYPE:
365 throw new PersistenceException("can't create LR [step 3] in DB, invalid LR Type");
366 default:
367 throw new PersistenceException(
368 "can't create LR [step 3] in DB : ["+ sqle.getMessage()+"]");
369 }
370 }
371 finally {
372 DBHelper.cleanup(stmt);
373 }
374 }
375
376
377
378 /**
379 * updates the content of the document if it is binary or a long string
380 * (that does not fit into VARCHAR2)
381 */
382 // private void updateDocumentContent(Long docContentID,DocumentContent content)
383 protected void updateDocumentContent(Long docID,DocumentContent content)
384 throws PersistenceException {
385
386 //1. get LOB locators from DB
387 PreparedStatement pstmt = null;
388 ResultSet rs = null;
389 CallableStatement cstmt = null;
390 try {
391 String sql = "select dc.dc_id, "+
392 " dc.dc_content_type, " +
393 " dc.dc_character_content, " +
394 " dc.dc_binary_content " +
395 "from "+gate.Gate.DB_OWNER+".t_doc_content dc , " +
396 gate.Gate.DB_OWNER+".t_document doc " +
397 "where dc.dc_id = doc.doc_content_id " +
398 //--was " and doc.doc_content_id = ? " +
399 " and doc.doc_id = ? " +
400 "for update ";
401 pstmt = this.jdbcConn.prepareStatement(sql);
402 pstmt.setLong(1,docID.longValue());
403 rs = pstmt.executeQuery();
404
405 //rs = pstmt.getResultSet();
406
407 rs.next();
408 //important: read the objects in the order they appear in
409 //the ResultSet, otherwise data may be lost
410 Long contentID = new Long(rs.getLong("dc_id"));
411 long contentType = rs.getLong("DC_CONTENT_TYPE");
412 Clob clob = (Clob)rs.getClob("dc_character_content");
413 Blob blob = (Blob)rs.getBlob("dc_binary_content");
414
415 Assert.assertTrue(contentType == DBHelper.CHARACTER_CONTENT ||
416 contentType == DBHelper.BINARY_CONTENT ||
417 contentType == DBHelper.EMPTY_CONTENT);
418
419
420 //2. write data using the LOB locators
421 //NOTE: so far only character content is supported
422 writeCLOB(content.toString(),clob);
423 long newContentType = DBHelper.CHARACTER_CONTENT;
424
425 //3. update content type
426 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.change_content_type(?,?) }");
427 cstmt.setLong(1,contentID.longValue());
428 cstmt.setLong(2,newContentType);
429 cstmt.execute();
430 }
431 catch(IOException ioe) {
432 throw new PersistenceException("can't update document content in DB : ["+
433 ioe.getMessage()+"]");
434 }
435 catch(SQLException sqle) {
436 throw new PersistenceException("can't update document content in DB : ["+
437 sqle.getMessage()+"]");
438 }
439 finally {
440 DBHelper.cleanup(rs);
441 DBHelper.cleanup(pstmt);
442 DBHelper.cleanup(cstmt);
443 }
444
445 }
446
447
448
449 /**
450 * helper for adopt
451 * creates a LR of type Document
452 */
453 /* protected Document createDocument(Document doc,SecurityInfo secInfo)
454 throws PersistenceException,SecurityException {
455
456 //delegate, set to Null
457 return createDocument(doc,null,secInfo);
458 }
459 */
460
461 /**
462 * helper for adopt
463 * never call directly
464 */
465 protected Long createDoc(Long _lrID,
466 URL _docURL,
467 String _docEncoding,
468 Long _docStartOffset,
469 Long _docEndOffset,
470 Boolean _docIsMarkupAware,
471 Long _corpusID)
472 throws PersistenceException {
473
474 CallableStatement cstmt = null;
475 Long docID = null;
476
477 try {
478 cstmt = this.jdbcConn.prepareCall(
479 "{ call "+Gate.DB_OWNER+".persist.create_document(?,?,?,?,?,?,?,?) }");
480 cstmt.setLong(1,_lrID.longValue());
481 if (_docURL == null) {
482 cstmt.setNull(2,java.sql.Types.VARCHAR);
483 }else{
484 cstmt.setString(2,_docURL.toString());
485 }
486 //do we have doc encoding?
487 if (null == _docEncoding) {
488 cstmt.setNull(3,java.sql.Types.VARCHAR);
489 }
490 else {
491 cstmt.setString(3,_docEncoding);
492 }
493 //do we have start offset?
494 if (null==_docStartOffset) {
495 cstmt.setNull(4,java.sql.Types.NUMERIC);
496 }
497 else {
498 cstmt.setLong(4,_docStartOffset.longValue());
499 }
500 //do we have end offset?
501 if (null==_docEndOffset) {
502 cstmt.setNull(5,java.sql.Types.NUMERIC);
503 }
504 else {
505 cstmt.setLong(5,_docEndOffset.longValue());
506 }
507
508 cstmt.setBoolean(6,_docIsMarkupAware.booleanValue());
509
510 //is the document part of a corpus?
511 if (null == _corpusID) {
512 cstmt.setNull(7,java.sql.Types.BIGINT);
513 }
514 else {
515 cstmt.setLong(7,_corpusID.longValue());
516 }
517
518 //results
519 cstmt.registerOutParameter(8,java.sql.Types.BIGINT);
520
521 cstmt.execute();
522 docID = new Long(cstmt.getLong(8));
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 ? OracleDataStore.ORACLE_TRUE : OracleDataStore.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 ? OracleDataStore.ORACLE_TRUE : OracleDataStore.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 < OracleDataStore.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 //do we have URL or create from string
2222 if (null==doc.getSourceUrl()) {
2223 stmt.setNull(2,java.sql.Types.VARCHAR);
2224 }
2225 else {
2226 stmt.setString(2,doc.getSourceUrl().toString());
2227 }
2228 //do we have start offset?
2229 if (null==doc.getSourceUrlStartOffset()) {
2230 stmt.setNull(3,java.sql.Types.NUMERIC);
2231 }
2232 else {
2233 stmt.setLong(3,doc.getSourceUrlStartOffset().longValue());
2234 }
2235 //do we have end offset?
2236 if (null==doc.getSourceUrlEndOffset()) {
2237 stmt.setNull(4,java.sql.Types.NUMERIC);
2238 }
2239 else {
2240 stmt.setLong(4,doc.getSourceUrlEndOffset().longValue());
2241 }
2242
2243 stmt.setLong(5,true == doc.getMarkupAware().booleanValue() ? OracleDataStore.ORACLE_TRUE
2244 : OracleDataStore.ORACLE_FALSE);
2245
2246 stmt.execute();
2247 }
2248 catch(SQLException sqle) {
2249
2250 switch(sqle.getErrorCode()) {
2251 case DBHelper.X_ORACLE_INVALID_LR :
2252 throw new PersistenceException("invalid LR supplied: no such document: ["+
2253 sqle.getMessage()+"]");
2254 default:
2255 throw new PersistenceException("can't change document data: ["+
2256 sqle.getMessage()+"]");
2257 }
2258 }
2259 finally {
2260 DBHelper.cleanup(stmt);
2261 }
2262
2263 }
2264
2265
2266
2267 /** helper for sync() - never call directly */
2268 protected void _syncDocumentContent(Document doc)
2269 throws PersistenceException {
2270/*
2271 PreparedStatement pstmt = null;
2272 ResultSet rs = null;
2273 Long docContID = null;
2274
2275 //1. read from DB
2276 try {
2277
2278 String sql = " select dc_id " +
2279 " from "+Gate.DB_OWNER+".v_content " +
2280 " where lr_id = ? ";
2281
2282 pstmt = this.jdbcConn.prepareStatement(sql);
2283 pstmt.setLong(1,((Long)doc.getLRPersistenceId()).longValue());
2284 pstmt.execute();
2285 rs = pstmt.getResultSet();
2286
2287 if (false == rs.next()) {
2288 throw new PersistenceException("invalid LR ID supplied");
2289 }
2290
2291 //1, get DC_ID
2292 docContID = new Long(rs.getLong(1));
2293*/
2294 //2, update LOBs
2295 //was: updateDocumentContent(docContID,doc.getContent());
2296 Long docID = (Long)doc.getLRPersistenceId();
2297 updateDocumentContent(docID,doc.getContent());
2298
2299/*
2300 }
2301 catch(SQLException sqle) {
2302 throw new PersistenceException("Cannot update document content ["+
2303 sqle.getMessage()+"]");
2304 }
2305 finally {
2306 DBHelper.cleanup(rs);
2307 DBHelper.cleanup(pstmt);
2308 }
2309 */
2310
2311 }
2312
2313
2314
2315 /** helper for sync() - never call directly */
2316/* protected void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes)
2317 throws PersistenceException {
2318
2319 //0.preconditions
2320 Assert.assertNotNull(doc);
2321 Assert.assertNotNull(as);
2322 Assert.assertNotNull(changes);
2323 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2324 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
2325 Assert.assertTrue(changes.size() > 0);
2326
2327
2328 PreparedStatement pstmt = null;
2329 ResultSet rs = null;
2330 CallableStatement cstmt = null;
2331 Long lrID = (Long)doc.getLRPersistenceId();
2332// Long docID = null;
2333 Long asetID = null;
2334
2335 try {
2336 //1. get the a-set ID in the database
2337 String sql = " select as_id " +
2338// " as_doc_id " +
2339 " from "+Gate.DB_OWNER+".v_annotation_set " +
2340 " where lr_id = ? ";
2341 //do we have aset name?
2342 String clause = null;
2343 String name = as.getName();
2344 if (null != name) {
2345 clause = " and as_name = ? ";
2346 }
2347 else {
2348 clause = " and as_name is null ";
2349 }
2350 sql = sql + clause;
2351
2352 pstmt = this.jdbcConn.prepareStatement(sql);
2353 pstmt.setLong(1,lrID.longValue());
2354 if (null != name) {
2355 pstmt.setString(2,name);
2356 }
2357 pstmt.execute();
2358 rs = pstmt.getResultSet();
2359
2360 if (rs.next()) {
2361 asetID = new Long(rs.getLong("as_id"));
2362// docID = new Long(rs.getLong("as_doc_id"));
2363//System.out.println("syncing annots, lr_id=["+lrID+"],doc_id=["+docID+"], set_id=["+asetID+"]");
2364 }
2365 else {
2366 throw new PersistenceException("cannot find annotation set with" +
2367 " name=["+name+"] , LRID=["+lrID+"] in database");
2368 }
2369
2370 //3. insert the new annotations from this set
2371
2372 //3.1. prepare call
2373 cstmt = this.jdbcConn.prepareCall(
2374 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }");
2375
2376 Long annGlobalID = null;
2377 Iterator it = changes.iterator();
2378
2379 while (it.hasNext()) {
2380
2381 //3.2. insert annotation
2382 Annotation ann = (Annotation)it.next();
2383
2384 Node start = (Node)ann.getStartNode();
2385 Node end = (Node)ann.getEndNode();
2386 String type = ann.getType();
2387
2388 cstmt.setLong(1,lrID.longValue());
2389 cstmt.setLong(2,ann.getId().longValue());
2390 cstmt.setLong(3,asetID.longValue());
2391 cstmt.setLong(4,start.getId().longValue());
2392 cstmt.setLong(5,start.getOffset().longValue());
2393 cstmt.setLong(6,end.getId().longValue());
2394 cstmt.setLong(7,end.getOffset().longValue());
2395 cstmt.setString(8,type);
2396 cstmt.registerOutParameter(9,java.sql.Types.BIGINT);
2397
2398 cstmt.execute();
2399 annGlobalID = new Long(cstmt.getLong(9));
2400
2401 //3.3. set annotation features
2402 FeatureMap features = ann.getFeatures();
2403 Assert.assertNotNull(features);
2404// createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
2405 createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
2406 }
2407 }
2408 catch(SQLException sqle) {
2409 throw new PersistenceException("can't add annotations in DB : ["+
2410 sqle.getMessage()+"]");
2411 }
2412 finally {
2413 DBHelper.cleanup(rs);
2414 DBHelper.cleanup(pstmt);
2415 DBHelper.cleanup(cstmt);
2416 }
2417 }
2418*/
2419
2420
2421 /** helper for sync() - never call directly */
2422/* protected void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes)
2423 throws PersistenceException {
2424
2425 //technically this approach sux
2426 //at least it works
2427
2428 //1. delete
2429 _syncRemovedAnnotations(doc,as,changes);
2430 //2. recreate
2431 _syncAddedAnnotations(doc,as,changes);
2432 }
2433*/
2434
2435 /** helper for sync() - never call directly */
2436 protected void _syncRemovedDocumentsFromCorpus(List docLRIDs, Long corpLRID)
2437 throws PersistenceException {
2438
2439 //0.preconditions
2440 Assert.assertNotNull(docLRIDs);
2441 Assert.assertNotNull(corpLRID);
2442 Assert.assertTrue(docLRIDs.size() > 0);
2443
2444 CallableStatement cstmt = null;
2445
2446 try {
2447 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2448 ".persist.remove_document_from_corpus(?,?) }");
2449
2450 Iterator it = docLRIDs.iterator();
2451 while (it.hasNext()) {
2452 Long currLRID = (Long)it.next();
2453 cstmt.setLong(1,currLRID.longValue());
2454 cstmt.setLong(2,corpLRID.longValue());
2455 cstmt.execute();
2456 }
2457 }
2458 catch(SQLException sqle) {
2459
2460 switch(sqle.getErrorCode()) {
2461 case DBHelper.X_ORACLE_INVALID_LR :
2462 throw new PersistenceException("invalid LR supplied: no such document: ["+
2463 sqle.getMessage()+"]");
2464 default:
2465 throw new PersistenceException("can't change document data: ["+
2466 sqle.getMessage()+"]");
2467 }
2468 }
2469 finally {
2470 DBHelper.cleanup(cstmt);
2471 }
2472
2473 }
2474
2475
2476 /** helper for sync() - never call directly */
2477/* protected void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes)
2478 throws PersistenceException {
2479 //0.preconditions
2480 Assert.assertNotNull(doc);
2481 Assert.assertNotNull(as);
2482 Assert.assertNotNull(changes);
2483 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2484 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
2485 Assert.assertTrue(changes.size() > 0);
2486
2487
2488 PreparedStatement pstmt = null;
2489 ResultSet rs = null;
2490 CallableStatement cstmt = null;
2491 Long lrID = (Long)doc.getLRPersistenceId();
2492 Long docID = null;
2493 Long asetID = null;
2494
2495 try {
2496 //1. get the a-set ID in the database
2497 String sql = " select as_id, " +
2498 " as_doc_id " +
2499 " from "+Gate.DB_OWNER+".v_annotation_set " +
2500 " where lr_id = ? ";
2501 //do we have aset name?
2502 String clause = null;
2503 String name = as.getName();
2504 if (null != name) {
2505 clause = " and as_name = ? ";
2506 }
2507 else {
2508 clause = " and as_name is null ";
2509 }
2510 sql = sql + clause;
2511
2512 pstmt = this.jdbcConn.prepareStatement(sql);
2513 pstmt.setLong(1,lrID.longValue());
2514 if (null != name) {
2515 pstmt.setString(2,name);
2516 }
2517 pstmt.execute();
2518 rs = pstmt.getResultSet();
2519
2520 if (rs.next()) {
2521 asetID = new Long(rs.getLong("as_id"));
2522 docID = new Long(rs.getLong("as_doc_id"));
2523 }
2524 else {
2525 throw new PersistenceException("cannot find annotation set with" +
2526 " name=["+name+"] , LRID=["+lrID+"] in database");
2527 }
2528
2529 //3. delete the removed annotations from this set
2530
2531 //3.1. prepare call
2532 cstmt = this.jdbcConn.prepareCall(
2533 "{ call "+Gate.DB_OWNER+".persist.delete_annotation(?,?) }");
2534
2535
2536 Iterator it = changes.iterator();
2537
2538 while (it.hasNext()) {
2539
2540 //3.2. insert annotation
2541 Annotation ann = (Annotation)it.next();
2542
2543 cstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs!
2544 cstmt.setLong(2,ann.getId().longValue());
2545 cstmt.execute();
2546 }
2547 }
2548 catch(SQLException sqle) {
2549 throw new PersistenceException("can't delete annotations in DB : ["+
2550 sqle.getMessage()+"]");
2551 }
2552 finally {
2553 DBHelper.cleanup(rs);
2554 DBHelper.cleanup(pstmt);
2555 DBHelper.cleanup(cstmt);
2556 }
2557 }
2558*/
2559
2560
2561 /** helper for sync() - never call directly */
2562/* protected void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets)
2563 throws PersistenceException {
2564
2565 //0. preconditions
2566 Assert.assertNotNull(doc);
2567 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2568 Assert.assertNotNull(doc.getLRPersistenceId());
2569 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
2570 this.getDatabaseID());
2571 Assert.assertNotNull(removedSets);
2572 Assert.assertNotNull(addedSets);
2573
2574 Long lrID = (Long)doc.getLRPersistenceId();
2575
2576 //1. delete from DB removed a-sets
2577 CallableStatement cstmt = null;
2578
2579 try {
2580 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2581 ".persist.delete_annotation_set(?,?) }");
2582
2583 Iterator it = removedSets.iterator();
2584 while (it.hasNext()) {
2585 String setName = (String)it.next();
2586 cstmt.setLong(1,lrID.longValue());
2587 cstmt.setString(2,setName);
2588 cstmt.execute();
2589 }
2590 }
2591 catch(SQLException sqle) {
2592 throw new PersistenceException("can't remove annotation set from DB: ["+ sqle.getMessage()+"]");
2593 }
2594 finally {
2595 DBHelper.cleanup(cstmt);
2596 }
2597
2598 //2. create in DB new a-sets
2599 Iterator it = addedSets.iterator();
2600 while (it.hasNext()) {
2601 String setName = (String)it.next();
2602 AnnotationSet aset = doc.getAnnotations(setName);
2603
2604 Assert.assertNotNull(aset);
2605 Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl);
2606
2607 createAnnotationSet(lrID,aset);
2608 }
2609 }
2610
2611*/
2612
2613 /** helper for sync() - never call directly */
2614/* protected void _syncAnnotations(Document doc)
2615 throws PersistenceException {
2616
2617 //0. preconditions
2618 Assert.assertNotNull(doc);
2619 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2620 Assert.assertNotNull(doc.getLRPersistenceId());
2621 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
2622 this.getDatabaseID());
2623
2624
2625 EventAwareDocument ead = (EventAwareDocument)doc;
2626 //1. get the sets read from the DB for this document
2627 //chnaged annotations can occur only in such sets
2628 Collection loadedSets = ead.getLoadedAnnotationSets();
2629
2630 Iterator it = loadedSets.iterator();
2631 while (it.hasNext()) {
2632 AnnotationSet as = (AnnotationSet)it.next();
2633 //check that this set is neither NEW nor DELETED
2634 //they should be already synced
2635 if (ead.getAddedAnnotationSets().contains(as.getName()) ||
2636 ead.getRemovedAnnotationSets().contains(as.getName())) {
2637 //oops, ignore it
2638 continue;
2639 }
2640
2641 EventAwareAnnotationSet eas = (EventAwareAnnotationSet)as;
2642 Assert.assertNotNull(as);
2643
2644 Collection anns = null;
2645 anns = eas.getAddedAnnotations();
2646 Assert.assertNotNull(anns);
2647 if (anns.size()>0) {
2648 _syncAddedAnnotations(doc,as,anns);
2649 }
2650
2651 anns = eas.getRemovedAnnotations();
2652 Assert.assertNotNull(anns);
2653 if (anns.size()>0) {
2654 _syncRemovedAnnotations(doc,as,anns);
2655 }
2656
2657 anns = eas.getChangedAnnotations();
2658 Assert.assertNotNull(anns);
2659 if (anns.size()>0) {
2660 _syncChangedAnnotations(doc,as,anns);
2661 }
2662 }
2663 }
2664*/
2665
2666
2667 /** helper for sync() - never call directly */
2668 protected void _syncFeatures(LanguageResource lr)
2669 throws PersistenceException {
2670
2671 //0. preconditions
2672 Assert.assertNotNull(lr);
2673 Assert.assertNotNull(lr.getLRPersistenceId());
2674 Assert.assertEquals(((DatabaseDataStore)lr.getDataStore()).getDatabaseID(),
2675 this.getDatabaseID());
2676 Assert.assertTrue(lr instanceof Document || lr instanceof Corpus);
2677 //we have to be in the context of transaction
2678
2679 //1, get ID in the DB
2680 Long lrID = (Long)lr.getLRPersistenceId();
2681 int entityType;
2682
2683 //2. delete features
2684 CallableStatement stmt = null;
2685 try {
2686 Assert.assertTrue(false == this.jdbcConn.getAutoCommit());
2687 stmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+
2688 ".persist.delete_features(?,?) }");
2689 stmt.setLong(1,lrID.longValue());
2690
2691 if (lr instanceof Document) {
2692 entityType = DBHelper.FEATURE_OWNER_DOCUMENT;
2693 }
2694 else if (lr instanceof Corpus) {
2695 entityType = DBHelper.FEATURE_OWNER_CORPUS;
2696 }
2697 else {
2698 throw new IllegalArgumentException();
2699 }
2700
2701 stmt.setInt(2,entityType);
2702 stmt.execute();
2703 }
2704 catch(SQLException sqle) {
2705 throw new PersistenceException("can't delete features in DB: ["+ sqle.getMessage()+"]");
2706 }
2707 finally {
2708 DBHelper.cleanup(stmt);
2709 }
2710
2711 //3. recreate them
2712 //createFeatures(lrID,entityType, lr.getFeatures());
2713 createFeaturesBulk(lrID,entityType, lr.getFeatures());
2714
2715 }
2716
2717
2718
2719 /** helper for sync() - saves a Corpus in the database */
2720/* protected void syncCorpus(Corpus corp)
2721 throws PersistenceException,SecurityException {
2722
2723 //0. preconditions
2724 Assert.assertNotNull(corp);
2725 Assert.assertTrue(corp instanceof DatabaseCorpusImpl);
2726 Assert.assertEquals(this,corp.getDataStore());
2727 Assert.assertNotNull(corp.getLRPersistenceId());
2728
2729 EventAwareCorpus dbCorpus = (EventAwareCorpus)corp;
2730
2731 //1. sync the corpus name?
2732 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_NAME)) {
2733 _syncLR(corp);
2734 }
2735
2736 //2. sync the corpus features?
2737 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) {
2738 _syncFeatures(corp);
2739 }
2740
2741 //2.5 get removed documents and detach (not remove) them from the corpus in the
2742 //database
2743 List removedDocLRIDs = dbCorpus.getRemovedDocuments();
2744 if (removedDocLRIDs.size() > 0) {
2745 _syncRemovedDocumentsFromCorpus(removedDocLRIDs,(Long)corp.getLRPersistenceId());
2746 }
2747
2748 //3. get all documents
2749 //--Iterator it = corp.iterator();
2750 Iterator it = dbCorpus.getLoadedDocuments().iterator();
2751
2752 while (it.hasNext()) {
2753 Document dbDoc = (Document)it.next();
2754 //note - document may be NULL which means it was not loaded (load on demand)
2755 //just ignore it then
2756 if (null == dbDoc) {
2757 continue;
2758 }
2759
2760 //adopt/sync?
2761 if (null == dbDoc.getLRPersistenceId()) {
2762 //doc was never adopted, adopt it
2763
2764 //3.1 remove the transient doc from the corpus
2765 it.remove();
2766
2767 //3.2 get the security info for the corpus
2768 SecurityInfo si = getSecurityInfo(corp);
2769
2770
2771 Document adoptedDoc = null;
2772 try {
2773 //3.3. adopt the doc with the sec info
2774//System.out.println("adopting ["+dbDoc.getName()+"] ...");
2775 //don't open a new transaction, since sync() already has opended one
2776 adoptedDoc = (Document)_adopt(dbDoc,si,true);
2777
2778 //3.4. add doc to corpus in DB
2779 addDocumentToCorpus((Long)adoptedDoc.getLRPersistenceId(),
2780 (Long)corp.getLRPersistenceId());
2781 }
2782 catch(SecurityException se) {
2783 throw new PersistenceException(se);
2784 }
2785
2786 //3.5 add back to corpus the new DatabaseDocument
2787 corp.add(adoptedDoc);
2788 }
2789 else {
2790 //don't open a new transaction, the sync() called for corpus has already
2791 //opened one
2792 try {
2793 _sync(dbDoc,true);
2794
2795 // let the world know about it
2796 fireResourceWritten( new DatastoreEvent(this,
2797 DatastoreEvent.RESOURCE_WRITTEN,
2798 dbDoc,
2799 dbDoc.getLRPersistenceId()
2800 )
2801 );
2802
2803 //if the document is form the same DS but did not belong to the corpus add it now
2804 //NOTE: if the document already belongs to the corpus then nothing will be changed
2805 //in the DB
2806 addDocumentToCorpus((Long)dbDoc.getLRPersistenceId(),
2807 (Long)corp.getLRPersistenceId());
2808 }
2809 catch(SecurityException se) {
2810 gate.util.Err.prln("document cannot be synced: ["+se.getMessage()+"]");
2811 }
2812 }
2813 }
2814 }
2815*/
2816
2817
2818 /**
2819 * Try to acquire exlusive lock on a resource from the persistent store.
2820 * Always call unlockLR() when the lock is no longer needed
2821 */
2822 public boolean lockLr(LanguageResource lr)
2823 throws PersistenceException,SecurityException {
2824
2825 //0. preconditions
2826 Assert.assertNotNull(lr);
2827 Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
2828 lr instanceof DatabaseCorpusImpl);
2829 Assert.assertNotNull(lr.getLRPersistenceId());
2830 Assert.assertEquals(lr.getDataStore(),this);
2831
2832 //1. delegate
2833 return _lockLr((Long)lr.getLRPersistenceId());
2834 }
2835
2836
2837
2838 /**
2839 * helper for lockLR()
2840 * never call directly
2841 */
2842 private boolean _lockLr(Long lrID)
2843 throws PersistenceException,SecurityException {
2844
2845 //0. preconditions
2846 Assert.assertNotNull(lrID);
2847
2848 //1. check session
2849 if (null == this.session) {
2850 throw new SecurityException("session not set");
2851 }
2852
2853 if (false == this.ac.isValidSession(this.session)) {
2854 throw new SecurityException("invalid session supplied");
2855 }
2856
2857 //2. check permissions
2858 if (false == canWriteLR(lrID)) {
2859 throw new SecurityException("no write access granted to the user");
2860 }
2861
2862 //3. try to lock
2863 CallableStatement cstmt = null;
2864 boolean lockSucceeded = false;
2865
2866 try {
2867 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.lock_lr(?,?,?,?) }");
2868 cstmt.setLong(1,lrID.longValue());
2869 cstmt.setLong(2,this.session.getUser().getID().longValue());
2870 cstmt.setLong(3,this.session.getGroup().getID().longValue());
2871 cstmt.registerOutParameter(4,java.sql.Types.NUMERIC);
2872 cstmt.execute();
2873
2874 lockSucceeded = cstmt.getLong(4) == OracleDataStore.ORACLE_TRUE
2875 ? true
2876 : false;
2877 }
2878 catch(SQLException sqle) {
2879
2880 switch(sqle.getErrorCode()) {
2881 case DBHelper.X_ORACLE_INVALID_LR:
2882 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
2883 default:
2884 throw new PersistenceException(
2885 "can't lock LR in DB : ["+ sqle.getMessage()+"]");
2886 }
2887 }
2888 finally {
2889 DBHelper.cleanup(cstmt);
2890 }
2891
2892 return lockSucceeded;
2893 }
2894
2895
2896
2897 /**
2898 * Releases the exlusive lock on a resource from the persistent store.
2899 */
2900 public void unlockLr(LanguageResource lr)
2901 throws PersistenceException,SecurityException {
2902
2903 //0. preconditions
2904 Assert.assertNotNull(lr);
2905 Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
2906 lr instanceof DatabaseCorpusImpl);
2907 Assert.assertNotNull(lr.getLRPersistenceId());
2908 Assert.assertEquals(lr.getDataStore(),this);
2909
2910 //1. check session
2911 if (null == this.session) {
2912 throw new SecurityException("session not set");
2913 }
2914
2915 if (false == this.ac.isValidSession(this.session)) {
2916 throw new SecurityException("invalid session supplied");
2917 }
2918
2919 //2. check permissions
2920 if (false == canWriteLR(lr.getLRPersistenceId())) {
2921 throw new SecurityException("no write access granted to the user");
2922 }
2923
2924 //3. try to unlock
2925 CallableStatement cstmt = null;
2926 boolean lockSucceeded = false;
2927
2928 try {
2929 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.unlock_lr(?,?) }");
2930 cstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
2931 cstmt.setLong(2,this.session.getUser().getID().longValue());
2932 cstmt.execute();
2933 }
2934 catch(SQLException sqle) {
2935
2936 switch(sqle.getErrorCode()) {
2937 case DBHelper.X_ORACLE_INVALID_LR:
2938 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
2939 default:
2940 throw new PersistenceException(
2941 "can't unlock LR in DB : ["+ sqle.getMessage()+"]");
2942 }
2943 }
2944 finally {
2945 DBHelper.cleanup(cstmt);
2946 }
2947 }
2948
2949
2950
2951
2952 /**
2953 * adds document to corpus in the database
2954 * if the document is already part of the corpus nothing
2955 * changes
2956 */
2957 protected void addDocumentToCorpus(Long docID,Long corpID)
2958 throws PersistenceException,SecurityException {
2959
2960 //0. preconditions
2961 Assert.assertNotNull(docID);
2962 Assert.assertNotNull(corpID);
2963
2964 //1. check session
2965 if (null == this.session) {
2966 throw new SecurityException("session not set");
2967 }
2968
2969 if (false == this.ac.isValidSession(this.session)) {
2970 throw new SecurityException("invalid session supplied");
2971 }
2972
2973 //2. check permissions
2974 if (false == canWriteLR(corpID)) {
2975 throw new SecurityException("no write access granted to the user");
2976 }
2977
2978 if (false == canWriteLR(docID)) {
2979 throw new SecurityException("no write access granted to the user");
2980 }
2981
2982 //3. database
2983 CallableStatement cstmt = null;
2984
2985 try {
2986 cstmt = this.jdbcConn.prepareCall("{ call "+
2987 Gate.DB_OWNER+".persist.add_document_to_corpus(?,?) }");
2988 cstmt.setLong(1,docID.longValue());
2989 cstmt.setLong(2,corpID.longValue());
2990 cstmt.execute();
2991 }
2992 catch(SQLException sqle) {
2993
2994 switch(sqle.getErrorCode()) {
2995 case DBHelper.X_ORACLE_INVALID_LR:
2996 throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
2997 default:
2998 throw new PersistenceException(
2999 "can't add document to corpus : ["+ sqle.getMessage()+"]");
3000 }
3001 }
3002 finally {
3003 DBHelper.cleanup(cstmt);
3004 }
3005 }
3006
3007
3008
3009 /**
3010 * unloads a LR from the GUI
3011 */
3012/* protected void unloadLR(Long lrID)
3013 throws GateException{
3014
3015 //0. preconfitions
3016 Assert.assertNotNull(lrID);
3017
3018 //1. get all LRs in the system
3019 List resources = Gate.getCreoleRegister().getAllInstances("gate.LanguageResource");
3020
3021 Iterator it = resources.iterator();
3022 while (it.hasNext()) {
3023 LanguageResource lr = (LanguageResource)it.next();
3024 if (lrID.equals(lr.getLRPersistenceId()) &&
3025 this.equals(lr.getDataStore())) {
3026 //found it - unload it
3027 Factory.deleteResource(lr);
3028 break;
3029 }
3030 }
3031 }
3032*/
3033
3034 /** Get a list of LRs that satisfy some set or restrictions
3035 *
3036 * @param constraints list of Restriction objects
3037 */
3038 public List findLrIds(List constraints) throws PersistenceException {
3039 return findLrIds(constraints,null);
3040 }
3041
3042 /**
3043 * Get a list of LRs IDs that satisfy some set or restrictions and are
3044 * of a particular type
3045 *
3046 * @param constraints list of Restriction objects
3047 * @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS
3048 */
3049 public List findLrIds(List constraints, String lrType) throws PersistenceException {
3050 return findLrIds(constraints, lrType, null, -1);
3051 }
3052
3053 /**
3054 * Get a list of LRs IDs that satisfy some set or restrictions and are
3055 * of a particular type
3056 *
3057 * @param constraints list of Restriction objects
3058 * @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS
3059 * @param orderByConstraints liat of OrderByRestriction objects
3060 * @param limitcount limit returning objects -1 for unlimited
3061 */
3062 public List findLrIds(List constraints, String lrType,
3063 List orderByConstraints, int limitcount) throws PersistenceException {
3064 Vector lrsIDs = new Vector();
3065 CallableStatement stmt = null;
3066 ResultSet rs = null;
3067 Connection conn = null;
3068
3069 try {
3070 Vector sqlValues = new Vector();
3071 String sql = getSQLQuery(constraints, lrType, false, orderByConstraints, limitcount, sqlValues);
3072 conn = DBHelper.connect(this.getStorageUrl(), true);
3073 stmt = conn.prepareCall(sql);
3074 for (int i = 0; i<sqlValues.size(); i++){
3075 if (sqlValues.elementAt(i) instanceof String){
3076 stmt.setString(i+1,sqlValues.elementAt(i).toString());
3077 }
3078 else if (sqlValues.elementAt(i) instanceof Long){
3079 stmt.setLong(i+1,((Long) sqlValues.elementAt(i)).longValue());
3080 }
3081 else if (sqlValues.elementAt(i) instanceof Integer){
3082 stmt.setLong(i+1,((Integer) sqlValues.elementAt(i)).intValue());
3083 }
3084 }
3085 stmt.execute();
3086 rs = stmt.getResultSet();
3087
3088 while (rs.next()) {
3089 long lr_ID = rs.getLong(1);
3090 lrsIDs.addElement(new Long(lr_ID));
3091 }
3092 return lrsIDs;
3093 }
3094 catch(SQLException sqle) {
3095 throw new PersistenceException("can't get LRs from DB: ["+ sqle+"]");
3096 }
3097 catch (ClassNotFoundException cnfe){
3098 throw new PersistenceException("can't not find driver: ["+ cnfe +"]");
3099 }
3100 finally {
3101 DBHelper.cleanup(rs);
3102 DBHelper.cleanup(stmt);
3103 DBHelper.disconnect(conn, true);
3104 }
3105 }
3106 /**
3107 * Return count of LRs which matches the constraints.
3108 *
3109 * @param constraints list of Restriction objects
3110 * @param lrType type of Lrs. DBHelper.DOCUMENT_CLASS or DBHelper.CORPUS_CLASS
3111 */
3112 public long getLrsCount(List constraints, String lrType) throws PersistenceException {
3113 Vector lrs = new Vector();
3114 CallableStatement stmt = null;
3115 ResultSet rs = null;
3116 Connection conn = null;
3117
3118 try {
3119 Vector sqlValues = new Vector();
3120 String sql = getSQLQuery(constraints,lrType, true, null, -1, sqlValues);
3121 conn = DBHelper.connect(this.getStorageUrl(), true);
3122 stmt = conn.prepareCall(sql);
3123 for (int i = 0; i<sqlValues.size(); i++){
3124 if (sqlValues.elementAt(i) instanceof String){
3125 stmt.setString(i+1,sqlValues.elementAt(i).toString());
3126 }
3127 else if (sqlValues.elementAt(i) instanceof Long){
3128 stmt.setLong(i+1,((Long) sqlValues.elementAt(i)).longValue());
3129 }
3130 else if (sqlValues.elementAt(i) instanceof Integer){
3131 stmt.setLong(i+1,((Integer) sqlValues.elementAt(i)).intValue());
3132 }
3133 }
3134
3135 stmt.execute();
3136 rs = stmt.getResultSet();
3137 rs.next();
3138 return rs.getLong(1);
3139 }
3140 catch(SQLException sqle) {
3141 throw new PersistenceException("can't get LRs Count from DB: ["+ sqle+"]");
3142 }
3143 catch (ClassNotFoundException cnfe){
3144 throw new PersistenceException("can't not find driver: ["+ cnfe +"]");
3145 }
3146 finally {
3147 DBHelper.cleanup(rs);
3148 DBHelper.cleanup(stmt);
3149 DBHelper.disconnect(conn, true);
3150 }
3151 }
3152
3153 private String getSQLQuery(List filter, String lrType, boolean count,
3154 List orderByFilter, int limitcount, Vector sqlValues){
3155 StringBuffer query = new StringBuffer("");
3156 String join = getJoinQuery(filter, orderByFilter, sqlValues);
3157 String select = "lr_id";
3158 if (count){
3159 select = "count(*)";
3160 }
3161
3162 query = query.append(" SELECT " + select + " " +
3163 " FROM "+Gate.DB_OWNER+".t_lang_resource LR " + join);
3164
3165 if (filter != null && filter.size()>0) {
3166 query = query.append(" ( ");
3167 query = query.append(getIntersectionPart(filter, sqlValues));
3168 query = query.append(" ) intersected_feat_restr ");
3169 }
3170
3171 String endPartOfJoin = getEndPartOfJoin(filter,orderByFilter, lrType,sqlValues);
3172 query = query.append(endPartOfJoin);
3173
3174 if (limitcount>0){
3175 query = query.insert(0,"select lr_id from ( ");
3176 query = query.append( ") where rownum<"+(limitcount+1));
3177 }
3178
3179 return query.toString();
3180 }
3181
3182 private String getIntersectionPart(List filter, Vector sqlValues){
3183 StringBuffer query = new StringBuffer(" ");
3184
3185 Collections.sort(filter, new RestrictionComepator());
3186 Vector list_of_filters = new Vector();
3187 for (int i=0; i<filter.size(); i++){
3188 if (i>0){
3189 Restriction rest = (Restriction) filter.get(i);
3190 Restriction prev = (Restriction) filter.get(i-1);
3191 if (rest.getKey().equals(prev.getKey())){
3192 Vector temp = (Vector) list_of_filters.get(list_of_filters.size()-1);
3193 temp.add(rest);
3194 } else {
3195 Vector temp = new Vector();
3196 temp.add(rest);
3197 list_of_filters.add(temp);
3198 }
3199 } else {
3200 Vector temp = new Vector();
3201 temp.add(filter.get(0));
3202 list_of_filters.add(temp);
3203 }
3204 }
3205
3206 if (filter!=null && filter.size()>0){
3207 for (int i=0; i<list_of_filters.size(); i++){
3208 query = query.append(getRestrictionPartOfQuery((List) list_of_filters.get(i),sqlValues));
3209 if (i<list_of_filters.size()-1) {
3210 query = query.append(" intersect ");
3211 }
3212 }
3213 }
3214 return query.toString();
3215 }
3216
3217 private String getRestrictionPartOfQuery(List list, Vector sqlValues){
3218 StringBuffer expresion = new StringBuffer(
3219 " SELECT ft_entity_id "+
3220 " FROM "+Gate.DB_OWNER+".t_feature FEATURE, " +
3221 Gate.DB_OWNER + ".t_feature_key FTK" +
3222 " WHERE FEATURE.ft_entity_type = 2 ");
3223
3224 Restriction restr = (Restriction) list.get(0);
3225
3226 if (restr.getKey() != null){
3227 expresion = expresion.append(" AND FTK.fk_id = FEATURE.ft_key_id ");
3228 expresion = expresion.append(" AND FTK.fk_string = ? ");
3229 sqlValues.addElement(restr.getKey());
3230 }
3231
3232 for (int i =0; i<list.size(); i++) {
3233 restr = (Restriction) list.get(i);
3234 if (restr.getValue() != null){
3235 expresion = expresion.append(" AND ");
3236 switch (this.findFeatureType(restr.getValue())){
3237 case DBHelper.VALUE_TYPE_INTEGER:
3238 expresion = expresion.append(getNumberExpresion(restr, sqlValues));
3239 break;
3240 case DBHelper.VALUE_TYPE_LONG:
3241 expresion = expresion.append(getNumberExpresion(restr, sqlValues));
3242 break;
3243 default:
3244 if (restr.getOperator()==Restriction.OPERATOR_EQUATION){
3245 expresion = expresion.append(" FEATURE.ft_character_value = ? ");
3246 sqlValues.addElement(restr.getStringValue());
3247 }
3248 if (restr.getOperator()==Restriction.OPERATOR_LIKE){
3249 expresion = expresion.append(" upper(FEATURE.ft_character_value) like ? ");
3250 sqlValues.addElement("%"+restr.getStringValue().toUpperCase()+"%");
3251 }
3252 break;
3253 }
3254 }
3255 }
3256
3257 return expresion.toString();
3258 }
3259
3260 private String getNumberExpresion(Restriction restr, Vector sqlValues){
3261 StringBuffer expr = new StringBuffer("FEATURE.ft_number_value ");
3262
3263 switch (restr.getOperator()){
3264 case Restriction.OPERATOR_EQUATION:
3265 expr = expr.append(" = ");
3266 break;
3267 case Restriction.OPERATOR_BIGGER:
3268 expr = expr.append(" > ");
3269 break;
3270 case Restriction.OPERATOR_LESS:
3271 expr = expr.append(" < ");
3272 break;
3273 case Restriction.OPERATOR_EQUATION_OR_BIGGER:
3274 expr = expr.append(" >= ");
3275 break;
3276 case Restriction.OPERATOR_EQUATION_OR_LESS:
3277 expr = expr.append(" <= ");
3278 break;
3279 default:
3280 return " 0 = 0 ";
3281 }
3282
3283 expr.append(" ? ");
3284 sqlValues.addElement(restr.getValue());
3285
3286 return expr.toString();
3287 }
3288
3289 private String getJoinQuery(List filter, List orderByFilter, Vector sqlValues){
3290 StringBuffer join = new StringBuffer("");
3291 if (filter !=null && filter.size()>0) {
3292 join = join.append(" , ");
3293 }
3294 if (orderByFilter!=null){
3295 for (int i = 0; i<orderByFilter.size(); i++){
3296 join = join.append(Gate.DB_OWNER+".t_feature FT"+i);
3297 join = join.append(" , "+Gate.DB_OWNER+".t_feature_key FTK"+i +" , ");
3298 }
3299 }
3300 return join.toString();
3301 }
3302
3303 private String getEndPartOfJoin(List filter, List orderByFilter, String lrType, Vector sqlValues){
3304 StringBuffer endJoin = new StringBuffer("");
3305 endJoin = endJoin.append(" WHERE ");
3306
3307 endJoin = endJoin.append(" LR.lr_type_id = ? ");
3308 if (lrType.equals(DBHelper.CORPUS_CLASS)) {
3309 sqlValues.addElement(new Long(2));
3310 }// if DBHelper.CORPUS_CLASS
3311 if (lrType.equals(DBHelper.DOCUMENT_CLASS)) {
3312 sqlValues.addElement(new Long(1));
3313 }// if DBHelper.DOCUMENT_CLASS
3314
3315 if (filter != null && filter.size()>0){
3316 endJoin = endJoin.append(" and intersected_feat_restr.ft_entity_id = lr.lr_id ");
3317 }
3318
3319 if (orderByFilter!=null && orderByFilter.size()>0){
3320 for (int i=0; i<orderByFilter.size(); i++){
3321 endJoin = endJoin.append(" and lr_id=FT"+i+".ft_entity_id ");
3322 endJoin = endJoin.append(" and FT"+i+".ft_key_id = FTK"+i+".fk_id ");
3323 endJoin = endJoin.append(" and FTK"+i+".fk_string= ? ");
3324 OrderByRestriction restr = (OrderByRestriction) orderByFilter.get(i);
3325 sqlValues.addElement(restr.getKey());
3326 }
3327 endJoin = endJoin.append(" order by ");
3328 for (int i=0; i<orderByFilter.size(); i++){
3329 OrderByRestriction restr = (OrderByRestriction) orderByFilter.get(i);
3330
3331 endJoin = endJoin.append(" FT"+i+".ft_number_value ");
3332 if (restr.getOperator()==OrderByRestriction.OPERATOR_ASCENDING){
3333 endJoin = endJoin.append(" asc ");
3334 } else {
3335 endJoin = endJoin.append(" desc ");
3336 }
3337 /* endJoin = endJoin.append(", FT"+i+".ft_character_value ");
3338 if (restr.getOperator()==OrderByRestriction.OPERATOR_ASCENDING){
3339 endJoin = endJoin.append(" asc ");
3340 } else {
3341 endJoin = endJoin.append(" desc ");
3342 }*/
3343 if (i<orderByFilter.size()-1){
3344 endJoin = endJoin.append(" , ");
3345 }
3346 }
3347 }
3348 return endJoin.toString();
3349 }
3350
3351 public List findDocIdsByAnn(List constraints, int limitcount) throws PersistenceException {
3352 Vector lrsIDs = new Vector();
3353 CallableStatement stmt = null;
3354 ResultSet rs = null;
3355 Connection conn = null;
3356
3357 try {
3358 Vector sqlValues = new Vector();
3359 String sql = getSQLQueryAnn(constraints, limitcount, sqlValues);
3360 conn = DBHelper.connect(this.getStorageUrl(), true);
3361 stmt = conn.prepareCall(sql);
3362 for (int i = 0; i<sqlValues.size(); i++){
3363 if (sqlValues.elementAt(i) instanceof String){
3364 stmt.setString(i+1,sqlValues.elementAt(i).toString());
3365 }
3366 else if (sqlValues.elementAt(i) instanceof Long){
3367 stmt.setLong(i+1,((Long) sqlValues.elementAt(i)).longValue());
3368 }
3369 else if (sqlValues.elementAt(i) instanceof Integer){
3370 stmt.setLong(i+1,((Integer) sqlValues.elementAt(i)).intValue());
3371 }
3372 }
3373 stmt.execute();
3374 rs = stmt.getResultSet();
3375
3376 while (rs.next()) {
3377 long lr_ID = rs.getLong(1);
3378 lrsIDs.addElement(new Long(lr_ID));
3379 }
3380 return lrsIDs;
3381 }
3382 catch(SQLException sqle) {
3383 throw new PersistenceException("can't get LRs from DB: ["+ sqle+"]");
3384 }
3385 catch (ClassNotFoundException cnfe){
3386 throw new PersistenceException("can't not find driver: ["+ cnfe +"]");
3387 }
3388 finally {
3389 DBHelper.cleanup(rs);
3390 DBHelper.cleanup(stmt);
3391 DBHelper.disconnect(conn, true);
3392 }
3393 }
3394
3395 private String getSQLQueryAnn(List constraints, int limitcount, Vector sqlValues){
3396 StringBuffer sql = new StringBuffer("");
3397 sql.append("SELECT lr_id ");
3398 sql.append(" FROM gateadmin.t_lang_resource LR ");
3399 sql.append(" WHERE LR.lr_type_id = 1 ");
3400
3401 for (int i = 0; i<constraints.size(); i++){
3402 Restriction rest = (Restriction) constraints.get(i);
3403 sql.append(" AND EXISTS( ");
3404 sql.append(" SELECT F.ft_id ");
3405 sql.append(" FROM gateadmin.t_feature F, ");
3406 sql.append(" gateadmin.T_AS_ANNOTATION A, ");
3407 sql.append(" gateadmin.T_ANNOT_SET S, ");
3408 sql.append(" gateadmin.T_DOCUMENT D, ");
3409 sql.append(" gateadmin.t_feature_key FK ");
3410 sql.append(" WHERE F.ft_entity_id = A.asann_ann_id ");
3411 sql.append(" AND A.asann_as_id = S.as_id ");
3412 sql.append(" AND S.as_doc_id = D.doc_id ");
3413 sql.append(" AND D.doc_lr_id = LR.LR_ID ");
3414 sql.append(" AND S.AS_NAME = ? ");
3415 sqlValues.add("NewsCollector");
3416 sql.append(" AND FK.fk_id = F.ft_key_id ");
3417 sql.append(" AND FK.fk_string= ? ");
3418 sqlValues.add(rest.getKey());
3419 sql.append(" AND F.FT_CHARACTER_VALUE = ? ");
3420 sqlValues.add(rest.getStringValue());
3421 sql.append(" ) ");
3422 }
3423 sql.append(" group by lr_id ");
3424 if (limitcount>0){
3425 sql = sql.insert(0,"select lr_id from ( ");
3426 sql = sql.append( ") where rownum<"+(limitcount+1));
3427 }
3428 return sql.toString();
3429 }
3430 private class Feature {
3431
3432 Long entityID;
3433 int entityType;
3434 String key;
3435 Object value;
3436 int valueType;
3437
3438 public Feature(Long eid, int eType, String key, Object value, int vType) {
3439
3440 this.entityID = eid;
3441 this.entityType = eType;
3442 this.key = key;
3443 this.value = value;
3444 this.valueType = vType;
3445 }
3446 }
3447
3448 private class RestrictionComepator implements Comparator{
3449 public int compare(Object o1, Object o2){
3450 Restriction r1 = (Restriction) o1;
3451 Restriction r2 = (Restriction) o2;
3452 return r1.getKey().compareTo(r2.getKey());
3453 }
3454
3455 public boolean equals(Object o){
3456 return false;
3457 }
3458 }
3459
3460
3461}
3462
3463