JDBCDataStore.java
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 storageUrlthrows 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 lrTypethrows 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 lrTypethrows 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((LonglrID,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((LonglrID,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 constraintsthrows 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     ifnull == 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           fireResourceWrittennew 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( (LongdbDoc.getLRPersistenceId(),
1693                                 (Longcorp.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() 0result.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 = , 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 }