1     /*
2    *  JDBCDataStore.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: JDBCDataStore.java,v 1.82 2002/04/09 13:45:26 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 junit.framework.*;
24  import oracle.jdbc.driver.*;
25  
26  import gate.*;
27  import gate.util.*;
28  import gate.event.*;
29  import gate.security.*;
30  import gate.security.SecurityException;
31  import gate.corpora.*;
32  import gate.annotation.*;
33  
34  public abstract class JDBCDataStore extends AbstractFeatureBearer
35                                      implements DatabaseDataStore,
36                                                  CreoleListener {
37  
38    /** --- */
39    private static final boolean DEBUG = false;
40  
41    /** jdbc url for the database */
42    private   String      dbURL;
43    protected String      dbSchema;
44    protected int         dbType;
45  
46    protected String      datastoreComment;
47    protected String      iconName;
48  
49    /** jdbc driver name */
50  //  private   String      driverName;
51  
52    /**
53     *  GUID of the datastore
54     *  read from T_PARAMETER table
55     *  */
56    private   String      dbID;
57  
58    /** security session identifying all access to the datastore */
59    protected   Session           session;
60  
61    /** datastore name? */
62    protected   String            name;
63  
64    /** jdbc connection, all access to the database is made through this connection
65     */
66    protected transient Connection  jdbcConn;
67  
68    /** Security factory that contols access to objects in the datastore
69     *  the security session is from this factory
70     *  */
71    protected transient AccessController  ac;
72  
73    /** anyone interested in datastore related events */
74    private   transient Vector datastoreListeners;
75  
76    /** resources that should be sync-ed if datastore is close()-d */
77    protected transient Vector dependentResources;
78  
79    /** Do not use this class directly - use one of the subclasses */
80    protected JDBCDataStore() {
81  
82      this.datastoreListeners = new Vector();
83      this.dependentResources = new Vector();
84    }
85  
86  
87    /*  interface DataStore  */
88  
89    /**
90     * Save: synchonise the in-memory image of the LR with the persistent
91     * image.
92     */
93    public String getComment() {
94  
95      Assert.assertNotNull(this.datastoreComment);
96      return this.datastoreComment;
97    }
98  
99    /**
100    * Returns the name of the icon to be used when this datastore is displayed
101    * in the GUI
102    */
103   public String getIconName() {
104     Assert.assertNotNull(this.iconName);
105     return this.iconName;
106   }
107 
108 
109 
110   /** Get the name of an LR from its ID. */
111   public String getLrName(Object lrId)
112     throws PersistenceException {
113 
114     if (false == lrId instanceof Long) {
115       throw new IllegalArgumentException();
116     }
117 
118     Long ID = (Long)lrId;
119 
120     PreparedStatement pstmt = null;
121     ResultSet rset = null;
122 
123     try {
124       String sql = " select lr_name " +
125                   " from   "+this.dbSchema+"t_lang_resource " +
126                   " where  lr_id = ?";
127 
128       pstmt = this.jdbcConn.prepareStatement(sql);
129       pstmt.setLong(1,ID.longValue());
130       pstmt.execute();
131       rset = pstmt.getResultSet();
132 
133       rset.next();
134       String result = rset.getString("lr_name");
135 
136       return result;
137     }
138     catch(SQLException sqle) {
139       throw new PersistenceException("can't get LR name from DB: ["+ sqle.getMessage()+"]");
140     }
141     finally {
142       DBHelper.cleanup(pstmt);
143       DBHelper.cleanup(rset);
144     }
145   }
146 
147 
148 
149   /** Set the URL for the underlying storage mechanism. */
150   public void setStorageUrl(String storageUrl) throws PersistenceException {
151 
152     if (!storageUrl.startsWith("jdbc:")) {
153       throw new PersistenceException("Incorrect JDBC url (should start with \"jdbc:\")");
154     }
155     else {
156       this.dbURL = storageUrl;
157       this.dbSchema = DBHelper.getSchemaPrefix(this.dbURL);
158       this.dbType = DBHelper.getDatabaseType(this.dbURL);
159       Assert.assertNotNull(this.dbSchema);
160       Assert.assertTrue(this.dbType > 0);
161     }
162 
163   }
164 
165   /** Get the URL for the underlying storage mechanism. */
166   public String getStorageUrl() {
167 
168     return this.dbURL;
169   }
170 
171 
172   /**
173    * Create a new data store. <B>NOTE:</B> for some data stores
174    * creation is an system administrator task; in such cases this
175    * method will throw an UnsupportedOperationException.
176    */
177   public void create()
178   throws PersistenceException, UnsupportedOperationException {
179 
180     throw new UnsupportedOperationException("create() is not supported for DatabaseDataStore");
181   }
182 
183 
184 
185   /** Open a connection to the data store. */
186   public void open() throws PersistenceException {
187     try {
188 
189       //1, get connection to the DB
190       jdbcConn = DBHelper.connect(dbURL);
191 
192       //2. create security factory
193 //      this.ac = new AccessControllerImpl();
194       this.ac = Factory.createAccessController(dbURL);
195 
196       //3. open and init the security factory with the same DB repository
197       ac.open();
198 
199       //4. get DB ID
200       this.dbID = this.readDatabaseID();
201 
202     }
203     catch(SQLException sqle) {
204       throw new PersistenceException("could not get DB connection ["+ sqle.getMessage() +"]");
205     }
206     catch(ClassNotFoundException clse) {
207       throw new PersistenceException("cannot locate JDBC driver ["+ clse.getMessage() +"]");
208     }
209 
210     //5. register for Creole events
211     Gate.getCreoleRegister().addCreoleListener(this);
212   }
213 
214   /** Close the data store. */
215   public void close() throws PersistenceException {
216 
217     //-1. Unregister for Creole events
218     Gate.getCreoleRegister().removeCreoleListener(this);
219 
220     //0. sync all dependednt resources
221     for (int i=0; i< this.dependentResources.size(); i++) {
222       LanguageResource lr = (LanguageResource)this.dependentResources.elementAt(i);
223 
224       try {
225         sync(lr);
226       }
227       catch(SecurityException se) {
228         //do nothing
229         //there was an oper and modified resource for which the user has no write
230         //privileges
231         //not doing anything is perfectly ok because the resource won't bechanged in DB
232       }
233 
234       //unload UI component
235       Factory.deleteResource(lr);
236     }
237 
238     //1. close security factory
239     ac.close();
240 
241     DBHelper.disconnect(this.jdbcConn);
242 
243     //finally unregister this datastore from the GATE register of datastores
244     Gate.getDataStoreRegister().remove(this);
245   }
246 
247   /**
248    * Delete the data store. <B>NOTE:</B> for some data stores
249    * deletion is an system administrator task; in such cases this
250    * method will throw an UnsupportedOperationException.
251    */
252   public void delete()
253   throws PersistenceException, UnsupportedOperationException {
254 
255     throw new UnsupportedOperationException("delete() is not supported for DatabaseDataStore");
256   }
257 
258   /**
259    * Delete a resource from the data store.
260    * @param lrId a data-store specific unique identifier for the resource
261    * @param lrClassName class name of the type of resource
262    */
263 
264   public void delete(String lrClassName, Object lrId)
265   throws PersistenceException,SecurityException {
266     //0. preconditions
267     if (false == lrId instanceof Long) {
268       throw new IllegalArgumentException();
269     }
270 
271     if (!lrClassName.equals(DBHelper.DOCUMENT_CLASS) &&
272         !lrClassName.equals(DBHelper.CORPUS_CLASS)) {
273       throw new IllegalArgumentException("Only Corpus and Document classes are supported" +
274                                           " by Database data store");
275     }
276 
277     //1. check session
278     if (null == this.session) {
279       throw new SecurityException("session not set");
280     }
281 
282     if (false == this.ac.isValidSession(this.session)) {
283       throw new SecurityException("invalid session supplied");
284     }
285 
286     //2. check permissions
287     if (false == canWriteLR(lrId)) {
288       throw new SecurityException("insufficient privileges");
289     }
290 
291     //3. try to lock document, so that we'll be sure no one is editing it
292     //NOTE: use the private method
293     User lockingUser = this.getLockingUser((Long)lrId);
294     User currUser = this.session.getUser();
295 
296     if (null != lockingUser && false == lockingUser.equals(currUser)) {
297       //oops, someone is editing now
298       throw new PersistenceException("LR locked by another user");
299     }
300 
301     boolean transFailed = false;
302     try {
303       //4. autocommit should be FALSE because of LOBs
304       beginTrans();
305 
306       //5. perform changes, if anything goes wrong, rollback
307       if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
308         deleteDocument((Long)lrId);
309       }
310       else {
311         deleteCorpus((Long)lrId);
312       }
313 
314       //6. done, commit
315       commitTrans();
316     }
317     catch(PersistenceException pe) {
318       transFailed = true;
319       throw(pe);
320     }
321     finally {
322       //problems?
323       if (transFailed) {
324         rollbackTrans();
325       }
326     }
327 
328     //7, unlock
329     //do nothing - the resource does not exist anymore
330 
331     //8. delete from the list of dependent resources
332     boolean resourceFound = false;
333     Iterator it = this.dependentResources.iterator();
334     while (it.hasNext()) {
335       LanguageResource lr = (LanguageResource)it.next();
336       if (lr.getLRPersistenceId().equals(lrId)) {
337         resourceFound = true;
338         it.remove();
339         break;
340       }
341     }
342 
343     //Assert.assertTrue(resourceFound);
344 
345     //9. let the world know about it
346     fireResourceDeleted(
347       new DatastoreEvent(this, DatastoreEvent.RESOURCE_DELETED, null, lrId));
348 
349     //10. unload the resource form the GUI
350     try {
351       unloadLR((Long)lrId);
352     }
353     catch(GateException ge) {
354       Err.prln("can't unload resource from GUI...");
355     }
356   }
357 
358 
359 
360   /**
361    * Save: synchonise the in-memory image of the LR with the persistent
362    * image.
363    */
364   public void sync(LanguageResource lr)
365   throws PersistenceException,SecurityException {
366 
367     //4.delegate (open a new transaction)
368     _sync(lr,true);
369   }
370 
371 
372   /**
373    * Set method for the autosaving behaviour of the data store.
374    * <B>NOTE:</B> many types of datastore have no auto-save function,
375    * in which case this will throw an UnsupportedOperationException.
376    */
377   public void setAutoSaving(boolean autoSaving)
378   throws UnsupportedOperationException,PersistenceException {
379     try {
380       this.jdbcConn.setAutoCommit(true);
381     }
382     catch(SQLException sqle) {
383       throw new PersistenceException("cannot change autosave mode ["+sqle.getMessage()+"]");
384     }
385 
386   }
387 
388   /** Get the autosaving behaviour of the LR. */
389   public boolean isAutoSaving() {
390     throw new MethodNotImplementedException();
391   }
392 
393   /** Adopt a resource for persistence. */
394   public LanguageResource adopt(LanguageResource lr, SecurityInfo secInfo)
395   throws PersistenceException,SecurityException {
396     //open a new transaction
397     return _adopt(lr,secInfo,true);
398   }
399 
400 
401   protected LanguageResource _adopt(LanguageResource lr,
402                                   SecurityInfo secInfo,
403                                   boolean openNewTrans)
404   throws PersistenceException,SecurityException {
405 
406     LanguageResource result = null;
407 
408     //-1. preconditions
409     Assert.assertNotNull(lr);
410     Assert.assertNotNull(secInfo);
411     if (false == lr instanceof Document &&
412         false == lr instanceof Corpus) {
413       //only documents and corpuses could be serialized in DB
414       throw new IllegalArgumentException("only Documents and Corpuses could "+
415                                           "be serialized in DB");
416     }
417 
418     //0. check SecurityInfo
419     if (false == this.ac.isValidSecurityInfo(secInfo)) {
420       throw new SecurityException("Invalid security settings supplied");
421     }
422 
423     //1. user session should be set
424     if (null == this.session) {
425       throw new SecurityException("user session not set");
426     }
427 
428     //2. check the LR's current DS
429     DataStore currentDS = lr.getDataStore();
430     if(currentDS == null) {
431       // an orphan - do the adoption (later)
432     }
433     else if(currentDS.equals(this)){         // adopted already
434       return lr;
435     }
436     else {                      // someone else's child
437       throw new PersistenceException(
438         "Can't adopt a resource which is already in a different datastore");
439     }
440 
441 
442     //3. is the LR one of Document or Corpus?
443     if (false == lr instanceof Document &&
444         false == lr instanceof Corpus) {
445 
446       throw new IllegalArgumentException("Database datastore is implemented only for "+
447                                         "Documents and Corpora");
448     }
449 
450     //4.is the document already stored in this storage?
451     Object persistID = lr.getLRPersistenceId();
452     if (persistID != null) {
453       throw new PersistenceException("This LR is already stored in the " +
454                                       " database (persistance ID is =["+(Long)persistID+"] )");
455     }
456 
457     boolean transFailed = false;
458     try {
459       //5 autocommit should be FALSE because of LOBs
460       if (openNewTrans) {
461 //        this.jdbcConn.setAutoCommit(false);
462         beginTrans();
463       }
464 
465       //6. perform changes, if anything goes wrong, rollback
466       if (lr instanceof Document) {
467         result =  createDocument((Document)lr,secInfo);
468 //System.out.println("result ID=["+result.getLRPersistenceId()+"]");
469       }
470       else {
471         //adopt each document from the corpus in a separate transaction context
472         result =  createCorpus((Corpus)lr,secInfo,true);
473       }
474 
475       //7. done, commit
476       if (openNewTrans) {
477 //        this.jdbcConn.commit();
478         commitTrans();
479       }
480     }
481 /*
482     catch(SQLException sqle) {
483       transFailed = true;
484       throw new PersistenceException("Cannot start/commit a transaction, ["+sqle.getMessage()+"]");
485     }
486 */
487     catch(PersistenceException pe) {
488       transFailed = true;
489       throw(pe);
490     }
491     catch(SecurityException se) {
492       transFailed = true;
493       throw(se);
494     }
495     finally {
496       //problems?
497       if (transFailed) {
498 System.out.println("trans failed ...rollback");
499         rollbackTrans();
500 /*        try {
501           this.jdbcConn.rollback();
502         }
503         catch(SQLException sqle) {
504           throw new PersistenceException(sqle);
505         }
506 */
507       }
508     }
509 
510     //8. let the world know
511     fireResourceAdopted(
512         new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED,
513                            result,
514                            result.getLRPersistenceId())
515     );
516 
517     //9. fire also resource written event because it's now saved
518     fireResourceWritten(
519       new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN,
520                           result,
521                           result.getLRPersistenceId()
522       )
523     );
524 
525     //10. add the resource to the list of dependent resources - i.e. the ones that the
526     //data store should take care upon closing [and call sync()]
527     this.dependentResources.add(result);
528 
529     return result;
530   }
531 
532 
533   /** Get a list of the types of LR that are present in the data store. */
534   public List getLrTypes() throws PersistenceException {
535 
536     Vector lrTypes = new Vector();
537     Statement stmt = null;
538     ResultSet rs = null;
539 
540     try {
541       stmt = this.jdbcConn.createStatement();
542       rs = stmt.executeQuery(" SELECT lrtp_type " +
543                              " FROM   "+this.dbSchema+"t_lr_type LRTYPE ");
544 
545       while (rs.next()) {
546         //access by index is faster
547         String lrType = rs.getString(1);
548         lrTypes.add(lrType);
549       }
550 
551       return lrTypes;
552     }
553     catch(SQLException sqle) {
554       throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]");
555     }
556     finally {
557       DBHelper.cleanup(rs);
558       DBHelper.cleanup(stmt);
559     }
560   }
561 
562 
563   /** Get a list of the IDs of LRs of a particular type that are present. */
564   public List getLrIds(String lrType) throws PersistenceException {
565 
566     Vector lrIDs = new Vector();
567     PreparedStatement stmt = null;
568     ResultSet rs = null;
569 
570     try {
571       stmt = this.jdbcConn.prepareStatement(
572                       " SELECT lr_id " +
573                       " FROM   "+this.dbSchema+"t_lang_resource LR, " +
574                       "        "+this.dbSchema+"t_lr_type LRTYPE " +
575                       " WHERE  LR.lr_type_id = LRTYPE.lrtp_id " +
576                       "        AND LRTYPE.lrtp_type = ? " +
577                       " ORDER BY lr_name"
578                       );
579       stmt.setString(1,lrType);
580 
581       //oracle special
582       if (this.dbType == DBHelper.ORACLE_DB) {
583         ((OraclePreparedStatement)stmt).setRowPrefetch(DBHelper.CHINK_SIZE_SMALL);
584       }
585 
586       stmt.execute();
587       rs = stmt.getResultSet();
588 
589       while (rs.next()) {
590         //access by index is faster
591         Long lrID = new Long(rs.getLong(1));
592         lrIDs.add(lrID);
593       }
594 
595       return lrIDs;
596     }
597     catch(SQLException sqle) {
598       throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]");
599     }
600     finally {
601       DBHelper.cleanup(rs);
602       DBHelper.cleanup(stmt);
603     }
604 
605   }
606 
607 
608   /** Get a list of the names of LRs of a particular type that are present. */
609   public List getLrNames(String lrType) throws PersistenceException {
610 
611     Vector lrNames = new Vector();
612     PreparedStatement stmt = null;
613     ResultSet rs = null;
614 
615     try {
616       stmt = this.jdbcConn.prepareStatement(
617                 " SELECT lr_name " +
618                 " FROM   "+this.dbSchema+"t_lang_resource LR, " +
619                 "        t_lr_type LRTYPE " +
620                 " WHERE  LR.lr_type_id = LRTYPE.lrtp_id " +
621                 "        AND LRTYPE.lrtp_type = ? " +
622                 " ORDER BY lr_name desc"
623                 );
624       stmt.setString(1,lrType);
625 
626       //Oracle special
627       if (this.dbType == DBHelper.ORACLE_DB) {
628         ((OraclePreparedStatement)stmt).setRowPrefetch(DBHelper.CHINK_SIZE_SMALL);
629       }
630 
631       stmt.execute();
632       rs = stmt.getResultSet();
633 
634       while (rs.next()) {
635         //access by index is faster
636         String lrName = rs.getString(1);
637         lrNames.add(lrName);
638       }
639 
640       return lrNames;
641     }
642     catch(SQLException sqle) {
643       throw new PersistenceException("can't get LR types from DB: ["+ sqle.getMessage()+"]");
644     }
645     finally {
646       DBHelper.cleanup(rs);
647       DBHelper.cleanup(stmt);
648     }
649   }
650 
651   /**
652    * Checks if the user (identified by the sessionID)
653    *  has read access to the LR
654    */
655   public boolean canReadLR(Object lrID)
656     throws PersistenceException, SecurityException{
657 
658     return canAccessLR((Long) lrID,DBHelper.READ_ACCESS);
659   }
660 
661 
662 
663   /**
664    * Checks if the user (identified by the sessionID)
665    * has write access to the LR
666    */
667   public boolean canWriteLR(Object lrID)
668     throws PersistenceException, SecurityException{
669 
670     return canAccessLR((Long) lrID,DBHelper.WRITE_ACCESS);
671   }
672 
673   /**
674    * Checks if the user (identified by the sessionID)
675    * has some access (read/write) to the LR
676    */
677   protected boolean canAccessLR(Long lrID,int mode)
678     throws PersistenceException, SecurityException{
679 
680     //abstract
681     throw new MethodNotImplementedException();
682   }
683 
684   /*  interface DatabaseDataStore  */
685 
686   /**
687    * starts a transaction
688    * note that if u're already in transaction context this will not open
689    * nested transaction
690    * i.e. many consecutive calls to beginTrans() make no difference if no commit/rollback
691    * is made meanwhile
692    *  */
693   public void beginTrans()
694     throws PersistenceException,UnsupportedOperationException{
695 
696     try {
697       this.jdbcConn.setAutoCommit(false);
698     }
699     catch(SQLException sqle) {
700       throw new PersistenceException("cannot begin transaction, DB error is: ["
701                                                       +sqle.getMessage()+"]");
702     }
703   }
704 
705 
706   /**
707    * commits transaction
708    * note that this will commit all the uncommited calls made so far
709    *  */
710   public void commitTrans()
711     throws PersistenceException,UnsupportedOperationException{
712 
713     try {
714       this.jdbcConn.commit();
715     }
716     catch(SQLException sqle) {
717       throw new PersistenceException("cannot commit transaction, DB error is: ["
718                                                       +sqle.getMessage()+"]");
719     }
720 
721   }
722 
723   /** rollsback a transaction */
724   public void rollbackTrans()
725     throws PersistenceException,UnsupportedOperationException{
726 
727     try {
728       this.jdbcConn.rollback();
729     }
730     catch(SQLException sqle) {
731       throw new PersistenceException("cannot commit transaction, DB error is: ["
732                                                       +sqle.getMessage()+"]");
733     }
734 
735   }
736 
737   /** not used */
738   public Long timestamp()
739     throws PersistenceException{
740 
741     //implemented by the subclasses
742     throw new MethodNotImplementedException();
743   }
744 
745   /** not used */
746   public void deleteSince(Long timestamp)
747     throws PersistenceException{
748 
749     throw new MethodNotImplementedException();
750   }
751 
752   /** specifies the driver to be used to connect to the database? */
753 /*  public void setDriver(String driverName)
754     throws PersistenceException{
755 
756     this.driverName = driverName;
757   }
758 */
759   /** Sets the name of this resource*/
760   public void setName(String name){
761     this.name = name;
762   }
763 
764   /** Returns the name of this resource*/
765   public String getName(){
766     return name;
767   }
768 
769 
770   /** --- */
771   protected int findFeatureType(Object value) {
772 
773     if (null == value)
774       return DBHelper.VALUE_TYPE_NULL;
775     else if (value instanceof Integer)
776       return DBHelper.VALUE_TYPE_INTEGER;
777     else if (value instanceof Long)
778       return DBHelper.VALUE_TYPE_LONG;
779     else if (value instanceof Boolean)
780       return DBHelper.VALUE_TYPE_BOOLEAN;
781     else if (value instanceof Double ||
782              value instanceof Float)
783       return DBHelper.VALUE_TYPE_FLOAT;
784     else if (value instanceof String)
785       return DBHelper.VALUE_TYPE_STRING;
786     else if (value instanceof List) {
787       //is the array empty?
788       List arr = (List)value;
789 
790       if (arr.isEmpty()) {
791         return DBHelper.VALUE_TYPE_EMPTY_ARR;
792       }
793       else {
794         Object element = arr.get(0);
795 
796         if (element  instanceof Integer)
797           return DBHelper.VALUE_TYPE_INTEGER_ARR;
798         else if (element  instanceof Long)
799           return DBHelper.VALUE_TYPE_LONG_ARR;
800         else if (element instanceof Boolean)
801           return DBHelper.VALUE_TYPE_BOOLEAN_ARR;
802         else if (element instanceof Double ||
803                  element instanceof Float)
804           return DBHelper.VALUE_TYPE_FLOAT_ARR;
805         else if (element instanceof String)
806           return DBHelper.VALUE_TYPE_STRING_ARR;
807       }
808     }
809     else if (value instanceof Serializable) {
810       return DBHelper.VALUE_TYPE_BINARY;
811     }
812 
813     //this should never happen
814     throw new IllegalArgumentException();
815   }
816 
817   /** --- */
818   public String getDatabaseID() {
819     return this.dbID;
820   }
821 
822   /** reads the GUID from the database */
823 /*  protected abstract String readDatabaseID()
824     throws PersistenceException;
825 */
826   /**
827    *  reads the ID of the database
828    *  every database should have unique string ID
829    */
830   protected String readDatabaseID() throws PersistenceException{
831 
832     PreparedStatement pstmt = null;
833     ResultSet rs = null;
834     String  result = null;
835 
836     //1. read from DB
837     try {
838       String sql = " select par_value_string " +
839                    " from  "+this.dbSchema+"t_parameter " +
840                    " where  par_key = ? ";
841 
842       pstmt = this.jdbcConn.prepareStatement(sql);
843       pstmt.setString(1,DBHelper.DB_PARAMETER_GUID);
844       pstmt.execute();
845       rs = pstmt.getResultSet();
846 
847       if (false == rs.next()) {
848         throw new PersistenceException("Can't read database parameter ["+
849                                           DBHelper.DB_PARAMETER_GUID+"]");
850       }
851       result = rs.getString(1);
852     }
853     catch(SQLException sqle) {
854         throw new PersistenceException("Can't read database parameter ["+
855                                           sqle.getMessage()+"]");
856     }
857     finally {
858       DBHelper.cleanup(rs);
859       DBHelper.cleanup(pstmt);
860     }
861 
862     if (DEBUG) {
863       Out.println("reult=["+result+"]");
864     }
865 
866     return result;
867   }
868 
869 
870   /**
871    * Removes a a previously registered {@link gate.event.DatastoreListener}
872    * from the list listeners for this datastore
873    */
874   public void removeDatastoreListener(DatastoreListener l) {
875 
876     Assert.assertNotNull(this.datastoreListeners);
877 
878     synchronized(this.datastoreListeners) {
879       this.datastoreListeners.remove(l);
880     }
881   }
882 
883 
884   /**
885    * Registers a new {@link gate.event.DatastoreListener} with this datastore
886    */
887   public void addDatastoreListener(DatastoreListener l) {
888 
889     Assert.assertNotNull(this.datastoreListeners);
890 
891     //this is not thread safe
892 /*    if (false == this.datastoreListeners.contains(l)) {
893       Vector temp = (Vector)this.datastoreListeners.clone();
894       temp.add(l);
895       this.datastoreListeners = temp;
896     }
897 */
898     synchronized(this.datastoreListeners) {
899       if (false == this.datastoreListeners.contains(l)) {
900         this.datastoreListeners.add(l);
901       }
902     }
903   }
904 
905   protected void fireResourceAdopted(DatastoreEvent e) {
906 
907     Assert.assertNotNull(datastoreListeners);
908     Vector temp = this.datastoreListeners;
909 
910     int count = temp.size();
911     for (int i = 0; i < count; i++) {
912       ((DatastoreListener)temp.elementAt(i)).resourceAdopted(e);
913     }
914   }
915 
916 
917   protected void fireResourceDeleted(DatastoreEvent e) {
918 
919     Assert.assertNotNull(datastoreListeners);
920     Vector temp = this.datastoreListeners;
921 
922     int count = temp.size();
923     for (int i = 0; i < count; i++) {
924       ((DatastoreListener)temp.elementAt(i)).resourceDeleted(e);
925     }
926   }
927 
928 
929   protected void fireResourceWritten(DatastoreEvent e) {
930     Assert.assertNotNull(datastoreListeners);
931     Vector temp = this.datastoreListeners;
932 
933     int count = temp.size();
934     for (int i = 0; i < count; i++) {
935       ((DatastoreListener)temp.elementAt(i)).resourceWritten(e);
936     }
937   }
938 
939   public void resourceLoaded(CreoleEvent e) {
940     if(DEBUG)
941       System.out.println("resource loaded...");
942   }
943 
944   public void resourceRenamed(Resource resource, String oldName,
945                               String newName){
946   }
947 
948 
949   public void resourceUnloaded(CreoleEvent e) {
950 
951     Assert.assertNotNull(e.getResource());
952     if(! (e.getResource() instanceof LanguageResource))
953       return;
954 
955     //1. check it's our resource
956     LanguageResource lr = (LanguageResource)e.getResource();
957 
958     //this is a resource from another DS, so no need to do anything
959     if(lr.getDataStore() != this)
960       return;
961 
962     //2. remove from the list of reosurce that should be sunced if DS is closed
963     this.dependentResources.remove(lr);
964 
965     //3. don't save it, this may not be the user's choice
966 
967     //4. remove the reource as listener for events from the DataStore
968     //otherwise the DS will continue sending it events when the reource is
969     // no longer active
970     this.removeDatastoreListener((DatastoreListener)lr);
971   }
972 
973   public void datastoreOpened(CreoleEvent e) {
974     if(DEBUG)
975       System.out.println("datastore opened...");
976   }
977 
978   public void datastoreCreated(CreoleEvent e) {
979     if(DEBUG)
980       System.out.println("datastore created...");
981   }
982 
983   public void datastoreClosed(CreoleEvent e) {
984     if(DEBUG)
985       System.out.println("datastore closed...");
986     //sync all dependent resources
987   }
988 
989   /** identify user using this datastore */
990   public void setSession(Session s)
991     throws gate.security.SecurityException {
992 
993     this.session = s;
994   }
995 
996 
997 
998   /** identify user using this datastore */
999   public Session getSession(Session s)
1000    throws gate.security.SecurityException {
1001
1002    return this.session;
1003  }
1004
1005  /** Get a list of LRs that satisfy some set or restrictions */
1006  public abstract List findLrIds(List constraints) throws PersistenceException;
1007
1008  /**
1009   *  Get a list of LRs that satisfy some set or restrictions and are
1010   *  of a particular type
1011   */
1012  public abstract List findLrIds(List constraints, String lrType)
1013  throws PersistenceException;
1014
1015
1016  /** get security information for LR . */
1017  public SecurityInfo getSecurityInfo(LanguageResource lr)
1018    throws PersistenceException {
1019
1020    //0. preconditions
1021    Assert.assertNotNull(lr);
1022    Assert.assertNotNull(lr.getLRPersistenceId());
1023    Assert.assertTrue(lr.getLRPersistenceId() instanceof Long);
1024    Assert.assertEquals(this,lr.getDataStore());
1025    Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
1026                      lr instanceof DatabaseCorpusImpl);
1027
1028    PreparedStatement pstmt = null;
1029    ResultSet rs = null;
1030
1031    //1. read data
1032    Long userID = null;
1033    Long groupID = null;
1034    int  perm;
1035    try {
1036      String sql =  "   select lr_owner_user_id, "+
1037                    "          lr_owner_group_id, " +
1038                    "          lr_access_mode "+
1039                    "   from   "+this.dbSchema+"t_lang_resource "+
1040                    "   where  lr_id = ?";
1041      pstmt = this.jdbcConn.prepareStatement(sql);
1042      pstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
1043      rs = pstmt.executeQuery();
1044
1045      if (false == rs.next()) {
1046        throw new PersistenceException("Invalid LR ID supplied - no data found");
1047      }
1048
1049      userID = new Long(rs.getLong("lr_owner_user_id"));
1050      groupID = new Long(rs.getLong("lr_owner_group_id"));
1051      perm = rs.getInt("lr_access_mode");
1052
1053      Assert.assertTrue(perm == SecurityInfo.ACCESS_GR_GW ||
1054                        perm == SecurityInfo.ACCESS_GR_OW ||
1055                        perm == SecurityInfo.ACCESS_OR_OW ||
1056                        perm == SecurityInfo.ACCESS_WR_GW);
1057    }
1058    catch(SQLException sqle) {
1059      throw new PersistenceException("Can't read document permissions from DB, error is [" +
1060                                      sqle.getMessage() +"]");
1061    }
1062    finally {
1063      DBHelper.cleanup(rs);
1064      DBHelper.cleanup(pstmt);
1065    }
1066
1067    //2. get data from AccessController
1068    User usr = null;
1069    Group grp = null;
1070    try {
1071      usr = this.ac.findUser(userID);
1072      grp = this.ac.findGroup(groupID);
1073    }
1074    catch (SecurityException se) {
1075      throw new PersistenceException("Invalid security settings found in DB [" +
1076                                      se.getMessage() +"]");
1077    }
1078
1079    //3. construct SecurityInfo
1080    SecurityInfo si = new SecurityInfo(perm,usr,grp);
1081
1082
1083    return si;
1084  }
1085
1086  /** creates a LR of type Corpus  */
1087  protected Corpus createCorpus(Corpus corp,SecurityInfo secInfo, boolean newTransPerDocument)
1088    throws PersistenceException,SecurityException {
1089
1090    //1. create an LR entry for the corpus (T_LANG_RESOURCE table)
1091    Long lrID = createLR(DBHelper.CORPUS_CLASS,corp.getName(),secInfo,null);
1092
1093    //2.create am entry in the T_COPRUS table
1094    Long corpusID = null;
1095    //DB stuff
1096    CallableStatement cstmt = null;
1097    PreparedStatement pstmt = null;
1098    ResultSet rs = null;
1099
1100    try {
1101      if (this.dbType == DBHelper.ORACLE_DB) {
1102        cstmt = this.jdbcConn.prepareCall("{ call "+Gate.DB_OWNER+".persist.create_corpus(?,?) }");
1103        cstmt.setLong(1,lrID.longValue());
1104        cstmt.registerOutParameter(2,java.sql.Types.BIGINT);
1105        cstmt.execute();
1106        corpusID = new Long(cstmt.getLong(2));
1107      }
1108      else if (this.dbType == DBHelper.POSTGRES_DB) {
1109        pstmt = this.jdbcConn.prepareStatement("select persist_create_corpus(?) ");
1110        pstmt.setLong(1,lrID.longValue());
1111        pstmt.execute();
1112        rs = pstmt.getResultSet();
1113
1114        if (false == rs.next()) {
1115          throw new PersistenceException("empty result set");
1116        }
1117
1118        corpusID = new Long(rs.getLong(1));
1119      }
1120      else {
1121        Assert.fail();
1122      }
1123    }
1124    catch(SQLException sqle) {
1125      throw new PersistenceException("can't create corpus [step 2] in DB: ["+ sqle.getMessage()+"]");
1126    }
1127    finally {
1128      DBHelper.cleanup(cstmt);
1129      DBHelper.cleanup(pstmt);
1130      DBHelper.cleanup(rs);
1131    }
1132
1133    //3. for each document in the corpus call createDocument()
1134    Iterator itDocuments = corp.iterator();
1135    Vector dbDocs = new Vector();
1136    while (itDocuments.hasNext()) {
1137      Document doc = (Document)itDocuments.next();
1138
1139      //3.1. ensure that the document is either transient or is from the ...
1140      // same DataStore
1141      if (doc.getLRPersistenceId() == null) {
1142        //transient document
1143
1144        //now this is a bit ugly patch, the transaction related functionality
1145        //should not be in this method
1146        if (newTransPerDocument) {
1147          beginTrans();
1148        }
1149
1150        Document dbDoc = createDocument(doc,corpusID,secInfo);
1151
1152        if (newTransPerDocument) {
1153          commitTrans();
1154        }
1155
1156        dbDocs.add(dbDoc);
1157        //8. let the world know
1158        fireResourceAdopted(new DatastoreEvent(this,
1159                                                DatastoreEvent.RESOURCE_ADOPTED,
1160                                                dbDoc,
1161                                                dbDoc.getLRPersistenceId()
1162                                              )
1163                            );
1164
1165        //9. fire also resource written event because it's now saved
1166        fireResourceWritten(new DatastoreEvent(this,
1167                                                DatastoreEvent.RESOURCE_WRITTEN,
1168                                                dbDoc,
1169                                                dbDoc.getLRPersistenceId()
1170                                              )
1171                           );
1172
1173      }
1174      else if (doc.getDataStore().equals(this)) {
1175        //persistent doc from the same DataStore
1176        fireResourceAdopted(
1177            new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED,
1178                               doc,
1179                               doc.getLRPersistenceId()));
1180
1181        //6. fire also resource written event because it's now saved
1182        fireResourceWritten(
1183          new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN,
1184                              doc,
1185                              doc.getLRPersistenceId()));
1186      }
1187      else {
1188        //persistent doc from other datastore
1189        //skip
1190        gate.util.Err.prln("document ["+doc.getLRPersistenceId()+"] is adopted from another "+
1191                            " datastore. Skipped.");
1192      }
1193    }
1194
1195    //4. create features
1196    if (this.dbType == DBHelper.ORACLE_DB) {
1197      createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures());
1198    }
1199    else if (this.dbType == DBHelper.POSTGRES_DB) {
1200      createFeatures(lrID,DBHelper.FEATURE_OWNER_CORPUS,corp.getFeatures());
1201    }
1202    else {
1203      Assert.fail();
1204    }
1205
1206
1207    //5. create a DatabaseCorpusImpl and return it
1208///    Corpus dbCorpus = new DatabaseCorpusImpl(corp.getName(),
1209///                                             this,
1210///                                              lrID,
1211///                                              corp.getFeatures(),
1212///                                              dbDocs);
1213///
1214
1215    Corpus dbCorpus = null;
1216    FeatureMap params = Factory.newFeatureMap();
1217    HashMap initData = new HashMap();
1218
1219    initData.put("DS",this);
1220    initData.put("LR_ID",lrID);
1221    initData.put("CORP_NAME",corp.getName());
1222    initData.put("CORP_FEATURES",corp.getFeatures());
1223    initData.put("CORP_SUPPORT_LIST",dbDocs);
1224
1225    params.put("initData__$$__", initData);
1226
1227    try {
1228      //here we create the persistent LR via Factory, so it's registered
1229      //in GATE
1230      dbCorpus = (Corpus)Factory.createResource("gate.corpora.DatabaseCorpusImpl", params);
1231    }
1232    catch (gate.creole.ResourceInstantiationException ex) {
1233      throw new GateRuntimeException(ex.getMessage());
1234    }
1235
1236    //6. done
1237    return dbCorpus;
1238  }
1239
1240  /**
1241   * helper for adopt
1242   * creates a LR of type Document
1243   */
1244  protected Document createDocument(Document doc,SecurityInfo secInfo)
1245  throws PersistenceException,SecurityException {
1246
1247    //delegate, set to Null
1248    return createDocument(doc,null,secInfo);
1249  }
1250
1251
1252  /**
1253   * helper for adopt
1254   * creates a LR of type Document
1255   */
1256  protected Document createDocument(Document doc, Long corpusID,SecurityInfo secInfo)
1257  throws PersistenceException,SecurityException {
1258
1259    //-1. preconditions
1260    Assert.assertNotNull(doc);
1261    Assert.assertNotNull(secInfo);
1262
1263    //0. check securoity settings
1264    if (false == this.ac.isValidSecurityInfo(secInfo)) {
1265      throw new SecurityException("Invalid security settings");
1266    }
1267
1268    //1. get the data to be stored
1269    AnnotationSet defaultAnnotations = doc.getAnnotations();
1270    DocumentContent docContent = doc.getContent();
1271    FeatureMap docFeatures = doc.getFeatures();
1272    String docName  = doc.getName();
1273    URL docURL = doc.getSourceUrl();
1274    Boolean docIsMarkupAware = doc.getMarkupAware();
1275    Long docStartOffset = doc.getSourceUrlStartOffset();
1276    Long docEndOffset = doc.getSourceUrlEndOffset();
1277    String docEncoding = null;
1278    try {
1279      docEncoding = (String)doc.
1280        getParameterValue(Document.DOCUMENT_ENCODING_PARAMETER_NAME);
1281    }
1282    catch(gate.creole.ResourceInstantiationException re) {
1283      throw new PersistenceException("cannot create document: error getting " +
1284                                     " document encoding ["+re.getMessage()+"]");
1285    }
1286
1287
1288    //3. create a Language Resource (an entry in T_LANG_RESOURCE) for this document
1289    Long lrID = createLR(DBHelper.DOCUMENT_CLASS,docName,secInfo,null);
1290
1291    //4. create a record in T_DOCUMENT for this document
1292    Long docID = createDoc(lrID,
1293                            docURL,
1294                            docEncoding,
1295                            docStartOffset,
1296                            docEndOffset,
1297                            docIsMarkupAware,
1298                            corpusID);
1299
1300
1301    //5. fill document content (record[s] in T_DOC_CONTENT)
1302
1303    //do we have content at all?
1304    if (docContent.size().longValue() > 0) {
1305//      updateDocumentContent(docContentID,docContent);
1306      updateDocumentContent(docID,docContent);
1307    }
1308
1309    //6. insert annotations, etc
1310
1311    //6.1. create default annotation set
1312    createAnnotationSet(lrID,defaultAnnotations);
1313
1314    //6.2. create named annotation sets
1315    Map namedAnns = doc.getNamedAnnotationSets();
1316    //the map may be null
1317    if (null != namedAnns) {
1318      Set setAnns = namedAnns.entrySet();
1319      Iterator itAnns = setAnns.iterator();
1320
1321      while (itAnns.hasNext()) {
1322        Map.Entry mapEntry = (Map.Entry)itAnns.next();
1323        //String currAnnName = (String)mapEntry.getKey();
1324        AnnotationSet currAnnSet = (AnnotationSet)mapEntry.getValue();
1325
1326        //create a-sets
1327        createAnnotationSet(lrID,currAnnSet);
1328      }
1329    }
1330
1331    //7. create features
1332    if (this.dbType == DBHelper.ORACLE_DB) {
1333      createFeaturesBulk(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures);
1334    }
1335    else if (this.dbType == DBHelper.POSTGRES_DB) {
1336      createFeatures(lrID,DBHelper.FEATURE_OWNER_DOCUMENT,docFeatures);
1337    }
1338    else {
1339      Assert.fail();
1340    }
1341
1342
1343    //9. create a DatabaseDocument wrapper and return it
1344
1345/*    Document dbDoc = new DatabaseDocumentImpl(this.jdbcConn,
1346                                              doc.getName(),
1347                                              this,
1348                                              lrID,
1349                                              doc.getContent(),
1350                                              doc.getFeatures(),
1351                                              doc.getMarkupAware(),
1352                                              doc.getSourceUrl(),
1353                                              doc.getSourceUrlStartOffset(),
1354                                              doc.getSourceUrlEndOffset(),
1355                                              doc.getAnnotations(),
1356                                              doc.getNamedAnnotationSets());
1357*/
1358    Document dbDoc = null;
1359    FeatureMap params = Factory.newFeatureMap();
1360
1361    HashMap initData = new HashMap();
1362    initData.put("JDBC_CONN",this.jdbcConn);
1363    initData.put("DS",this);
1364    initData.put("LR_ID",lrID);
1365    initData.put("DOC_NAME",doc.getName());
1366    initData.put("DOC_CONTENT",doc.getContent());
1367    initData.put("DOC_FEATURES",doc.getFeatures());
1368    initData.put("DOC_MARKUP_AWARE",doc.getMarkupAware());
1369    initData.put("DOC_SOURCE_URL",doc.getSourceUrl());
1370    initData.put("DOC_SOURCE_URL_START",doc.getSourceUrlStartOffset());
1371    initData.put("DOC_SOURCE_URL_END",doc.getSourceUrlEndOffset());
1372    initData.put("DOC_DEFAULT_ANNOTATIONS",doc.getAnnotations());
1373    initData.put("DOC_NAMED_ANNOTATION_SETS",doc.getNamedAnnotationSets());
1374
1375    params.put("initData__$$__", initData);
1376
1377    try {
1378      //here we create the persistent LR via Factory, so it's registered
1379      //in GATE
1380      dbDoc = (Document)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params);
1381    }
1382    catch (gate.creole.ResourceInstantiationException ex) {
1383      throw new GateRuntimeException(ex.getMessage());
1384    }
1385
1386    return dbDoc;
1387  }
1388
1389  protected abstract Long createLR(String lrType,
1390                          String lrName,
1391                          SecurityInfo si,
1392                          Long lrParentID)
1393    throws PersistenceException,SecurityException;
1394
1395
1396  protected abstract Long createDoc(Long _lrID,
1397                          URL _docURL,
1398                          String _docEncoding,
1399                          Long _docStartOffset,
1400                          Long _docEndOffset,
1401                          Boolean _docIsMarkupAware,
1402                          Long _corpusID)
1403    throws PersistenceException;
1404
1405  protected abstract void updateDocumentContent(Long docID,DocumentContent content)
1406    throws PersistenceException;
1407
1408  protected abstract void createAnnotationSet(Long lrID, AnnotationSet aset)
1409    throws PersistenceException;
1410
1411  protected abstract void createFeaturesBulk(Long entityID, int entityType, FeatureMap features)
1412    throws PersistenceException;
1413
1414  protected abstract void createFeatures(Long entityID, int entityType, FeatureMap features)
1415    throws PersistenceException;
1416
1417  /**
1418   * Save: synchonise the in-memory image of the LR with the persistent
1419   * image.
1420   */
1421  protected void _sync(LanguageResource lr, boolean openNewTrans)
1422    throws PersistenceException,SecurityException {
1423
1424    //0.preconditions
1425    Assert.assertNotNull(lr);
1426    Long lrID = (Long)lr.getLRPersistenceId();
1427
1428    if (false == lr instanceof Document &&
1429        false == lr instanceof Corpus) {
1430      //only documents and corpuses could be serialized in DB
1431      throw new IllegalArgumentException("only Documents and Corpuses could "+
1432                                          "be serialized in DB");
1433    }
1434
1435    // check that this LR is one of ours (i.e. has been adopted)
1436    if( null == lr.getDataStore() || false == lr.getDataStore().equals(this))
1437      throw new PersistenceException(
1438        "This LR is not stored in this DataStore"
1439      );
1440
1441
1442    //1. check session
1443    if (null == this.session) {
1444      throw new SecurityException("session not set");
1445    }
1446
1447    if (false == this.ac.isValidSession(this.session)) {
1448      throw new SecurityException("invalid session supplied");
1449    }
1450
1451    //2. check permissions
1452    if (false == canWriteLR(lrID)) {
1453      throw new SecurityException("insufficient privileges");
1454    }
1455
1456    //3. is the resource locked?
1457    User lockingUser = getLockingUser(lr);
1458    User currUser = this.session.getUser();
1459
1460    if (lockingUser != null && false == lockingUser.equals(currUser)) {
1461      throw new PersistenceException("document is locked by another user and cannot be synced");
1462    }
1463
1464
1465    boolean transFailed = false;
1466    try {
1467      //2. autocommit should be FALSE because of LOBs
1468      if (openNewTrans) {
1469        beginTrans();
1470      }
1471
1472      //3. perform changes, if anything goes wrong, rollback
1473      if (lr instanceof Document) {
1474        syncDocument((Document)lr);
1475      }
1476      else {
1477        syncCorpus((Corpus)lr);
1478      }
1479
1480      //4. done, commit
1481      if (openNewTrans) {
1482        commitTrans();
1483      }
1484    }
1485    catch(PersistenceException pe) {
1486      transFailed = true;
1487      throw(pe);
1488    }
1489    finally {
1490      //problems?
1491      if (transFailed) {
1492        rollbackTrans();
1493      }
1494    }
1495
1496    // let the world know about it
1497    fireResourceWritten(
1498      new DatastoreEvent(this, DatastoreEvent.RESOURCE_WRITTEN, lr, lr.getLRPersistenceId()));
1499  }
1500
1501  /**
1502   * Releases the exlusive lock on a resource from the persistent store.
1503   */
1504  protected User getLockingUser(LanguageResource lr)
1505    throws PersistenceException,SecurityException {
1506
1507    //0. preconditions
1508    Assert.assertNotNull(lr);
1509    Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
1510                      lr instanceof DatabaseCorpusImpl);
1511    Assert.assertNotNull(lr.getLRPersistenceId());
1512    Assert.assertEquals(lr.getDataStore(),this);
1513
1514    //delegate
1515    return getLockingUser((Long)lr.getLRPersistenceId());
1516  }
1517
1518
1519
1520  /**
1521   * Releases the exlusive lock on a resource from the persistent store.
1522   */
1523  protected User getLockingUser(Long lrID)
1524  throws PersistenceException,SecurityException {
1525
1526    //1. check session
1527    if (null == this.session) {
1528      throw new SecurityException("session not set");
1529    }
1530
1531    if (false == this.ac.isValidSession(this.session)) {
1532      throw new SecurityException("invalid session supplied");
1533    }
1534
1535    //3. read from DB
1536    PreparedStatement pstmt = null;
1537    Long userID = null;
1538    ResultSet rs = null;
1539
1540    try {
1541
1542      String sql = null;
1543
1544      if (this.dbType == DBHelper.ORACLE_DB) {
1545        sql = "   select  nvl(lr_locking_user_id,0) as user_id" +
1546              "   from "+this.dbSchema+"t_lang_resource " +
1547              "   where   lr_id = ?";
1548      }
1549      else if (this.dbType == DBHelper.POSTGRES_DB) {
1550        sql = "   select  coalesce(lr_locking_user_id,0) as user_id" +
1551              "   from t_lang_resource " +
1552              "   where   lr_id = ?";
1553      }
1554      else {
1555        throw new IllegalArgumentException();
1556      }
1557
1558      pstmt = this.jdbcConn.prepareStatement(sql);
1559      pstmt.setLong(1,lrID.longValue());
1560      pstmt.execute();
1561      rs = pstmt.getResultSet();
1562
1563      if (false == rs.next()) {
1564        throw new PersistenceException("LR not found in DB");
1565      }
1566
1567      long result = rs.getLong("user_id");
1568
1569      return result == 0  ? null
1570                          : this.ac.findUser(new Long(result));
1571    }
1572    catch(SQLException sqle) {
1573      throw new PersistenceException("can't get locking user from DB : ["+ sqle.getMessage()+"]");
1574    }
1575    finally {
1576      DBHelper.cleanup(rs);
1577      DBHelper.cleanup(pstmt);
1578    }
1579  }
1580
1581  /** helper for sync() - saves a Corpus in the database */
1582  protected void syncCorpus(Corpus corp)
1583    throws PersistenceException,SecurityException {
1584
1585    //0. preconditions
1586    Assert.assertNotNull(corp);
1587    Assert.assertTrue(corp instanceof DatabaseCorpusImpl);
1588    Assert.assertEquals(this,corp.getDataStore());
1589    Assert.assertNotNull(corp.getLRPersistenceId());
1590
1591    EventAwareCorpus dbCorpus = (EventAwareCorpus)corp;
1592
1593    //1. sync the corpus name?
1594    if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_NAME)) {
1595      _syncLR(corp);
1596    }
1597
1598    //2. sync the corpus features?
1599    if (dbCorpus.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) {
1600      _syncFeatures(corp);
1601    }
1602
1603    //2.5 get removed documents and detach (not remove) them from the corpus in the
1604    //database
1605    List removedDocLRIDs = dbCorpus.getRemovedDocuments();
1606    if (removedDocLRIDs.size() > 0) {
1607      _syncRemovedDocumentsFromCorpus(removedDocLRIDs,(Long)corp.getLRPersistenceId());
1608    }
1609
1610    //3. get all documents
1611    //--Iterator it = corp.iterator();
1612    Iterator it = dbCorpus.getLoadedDocuments().iterator();
1613
1614    while (it.hasNext()) {
1615      Document dbDoc = (Document)it.next();
1616      //note - document may be NULL which means it was not loaded (load on demand)
1617      //just ignore it then
1618      if (null == dbDoc) {
1619        continue;
1620      }
1621
1622      //adopt/sync?
1623      if (null == dbDoc.getLRPersistenceId()) {
1624        //doc was never adopted, adopt it
1625
1626        //3.1 remove the transient doc from the corpus
1627        it.remove();
1628
1629        //3.2 get the security info for the corpus
1630        SecurityInfo si = getSecurityInfo(corp);
1631
1632
1633        Document adoptedDoc = null;
1634        try {
1635          //3.3. adopt the doc with the sec info
1636//System.out.println("adopting ["+dbDoc.getName()+"] ...");
1637          //don't open a new transaction, since sync() already has opended one
1638          adoptedDoc = (Document)_adopt(dbDoc,si,true);
1639
1640          //3.4. add doc to corpus in DB
1641          addDocumentToCorpus((Long)adoptedDoc.getLRPersistenceId(),
1642                              (Long)corp.getLRPersistenceId());
1643        }
1644        catch(SecurityException se) {
1645          throw new PersistenceException(se);
1646        }
1647
1648        //3.5 add back to corpus the new DatabaseDocument
1649        corp.add(adoptedDoc);
1650      }
1651      else {
1652        //don't open a new transaction, the sync() called for corpus has already
1653        //opened one
1654        try {
1655          _sync(dbDoc,true);
1656
1657          // let the world know about it
1658          fireResourceWritten( new DatastoreEvent(this,
1659                                                  DatastoreEvent.RESOURCE_WRITTEN,
1660                                                  dbDoc,
1661                                                  dbDoc.getLRPersistenceId()
1662                                                  )
1663                              );
1664
1665          //if the document is form the same DS but did not belong to the corpus add it now
1666          //NOTE: if the document already belongs to the corpus then nothing will be changed
1667          //in the DB
1668          addDocumentToCorpus((Long)dbDoc.getLRPersistenceId(),
1669                              (Long)corp.getLRPersistenceId());
1670        }
1671        catch(SecurityException se) {
1672          gate.util.Err.prln("document cannot be synced: ["+se.getMessage()+"]");
1673        }
1674      }
1675    }
1676  }
1677
1678  /** helper for sync() - saves a Document in the database */
1679  /** helper for sync() - saves a Document in the database */
1680  protected void syncDocument(Document doc)
1681    throws PersistenceException, SecurityException {
1682
1683    Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1684    Assert.assertTrue(doc.getLRPersistenceId() instanceof Long);
1685
1686    Long lrID = (Long)doc.getLRPersistenceId();
1687    EventAwareLanguageResource dbDoc = (EventAwareLanguageResource)doc;
1688    //1. sync LR
1689    // only name can be changed here
1690    if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_NAME)) {
1691      _syncLR(doc);
1692    }
1693
1694    //2. sync Document
1695    if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_MAIN)) {
1696      _syncDocumentHeader(doc);
1697    }
1698
1699    //3. [optional] sync Content
1700    if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.DOC_CONTENT)) {
1701      _syncDocumentContent(doc);
1702    }
1703
1704    //4. [optional] sync Features
1705    if (true == dbDoc.isResourceChanged(EventAwareLanguageResource.RES_FEATURES)) {
1706      _syncFeatures(doc);
1707    }
1708
1709    //5. [optional] delete from DB named sets that were removed from the document
1710    Collection removedSets = ((EventAwareDocument)dbDoc).getRemovedAnnotationSets();
1711    Collection addedSets = ((EventAwareDocument)dbDoc).getAddedAnnotationSets();
1712    if (false == removedSets.isEmpty() || false == addedSets.isEmpty()) {
1713      _syncAnnotationSets(doc,removedSets,addedSets);
1714    }
1715
1716    //6. [optional] sync Annotations
1717    _syncAnnotations(doc);
1718  }
1719
1720
1721  /**
1722   *  helper for sync()
1723   *  NEVER call directly
1724   */
1725  protected abstract void _syncLR(LanguageResource lr)
1726    throws PersistenceException,SecurityException;
1727
1728  /** helper for sync() - never call directly */
1729  protected abstract void _syncDocumentHeader(Document doc)
1730    throws PersistenceException;
1731
1732  /** helper for sync() - never call directly */
1733  protected abstract void _syncDocumentContent(Document doc)
1734    throws PersistenceException;
1735
1736  /** helper for sync() - never call directly */
1737  protected abstract void _syncFeatures(LanguageResource lr)
1738    throws PersistenceException;
1739
1740  /** helper for sync() - never call directly */
1741  protected void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets)
1742    throws PersistenceException {
1743
1744    //0. preconditions
1745    Assert.assertNotNull(doc);
1746    Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1747    Assert.assertNotNull(doc.getLRPersistenceId());
1748    Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
1749                      this.getDatabaseID());
1750    Assert.assertNotNull(removedSets);
1751    Assert.assertNotNull(addedSets);
1752
1753    Long lrID = (Long)doc.getLRPersistenceId();
1754
1755    //1. delete from DB removed a-sets
1756    PreparedStatement stmt = null;
1757
1758    try {
1759
1760      if (this.dbType == DBHelper.ORACLE_DB) {
1761        stmt = this.jdbcConn.prepareCall("{ call "+this.dbSchema+"persist.delete_annotation_set(?,?) }");
1762      }
1763      else if (this.dbType == DBHelper.POSTGRES_DB) {
1764        stmt = this.jdbcConn.prepareStatement("select persist_delete_annotation_set(?,?)");
1765      }
1766      else {
1767        Assert.fail();
1768      }
1769
1770      Iterator it = removedSets.iterator();
1771      while (it.hasNext()) {
1772        String setName = (String)it.next();
1773        stmt.setLong(1,lrID.longValue());
1774        stmt.setString(2,setName);
1775        stmt.execute();
1776      }
1777    }
1778    catch(SQLException sqle) {
1779      throw new PersistenceException("can't remove annotation set from DB: ["+ sqle.getMessage()+"]");
1780    }
1781    finally {
1782      DBHelper.cleanup(stmt);
1783    }
1784
1785    //2. create in DB new a-sets
1786    Iterator it = addedSets.iterator();
1787    while (it.hasNext()) {
1788      String setName = (String)it.next();
1789      AnnotationSet aset = doc.getAnnotations(setName);
1790
1791      Assert.assertNotNull(aset);
1792      Assert.assertTrue(aset instanceof DatabaseAnnotationSetImpl);
1793
1794      createAnnotationSet(lrID,aset);
1795    }
1796  }
1797
1798
1799  /** helper for sync() - never call directly */
1800  protected void _syncAnnotations(Document doc)
1801    throws PersistenceException {
1802
1803    //0. preconditions
1804    Assert.assertNotNull(doc);
1805    Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1806    Assert.assertNotNull(doc.getLRPersistenceId());
1807    Assert.assertEquals(((DatabaseDataStore)doc.getDataStore()).getDatabaseID(),
1808                      this.getDatabaseID());
1809
1810
1811    EventAwareDocument ead = (EventAwareDocument)doc;
1812    //1. get the sets read from the DB for this document
1813    //chnaged annotations can occur only in such sets
1814    Collection loadedSets = ead.getLoadedAnnotationSets();
1815
1816    Iterator it = loadedSets.iterator();
1817    while (it.hasNext()) {
1818      AnnotationSet as = (AnnotationSet)it.next();
1819      //check that this set is neither NEW nor DELETED
1820      //they should be already synced
1821      if (ead.getAddedAnnotationSets().contains(as.getName()) ||
1822          ead.getRemovedAnnotationSets().contains(as.getName())) {
1823        //oops, ignore it
1824        continue;
1825      }
1826
1827      EventAwareAnnotationSet eas = (EventAwareAnnotationSet)as;
1828      Assert.assertNotNull(as);
1829
1830      Collection anns = null;
1831      anns = eas.getAddedAnnotations();
1832      Assert.assertNotNull(anns);
1833      if (anns.size()>0) {
1834        _syncAddedAnnotations(doc,as,anns);
1835      }
1836
1837      anns = eas.getRemovedAnnotations();
1838      Assert.assertNotNull(anns);
1839      if (anns.size()>0) {
1840        _syncRemovedAnnotations(doc,as,anns);
1841      }
1842
1843      anns = eas.getChangedAnnotations();
1844      Assert.assertNotNull(anns);
1845      if (anns.size()>0) {
1846        _syncChangedAnnotations(doc,as,anns);
1847      }
1848    }
1849  }
1850
1851  /** helper for sync() - never call directly */
1852  protected void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes)
1853    throws PersistenceException {
1854
1855    //0.preconditions
1856    Assert.assertNotNull(doc);
1857    Assert.assertNotNull(as);
1858    Assert.assertNotNull(changes);
1859    Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
1860    Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
1861    Assert.assertTrue(changes.size() > 0);
1862
1863
1864    PreparedStatement pstmt = null;
1865    ResultSet rs = null;
1866    CallableStatement cstmt = null;
1867    Long lrID = (Long)doc.getLRPersistenceId();
1868    Long asetID = null;
1869
1870    try {
1871      //1. get the a-set ID in the database
1872      String sql = " select as_id  " +
1873                   " from  "+this.dbSchema+"v_annotation_set " +
1874                   " where  lr_id = ? ";
1875      //do we have aset name?
1876      String clause = null;
1877      String name = as.getName();
1878      if (null != name) {
1879        clause =   "        and as_name = ? ";
1880      }
1881      else {
1882        clause =   "        and as_name is null ";
1883      }
1884      sql = sql + clause;
1885
1886      pstmt = this.jdbcConn.prepareStatement(sql);
1887      pstmt.setLong(1,lrID.longValue());
1888      if (null != name) {
1889        pstmt.setString(2,name);
1890      }
1891      pstmt.execute();
1892      rs = pstmt.getResultSet();
1893
1894      if (rs.next()) {
1895        asetID = new Long(rs.getLong("as_id"));
1896      }
1897      else {
1898        throw new PersistenceException("cannot find annotation set with" +
1899                                      " name=["+name+"] , LRID=["+lrID+"] in database");
1900      }
1901
1902      //cleanup
1903      DBHelper.cleanup(rs);
1904      DBHelper.cleanup(pstmt);
1905
1906      //3. insert the new annotations from this set
1907
1908      //3.1. prepare call
1909      if (this.dbType == DBHelper.ORACLE_DB) {
1910
1911        cstmt = this.jdbcConn.prepareCall(
1912                "{ call "+Gate.DB_OWNER+".persist.create_annotation(?,?,?,?,?,?,?,?,?) }");
1913
1914        Long annGlobalID = null;
1915        Iterator it = changes.iterator();
1916
1917        while (it.hasNext()) {
1918
1919          //3.2. insert annotation
1920          Annotation ann = (Annotation)it.next();
1921
1922          Node start = (Node)ann.getStartNode();
1923          Node end = (Node)ann.getEndNode();
1924          String type = ann.getType();
1925
1926          cstmt.setLong(1,lrID.longValue());
1927          cstmt.setLong(2,ann.getId().longValue());
1928          cstmt.setLong(3,asetID.longValue());
1929          cstmt.setLong(4,start.getId().longValue());
1930          cstmt.setLong(5,start.getOffset().longValue());
1931          cstmt.setLong(6,end.getId().longValue());
1932          cstmt.setLong(7,end.getOffset().longValue());
1933          cstmt.setString(8,type);
1934          cstmt.registerOutParameter(9,java.sql.Types.BIGINT);
1935
1936          cstmt.execute();
1937          annGlobalID = new Long(cstmt.getLong(9));
1938
1939          //3.3. set annotation features
1940          FeatureMap features = ann.getFeatures();
1941          Assert.assertNotNull(features);
1942
1943          if (this.dbType == DBHelper.ORACLE_DB) {
1944            createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
1945          }
1946          else if (this.dbType == DBHelper.POSTGRES_DB) {
1947            createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
1948          }
1949          else {
1950            Assert.fail();
1951          }
1952        }
1953      }
1954      else if (this.dbType == DBHelper.POSTGRES_DB) {
1955
1956        sql = "select persist_create_annotation(?,?,?,?,?,?,?,?)";
1957        pstmt = this.jdbcConn.prepareStatement(sql);
1958
1959        Long annGlobalID = null;
1960        Iterator it = changes.iterator();
1961
1962        while (it.hasNext()) {
1963
1964          //3.2. insert annotation
1965          Annotation ann = (Annotation)it.next();
1966
1967          Node start = (Node)ann.getStartNode();
1968          Node end = (Node)ann.getEndNode();
1969          String type = ann.getType();
1970
1971          pstmt.setLong(1,lrID.longValue());
1972          pstmt.setLong(2,ann.getId().longValue());
1973          pstmt.setLong(3,asetID.longValue());
1974          pstmt.setLong(4,start.getId().longValue());
1975          pstmt.setLong(5,start.getOffset().longValue());
1976          pstmt.setLong(6,end.getId().longValue());
1977          pstmt.setLong(7,end.getOffset().longValue());
1978          pstmt.setString(8,type);
1979          pstmt.execute();
1980
1981          rs = pstmt.getResultSet();
1982
1983          if (false == rs.next()) {
1984            throw new PersistenceException("empty result set");
1985          }
1986          annGlobalID = new Long(rs.getLong(1));
1987
1988          //3.3. set annotation features
1989          FeatureMap features = ann.getFeatures();
1990          Assert.assertNotNull(features);
1991          createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
1992        }
1993      }
1994
1995      else {
1996        throw new IllegalArgumentException();
1997      }
1998
1999    }
2000    catch(SQLException sqle) {
2001      throw new PersistenceException("can't add annotations in DB : ["+
2002                                      sqle.getMessage()+"]");
2003    }
2004    finally {
2005      DBHelper.cleanup(rs);
2006      DBHelper.cleanup(pstmt);
2007      DBHelper.cleanup(cstmt);
2008    }
2009  }
2010
2011  /** helper for sync() - never call directly */
2012  protected void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes)
2013    throws PersistenceException {
2014
2015    //0.preconditions
2016    Assert.assertNotNull(doc);
2017    Assert.assertNotNull(as);
2018    Assert.assertNotNull(changes);
2019    Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
2020    Assert.assertTrue(as instanceof DatabaseAnnotationSetImpl);
2021    Assert.assertTrue(changes.size() > 0);
2022
2023
2024    PreparedStatement pstmt = null;
2025    ResultSet rs = null;
2026    Long lrID = (Long)doc.getLRPersistenceId();
2027    Long docID = null;
2028    Long asetID = null;
2029
2030    try {
2031      //1. get the a-set ID in the database
2032      String sql = " select as_id,  " +
2033                   "        as_doc_id " +
2034                   " from  "+this.dbSchema+"v_annotation_set " +
2035                   " where  lr_id = ? ";
2036      //do we have aset name?
2037      String clause = null;
2038      String name = as.getName();
2039      if (null != name) {
2040        clause =   "        and as_name = ? ";
2041      }
2042      else {
2043        clause =   "        and as_name is null ";
2044      }
2045      sql = sql + clause;
2046
2047      pstmt = this.jdbcConn.prepareStatement(sql);
2048      pstmt.setLong(1,lrID.longValue());
2049      if (null != name) {
2050        pstmt.setString(2,name);
2051      }
2052      pstmt.execute();
2053      rs = pstmt.getResultSet();
2054
2055      if (rs.next()) {
2056        asetID = new Long(rs.getLong("as_id"));
2057        docID = new Long(rs.getLong("as_doc_id"));
2058      }
2059      else {
2060        throw new PersistenceException("cannot find annotation set with" +
2061                                      " name=["+name+"] , LRID=["+lrID+"] in database");
2062      }
2063
2064      //3. delete the removed annotations from this set
2065
2066      //cleanup
2067      DBHelper.cleanup(rs);
2068      DBHelper.cleanup(pstmt);
2069
2070      //3.1. prepare call
2071
2072      if (this.dbType == DBHelper.ORACLE_DB) {
2073        pstmt = this.jdbcConn.prepareCall("{ call "+this.dbSchema+"persist.delete_annotation(?,?) }");
2074      }
2075      else if (this.dbType == DBHelper.POSTGRES_DB) {
2076        pstmt = this.jdbcConn.prepareStatement("select persist_delete_annotation(?,?)");
2077      }
2078      else {
2079        throw new IllegalArgumentException();
2080      }
2081
2082      Iterator it = changes.iterator();
2083
2084      while (it.hasNext()) {
2085
2086        //3.2. insert annotation
2087        Annotation ann = (Annotation)it.next();
2088
2089        pstmt.setLong(1,docID.longValue()); //annotations are linked with documents, not LRs!
2090        pstmt.setLong(2,ann.getId().longValue());
2091        pstmt.execute();
2092      }
2093    }
2094    catch(SQLException sqle) {
2095      throw new PersistenceException("can't delete annotations in DB : ["+
2096                                      sqle.getMessage()+"]");
2097    }
2098    finally {
2099      DBHelper.cleanup(rs);
2100      DBHelper.cleanup(pstmt);
2101    }
2102  }
2103
2104
2105  /** helper for sync() - never call directly */
2106  protected void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes)
2107    throws PersistenceException {
2108
2109    //technically this approach sux
2110    //at least it works
2111
2112    //1. delete
2113    _syncRemovedAnnotations(doc,as,changes);
2114    //2. recreate
2115    _syncAddedAnnotations(doc,as,changes);
2116  }
2117
2118  /**
2119   * Get a resource from the persistent store.
2120   * <B>Don't use this method - use Factory.createResource with
2121   * DataStore and DataStoreInstanceId parameters set instead.</B>
2122   */
2123  public LanguageResource getLr(String lrClassName, Object lrPersistenceId)
2124  throws PersistenceException,SecurityException {
2125
2126    LanguageResource result = null;
2127
2128    //0. preconditions
2129    Assert.assertNotNull(lrPersistenceId);
2130
2131    //1. check session
2132    if (null == this.session) {
2133      throw new SecurityException("session not set");
2134    }
2135
2136    if (false == this.ac.isValidSession(this.session)) {
2137      throw new SecurityException("invalid session supplied");
2138    }
2139
2140    //2. check permissions
2141    if (false == canReadLR(lrPersistenceId)) {
2142      throw new SecurityException("insufficient privileges");
2143    }
2144
2145    //3. get resource from DB
2146    if (lrClassName.equals(DBHelper.DOCUMENT_CLASS)) {
2147      result = readDocument(lrPersistenceId);
2148      Assert.assertTrue(result instanceof DatabaseDocumentImpl);
2149    }
2150    else if (lrClassName.equals(DBHelper.CORPUS_CLASS)) {
2151      result = readCorpus(lrPersistenceId);
2152      Assert.assertTrue(result instanceof DatabaseCorpusImpl);
2153    }
2154    else {
2155      throw new IllegalArgumentException("resource class should be either Document or Corpus");
2156    }
2157
2158    //4. postconditions
2159    Assert.assertNotNull(result.getDataStore());
2160    Assert.assertTrue(result.getDataStore() instanceof DatabaseDataStore);
2161    Assert.assertNotNull(result.getLRPersistenceId());
2162
2163    //5. register the read doc as listener for sync events
2164    addDatastoreListener((DatastoreListener)result);
2165
2166    //6. add the resource to the list of dependent resources - i.e. the ones that the
2167    //data store should take care upon closing [and call sync()]
2168    this.dependentResources.add(result);
2169
2170    //7. done
2171    return result;
2172  }
2173
2174  /** helper method for getLR - reads LR of type Document */
2175  private DatabaseDocumentImpl readDocument(Object lrPersistenceId)
2176    throws PersistenceException {
2177
2178    //0. preconditions
2179    Assert.assertNotNull(lrPersistenceId);
2180
2181    if (false == lrPersistenceId instanceof Long) {
2182      throw new IllegalArgumentException();
2183    }
2184
2185    // 1. dummy document to be initialized
2186    DatabaseDocumentImpl result = new DatabaseDocumentImpl(this.jdbcConn);
2187
2188    PreparedStatement pstmt = null;
2189    ResultSet rs = null;
2190
2191    //3. read from DB
2192    try {
2193      String sql = " select lr_name, " +
2194                   "        lrtp_type, " +
2195                   "        lr_id, " +
2196                   "        lr_parent_id, " +
2197                   "        doc_id, " +
2198                   "        doc_url, " +
2199                   "        doc_start, " +
2200                   "        doc_end, " +
2201                   "        doc_is_markup_aware " +
2202                   " from  "+this.dbSchema+"v_document " +
2203                   " where  lr_id = ? ";
2204
2205      pstmt = this.jdbcConn.prepareStatement(sql);
2206      pstmt.setLong(1,((Long)lrPersistenceId).longValue());
2207      pstmt.execute();
2208      rs = pstmt.getResultSet();
2209
2210      if (false == rs.next()) {
2211        //ooops mo data found
2212        throw new PersistenceException("Invalid LR ID supplied - no data found");
2213      }
2214
2215      //4. fill data
2216
2217      //4.0 name
2218      String lrName = rs.getString("lr_name");
2219      Assert.assertNotNull(lrName);
2220      result.setName(lrName);
2221
2222      //4.1 parent
2223      Long parentID = null;
2224      long parent_id = rs.getLong("lr_parent_id");
2225      if (false == rs.wasNull()) {
2226        parentID = new Long(parent_id);
2227
2228        //read parent resource
2229        LanguageResource parentLR = this.getLr(DBHelper.DOCUMENT_CLASS,parentID);
2230        Assert.assertNotNull(parentLR);
2231        Assert.assertTrue(parentLR instanceof DatabaseDocumentImpl);
2232
2233        result.setParent(parentLR);
2234      }
2235
2236
2237      //4.2. markup aware
2238      if (this.dbType == DBHelper.ORACLE_DB) {
2239        long markup = rs.getLong("doc_is_markup_aware");
2240        Assert.assertTrue(markup == DBHelper.FALSE || markup == DBHelper.TRUE);
2241        if (markup == DBHelper.FALSE) {
2242          result.setMarkupAware(Boolean.FALSE);
2243        }
2244        else {
2245          result.setMarkupAware(Boolean.TRUE);
2246
2247        }
2248      }
2249      else if (this.dbType == DBHelper.POSTGRES_DB) {
2250        boolean markup = rs.getBoolean("doc_is_markup_aware");
2251        result.setMarkupAware(new Boolean(markup));
2252      }
2253      else {
2254        throw new IllegalArgumentException();
2255      }
2256
2257
2258      //4.3 datastore
2259      result.setDataStore(this);
2260
2261      //4.4. persist ID
2262      Long persistID = new Long(rs.getLong("lr_id"));
2263      result.setLRPersistenceId(persistID);
2264
2265      //4.5  source url
2266      String url = rs.getString("doc_url");
2267      result.setSourceUrl(new URL(url));
2268
2269      //4.6. start offset
2270      Long start = null;
2271      long longVal = rs.getLong("doc_start");
2272      //null?
2273      //if NULL is stored in the DB, Oracle returns 0 which is not what we want
2274      if (false == rs.wasNull()) {
2275        start = new Long(longVal);
2276      }
2277      result.setSourceUrlStartOffset(start);
2278//      initData.put("DOC_SOURCE_URL_START",start);
2279
2280      //4.7. end offset
2281      Long end = null;
2282      longVal = rs.getLong("doc_end");
2283      //null?
2284      //if NULL is stored in the DB, Oracle returns 0 which is not what we want
2285      if (false == rs.wasNull()) {
2286        end = new Long(longVal);
2287      }
2288      result.setSourceUrlEndOffset(end);
2289//      initData.put("DOC_SOURCE_URL_END",end);
2290
2291      //4.8 features
2292      FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_DOCUMENT);
2293      result.setFeatures(features);
2294      //initData.put("DOC_FEATURES",features);
2295
2296      //4.9 set the nextAnnotationID correctly
2297      long doc_id = rs.getLong("doc_id");
2298
2299      //cleanup
2300      DBHelper.cleanup(rs);
2301      DBHelper.cleanup(pstmt);
2302
2303      sql = " select  max(ann_local_id),'ann_id'" +
2304            " from "+this.dbSchema+"t_annotation " +
2305            " where ann_doc_id = ?" +
2306            " union " +
2307            " select max(node_local_id),'node_id' " +
2308            " from "+this.dbSchema+"t_node " +
2309            " where node_doc_id = ?";
2310
2311      pstmt = this.jdbcConn.prepareStatement(sql);
2312      pstmt.setLong(1,doc_id);
2313      pstmt.setLong(2,doc_id);
2314      pstmt.execute();
2315      rs = pstmt.getResultSet();
2316
2317      int maxAnnID = 0 , maxNodeID = 0;
2318      //ann id
2319      if (false == rs.next()) {
2320        //ooops no data found
2321        throw new PersistenceException("Invalid LR ID supplied - no data found");
2322      }
2323      if (rs.getString(2).equals("ann_id"))
2324        maxAnnID = rs.getInt(1);
2325      else
2326        maxNodeID = rs.getInt(1);
2327
2328      if (false == rs.next()) {
2329        //ooops no data found
2330        throw new PersistenceException("Invalid LR ID supplied - no data found");
2331      }
2332      if (rs.getString(2).equals("node_id"))
2333        maxNodeID = rs.getInt(1);
2334      else
2335        maxAnnID = rs.getInt(1);
2336
2337      result.setNextNodeId(maxNodeID+1);
2338//      initData.put("DOC_NEXT_NODE_ID",new Integer(maxNodeID+1));
2339      result.setNextAnnotationId(maxAnnID+1);
2340//      initData.put("DOC_NEXT_ANN_ID",new Integer(maxAnnID+1));
2341
2342
2343//      params.put("initData__$$__", initData);
2344//      try {
2345        //here we create the persistent LR via Factory, so it's registered
2346        //in GATE
2347//        result = (DatabaseDocumentImpl)Factory.createResource("gate.corpora.DatabaseDocumentImpl", params);
2348//      }
2349//      catch (gate.creole.ResourceInstantiationException ex) {
2350//        throw new GateRuntimeException(ex.getMessage());
2351//      }
2352    }
2353    catch(SQLException sqle) {
2354      throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]");
2355    }
2356    catch(Exception e) {
2357      throw new PersistenceException(e);
2358    }
2359    finally {
2360      DBHelper.cleanup(rs);
2361      DBHelper.cleanup(pstmt);
2362    }
2363
2364    return result;
2365  }
2366
2367
2368  /**
2369   *  helper method for getLR - reads LR of type Corpus
2370   */
2371  private DatabaseCorpusImpl readCorpus(Object lrPersistenceId)
2372    throws PersistenceException {
2373
2374    //0. preconditions
2375    Assert.assertNotNull(lrPersistenceId);
2376
2377    if (false == lrPersistenceId instanceof Long) {
2378      throw new IllegalArgumentException();
2379    }
2380
2381    //3. read from DB
2382    PreparedStatement pstmt = null;
2383    ResultSet rs = null;
2384    DatabaseCorpusImpl result = null;
2385
2386    try {
2387      String sql = " select lr_name " +
2388                   " from  "+this.dbSchema+"t_lang_resource " +
2389                   " where  lr_id = ? ";
2390      pstmt = this.jdbcConn.prepareStatement(sql);
2391      pstmt.setLong(1,((Long)lrPersistenceId).longValue());
2392      pstmt.execute();
2393      rs = pstmt.getResultSet();
2394
2395      if (false == rs.next()) {
2396        //ooops mo data found
2397        throw new PersistenceException("Invalid LR ID supplied - no data found");
2398      }
2399
2400      //4. fill data
2401
2402      //4.1 name
2403      String lrName = rs.getString("lr_name");
2404      Assert.assertNotNull(lrName);
2405
2406      //4.8 features
2407      FeatureMap features = readFeatures((Long)lrPersistenceId,DBHelper.FEATURE_OWNER_CORPUS);
2408
2409      //4.9 cleanup
2410      DBHelper.cleanup(rs);
2411      DBHelper.cleanup(pstmt);
2412
2413      sql = " select lr_id ," +
2414            "         lr_name " +
2415            " from "+this.dbSchema+"t_document        doc, " +
2416            "      "+this.dbSchema+"t_lang_resource   lr, " +
2417            "      "+this.dbSchema+"t_corpus_document corpdoc, " +
2418            "      "+this.dbSchema+"t_corpus          corp " +
2419            " where lr.lr_id = doc.doc_lr_id " +
2420            "       and doc.doc_id = corpdoc.cd_doc_id " +
2421            "       and corpdoc.cd_corp_id = corp.corp_id " +
2422            "       and corp_lr_id = ? ";
2423      pstmt = this.jdbcConn.prepareStatement(sql);
2424      pstmt.setLong(1,((Long)lrPersistenceId).longValue());
2425      pstmt.execute();
2426      rs = pstmt.getResultSet();
2427
2428      Vector documentData = new Vector();
2429      while (rs.next()) {
2430        Long docLRID = new Long(rs.getLong("lr_id"));
2431        String docName = rs.getString("lr_name");
2432        documentData.add(new DocumentData(docName, docLRID));
2433      }
2434      DBHelper.cleanup(rs);
2435      DBHelper.cleanup(pstmt);
2436
2437      result = new DatabaseCorpusImpl(lrName,
2438                                      this,
2439                                      (Long)lrPersistenceId,
2440                                      features,
2441                                      documentData);
2442    }
2443    catch(SQLException sqle) {
2444      throw new PersistenceException("can't read LR from DB: ["+ sqle.getMessage()+"]");
2445    }
2446    catch(Exception e) {
2447      throw new PersistenceException(e);
2448    }
2449    finally {
2450      DBHelper.cleanup(rs);
2451      DBHelper.cleanup(pstmt);
2452    }
2453
2454    return result;
2455  }
2456
2457  /**
2458   *  reads the features of an entity
2459   *  entities are of type LR or Annotation
2460   */
2461  protected abstract FeatureMap readFeatures(Long entityID, int entityType)
2462    throws PersistenceException;
2463
2464  /**
2465   *  helper method for delete()
2466   *  never call it directly beause proper events will not be fired
2467   */
2468  protected abstract void deleteDocument(Long lrId)
2469    throws PersistenceException;
2470
2471  /**
2472   *  helper method for delete()
2473   *  never call it directly beause proper events will not be fired
2474   */
2475  protected abstract void deleteCorpus(Long lrId)
2476    throws PersistenceException;
2477
2478  /**
2479   *   unloads a LR from the GUI
2480   */
2481  protected void unloadLR(Long lrID)
2482  throws GateException{
2483
2484    //0. preconfitions
2485    Assert.assertNotNull(lrID);
2486
2487    //1. get all LRs in the system
2488    List resources = Gate.getCreoleRegister().getAllInstances("gate.LanguageResource");
2489
2490    Iterator it = resources.iterator();
2491    while (it.hasNext()) {
2492      LanguageResource lr = (LanguageResource)it.next();
2493      if (lrID.equals(lr.getLRPersistenceId()) &&
2494          this.equals(lr.getDataStore())) {
2495        //found it - unload it
2496        Factory.deleteResource(lr);
2497        break;
2498      }
2499    }
2500  }
2501
2502  /** helper for sync() - never call directly */
2503  protected abstract void _syncRemovedDocumentsFromCorpus(List docLRIDs, Long corpLRID)
2504    throws PersistenceException;
2505
2506  /**
2507   *   adds document to corpus in the database
2508   *   if the document is already part of the corpus nothing
2509   *   changes
2510   */
2511  protected abstract void addDocumentToCorpus(Long docID,Long corpID)
2512  throws PersistenceException,SecurityException;
2513
2514
2515}
2516