| OracleDataStore |

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

| OracleDataStore |