0001 /*
0002 * JDBCDataStore.java
0003 *
0004 * Copyright (c) 1995-2010, The University of Sheffield. See the file
0005 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006 *
0007 * This file is part of GATE (see http://gate.ac.uk/), and is free
0008 * software, licenced under the GNU Library General Public License,
0009 * Version 2, June 1991 (in the distribution as file licence.html,
0010 * and also available at http://gate.ac.uk/gate/licence.html).
0011 *
0012 * Marin Dimitrov, 18/Sep/2001
0013 *
0014 * $Id: JDBCDataStore.java 12006 2009-12-01 17:24:28Z thomas_heitz $
0015 */
0016
0017 package gate.persist;
0018
0019 import java.io.Serializable;
0020 import java.net.URL;
0021 import java.sql.*;
0022 import java.util.*;
0023
0024 import junit.framework.Assert;
0025 import oracle.jdbc.driver.OraclePreparedStatement;
0026
0027 import gate.*;
0028 import gate.annotation.DatabaseAnnotationSetImpl;
0029 import gate.annotation.EventAwareAnnotationSet;
0030 import gate.corpora.*;
0031 import gate.event.*;
0032 import gate.security.*;
0033 import gate.security.SecurityException;
0034 import gate.util.*;
0035
0036 public abstract class JDBCDataStore extends AbstractFeatureBearer
0037 implements DatabaseDataStore,
0038 CreoleListener {
0039
0040 /** --- */
0041 private static final boolean DEBUG = false;
0042
0043 /** jdbc url for the database */
0044 private String dbURL;
0045 protected String dbSchema;
0046 protected int dbType;
0047
0048 protected String datastoreComment;
0049 protected String iconName;
0050
0051 /** jdbc driver name */
0052 // private String driverName;
0053
0054 /**
0055 * GUID of the datastore
0056 * read from T_PARAMETER table
0057 * */
0058 private String dbID;
0059
0060 /** security session identifying all access to the datastore */
0061 protected Session session;
0062
0063 /** datastore name? */
0064 protected String name;
0065
0066 /** jdbc connection, all access to the database is made through this connection
0067 */
0068 protected transient Connection jdbcConn;
0069
0070 /** Security factory that contols access to objects in the datastore
0071 * the security session is from this factory
0072 * */
0073 protected transient AccessController ac;
0074
0075 /** anyone interested in datastore related events */
0076 private transient Vector datastoreListeners;
0077
0078 /** resources that should be sync-ed if datastore is close()-d */
0079 protected transient Vector dependentResources;
0080
0081 /** Do not use this class directly - use one of the subclasses */
0082 protected JDBCDataStore() {
0083
0084 this.datastoreListeners = new Vector();
0085 this.dependentResources = new Vector();
0086 }
0087
0088
0089 /* interface DataStore */
0090
0091 /**
0092 * Save: synchonise the in-memory image of the LR with the persistent
0093 * image.
0094 */
0095 public String getComment() {
0096
0097 Assert.assertNotNull(this.datastoreComment);
0098 return this.datastoreComment;
0099 }
0100
0101 /**
0102 * Returns the name of the icon to be used when this datastore is displayed
0103 * in the GUI
0104 */
0105 public String getIconName() {
0106 Assert.assertNotNull(this.iconName);
0107 return this.iconName;
0108 }
0109
0110
0111
0112 /** Get the name of an LR from its ID. */
0113 public String getLrName(Object lrId)
0114 throws PersistenceException {
0115
0116 if (false == lrId instanceof Long) {
0117 throw new IllegalArgumentException();
0118 }
0119
0120 Long ID = (Long)lrId;
0121
0122 PreparedStatement pstmt = null;
0123 ResultSet rset = null;
0124
0125 try {
0126 String sql = " select lr_name " +
0127 " from "+this.dbSchema+"t_lang_resource " +
0128 " where lr_id = ?";
0129
0130 pstmt = this.jdbcConn.prepareStatement(sql);
0131 pstmt.setLong(1,ID.longValue());
0132 pstmt.execute();
0133 rset = pstmt.getResultSet();
0134
0135 rset.next();
0136 String result = rset.getString("lr_name");
0137
0138 return result;
0139 }
0140 catch(SQLException sqle) {
0141 throw new PersistenceException("can't get LR name from DB: ["+ sqle.getMessage()+"]");
0142 }
0143 finally {
0144 DBHelper.cleanup(pstmt);
0145 DBHelper.cleanup(rset);
0146 }
0147 }
0148
0149
0150
0151 /** Set the URL for the underlying storage mechanism. */
0152 public void setStorageUrl(String storageUrl) throws PersistenceException {
0153
0154 if (!storageUrl.startsWith("jdbc:")) {
0155 throw new PersistenceException("Incorrect JDBC url (should start with \"jdbc:\")");
0156 }
0157 else {
0158 this.dbURL = storageUrl;
0159 this.dbSchema = DBHelper.getSchemaPrefix(this.dbURL);
0160 this.dbType = DBHelper.getDatabaseType(this.dbURL);
0161 Assert.assertNotNull(this.dbSchema);
0162 Assert.assertTrue(this.dbType > 0);
0163 }
0164
0165 }
0166
0167 /** Get the URL for the underlying storage mechanism. */
0168 public String getStorageUrl() {
0169
0170 return this.dbURL;
0171 }
0172
0173
0174 /**
0175 * Create a new data store. <B>NOTE:</B> for some data stores
0176 * creation is an system administrator task; in such cases this
0177 * method will throw an UnsupportedOperationException.
0178 */
0179 public void create()
0180 throws PersistenceException, UnsupportedOperationException {
0181
0182 throw new UnsupportedOperationException("create() is not supported for DatabaseDataStore");
0183 }
0184
0185
0186
0187 /** Open a connection to the data store. */
0188 public void open() throws PersistenceException {
0189 try {
0190
0191 //1, get connection to the DB
0192 jdbcConn = DBHelper.connect(dbURL);
0193
0194 //2. create security factory
0195 // this.ac = new AccessControllerImpl();
0196 this.ac = Factory.createAccessController(dbURL);
0197
0198 //3. open and init the security factory with the same DB repository
0199 ac.open();
0200
0201 //4. get DB ID
0202 this.dbID = this.readDatabaseID();
0203
0204 }
0205 catch(SQLException sqle) {
0206 throw new PersistenceException("could not get DB connection ["+ sqle.getMessage() +"]");
0207 }
0208 catch(ClassNotFoundException clse) {
0209 throw new PersistenceException("cannot locate JDBC driver ["+ clse.getMessage() +"]");
0210 }
0211
0212 //5. register for Creole events
0213 Gate.getCreoleRegister().addCreoleListener(this);
0214 }
0215
0216 /** Close the data store. */
0217 public void close() throws PersistenceException {
0218
0219 //-1. Unregister for Creole events
0220 Gate.getCreoleRegister().removeCreoleListener(this);
0221
0222 //0. sync all dependednt resources
0223 for (int i=0; i< this.dependentResources.size(); i++) {
0224 LanguageResource lr = (LanguageResource)this.dependentResources.elementAt(i);
0225
0226 try {
0227 sync(lr);
0228 }
0229 catch(SecurityException se) {
0230 //do nothing
0231 //there was an oper and modified resource for which the user has no write
0232 //privileges
0233 //not doing anything is perfectly ok because the resource won't bechanged in DB
0234 }
0235
0236 //unload UI component
0237 Factory.deleteResource(lr);
0238 }
0239
0240 //1. close security factory
0241 ac.close();
0242
0243 DBHelper.disconnect(this.jdbcConn);
0244
0245 //finally unregister this datastore from the GATE register of datastores
0246 Gate.getDataStoreRegister().remove(this);
0247 }
0248
0249 /**
0250 * Delete the data store. <B>NOTE:</B> for some data stores
0251 * deletion is an system administrator task; in such cases this
0252 * method will throw an UnsupportedOperationException.
0253 */
0254 public void delete()
0255 throws PersistenceException, UnsupportedOperationException {
0256
0257 throw new UnsupportedOperationException("delete() is not supported for DatabaseDataStore");
0258 }
0259
0260 /**
0261 * Delete a resource from the data store.
0262 * @param lrId a data-store specific unique identifier for the resource
0263 * @param lrClassName class name of the type of resource
0264 */
0265
0266 public void delete(String lrClassName, Object lrId)
0267 throws PersistenceException,SecurityException {
0268 //0. preconditions
0269 if (false == lrId instanceof Long) {
0270 throw new IllegalArgumentException();
0271 }
0272
0273 if (!lrClassName.equals(DBHelper.DOCUMENT_CLASS) &&
0274 !lrClassName.equals(DBHelper.CORPUS_CLASS)) {
0275 throw new IllegalArgumentException("Only Corpus and Document classes are supported" +
0276 " by Database data store");
0277 }
0278
0279 //1. check session
0280 if (null == this.session) {
0281 throw new SecurityException("session not set");
0282 }
0283
0284 if (false == this.ac.isValidSession(this.session)) {
0285 throw new SecurityException("invalid session supplied");
0286 }
0287
0288 //2. check permissions
0289 if (false == canWriteLR(lrId)) {
0290 throw new SecurityException("insufficient privileges");
0291 }
0292
0293 //3. try to lock document, so that we'll be sure no one is editing it
0294 //NOTE: use the private method
0295 User lockingUser = this.getLockingUser((Long)lrId);
0296 User currUser = this.session.getUser();
0297
0298 if (null != lockingUser && false == lockingUser.equals(currUser)) {
0299 //oops, someone is editing now
0300 throw new PersistenceException("LR locked by another user");
0301 }
0302
0303 boolean transFailed = false;
0304 try {
0305 //4. autocommit should be FALSE because of LOBs
0306 beginTrans();
0307
0308 //5. perform changes, if anything goes wrong, rollback
0309 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
0310 deleteDocument((Long)lrId);
0311 }
0312 else {
0313 deleteCorpus((Long)lrId);
0314 }
0315
0316 //6. done, commit
0317 commitTrans();
0318 }
0319 catch(PersistenceException pe) {
0320 transFailed = true;
0321 throw(pe);
0322 }
0323 finally {
0324 //problems?
0325 if (transFailed) {
0326 rollbackTrans();
0327 }
0328 }
0329
0330 //7, unlock
0331 //do nothing - the resource does not exist anymore
0332
0333 //8. delete from the list of dependent resources
0334 boolean resourceFound = false;
0335 Iterator it = this.dependentResources.iterator();
0336 while (it.hasNext()) {
0337 LanguageResource lr = (LanguageResource)it.next();
0338 if (lr.getLRPersistenceId().equals(lrId)) {
0339 resourceFound = true;
0340 it.remove();
0341 break;
0342 }
0343 }
0344
0345 //Assert.assertTrue(resourceFound);
0346
0347 //9. let the world know about it
0348 fireResourceDeleted(
0349 new DatastoreEvent(this, DatastoreEvent.RESOURCE_DELETED, null, lrId));
0350
0351 //10. unload the resource form the GUI
0352 try {
0353 unloadLR((Long)lrId);
0354 }
0355 catch(GateException ge) {
0356 Err.prln("can't unload resource from GUI...");
0357 }
0358 }
0359
0360
0361
0362 /**
0363 * Save: synchonise the in-memory image of the LR with the persistent
0364 * image.
0365 */
0366 public void sync(LanguageResource lr)
0367 throws PersistenceException,SecurityException {
0368
0369 //4.delegate (open a new transaction)
0370 _sync(lr,true);
0371 }
0372
0373
0374 /**
0375 * Set method for the autosaving behaviour of the data store.
0376 * <B>NOTE:</B> many types of datastore have no auto-save function,
0377 * in which case this will throw an UnsupportedOperationException.
0378 */
0379 public void setAutoSaving(boolean autoSaving)
0380 throws UnsupportedOperationException,PersistenceException {
0381 try {
0382 this.jdbcConn.setAutoCommit(true);
0383 }
0384 catch(SQLException sqle) {
0385 throw new PersistenceException("cannot change autosave mode ["+sqle.getMessage()+"]");
0386 }
0387
0388 }
0389
0390 /** Get the autosaving behaviour of the LR. */
0391 public boolean isAutoSaving() {
0392 throw new MethodNotImplementedException();
0393 }
0394
0395 /** Adopt a resource for persistence. */
0396 public LanguageResource adopt(LanguageResource lr, SecurityInfo secInfo)
0397 throws PersistenceException,SecurityException {
0398 //open a new transaction
0399 return _adopt(lr,secInfo,true);
0400 }
0401
0402
0403 protected LanguageResource _adopt(LanguageResource lr,
0404 SecurityInfo secInfo,
0405 boolean openNewTrans)
0406 throws PersistenceException,SecurityException {
0407
0408 LanguageResource result = null;
0409
0410 //-1. preconditions
0411 Assert.assertNotNull(lr);
0412 Assert.assertNotNull(secInfo);
0413 if (false == lr instanceof Document &&
0414 false == lr instanceof Corpus) {
0415 //only documents and corpuses could be serialized in DB
0416 throw new IllegalArgumentException("only Documents and Corpuses could "+
0417 "be serialized in DB");
0418 }
0419
0420 //0. check SecurityInfo
0421 if (false == this.ac.isValidSecurityInfo(secInfo)) {
0422 throw new SecurityException("Invalid security settings supplied");
0423 }
0424
0425 //1. user session should be set
0426 if (null == this.session) {
0427 throw new SecurityException("user session not set");
0428 }
0429
0430 //2. check the LR's current DS
0431 DataStore currentDS = lr.getDataStore();
0432 if(currentDS == null) {
0433 // an orphan - do the adoption (later)
0434 }
0435 else if(currentDS.equals(this)){ // adopted already
0436 return lr;
0437 }
0438 else { // someone else's child
0439 throw new PersistenceException(
0440 "Can't adopt a resource which is already in a different datastore");
0441 }
0442
0443
0444 //3. is the LR one of Document or Corpus?
0445 if (false == lr instanceof Document &&
0446 false == lr instanceof Corpus) {
0447
0448 throw new IllegalArgumentException("Database datastore is implemented only for "+
0449 "Documents and Corpora");
0450 }
0451
0452 //4.is the document already stored in this storage?
0453 Object persistID = lr.getLRPersistenceId();
0454 if (persistID != null) {
0455 throw new PersistenceException("This LR is already stored in the " +
0456 " database (persistance ID is =["+(Long)persistID+"] )");
0457 }
0458
0459 boolean transFailed = false;
0460 try {
0461 //5 autocommit should be FALSE because of LOBs
0462 if (openNewTrans) {
0463 // this.jdbcConn.setAutoCommit(false);
0464 beginTrans();
0465 }
0466
0467 //6. perform changes, if anything goes wrong, rollback
0468 if (lr instanceof Document) {
0469 result = createDocument((Document)lr,secInfo);
0470 //System.out.println("result ID=["+result.getLRPersistenceId()+"]");
0471 }
0472 else {
0473 //adopt each document from the corpus in a separate transaction context
0474 result = createCorpus((Corpus)lr,secInfo,true);
0475 }
0476
0477 //7. done, commit
0478 if (openNewTrans) {
0479 // this.jdbcConn.commit();
0480 commitTrans();
0481 }
0482 }
0483 /*
0484 catch(SQLException sqle) {
0485 transFailed = true;
0486 throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]");
0487 }
0488 */
0489 catch(PersistenceException pe) {
0490 transFailed = true;
0491 throw(pe);
0492 }
0493 catch(SecurityException se) {
0494 transFailed = true;
0495 throw(se);
0496 }
0497 finally {
0498 //problems?
0499 if (transFailed) {
0500 System.out.println("trans failed ...rollback");
0501 rollbackTrans();
0502 /* try {
0503 this.jdbcConn.rollback();
0504 }
0505 catch(SQLException sqle) {
0506 throw new PersistenceException(sqle);
0507 }
0508 */
0509 }
0510 }
0511
0512 //8. let the world know
0513 fireResourceAdopted(
0514 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED,
0515 result,
0516 result.getLRPersistenceId())
0517 );
0518
0519 //9. fire also resource written event because it's now saved
0520 fireResourceWritten(
0521 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN,
0522 result,
0523 result.getLRPersistenceId()
0524 )
0525 );
0526
0527 //10. add the resource to the list of dependent resources - i.e. the ones that the
0528 //data store should take care upon closing [and call sync()]
0529 this.dependentResources.add(result);
0530
0531 return result;
0532 }
0533
0534
0535 /** Get a list of the types of LR that are present in the data store. */
0536 public List getLrTypes() throws PersistenceException {
0537
0538 Vector lrTypes = new Vector();
0539 Statement stmt = null;
0540 ResultSet rs = null;
0541
0542 try {
0543 stmt = this.jdbcConn.createStatement();
0544 rs = stmt.executeQuery(" SELECT lrtp_type " +
0545 " FROM "+this.dbSchema+"t_lr_type LRTYPE ");
0546
0547 while (rs.next()) {
0548 //access by index is faster
0549 String lrType = rs.getString(1);
0550 lrTypes.add(lrType);
0551 }
0552
0553 return lrTypes;
0554 }
0555 catch(SQLException sqle) {
0556 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]");
0557 }
0558 finally {
0559 DBHelper.cleanup(rs);
0560 DBHelper.cleanup(stmt);
0561 }
0562 }
0563
0564
0565 /** Get a list of the IDs of LRs of a particular type that are present. */
0566 public List getLrIds(String lrType) throws PersistenceException {
0567
0568 Vector lrIDs = new Vector();
0569 PreparedStatement stmt = null;
0570 ResultSet rs = null;
0571
0572 try {
0573 stmt = this.jdbcConn.prepareStatement(
0574 " SELECT lr_id " +
0575 " FROM "+this.dbSchema+"t_lang_resource LR, " +
0576 " "+this.dbSchema+"t_lr_type LRTYPE " +
0577 " WHERE LR.lr_type_id = LRTYPE.lrtp_id " +
0578 " AND LRTYPE.lrtp_type = ? " +
0579 " ORDER BY lr_name"
0580 );
0581 stmt.setString(1,lrType);
0582
0583 //oracle special
0584 if (this.dbType == DBHelper.ORACLE_DB) {
0585 ((OraclePreparedStatement)stmt).setRowPrefetch(DBHelper.CHINK_SIZE_SMALL);
0586 }
0587
0588 stmt.execute();
0589 rs = stmt.getResultSet();
0590
0591 while (rs.next()) {
0592 //access by index is faster
0593 Long lrID = new Long(rs.getLong(1));
0594 lrIDs.add(lrID);
0595 }
0596
0597 return lrIDs;
0598 }
0599 catch(SQLException sqle) {
0600 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]");
0601 }
0602 finally {
0603 DBHelper.cleanup(rs);
0604 DBHelper.cleanup(stmt);
0605 }
0606
0607 }
0608
0609
0610 /** Get a list of the names of LRs of a particular type that are present. */
0611 public List getLrNames(String lrType) throws PersistenceException {
0612
0613 Vector lrNames = new Vector();
0614 PreparedStatement stmt = null;
0615 ResultSet rs = null;
0616
0617 try {
0618 stmt = this.jdbcConn.prepareStatement(
0619 " SELECT lr_name " +
0620 " FROM "+this.dbSchema+"t_lang_resource LR, " +
0621 " t_lr_type LRTYPE " +
0622 " WHERE LR.lr_type_id = LRTYPE.lrtp_id " +
0623 " AND LRTYPE.lrtp_type = ? " +
0624 " ORDER BY lr_name desc"
0625 );
0626 stmt.setString(1,lrType);
0627
0628 //Oracle special
0629 if (this.dbType == DBHelper.ORACLE_DB) {
0630 ((OraclePreparedStatement)stmt).setRowPrefetch(DBHelper.CHINK_SIZE_SMALL);
0631 }
0632
0633 stmt.execute();
0634 rs = stmt.getResultSet();
0635
0636 while (rs.next()) {
0637 //access by index is faster
0638 String lrName = rs.getString(1);
0639 lrNames.add(lrName);
0640 }
0641
0642 return lrNames;
0643 }
0644 catch(SQLException sqle) {
0645 throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]");
0646 }
0647 finally {
0648 DBHelper.cleanup(rs);
0649 DBHelper.cleanup(stmt);
0650 }
0651 }
0652
0653 /**
0654 * Checks if the user (identified by the sessionID)
0655 * has read access to the LR
0656 */
0657 public boolean canReadLR(Object lrID)
0658 throws PersistenceException, SecurityException{
0659
0660 return canAccessLR((Long) lrID,DBHelper.READ_ACCESS);
0661 }
0662
0663
0664
0665 /**
0666 * Checks if the user (identified by the sessionID)
0667 * has write access to the LR
0668 */
0669 public boolean canWriteLR(Object lrID)
0670 throws PersistenceException, SecurityException{
0671
0672 return canAccessLR((Long) lrID,DBHelper.WRITE_ACCESS);
0673 }
0674
0675 /**
0676 * Checks if the user (identified by the sessionID)
0677 * has some access (read/write) to the LR
0678 */
0679 protected boolean canAccessLR(Long lrID,int mode)
0680 throws PersistenceException, SecurityException{
0681
0682 //abstract
0683 throw new MethodNotImplementedException();
0684 }
0685
0686 /* interface DatabaseDataStore */
0687
0688 /**
0689 * starts a transaction
0690 * note that if u're already in transaction context this will not open
0691 * nested transaction
0692 * i.e. many consecutive calls to beginTrans() make no difference if no commit/rollback
0693 * is made meanwhile
0694 * */
0695 public void beginTrans()
0696 throws PersistenceException,UnsupportedOperationException{
0697
0698 try {
0699 this.jdbcConn.setAutoCommit(false);
0700 }
0701 catch(SQLException sqle) {
0702 throw new PersistenceException("cannot begin transaction, DB error is: ["
0703 +sqle.getMessage()+"]");
0704 }
0705 }
0706
0707
0708 /**
0709 * commits transaction
0710 * note that this will commit all the uncommited calls made so far
0711 * */
0712 public void commitTrans()
0713 throws PersistenceException,UnsupportedOperationException{
0714
0715 try {
0716 this.jdbcConn.commit();
0717 }
0718 catch(SQLException sqle) {
0719 throw new PersistenceException("cannot commit transaction, DB error is: ["
0720 +sqle.getMessage()+"]");
0721 }
0722
0723 }
0724
0725 /** rollsback a transaction */
0726 public void rollbackTrans()
0727 throws PersistenceException,UnsupportedOperationException{
0728
0729 try {
0730 this.jdbcConn.rollback();
0731 }
0732 catch(SQLException sqle) {
0733 throw new PersistenceException("cannot commit transaction, DB error is: ["
0734 +sqle.getMessage()+"]");
0735 }
0736
0737 }
0738
0739 /** not used */
0740 public Long timestamp()
0741 throws PersistenceException{
0742
0743 //implemented by the subclasses
0744 throw new MethodNotImplementedException();
0745 }
0746
0747 /** not used */
0748 public void deleteSince(Long timestamp)
0749 throws PersistenceException{
0750
0751 throw new MethodNotImplementedException();
0752 }
0753
0754 /** specifies the driver to be used to connect to the database? */
0755 /* public void setDriver(String driverName)
0756 throws PersistenceException{
0757
0758 this.driverName = driverName;
0759 }
0760 */
0761 /** Sets the name of this resource*/
0762 public void setName(String name){
0763 this.name = name;
0764 }
0765
0766 /** Returns the name of this resource*/
0767 public String getName(){
0768 return name;
0769 }
0770
0771
0772 /** --- */
0773 protected int findFeatureType(Object value) {
0774
0775 if (null == value)
0776 return DBHelper.VALUE_TYPE_NULL;
0777 else if (value instanceof Integer)
0778 return DBHelper.VALUE_TYPE_INTEGER;
0779 else if (value instanceof Long)
0780 return DBHelper.VALUE_TYPE_LONG;
0781 else if (value instanceof Boolean)
0782 return DBHelper.VALUE_TYPE_BOOLEAN;
0783 else if (value instanceof Double ||
0784 value instanceof Float)
0785 return DBHelper.VALUE_TYPE_FLOAT;
0786 else if (value instanceof String)
0787 return DBHelper.VALUE_TYPE_STRING;
0788 else if (value instanceof List) {
0789 //is the array empty?
0790 List arr = (List)value;
0791
0792 if (arr.isEmpty()) {
0793 return DBHelper.VALUE_TYPE_EMPTY_ARR;
0794 }
0795 else {
0796 Object element = arr.get(0);
0797
0798 if (element instanceof Integer)
0799 return DBHelper.VALUE_TYPE_INTEGER_ARR;
0800 else if (element instanceof Long)
0801 return DBHelper.VALUE_TYPE_LONG_ARR;
0802 else if (element instanceof Boolean)
0803 return DBHelper.VALUE_TYPE_BOOLEAN_ARR;
0804 else if (element instanceof Double ||
0805 element instanceof Float)
0806 return DBHelper.VALUE_TYPE_FLOAT_ARR;
0807 else if (element instanceof String)
0808 return DBHelper.VALUE_TYPE_STRING_ARR;
0809 }
0810 }
0811 else if (value instanceof Serializable) {
0812 return DBHelper.VALUE_TYPE_BINARY;
0813 }
0814
0815 //this should never happen
0816 throw new IllegalArgumentException();
0817 }
0818
0819 /** --- */
0820 public String getDatabaseID() {
0821 return this.dbID;
0822 }
0823
0824 /** reads the GUID from the database */
0825 /* protected abstract String readDatabaseID()
0826 throws PersistenceException;
0827 */
0828 /**
0829 * reads the ID of the database
0830 * every database should have unique string ID
0831 */
0832 protected String readDatabaseID() throws PersistenceException{
0833
0834 PreparedStatement pstmt = null;
0835 ResultSet rs = null;
0836 String result = null;
0837
0838 //1. read from DB
0839 try {
0840 String sql = " select par_value_string " +
0841 " from "+this.dbSchema+"t_parameter " +
0842 " where par_key = ? ";
0843
0844 pstmt = this.jdbcConn.prepareStatement(sql);
0845 pstmt.setString(1,DBHelper.DB_PARAMETER_GUID);
0846 pstmt.execute();
0847 rs = pstmt.getResultSet();
0848
0849 if (false == rs.next()) {
0850 throw new PersistenceException("Can't read database parameter ["+
0851 DBHelper.DB_PARAMETER_GUID+"]");
0852 }
0853 result = rs.getString(1);
0854 }
0855 catch(SQLException sqle) {
0856 throw new PersistenceException("Can't read database parameter ["+
0857 sqle.getMessage()+"]");
0858 }
0859 finally {
0860 DBHelper.cleanup(rs);
0861 DBHelper.cleanup(pstmt);
0862 }
0863
0864 if (DEBUG) {
0865 Out.println("reult=["+result+"]");
0866 }
0867
0868 return result;
0869 }
0870
0871
0872 /**
0873 * Removes a a previously registered {@link gate.event.DatastoreListener}
0874 * from the list listeners for this datastore
0875 */
0876 public void removeDatastoreListener(DatastoreListener l) {
0877
0878 Assert.assertNotNull(this.datastoreListeners);
0879
0880 synchronized(this.datastoreListeners) {
0881 this.datastoreListeners.remove(l);
0882 }
0883 }
0884
0885
0886 /**
0887 * Registers a new {@link gate.event.DatastoreListener} with this datastore
0888 */
0889 public void addDatastoreListener(DatastoreListener l) {
0890
0891 Assert.assertNotNull(this.datastoreListeners);
0892
0893 //this is not thread safe
0894 /* if (false == this.datastoreListeners.contains(l)) {
0895 Vector temp = (Vector)this.datastoreListeners.clone();
0896 temp.add(l);
0897 this.datastoreListeners = temp;
0898 }
0899 */
0900 synchronized(this.datastoreListeners) {
0901 if (false == this.datastoreListeners.contains(l)) {
0902 this.datastoreListeners.add(l);
0903 }
0904 }
0905 }
0906
0907 protected void fireResourceAdopted(DatastoreEvent e) {
0908
0909 Assert.assertNotNull(datastoreListeners);
0910 Vector temp = this.datastoreListeners;
0911
0912 int count = temp.size();
0913 for (int i = 0; i < count; i++) {
0914 ((DatastoreListener)temp.elementAt(i)).resourceAdopted(e);
0915 }
0916 }
0917
0918
0919 protected void fireResourceDeleted(DatastoreEvent e) {
0920
0921 Assert.assertNotNull(datastoreListeners);
0922 Vector temp = this.datastoreListeners;
0923
0924 int count = temp.size();
0925 for (int i = 0; i < count; i++) {
0926 ((DatastoreListener)temp.elementAt(i)).resourceDeleted(e);
0927 }
0928 }
0929
0930
0931 protected void fireResourceWritten(DatastoreEvent e) {
0932 Assert.assertNotNull(datastoreListeners);
0933 Vector temp = this.datastoreListeners;
0934
0935 int count = temp.size();
0936 for (int i = 0; i < count; i++) {
0937 ((DatastoreListener)temp.elementAt(i)).resourceWritten(e);
0938 }
0939 }
0940
0941 public void resourceLoaded(CreoleEvent e) {
0942 if(DEBUG)
0943 System.out.println("resource loaded...");
0944 }
0945
0946 public void resourceRenamed(Resource resource, String oldName,
0947 String newName){
0948 }
0949
0950
0951 public void resourceUnloaded(CreoleEvent e) {
0952
0953 Assert.assertNotNull(e.getResource());
0954 if(! (e.getResource() instanceof LanguageResource))
0955 return;
0956
0957 //1. check it's our resource
0958 LanguageResource lr = (LanguageResource)e.getResource();
0959
0960 //this is a resource from another DS, so no need to do anything
0961 if(lr.getDataStore() != this)
0962 return;
0963
0964 //2. remove from the list of reosurce that should be sunced if DS is closed
0965 this.dependentResources.remove(lr);
0966
0967 //3. don't save it, this may not be the user's choice
0968
0969 //4. remove the reource as listener for events from the DataStore
0970 //otherwise the DS will continue sending it events when the reource is
0971 // no longer active
0972 this.removeDatastoreListener((DatastoreListener)lr);
0973 }
0974
0975 public void datastoreOpened(CreoleEvent e) {
0976 if(DEBUG)
0977 System.out.println("datastore opened...");
0978 }
0979
0980 public void datastoreCreated(CreoleEvent e) {
0981 if(DEBUG)
0982 System.out.println("datastore created...");
0983 }
0984
0985 public void datastoreClosed(CreoleEvent e) {
0986 if(DEBUG)
0987 System.out.println("datastore closed...");
0988 //sync all dependent resources
0989 }
0990
0991 /** identify user using this datastore */
0992 public void setSession(Session s)
0993 throws gate.security.SecurityException {
0994
0995 this.session = s;
0996 }
0997
0998
0999
1000 /** identify user using this datastore */
1001 public Session getSession(Session s)
1002 throws gate.security.SecurityException {
1003
1004 return this.session;
1005 }
1006
1007 /** Get a list of LRs that satisfy some set or restrictions */
1008 public abstract List findLrIds(List constraints) throws PersistenceException;
1009
1010 /**
1011 * Get a list of LRs that satisfy some set or restrictions and are
1012 * of a particular type
1013 */
1014 public abstract List findLrIds(List constraints, String lrType)
1015 throws PersistenceException;
1016
1017
1018 /** get security information for LR . */
1019 public SecurityInfo getSecurityInfo(LanguageResource lr)
1020 throws PersistenceException {
1021
1022 //0. preconditions
1023 Assert.assertNotNull(lr);
1024 Assert.assertNotNull(lr.getLRPersistenceId());
1025 Assert.assertTrue(lr.getLRPersistenceId() instanceof Long);
1026 Assert.assertEquals(this,lr.getDataStore());
1027 Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
1028 lr instanceof DatabaseCorpusImpl);
1029
1030 PreparedStatement pstmt = null;
1031 ResultSet rs = null;
1032
1033 //1. read data
1034 Long userID = null;
1035 Long groupID = null;
1036 int perm;
1037 try {
1038 String sql = " select lr_owner_user_id, "+
1039 " lr_owner_group_id, " +
1040 " lr_access_mode "+
1041 " from "+this.dbSchema+"t_lang_resource "+
1042 " where lr_id = ?";
1043 pstmt = this.jdbcConn.prepareStatement(sql);
1044 pstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
1045 rs = pstmt.executeQuery();
1046
1047 if (false == rs.next()) {
1048 throw new PersistenceException("Invalid LR ID supplied - no data found");
1049 }
1050
1051 userID = new Long(rs.getLong("lr_owner_user_id"));
1052 groupID = new Long(rs.getLong("lr_owner_group_id"));
1053 perm = rs.getInt("lr_access_mode");
1054
1055 Assert.assertTrue(perm == SecurityInfo.ACCESS_GR_GW ||
1056 perm == SecurityInfo.ACCESS_GR_OW ||
1057 perm == SecurityInfo.ACCESS_OR_OW ||
1058 perm == SecurityInfo.ACCESS_WR_GW);
1059 }
1060 catch(SQLException sqle) {
1061 throw new PersistenceException("Can't read document permissions from DB, error is [" +
1062 sqle.getMessage() +"]");
1063 }
1064 finally {
1065 DBHelper.cleanup(rs);
1066 DBHelper.cleanup(pstmt);
1067 }
1068
1069 //2. get data from AccessController
1070 User usr = null;
1071 Group grp = null;
1072 try {
1073 usr = this.ac.findUser(userID);
1074 grp = this.ac.findGroup(groupID);
1075 }
1076 catch (SecurityException se) {
1077 throw new PersistenceException("Invalid security settings found in DB [" +
1078 se.getMessage() +"]");
1079 }
1080
1081 //3. construct SecurityInfo
1082 SecurityInfo si = new SecurityInfo(perm,usr,grp);
1083
1084
1085 return si;
1086 }
1087
1088 /** creates a LR of type Corpus */
1089 protected Corpus createCorpus(Corpus corp,SecurityInfo secInfo, boolean newTransPerDocument)
1090 throws PersistenceException,SecurityException {
1091
1092 //1. create an LR entry for the corpus (T_LANG_RESOURCE table)
1093 Long lrID = createLR(DBHelper.CORPUS_CLASS,corp.getName(),secInfo,null);
1094
1095 //2.create am entry in the T_COPRUS table
1096 Long corpusID = null;
1097 //DB stuff
1098 CallableStatement cstmt = null;
1099 PreparedStatement pstmt = null;
1100 ResultSet rs = null;
1101
1102 try {
1103 if (this.dbType == DBHelper.ORACLE_DB) {
1104 cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.create_corpus(?,?) }");
1105 cstmt.setLong(1,lrID.longValue());
1106 cstmt.registerOutParameter(2,java.sql.Types.BIGINT);
1107 cstmt.execute();
1108 corpusID = new Long(cstmt.getLong(2));
1109 }
1110 else if (this.dbType == DBHelper.POSTGRES_DB) {
1111 pstmt = this.jdbcConn.prepareStatement("select persist_create_corpus(?) ");
1112 pstmt.setLong(1,lrID.longValue());
1113 pstmt.execute();
1114 rs = pstmt.getResultSet();
1115
1116 if (false == rs.next()) {
1117 throw new PersistenceException("empty result set");
1118 }
1119
1120 corpusID = new Long(rs.getLong(1));
1121 }
1122 else {
1123 Assert.fail();
1124 }
1125 }
1126 catch(SQLException sqle) {
1127 throw new PersistenceException("can't create corpus [step 2] in DB: ["+ sqle.getMessage()+"]");
1128 }
1129 finally {
1130 DBHelper.cleanup(cstmt);
1131 DBHelper.cleanup(pstmt);
1132 DBHelper.cleanup(rs);
1133 }
1134
1135 //3. for each document in the corpus call createDocument()
1136 Iterator itDocuments = corp.iterator();
1137 Vector dbDocs = new Vector();
1138
1139 while (itDocuments.hasNext()) {
1140 Document doc = (Document)itDocuments.next();
1141
1142 //3.1. ensure that the document is either transient or is from the ...
1143 // same DataStore
1144 if (doc.getLRPersistenceId() == null) {
1145 //transient document
1146
1147 //now this is a bit ugly patch, the transaction related functionality
1148 //should not be in this method
1149 if (newTransPerDocument) {
1150 beginTrans();
1151 }
1152
1153 //do call iterator::remove before the call to createDocument because
1154 //...there is a factory::deleteResource() call for the transient document there
1155 //...and the iterator gets confused
1156 itDocuments.remove();
1157
1158 //create doc in database and return DB ddoc
1159 Document dbDoc = createDocument(doc,corpusID,secInfo);
1160
1161 if (newTransPerDocument) {
1162 commitTrans();
1163 }
1164
1165 dbDocs.add(dbDoc);
1166 //8. let the world know
1167 fireResourceAdopted(new DatastoreEvent(this,
1168 DatastoreEvent.RESOURCE_ADOPTED,
1169 dbDoc,
1170 dbDoc.getLRPersistenceId()
1171 )
1172 );
1173
1174 //9. fire also resource written event because it's now saved
1175 fireResourceWritten(new DatastoreEvent(this,
1176 DatastoreEvent.RESOURCE_WRITTEN,
1177 dbDoc,
1178 dbDoc.getLRPersistenceId()
1179 )
1180 );
1181
1182 //10.
1183 //DON'T make explicit Factory call, since createDocument called above
1184 ///...takes care to call Factory.deleteResource for the transient document
1185 }
1186 else if (doc.getDataStore().equals(this)) {
1187 //persistent doc from the same DataStore
1188 fireResourceAdopted(
1189 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED,
1190 doc,
1191 doc.getLRPersistenceId()));
1192
1193 //6. fire also resource written event because it's now saved
1194 fireResourceWritten(
1195 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN,
1196 doc,
1197 doc.getLRPersistenceId()));
1198 }
1199 else {
1200 //persistent doc from other datastore
1201 //skip
1202 gate.util.Err.prln("document ["+doc.getLRPersistenceId()+"] is adopted from another "+
1203 " datastore. Skipped.");
1204 }
1205 }
1206
1207 //4. create features
1208 if (this.dbType == DBHelper.ORACLE_DB) {
1209 createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures());
1210 }
1211 else if (this.dbType == DBHelper.POSTGRES_DB) {
1212 createFeatures(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures());
1213 }
1214 else {
1215 Assert.fail();
1216 }
1217
1218
1219 //5. create a DatabaseCorpusImpl and return it
1220 /// Corpus dbCorpus = new DatabaseCorpusImpl(corp.getName(),
1221 /// this,
1222 /// lrID,
1223 /// corp.getFeatures(),
1224 /// dbDocs);
1225 ///
1226
1227 Corpus dbCorpus = null;
1228 FeatureMap params = Factory.newFeatureMap();
1229 HashMap initData = new HashMap();
1230
1231 initData.put("DS",this);
1232 initData.put("LR_ID",lrID);
1233 initData.put("CORP_NAME",corp.getName());
1234 initData.put("CORP_FEATURES",corp.getFeatures());
1235 initData.put("CORP_SUPPORT_LIST",dbDocs);
1236
1237 params.put("initData__$$__", initData);
1238
1239 try {
1240 //here we create the persistent LR via Factory, so it's registered
1241 //in GATE
1242 dbCorpus = (Corpus)Factory.createResource("gate.corpora.DatabaseCorpusImpl", params);
1243 }
1244 catch (gate.creole.ResourceInstantiationException ex) {
1245 throw new GateRuntimeException(ex.getMessage());
1246 }
1247
1248 //6. done
1249 return dbCorpus;
1250 }
1251
1252 /**
1253 * helper for adopt
1254 * creates a LR of type Document
1255 */
1256 protected Document createDocument(Document doc,SecurityInfo secInfo)
1257 throws PersistenceException,SecurityException {
1258
1259 //delegate, set to Null
1260 return createDocument(doc,null,secInfo);
1261 }
1262
1263
1264 /**
1265 * helper for adopt
1266 * creates a LR of type Document
1267 */
1268 protected Document createDocument(Document doc, Long corpusID,SecurityInfo secInfo)
1269 throws PersistenceException,SecurityException {
1270
1271 //-1. preconditions
1272 Assert.assertNotNull(doc);
1273 Assert.assertNotNull(secInfo);
1274
1275 //0. check securoity settings
1276 if (false == this.ac.isValidSecurityInfo(secInfo)) {
1277 throw new SecurityException("Invalid security settings");
1278 }
1279
1280 //1. get the data to be stored
1281 AnnotationSet defaultAnnotations = doc.getAnnotations();
1282 DocumentContent docContent = doc.getContent();
1283 FeatureMap docFeatures = doc.getFeatures();
1284 String docName = doc.getName();
1285 URL docURL = doc.getSourceUrl();
1286 Boolean docIsMarkupAware = doc.getMarkupAware();
1287 Long docStartOffset = doc.getSourceUrlStartOffset();
1288 Long docEndOffset = doc.getSourceUrlEndOffset();
1289 String docEncoding = null;
1290 try {
1291 docEncoding = (String)doc.
1292 getParameterValue(Document.DOCUMENT_ENCODING_PARAMETER_NAME);
1293 }
1294 catch(gate.creole.ResourceInstantiationException re) {
1295 throw new PersistenceException("cannot create document: error getting " +
1296 " document encoding ["+re.getMessage()+"]");
1297 }
1298
1299
1300 //3. create a Language Resource (an entry in T_LANG_RESOURCE) for this document
1301 Long lrID = createLR(DBHelper.DOCUMENT_CLASS,docName,secInfo,null);
1302
1303 //4. create a record in T_DOCUMENT for this document
1304 Long docID = createDoc(lrID,
1305 docURL,
1306 docEncoding,
1307 docStartOffset,
1308 docEndOffset,
1309 docIsMarkupAware,
1310 corpusID);
1311
1312
1313 //5. fill document content (record[s] in T_DOC_CONTENT)
1314
1315 //do we have content at all?
1316 if (docContent.size().longValue() > 0) {
1317 // updateDocumentContent(docContentID,docContent);
1318 updateDocumentContent(docID,docContent);
1319 }
1320
1321 //6. insert annotations, etc
1322
1323 //6.1. create default annotation set
1324 createAnnotationSet(lrID,defaultAnnotations);
1325
1326 //6.2. create named annotation sets
1327 Map namedAnns = doc.getNamedAnnotationSets();
1328 //the map may be null
1329 if (null != namedAnns) {
1330 Set setAnns = namedAnns.entrySet();
1331 Iterator itAnns = setAnns.iterator();
1332
1333 while (itAnns.hasNext()) {
1334 Map.Entry mapEntry = (Map.Entry)itAnns.next();
1335 //String currAnnName = (String)mapEntry.getKey();
1336 AnnotationSet currAnnSet = (AnnotationSet)mapEntry.getValue();
1337
1338 //create a-sets
1339 createAnnotationSet(lrID,currAnnSet);
1340 }
1341 }
1342
1343 //7. create features
1344 if (this.dbType == DBHelper.ORACLE_DB) {
1345 createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures);
1346 }
1347 else if (this.dbType == DBHelper.POSTGRES_DB) {
1348 createFeatures(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures);
1349 }
1350 else {
1351 Assert.fail();
1352 }
1353
1354
1355 //9. create a DatabaseDocument wrapper and return it
1356
1357 /* Document dbDoc = new DatabaseDocumentImpl(this.jdbcConn,
1358 doc.getName(),
1359 this,
1360 lrID,
1361 doc.getContent(),
1362 doc.getFeatures(),
1363 doc.getMarkupAware(),
1364 doc.getSourceUrl(),
1365 doc.getSourceUrlStartOffset(),
1366 doc.getSourceUrlEndOffset(),
1367 doc.getAnnotations(),
1368 doc.getNamedAnnotationSets());
1369 */
1370 Document dbDoc = null;
1371 FeatureMap params = Factory.newFeatureMap();
1372
1373 HashMap initData = new HashMap();
1374 initData.put("JDBC_CONN",this.jdbcConn);
1375 initData.put("DS",this);
1376 initData.put("LR_ID",lrID);
1377 initData.put("DOC_NAME",doc.getName());
1378 initData.put("DOC_CONTENT",doc.getContent());
1379 initData.put("DOC_FEATURES",doc.getFeatures());
1380 initData.put("DOC_MARKUP_AWARE",doc.getMarkupAware());
1381 initData.put("DOC_SOURCE_URL",doc.getSourceUrl());
1382 if(doc instanceof DocumentImpl){
1383 initData.put("DOC_STRING_CONTENT",
1384 ((DocumentImpl)doc).getStringContent());
1385 }
1386 initData.put("DOC_SOURCE_URL_START",doc.getSourceUrlStartOffset());
1387 initData.put("DOC_SOURCE_URL_END",doc.getSourceUrlEndOffset());
1388 initData.put("DOC_DEFAULT_ANNOTATIONS",doc.getAnnotations());
1389 initData.put("DOC_NAMED_ANNOTATION_SETS",doc.getNamedAnnotationSets());
1390
1391 params.put("initData__$$__", initData);
1392
1393 try {
1394 //here we create the persistent LR via Factory, so it's registered
1395 //in GATE
1396 dbDoc = (Document)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params);
1397 }
1398 catch (gate.creole.ResourceInstantiationException ex) {
1399 throw new GateRuntimeException(ex.getMessage());
1400 }
1401
1402 //unload the transient document
1403 //System.out.println("unloading "+doc.getName() +"...");
1404 Factory.deleteResource(doc);
1405
1406 return dbDoc;
1407 }
1408
1409 protected abstract Long createLR(String lrType,
1410 String lrName,
1411 SecurityInfo si,
1412 Long lrParentID)
1413 throws PersistenceException,SecurityException;
1414
1415
1416 protected abstract Long createDoc(Long _lrID,
1417 URL _docURL,
1418 String _docEncoding,
1419 Long _docStartOffset,
1420 Long _docEndOffset,
1421 Boolean _docIsMarkupAware,
1422 Long _corpusID)
1423 throws PersistenceException;
1424
1425 protected abstract void updateDocumentContent(Long docID,DocumentContent content)
1426 throws PersistenceException;
1427
1428 protected abstract void createAnnotationSet(Long lrID, AnnotationSet aset)
1429 throws PersistenceException;
1430
1431 protected abstract void createFeaturesBulk(Long entityID, int entityType, FeatureMap features)
1432 throws PersistenceException;
1433
1434 protected abstract void createFeatures(Long entityID, int entityType, FeatureMap features)
1435 throws PersistenceException;
1436
1437 /**
1438 * Save: synchonise the in-memory image of the LR with the persistent
1439 * image.
1440 */
1441 protected void _sync(LanguageResource lr, boolean openNewTrans)
1442 throws PersistenceException,SecurityException {
1443
1444 //0.preconditions
1445 Assert.assertNotNull(lr);
1446 Long lrID = (Long)lr.getLRPersistenceId();
1447
1448 if (false == lr instanceof Document &&
1449 false == lr instanceof Corpus) {
1450 //only documents and corpuses could be serialized in DB
1451 throw new IllegalArgumentException("only Documents and Corpuses could "+
1452 "be serialized in DB");
1453 }
1454
1455 // check that this LR is one of ours (i.e. has been adopted)
1456 if( null == lr.getDataStore() || false == lr.getDataStore().equals(this))
1457 throw new PersistenceException(
1458 "This LR is not stored in this DataStore"
1459 );
1460
1461
1462 //1. check session
1463 if (null == this.session) {
1464 throw new SecurityException("session not set");
1465 }
1466
1467 if (false == this.ac.isValidSession(this.session)) {
1468 throw new SecurityException("invalid session supplied");
1469 }
1470
1471 //2. check permissions
1472 if (false == canWriteLR(lrID)) {
1473 throw new SecurityException("insufficient privileges");
1474 }
1475
1476 //3. is the resource locked?
1477 User lockingUser = getLockingUser(lr);
1478 User currUser = this.session.getUser();
1479
1480 if (lockingUser != null && false == lockingUser.equals(currUser)) {
1481 throw new PersistenceException("document is locked by another user and cannot be synced");
1482 }
1483
1484
1485 boolean transFailed = false;
1486 try {
1487 //2. autocommit should be FALSE because of LOBs
1488 if (openNewTrans) {
1489 beginTrans();
1490 }
1491
1492 //3. perform changes, if anything goes wrong, rollback
1493 if (lr instanceof Document) {
1494 syncDocument((Document)lr);
1495 }
1496 else {
1497 syncCorpus((Corpus)lr);
1498 }
1499
1500 //4. done, commit
1501 if (openNewTrans) {
1502 commitTrans();
1503 }
1504 }
1505 catch(PersistenceException pe) {
1506 transFailed = true;
1507 throw(pe);
1508 }
1509 finally {
1510 //problems?
1511 if (transFailed) {
1512 rollbackTrans();
1513 }
1514 }
1515
1516 // let the world know about it
1517 fireResourceWritten(
1518 new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, lr, lr.getLRPersistenceId()));
1519 }
1520
1521 /**
1522 * Releases the exlusive lock on a resource from the persistent store.
1523 */
1524 protected User getLockingUser(LanguageResource lr)
1525 throws PersistenceException,SecurityException {
1526
1527 //0. preconditions
1528 Assert.assertNotNull(lr);
1529 Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
1530 lr instanceof DatabaseCorpusImpl);
1531 Assert.assertNotNull(lr.getLRPersistenceId());
1532 Assert.assertEquals(lr.getDataStore(),this);
1533
1534 //delegate
1535 return getLockingUser((Long)lr.getLRPersistenceId());
1536 }
1537
1538
1539
1540 /**
1541 * Releases the exlusive lock on a resource from the persistent store.
1542 */
1543 protected User getLockingUser(Long lrID)
1544 throws PersistenceException,SecurityException {
1545
1546 //1. check session
1547 if (null == this.session) {
1548 throw new SecurityException("session not set");
1549 }
1550
1551 if (false == this.ac.isValidSession(this.session)) {
1552 throw new SecurityException("invalid session supplied");
1553 }
1554
1555 //3. read from DB
1556 PreparedStatement pstmt = null;
1557 Long userID = null;
1558 ResultSet rs = null;
1559
1560 try {
1561
1562 String sql = null;
1563
1564 if (this.dbType == DBHelper.ORACLE_DB) {
1565 sql = " select nvl(lr_locking_user_id,0) as user_id" +
1566 " from "+this.dbSchema+"t_lang_resource " +
1567 " where lr_id = ?";
1568 }
1569 else if (this.dbType == DBHelper.POSTGRES_DB) {
1570 sql = " select coalesce(lr_locking_user_id,0) as user_id" +
1571 " from t_lang_resource " +
1572 " where lr_id = ?";
1573 }
1574 else {
1575 throw new IllegalArgumentException();
1576 }
1577
1578 pstmt = this.jdbcConn.prepareStatement(sql);
1579 pstmt.setLong(1,lrID.longValue());
1580 pstmt.execute();
1581 rs = pstmt.getResultSet();
1582
1583 if (false == rs.next()) {
1584 throw new PersistenceException("LR not found in DB");
1585 }
1586
1587 long result = rs.getLong("user_id");
1588
1589 return result == 0 ? null
1590 : this.ac.findUser(new Long(result));
1591 }
1592 catch(SQLException sqle) {
1593 throw new PersistenceException("can't get locking user from DB : ["+ sqle.getMessage()+"]");
1594 }
1595 finally {
1596 DBHelper.cleanup(rs);
1597 DBHelper.cleanup(pstmt);
1598 }
1599 }
1600
1601 /** helper for sync() - saves a Corpus in the database */
1602 protected void syncCorpus(Corpus corp)
1603 throws PersistenceException,SecurityException {
1604
1605 //0. preconditions
1606 Assert.assertNotNull(corp);
1607 Assert.assertTrue(corp instanceof DatabaseCorpusImpl);
1608 Assert.assertEquals(this,corp.getDataStore());
1609 Assert.assertNotNull(corp.getLRPersistenceId());
1610
1611 EventAwareCorpus dbCorpus = (EventAwareCorpus)corp;
1612
1613 //1. sync the corpus name?
1614 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_NAME)) {
1615 _syncLR(corp);
1616 }
1617
1618 //2. sync the corpus features?
1619 if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) {
1620 _syncFeatures(corp);
1621 }
1622
1623 //2.5 get removed documents and detach (not remove) them from the corpus in the
1624 //database
1625 List removedDocLRIDs = dbCorpus.getRemovedDocuments();
1626 if (removedDocLRIDs.size() > 0) {
1627 _syncRemovedDocumentsFromCorpus(removedDocLRIDs,(Long)corp.getLRPersistenceId());
1628 }
1629
1630 //3. get all LODADED documents
1631 //--Iterator it = corp.iterator();
1632 Iterator it = dbCorpus.getLoadedDocuments().iterator();
1633 //Out.prln("loaded docs = ["+dbCorpus.getLoadedDocuments().size()+"]");
1634 List newlyAddedDocs = dbCorpus.getAddedDocuments();
1635
1636 while (it.hasNext()) {
1637 Document dbDoc = (Document)it.next();
1638 //note - document may be NULL which means it was not loaded (load on demand)
1639 //just ignore it then
1640 if (null == dbDoc) {
1641 continue;
1642 }
1643
1644 //adopt/sync?
1645 if (null == dbDoc.getLRPersistenceId()) {
1646 //doc was never adopted, adopt it
1647
1648 //3.1 remove the transient doc from the corpus
1649 it.remove();
1650
1651 //3.2 get the security info for the corpus
1652 SecurityInfo si = getSecurityInfo(corp);
1653
1654
1655 Document adoptedDoc = null;
1656 try {
1657 //3.3. adopt the doc with the sec info
1658 //System.out.println("adopting ["+dbDoc.getName()+"] ...");
1659 //don't open a new transaction, since sync() already has opended one
1660 adoptedDoc = (Document)_adopt(dbDoc,si,true);
1661
1662 //3.4. add doc to corpus in DB
1663 addDocumentToCorpus((Long)adoptedDoc.getLRPersistenceId(),
1664 (Long)corp.getLRPersistenceId());
1665 }
1666 catch(SecurityException se) {
1667 throw new PersistenceException(se);
1668 }
1669
1670 //3.5 add back to corpus the new DatabaseDocument
1671 corp.add(adoptedDoc);
1672 }
1673 else {
1674 //don't open a new transaction, the sync() called for corpus has already
1675 //opened one
1676 try {
1677 _sync(dbDoc,true);
1678
1679 // let the world know about it
1680 fireResourceWritten( new DatastoreEvent(this,
1681 DatastoreEvent.RESOURCE_WRITTEN,
1682 dbDoc,
1683 dbDoc.getLRPersistenceId()
1684 )
1685 );
1686
1687 //if the document is form the same DS but did not belong to the corpus add it now
1688 //BUT ONLY if it's newly added - i.e. do nothing if the document already belongs to the
1689 //corpus and this is reflected in the database
1690 if (newlyAddedDocs.contains(dbDoc.getLRPersistenceId())) {
1691 //Out.pr("A");
1692 addDocumentToCorpus( (Long) dbDoc.getLRPersistenceId(),
1693 (Long) corp.getLRPersistenceId());
1694 }
1695 else {
1696 //Out.pr("I");
1697 }
1698 }
1699 catch(SecurityException se) {
1700 gate.util.Err.prln("document cannot be synced: ["+se.getMessage()+"]");
1701 }
1702 }
1703 }
1704 }
1705
1706 /** helper for sync() - saves a Document in the database */
1707 /** helper for sync() - saves a Document in the database */
1708 protected void syncDocument(Document doc)
1709 throws PersistenceException, SecurityException {
1710
1711 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1712 Assert.assertTrue(doc.getLRPersistenceId() instanceof Long);
1713
1714 Long lrID = (Long)doc.getLRPersistenceId();
1715 EventAwareLanguageResource dbDoc = (EventAwareLanguageResource)doc;
1716 //1. sync LR
1717 // only name can be changed here
1718 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_NAME)) {
1719 _syncLR(doc);
1720 }
1721
1722 //2. sync Document
1723 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_MAIN)) {
1724 _syncDocumentHeader(doc);
1725 }
1726
1727 //3. [optional] sync Content
1728 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_CONTENT)) {
1729 _syncDocumentContent(doc);
1730 }
1731
1732 //4. [optional] sync Features
1733 if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) {
1734 _syncFeatures(doc);
1735 }
1736
1737 //5. [optional] delete from DB named sets that were removed from the document
1738 Collection removedSets = ((EventAwareDocument)dbDoc).getRemovedAnnotationSets();
1739 Collection addedSets = ((EventAwareDocument)dbDoc).getAddedAnnotationSets();
1740 if (false == removedSets.isEmpty() || false == addedSets.isEmpty()) {
1741 _syncAnnotationSets(doc,removedSets,addedSets);
1742 }
1743
1744 //6. [optional] sync Annotations
1745 _syncAnnotations(doc);
1746 }
1747
1748
1749 /**
1750 * helper for sync()
1751 * NEVER call directly
1752 */
1753 protected abstract void _syncLR(LanguageResource lr)
1754 throws PersistenceException,SecurityException;
1755
1756 /** helper for sync() - never call directly */
1757 protected abstract void _syncDocumentHeader(Document doc)
1758 throws PersistenceException;
1759
1760 /** helper for sync() - never call directly */
1761 protected abstract void _syncDocumentContent(Document doc)
1762 throws PersistenceException;
1763
1764 /** helper for sync() - never call directly */
1765 protected abstract void _syncFeatures(LanguageResource lr)
1766 throws PersistenceException;
1767
1768 /** helper for sync() - never call directly */
1769 protected void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets)
1770 throws PersistenceException {
1771
1772 //0. preconditions
1773 Assert.assertNotNull(doc);
1774 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1775 Assert.assertNotNull(doc.getLRPersistenceId());
1776 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
1777 this.getDatabaseID());
1778 Assert.assertNotNull(removedSets);
1779 Assert.assertNotNull(addedSets);
1780
1781 Long lrID = (Long)doc.getLRPersistenceId();
1782
1783 //1. delete from DB removed a-sets
1784 PreparedStatement stmt = null;
1785
1786 try {
1787
1788 if (this.dbType == DBHelper.ORACLE_DB) {
1789 stmt = this.jdbcConn.prepareCall("{ call "+this.dbSchema+"persist.delete_annotation_set(?,?) }");
1790 }
1791 else if (this.dbType == DBHelper.POSTGRES_DB) {
1792 stmt = this.jdbcConn.prepareStatement("select persist_delete_annotation_set(?,?)");
1793 }
1794 else {
1795 Assert.fail();
1796 }
1797
1798 Iterator it = removedSets.iterator();
1799 while (it.hasNext()) {
1800 String setName = (String)it.next();
1801 stmt.setLong(1,lrID.longValue());
1802 stmt.setString(2,setName);
1803 stmt.execute();
1804 }
1805 }
1806 catch(SQLException sqle) {
1807 throw new PersistenceException("can't remove annotation set from DB: ["+ sqle.getMessage()+"]");
1808 }
1809 finally {
1810 DBHelper.cleanup(stmt);
1811 }
1812
1813 //2. create in DB new a-sets
1814 Iterator it = addedSets.iterator();
1815 while (it.hasNext()) {
1816 String setName = (String)it.next();
1817 AnnotationSet aset = doc.getAnnotations(setName);
1818
1819 Assert.assertNotNull(aset);
1820 Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl);
1821
1822 createAnnotationSet(lrID,aset);
1823 }
1824 }
1825
1826
1827 /** helper for sync() - never call directly */
1828 protected void _syncAnnotations(Document doc)
1829 throws PersistenceException {
1830
1831 //0. preconditions
1832 Assert.assertNotNull(doc);
1833 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1834 Assert.assertNotNull(doc.getLRPersistenceId());
1835 Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
1836 this.getDatabaseID());
1837
1838
1839 EventAwareDocument ead = (EventAwareDocument)doc;
1840 //1. get the sets read from the DB for this document
1841 //chnaged annotations can occur only in such sets
1842 Collection loadedSets = ead.getLoadedAnnotationSets();
1843
1844 Iterator it = loadedSets.iterator();
1845 while (it.hasNext()) {
1846 AnnotationSet as = (AnnotationSet)it.next();
1847 //check that this set is neither NEW nor DELETED
1848 //they should be already synced
1849 if (ead.getAddedAnnotationSets().contains(as.getName()) ||
1850 ead.getRemovedAnnotationSets().contains(as.getName())) {
1851 //oops, ignore it
1852 continue;
1853 }
1854
1855 EventAwareAnnotationSet eas = (EventAwareAnnotationSet)as;
1856 Assert.assertNotNull(as);
1857
1858 Collection anns = null;
1859 anns = eas.getAddedAnnotations();
1860 Assert.assertNotNull(anns);
1861 if (anns.size()>0) {
1862 _syncAddedAnnotations(doc,as,anns);
1863 }
1864
1865 anns = eas.getRemovedAnnotations();
1866 Assert.assertNotNull(anns);
1867 if (anns.size()>0) {
1868 _syncRemovedAnnotations(doc,as,anns);
1869 }
1870
1871 anns = eas.getChangedAnnotations();
1872 Assert.assertNotNull(anns);
1873 if (anns.size()>0) {
1874 _syncChangedAnnotations(doc,as,anns);
1875 }
1876 }
1877 }
1878
1879 /** helper for sync() - never call directly */
1880 protected void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes)
1881 throws PersistenceException {
1882
1883 //0.preconditions
1884 Assert.assertNotNull(doc);
1885 Assert.assertNotNull(as);
1886 Assert.assertNotNull(changes);
1887 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1888 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
1889 Assert.assertTrue(changes.size() > 0);
1890
1891
1892 PreparedStatement pstmt = null;
1893 ResultSet rs = null;
1894 CallableStatement cstmt = null;
1895 Long lrID = (Long)doc.getLRPersistenceId();
1896 Long asetID = null;
1897
1898 try {
1899 //1. get the a-set ID in the database
1900 String sql = " select as_id " +
1901 " from "+this.dbSchema+"v_annotation_set " +
1902 " where lr_id = ? ";
1903 //do we have aset name?
1904 String clause = null;
1905 String name = as.getName();
1906 if (null != name) {
1907 clause = " and as_name = ? ";
1908 }
1909 else {
1910 clause = " and as_name is null ";
1911 }
1912 sql = sql + clause;
1913
1914 pstmt = this.jdbcConn.prepareStatement(sql);
1915 pstmt.setLong(1,lrID.longValue());
1916 if (null != name) {
1917 pstmt.setString(2,name);
1918 }
1919 pstmt.execute();
1920 rs = pstmt.getResultSet();
1921
1922 if (rs.next()) {
1923 asetID = new Long(rs.getLong("as_id"));
1924 }
1925 else {
1926 throw new PersistenceException("cannot find annotation set with" +
1927 " name=["+name+"] , LRID=["+lrID+"] in database");
1928 }
1929
1930 //cleanup
1931 DBHelper.cleanup(rs);
1932 DBHelper.cleanup(pstmt);
1933
1934 //3. insert the new annotations from this set
1935
1936 //3.1. prepare call
1937 if (this.dbType == DBHelper.ORACLE_DB) {
1938
1939 cstmt = this.jdbcConn.prepareCall(
1940 "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }");
1941
1942 Long annGlobalID = null;
1943 Iterator it = changes.iterator();
1944
1945 while (it.hasNext()) {
1946
1947 //3.2. insert annotation
1948 Annotation ann = (Annotation)it.next();
1949
1950 Node start = (Node)ann.getStartNode();
1951 Node end = (Node)ann.getEndNode();
1952 String type = ann.getType();
1953
1954 cstmt.setLong(1,lrID.longValue());
1955 cstmt.setLong(2,ann.getId().longValue());
1956 cstmt.setLong(3,asetID.longValue());
1957 cstmt.setLong(4,start.getId().longValue());
1958 cstmt.setLong(5,start.getOffset().longValue());
1959 cstmt.setLong(6,end.getId().longValue());
1960 cstmt.setLong(7,end.getOffset().longValue());
1961 cstmt.setString(8,type);
1962 cstmt.registerOutParameter(9,java.sql.Types.BIGINT);
1963
1964 cstmt.execute();
1965 annGlobalID = new Long(cstmt.getLong(9));
1966
1967 //3.3. set annotation features
1968 FeatureMap features = ann.getFeatures();
1969 Assert.assertNotNull(features);
1970
1971 if (this.dbType == DBHelper.ORACLE_DB) {
1972 createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
1973 }
1974 else if (this.dbType == DBHelper.POSTGRES_DB) {
1975 createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
1976 }
1977 else {
1978 Assert.fail();
1979 }
1980 }
1981 }
1982 else if (this.dbType == DBHelper.POSTGRES_DB) {
1983
1984 sql = "select persist_create_annotation(?,?,?,?,?,?,?,?)";
1985 pstmt = this.jdbcConn.prepareStatement(sql);
1986
1987 Long annGlobalID = null;
1988 Iterator it = changes.iterator();
1989
1990 while (it.hasNext()) {
1991
1992 //3.2. insert annotation
1993 Annotation ann = (Annotation)it.next();
1994
1995 Node start = (Node)ann.getStartNode();
1996 Node end = (Node)ann.getEndNode();
1997 String type = ann.getType();
1998
1999 pstmt.setLong(1,lrID.longValue());
2000 pstmt.setLong(2,ann.getId().longValue());
2001 pstmt.setLong(3,asetID.longValue());
2002 pstmt.setLong(4,start.getId().longValue());
2003 pstmt.setLong(5,start.getOffset().longValue());
2004 pstmt.setLong(6,end.getId().longValue());
2005 pstmt.setLong(7,end.getOffset().longValue());
2006 pstmt.setString(8,type);
2007 pstmt.execute();
2008
2009 rs = pstmt.getResultSet();
2010
2011 if (false == rs.next()) {
2012 throw new PersistenceException("empty result set");
2013 }
2014 annGlobalID = new Long(rs.getLong(1));
2015
2016 //3.3. set annotation features
2017 FeatureMap features = ann.getFeatures();
2018 Assert.assertNotNull(features);
2019 createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
2020 }
2021 }
2022
2023 else {
2024 throw new IllegalArgumentException();
2025 }
2026
2027 }
2028 catch(SQLException sqle) {
2029 throw new PersistenceException("can't add annotations in DB : ["+
2030 sqle.getMessage()+"]");
2031 }
2032 finally {
2033 DBHelper.cleanup(rs);
2034 DBHelper.cleanup(pstmt);
2035 DBHelper.cleanup(cstmt);
2036 }
2037 }
2038
2039 /** helper for sync() - never call directly */
2040 protected void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes)
2041 throws PersistenceException {
2042
2043 //0.preconditions
2044 Assert.assertNotNull(doc);
2045 Assert.assertNotNull(as);
2046 Assert.assertNotNull(changes);
2047 Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2048 Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
2049 Assert.assertTrue(changes.size() > 0);
2050
2051
2052 PreparedStatement pstmt = null;
2053 ResultSet rs = null;
2054 Long lrID = (Long)doc.getLRPersistenceId();
2055 Long docID = null;
2056 Long asetID = null;
2057
2058 try {
2059 //1. get the a-set ID in the database
2060 String sql = " select as_id, " +
2061 " as_doc_id " +
2062 " from "+this.dbSchema+"v_annotation_set " +
2063 " where lr_id = ? ";
2064 //do we have aset name?
2065 String clause = null;
2066 String name = as.getName();
2067 if (null != name) {
2068 clause = " and as_name = ? ";
2069 }
2070 else {
2071 clause = " and as_name is null ";
2072 }
2073 sql = sql + clause;
2074
2075 pstmt = this.jdbcConn.prepareStatement(sql);
2076 pstmt.setLong(1,lrID.longValue());
2077 if (null != name) {
2078 pstmt.setString(2,name);
2079 }
2080 pstmt.execute();
2081 rs = pstmt.getResultSet();
2082
2083 if (rs.next()) {
2084 asetID = new Long(rs.getLong("as_id"));
2085 docID = new Long(rs.getLong("as_doc_id"));
2086 }
2087 else {
2088 throw new PersistenceException("cannot find annotation set with" +
2089 " name=["+name+"] , LRID=["+lrID+"] in database");
2090 }
2091
2092 //3. delete the removed annotations from this set
2093
2094 //cleanup
2095 DBHelper.cleanup(rs);
2096 DBHelper.cleanup(pstmt);
2097
2098 //3.1. prepare call
2099
2100 if (this.dbType == DBHelper.ORACLE_DB) {
2101 pstmt = this.jdbcConn.prepareCall("{ call "+this.dbSchema+"persist.delete_annotation(?,?) }");
2102 }
2103 else if (this.dbType == DBHelper.POSTGRES_DB) {
2104 pstmt = this.jdbcConn.prepareStatement("select persist_delete_annotation(?,?)");
2105 }
2106 else {
2107 throw new IllegalArgumentException();
2108 }
2109
2110 Iterator it = changes.iterator();
2111
2112 while (it.hasNext()) {
2113
2114 //3.2. insert annotation
2115 Annotation ann = (Annotation)it.next();
2116
2117 pstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs!
2118 pstmt.setLong(2,ann.getId().longValue());
2119 pstmt.execute();
2120 }
2121 }
2122 catch(SQLException sqle) {
2123 throw new PersistenceException("can't delete annotations in DB : ["+
2124 sqle.getMessage()+"]");
2125 }
2126 finally {
2127 DBHelper.cleanup(rs);
2128 DBHelper.cleanup(pstmt);
2129 }
2130 }
2131
2132
2133 /** helper for sync() - never call directly */
2134 protected void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes)
2135 throws PersistenceException {
2136
2137 //technically this approach sux
2138 //at least it works
2139
2140 //1. delete
2141 _syncRemovedAnnotations(doc,as,changes);
2142 //2. recreate
2143 _syncAddedAnnotations(doc,as,changes);
2144 }
2145
2146 /**
2147 * Get a resource from the persistent store.
2148 * <B>Don't use this method - use Factory.createResource with
2149 * DataStore and DataStoreInstanceId parameters set instead.</B>
2150 */
2151 public LanguageResource getLr(String lrClassName, Object lrPersistenceId)
2152 throws PersistenceException,SecurityException {
2153
2154 LanguageResource result = null;
2155
2156 //0. preconditions
2157 Assert.assertNotNull(lrPersistenceId);
2158
2159 //1. check session
2160 if (null == this.session) {
2161 throw new SecurityException("session not set");
2162 }
2163
2164 if (false == this.ac.isValidSession(this.session)) {
2165 throw new SecurityException("invalid session supplied");
2166 }
2167
2168 //2. check permissions
2169 if (false == canReadLR(lrPersistenceId)) {
2170 throw new SecurityException("insufficient privileges");
2171 }
2172
2173 //3. get resource from DB
2174 if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
2175 result = readDocument(lrPersistenceId);
2176 Assert.assertTrue(result instanceof DatabaseDocumentImpl);
2177 }
2178 else if (lrClassName.equals(DBHelper.CORPUS_CLASS)) {
2179 result = readCorpus(lrPersistenceId);
2180 Assert.assertTrue(result instanceof DatabaseCorpusImpl);
2181 }
2182 else {
2183 throw new IllegalArgumentException("resource class should be either Document or Corpus");
2184 }
2185
2186 //4. postconditions
2187 Assert.assertNotNull(result.getDataStore());
2188 Assert.assertTrue(result.getDataStore() instanceof DatabaseDataStore);
2189 Assert.assertNotNull(result.getLRPersistenceId());
2190
2191 //5. register the read doc as listener for sync events
2192 addDatastoreListener((DatastoreListener)result);
2193
2194 //6. add the resource to the list of dependent resources - i.e. the ones that the
2195 //data store should take care upon closing [and call sync()]
2196 this.dependentResources.add(result);
2197
2198 //7. done
2199 return result;
2200 }
2201
2202 /** helper method for getLR - reads LR of type Document */
2203 private DatabaseDocumentImpl readDocument(Object lrPersistenceId)
2204 throws PersistenceException {
2205
2206 //0. preconditions
2207 Assert.assertNotNull(lrPersistenceId);
2208
2209 if (false == lrPersistenceId instanceof Long) {
2210 throw new IllegalArgumentException();
2211 }
2212
2213 // 1. dummy document to be initialized
2214 DatabaseDocumentImpl result = new DatabaseDocumentImpl(this.jdbcConn);
2215
2216 PreparedStatement pstmt = null;
2217 ResultSet rs = null;
2218
2219 //3. read from DB
2220 try {
2221 String sql = " select lr_name, " +
2222 " lrtp_type, " +
2223 " lr_id, " +
2224 " lr_parent_id, " +
2225 " doc_id, " +
2226 " doc_url, " +
2227 " doc_start, " +
2228 " doc_end, " +
2229 " doc_is_markup_aware " +
2230 " from "+this.dbSchema+"v_document " +
2231 " where lr_id = ? ";
2232
2233 pstmt = this.jdbcConn.prepareStatement(sql);
2234 pstmt.setLong(1,((Long)lrPersistenceId).longValue());
2235 pstmt.execute();
2236 rs = pstmt.getResultSet();
2237
2238 if (false == rs.next()) {
2239 //ooops mo data found
2240 throw new PersistenceException("Invalid LR ID supplied - no data found");
2241 }
2242
2243 //4. fill data
2244
2245 //4.0 name
2246 String lrName = rs.getString("lr_name");
2247 Assert.assertNotNull(lrName);
2248 result.setName(lrName);
2249
2250 //4.1 parent
2251 Long parentID = null;
2252 long parent_id = rs.getLong("lr_parent_id");
2253 if (false == rs.wasNull()) {
2254 parentID = new Long(parent_id);
2255
2256 //read parent resource
2257 LanguageResource parentLR = this.getLr(DBHelper.DOCUMENT_CLASS,parentID);
2258 Assert.assertNotNull(parentLR);
2259 Assert.assertTrue(parentLR instanceof DatabaseDocumentImpl);
2260
2261 result.setParent(parentLR);
2262 }
2263
2264
2265 //4.2. markup aware
2266 if (this.dbType == DBHelper.ORACLE_DB) {
2267 long markup = rs.getLong("doc_is_markup_aware");
2268 Assert.assertTrue(markup == DBHelper.FALSE || markup == DBHelper.TRUE);
2269 if (markup == DBHelper.FALSE) {
2270 result.setMarkupAware(Boolean.FALSE);
2271 }
2272 else {
2273 result.setMarkupAware(Boolean.TRUE);
2274
2275 }
2276 }
2277 else if (this.dbType == DBHelper.POSTGRES_DB) {
2278 boolean markup = rs.getBoolean("doc_is_markup_aware");
2279 result.setMarkupAware(new Boolean(markup));
2280 }
2281 else {
2282 throw new IllegalArgumentException();
2283 }
2284
2285
2286 //4.3 datastore
2287 result.setDataStore(this);
2288
2289 //4.4. persist ID
2290 Long persistID = new Long(rs.getLong("lr_id"));
2291 result.setLRPersistenceId(persistID);
2292
2293 //4.5 source url
2294 String url = rs.getString("doc_url");
2295 if(url != null && url.length() > 0) result.setSourceUrl(new URL(url));
2296
2297 //4.6. start offset
2298 Long start = null;
2299 long longVal = rs.getLong("doc_start");
2300 //null?
2301 //if NULL is stored in the DB, Oracle returns 0 which is not what we want
2302 if (false == rs.wasNull()) {
2303 start = new Long(longVal);
2304 }
2305 result.setSourceUrlStartOffset(start);
2306 // initData.put("DOC_SOURCE_URL_START",start);
2307
2308 //4.7. end offset
2309 Long end = null;
2310 longVal = rs.getLong("doc_end");
2311 //null?
2312 //if NULL is stored in the DB, Oracle returns 0 which is not what we want
2313 if (false == rs.wasNull()) {
2314 end = new Long(longVal);
2315 }
2316 result.setSourceUrlEndOffset(end);
2317 // initData.put("DOC_SOURCE_URL_END",end);
2318
2319 //4.8 features
2320 FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_DOCUMENT);
2321 result.setFeatures(features);
2322 //initData.put("DOC_FEATURES",features);
2323
2324 //4.9 set the nextAnnotationID correctly
2325 long doc_id = rs.getLong("doc_id");
2326
2327 //cleanup
2328 DBHelper.cleanup(rs);
2329 DBHelper.cleanup(pstmt);
2330
2331 sql = " select max(ann_local_id),'ann_id'" +
2332 " from "+this.dbSchema+"t_annotation " +
2333 " where ann_doc_id = ?" +
2334 " union " +
2335 " select max(node_local_id),'node_id' " +
2336 " from "+this.dbSchema+"t_node " +
2337 " where node_doc_id = ?";
2338
2339 pstmt = this.jdbcConn.prepareStatement(sql);
2340 pstmt.setLong(1,doc_id);
2341 pstmt.setLong(2,doc_id);
2342 pstmt.execute();
2343 rs = pstmt.getResultSet();
2344
2345 int maxAnnID = 0 , maxNodeID = 0;
2346 //ann id
2347 if (false == rs.next()) {
2348 //ooops no data found
2349 throw new PersistenceException("Invalid LR ID supplied - no data found");
2350 }
2351 if (rs.getString(2).equals("ann_id"))
2352 maxAnnID = rs.getInt(1);
2353 else
2354 maxNodeID = rs.getInt(1);
2355
2356 if (false == rs.next()) {
2357 //ooops no data found
2358 throw new PersistenceException("Invalid LR ID supplied - no data found");
2359 }
2360 if (rs.getString(2).equals("node_id"))
2361 maxNodeID = rs.getInt(1);
2362 else
2363 maxAnnID = rs.getInt(1);
2364
2365 result.setNextNodeId(maxNodeID+1);
2366 // initData.put("DOC_NEXT_NODE_ID",new Integer(maxNodeID+1));
2367 result.setNextAnnotationId(maxAnnID+1);
2368 // initData.put("DOC_NEXT_ANN_ID",new Integer(maxAnnID+1));
2369
2370
2371 // params.put("initData__$$__", initData);
2372 // try {
2373 //here we create the persistent LR via Factory, so it's registered
2374 //in GATE
2375 // result = (DatabaseDocumentImpl)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params);
2376 // }
2377 // catch (gate.creole.ResourceInstantiationException ex) {
2378 // throw new GateRuntimeException(ex.getMessage());
2379 // }
2380 }
2381 catch(SQLException sqle) {
2382 throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]");
2383 }
2384 catch(Exception e) {
2385 throw new PersistenceException(e);
2386 }
2387 finally {
2388 DBHelper.cleanup(rs);
2389 DBHelper.cleanup(pstmt);
2390 }
2391
2392 return result;
2393 }
2394
2395
2396 /**
2397 * helper method for getLR - reads LR of type Corpus
2398 */
2399 private DatabaseCorpusImpl readCorpus(Object lrPersistenceId)
2400 throws PersistenceException {
2401
2402 //0. preconditions
2403 Assert.assertNotNull(lrPersistenceId);
2404
2405 if (false == lrPersistenceId instanceof Long) {
2406 throw new IllegalArgumentException();
2407 }
2408
2409 //3. read from DB
2410 PreparedStatement pstmt = null;
2411 ResultSet rs = null;
2412 DatabaseCorpusImpl result = null;
2413
2414 try {
2415 String sql = " select lr_name " +
2416 " from "+this.dbSchema+"t_lang_resource " +
2417 " where lr_id = ? ";
2418 pstmt = this.jdbcConn.prepareStatement(sql);
2419 pstmt.setLong(1,((Long)lrPersistenceId).longValue());
2420 pstmt.execute();
2421 rs = pstmt.getResultSet();
2422
2423 if (false == rs.next()) {
2424 //ooops mo data found
2425 throw new PersistenceException("Invalid LR ID supplied - no data found");
2426 }
2427
2428 //4. fill data
2429
2430 //4.1 name
2431 String lrName = rs.getString("lr_name");
2432 Assert.assertNotNull(lrName);
2433
2434 //4.8 features
2435 FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_CORPUS);
2436
2437 //4.9 cleanup
2438 DBHelper.cleanup(rs);
2439 DBHelper.cleanup(pstmt);
2440
2441 sql = " select lr_id ," +
2442 " lr_name " +
2443 " from "+this.dbSchema+"t_document doc, " +
2444 " "+this.dbSchema+"t_lang_resource lr, " +
2445 " "+this.dbSchema+"t_corpus_document corpdoc, " +
2446 " "+this.dbSchema+"t_corpus corp " +
2447 " where lr.lr_id = doc.doc_lr_id " +
2448 " and doc.doc_id = corpdoc.cd_doc_id " +
2449 " and corpdoc.cd_corp_id = corp.corp_id " +
2450 " and corp_lr_id = ? ";
2451 pstmt = this.jdbcConn.prepareStatement(sql);
2452 pstmt.setLong(1,((Long)lrPersistenceId).longValue());
2453 pstmt.execute();
2454 rs = pstmt.getResultSet();
2455
2456 Vector documentData = new Vector();
2457 while (rs.next()) {
2458 Long docLRID = new Long(rs.getLong("lr_id"));
2459 String docName = rs.getString("lr_name");
2460 documentData.add(new DocumentData(docName, docLRID));
2461 }
2462 DBHelper.cleanup(rs);
2463 DBHelper.cleanup(pstmt);
2464
2465 result = new DatabaseCorpusImpl(lrName,
2466 this,
2467 (Long)lrPersistenceId,
2468 features,
2469 documentData);
2470 }
2471 catch(SQLException sqle) {
2472 throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]");
2473 }
2474 catch(Exception e) {
2475 throw new PersistenceException(e);
2476 }
2477 finally {
2478 DBHelper.cleanup(rs);
2479 DBHelper.cleanup(pstmt);
2480 }
2481
2482 return result;
2483 }
2484
2485 /**
2486 * reads the features of an entity
2487 * entities are of type LR or Annotation
2488 */
2489 protected abstract FeatureMap readFeatures(Long entityID, int entityType)
2490 throws PersistenceException;
2491
2492 /**
2493 * helper method for delete()
2494 * never call it directly beause proper events will not be fired
2495 */
2496 protected abstract void deleteDocument(Long lrId)
2497 throws PersistenceException;
2498
2499 /**
2500 * helper method for delete()
2501 * never call it directly beause proper events will not be fired
2502 */
2503 protected abstract void deleteCorpus(Long lrId)
2504 throws PersistenceException;
2505
2506 /**
2507 * unloads a LR from the GUI
2508 */
2509 protected void unloadLR(Long lrID)
2510 throws GateException{
2511
2512 //0. preconfitions
2513 Assert.assertNotNull(lrID);
2514
2515 //1. get all LRs in the system
2516 List resources = Gate.getCreoleRegister().getAllInstances("gate.LanguageResource");
2517
2518 Iterator it = resources.iterator();
2519 while (it.hasNext()) {
2520 LanguageResource lr = (LanguageResource)it.next();
2521 if (lrID.equals(lr.getLRPersistenceId()) &&
2522 this.equals(lr.getDataStore())) {
2523 //found it - unload it
2524 Factory.deleteResource(lr);
2525 break;
2526 }
2527 }
2528 }
2529
2530 /** helper for sync() - never call directly */
2531 protected abstract void _syncRemovedDocumentsFromCorpus(List docLRIDs, Long corpLRID)
2532 throws PersistenceException;
2533
2534 /**
2535 * adds document to corpus in the database
2536 * if the document is already part of the corpus nothing
2537 * changes
2538 */
2539 protected abstract void addDocumentToCorpus(Long docID,Long corpID)
2540 throws PersistenceException,SecurityException;
2541
2542
2543 }
|