PostgresDataStore.java
0001 /*
0002  *  PostgresDataStore.java
0003  *
0004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Marin Dimitrov, 18/Mar/2001
0013  *
0014  *  $Id: PostgresDataStore.java 12006 2009-12-01 17:24:28Z thomas_heitz $
0015  */
0016 
0017 package gate.persist;
0018 
0019 import java.io.*;
0020 import java.net.URL;
0021 import java.sql.*;
0022 import java.util.*;
0023 
0024 import junit.framework.Assert;
0025 
0026 import gate.*;
0027 import gate.corpora.DatabaseCorpusImpl;
0028 import gate.corpora.DatabaseDocumentImpl;
0029 import gate.security.SecurityException;
0030 import gate.security.SecurityInfo;
0031 import gate.util.MethodNotImplementedException;
0032 import gate.util.SimpleFeatureMapImpl;
0033 
0034 public class PostgresDataStore extends JDBCDataStore {
0035 
0036   /** Name of this resource */
0037   private static final String DS_COMMENT = "GATE PostgreSQL datastore";
0038 
0039   /** the icon for this resource */
0040   public static final String DS_ICON_NAME = "pgsql_ds";
0041 
0042   /** Debug flag */
0043   private static final boolean DEBUG = true;
0044 
0045   public PostgresDataStore() {
0046 
0047     super();
0048     this.datastoreComment = DS_COMMENT;
0049     this.iconName = DS_ICON_NAME;
0050   }
0051 
0052 
0053   public void setSecurityInfo(LanguageResource parm1, SecurityInfo parm2throws gate.persist.PersistenceException, gate.security.SecurityException {
0054     /**@todo: implement this gate.persist.JDBCDataStore abstract method*/
0055     throw new MethodNotImplementedException();
0056   }
0057 
0058   public List findLrIds(List constraints, String lrTypethrows gate.persist.PersistenceException {
0059     /**@todo: implement this gate.persist.JDBCDataStore abstract method*/
0060     throw new MethodNotImplementedException();
0061   }
0062 
0063 /*  public LanguageResource getLr(String lrClassName, Object lrPersistenceId) throws gate.security.SecurityException, gate.persist.PersistenceException {
0064     throw new MethodNotImplementedException();
0065   }
0066 */
0067 
0068 /*  public void delete(String lrClassName, Object lrId) throws gate.security.SecurityException, gate.persist.PersistenceException {
0069 
0070     throw new MethodNotImplementedException();
0071   }
0072 */
0073 
0074   public List findLrIds(List constraintsthrows gate.persist.PersistenceException {
0075     /**@todo: implement this gate.persist.JDBCDataStore abstract method*/
0076     throw new MethodNotImplementedException();
0077   }
0078 
0079 
0080   /**
0081    * Releases the exlusive lock on a resource from the persistent store.
0082    */
0083   public void unlockLr(LanguageResource lr)
0084   throws PersistenceException,SecurityException {
0085 
0086     //0. preconditions
0087     Assert.assertNotNull(lr);
0088     Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
0089                       lr instanceof DatabaseCorpusImpl);
0090     Assert.assertNotNull(lr.getLRPersistenceId());
0091     Assert.assertEquals(lr.getDataStore(),this);
0092 
0093     //1. check session
0094     if (null == this.session) {
0095       throw new SecurityException("session not set");
0096     }
0097 
0098     if (false == this.ac.isValidSession(this.session)) {
0099       throw new SecurityException("invalid session supplied");
0100     }
0101 
0102     //2. check permissions
0103     if (false == canWriteLR(lr.getLRPersistenceId())) {
0104       throw new SecurityException("no write access granted to the user");
0105     }
0106 
0107     //3. try to unlock
0108     PreparedStatement pstmt = null;
0109     boolean lockSucceeded = false;
0110 
0111     try {
0112       String sql = " select persist_unlock_lr(?,?) ";
0113       pstmt = this.jdbcConn.prepareStatement(sql);
0114       pstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
0115       pstmt.setLong(2,this.session.getUser().getID().longValue());
0116       pstmt.execute();
0117       //we don't care about the result set
0118     }
0119     catch(SQLException sqle) {
0120 
0121       switch(sqle.getErrorCode()) {
0122         case DBHelper.X_ORACLE_INVALID_LR:
0123           throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
0124         default:
0125           throw new PersistenceException(
0126                 "can't unlock LR in DB : ["+ sqle.getMessage()+"]");
0127       }
0128     }
0129     finally {
0130       DBHelper.cleanup(pstmt);
0131     }
0132   }
0133 
0134 
0135   /**
0136    * Checks if the user (identified by the sessionID)
0137    * has some access (read/write) to the LR
0138    */
0139   protected boolean canAccessLR(Long lrID,int mode)
0140     throws PersistenceException, SecurityException{
0141 
0142     //0. preconditions
0143     Assert.assertTrue(DBHelper.READ_ACCESS == mode || DBHelper.WRITE_ACCESS == mode);
0144 
0145     //1. is session initialised?
0146     if (null == this.session) {
0147       throw new SecurityException("user session not set");
0148     }
0149 
0150     //2.first check the session and then check whether the user is member of the group
0151     if (this.ac.isValidSession(this.session== false) {
0152       throw new SecurityException("invalid session supplied");
0153     }
0154 
0155     PreparedStatement pstmt = null;
0156     ResultSet rs = null;
0157 
0158     try {
0159       String sql = "select security_has_access_to_lr(?,?,?,?)";
0160       pstmt = this.jdbcConn.prepareStatement(sql);
0161       pstmt.setLong(1,lrID.longValue());
0162       pstmt.setLong(2,this.session.getUser().getID().longValue());
0163       pstmt.setLong(3,this.session.getGroup().getID().longValue());
0164       pstmt.setLong(4,mode);
0165       pstmt.execute();
0166       rs = pstmt.getResultSet();
0167 
0168       if (false == rs.next()) {
0169         throw new PersistenceException("empty result set");
0170       }
0171 
0172       return rs.getBoolean(1);
0173     }
0174     catch(SQLException sqle) {
0175       throw new PersistenceException("can't check permissions in DB: ["+ sqle.getMessage()+"]");
0176     }
0177     finally {
0178       DBHelper.cleanup(rs);
0179       DBHelper.cleanup(pstmt);
0180     }
0181 
0182   }
0183 
0184 
0185 
0186   /**
0187    * Try to acquire exlusive lock on a resource from the persistent store.
0188    * Always call unlockLR() when the lock is no longer needed
0189    */
0190   public boolean lockLr(LanguageResource lr)
0191   throws PersistenceException,SecurityException {
0192 
0193     //0. preconditions
0194     Assert.assertNotNull(lr);
0195     Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
0196                       lr instanceof DatabaseCorpusImpl);
0197     Assert.assertNotNull(lr.getLRPersistenceId());
0198     Assert.assertEquals(lr.getDataStore(),this);
0199 
0200     //1. delegate
0201     return _lockLr((Long)lr.getLRPersistenceId());
0202   }
0203 
0204 
0205   /**
0206    *  helper for lockLR()
0207    *  never call directly
0208    */
0209   private boolean _lockLr(Long lrID)
0210   throws PersistenceException,SecurityException {
0211 
0212     //0. preconditions
0213     Assert.assertNotNull(lrID);
0214 
0215     //1. check session
0216     if (null == this.session) {
0217       throw new SecurityException("session not set");
0218     }
0219 
0220     if (false == this.ac.isValidSession(this.session)) {
0221       throw new SecurityException("invalid session supplied");
0222     }
0223 
0224     //2. check permissions
0225     if (false == canWriteLR(lrID)) {
0226       throw new SecurityException("no write access granted to the user");
0227     }
0228 
0229     //3. try to lock
0230     PreparedStatement pstmt = null;
0231     ResultSet rset = null;
0232     boolean lockSucceeded = false;
0233 
0234     try {
0235       pstmt = this.jdbcConn.prepareStatement(" select persist_lock_lr(?,?,?) ");
0236       pstmt.setLong(1,lrID.longValue());
0237       pstmt.setLong(2,this.session.getUser().getID().longValue());
0238       pstmt.setLong(3,this.session.getGroup().getID().longValue());
0239 
0240       pstmt.execute();
0241       rset = pstmt.getResultSet();
0242 
0243       if (false == rset.next()) {
0244         throw new PersistenceException("empty result set");
0245       }
0246 
0247       lockSucceeded = rset.getBoolean(1);
0248     }
0249     catch(SQLException sqle) {
0250 
0251       switch(sqle.getErrorCode()) {
0252         case DBHelper.X_ORACLE_INVALID_LR:
0253           throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
0254         default:
0255           throw new PersistenceException(
0256                 "can't lock LR in DB : ["+ sqle.getMessage()+"]");
0257       }
0258     }
0259     finally {
0260       DBHelper.cleanup(rset);
0261       DBHelper.cleanup(pstmt);
0262     }
0263 
0264     return lockSucceeded;
0265   }
0266 
0267 
0268 /*  protected Corpus createCorpus(Corpus corp,SecurityInfo secInfo, boolean newTransPerDocument)
0269     throws PersistenceException,SecurityException {
0270 
0271     throw new MethodNotImplementedException();
0272   }
0273 */
0274   /**
0275    *  helper for adopt()
0276    *  never call directly
0277    */
0278   protected Long createLR(String lrType,
0279                           String lrName,
0280                           SecurityInfo si,
0281                           Long lrParentID)
0282     throws PersistenceException,SecurityException {
0283 
0284     //0. preconditions
0285     Assert.assertNotNull(lrName);
0286 
0287     //1. check the session
0288 //    if (this.ac.isValidSession(s) == false) {
0289 //      throw new SecurityException("invalid session provided");
0290 //    }
0291 
0292     //2. create a record in DB
0293     PreparedStatement pstmt = null;
0294     ResultSet rset = null;
0295 
0296     try {
0297       String sql = " select persist_create_lr(?,?,?,?,?,?) ";
0298       pstmt = this.jdbcConn.prepareStatement(sql);
0299       pstmt.setLong(1,si.getUser().getID().longValue());
0300       pstmt.setLong(2,si.getGroup().getID().longValue());
0301       pstmt.setString(3,lrType);
0302       pstmt.setString(4,lrName);
0303       pstmt.setInt(5,si.getAccessMode());
0304       if (null == lrParentID) {
0305         pstmt.setNull(6,java.sql.Types.INTEGER);
0306       }
0307       else {
0308         pstmt.setLong(6,lrParentID.longValue());
0309       }
0310 
0311       pstmt.execute();
0312       rset = pstmt.getResultSet();
0313       if (false == rset.next()) {
0314         throw new PersistenceException("empty result set");
0315       }
0316 
0317       Long result =  new Long(rset.getLong(1));
0318 
0319       return result;
0320     }
0321     catch(SQLException sqle) {
0322 
0323       switch(sqle.getErrorCode()) {
0324         case DBHelper.X_ORACLE_INVALID_LR_TYPE:
0325           throw new PersistenceException("can't create LR [step 3] in DB, invalid LR Type");
0326         default:
0327           throw new PersistenceException(
0328                 "can't create LR [step 3] in DB : ["+ sqle.getMessage()+"]");
0329       }
0330     }
0331     finally {
0332       DBHelper.cleanup(rset);
0333       DBHelper.cleanup(pstmt);
0334     }
0335   }
0336 
0337 
0338   /**
0339    * helper for adopt
0340    * never call directly
0341    */
0342   protected Long createDoc(Long _lrID,
0343                           URL _docURL,
0344                           String _docEncoding,
0345                           Long _docStartOffset,
0346                           Long _docEndOffset,
0347                           Boolean _docIsMarkupAware,
0348                           Long _corpusID)
0349     throws PersistenceException {
0350 
0351     PreparedStatement pstmt = null;
0352     ResultSet rset = null;
0353     Long docID = null;
0354 
0355     try {
0356       pstmt = this.jdbcConn.prepareStatement(
0357                 " select persist_create_document(?,?,?,?,?,?,?) ");
0358       pstmt.setLong(1,_lrID.longValue());
0359       pstmt.setString(2,_docURL != null ? _docURL.toString() "" );
0360       //do we have doc encoding?
0361       if (null == _docEncoding) {
0362         pstmt.setNull(3,java.sql.Types.VARCHAR);
0363       }
0364       else {
0365         pstmt.setString(3,_docEncoding);
0366       }
0367       //do we have start offset?
0368       if (null==_docStartOffset) {
0369         pstmt.setNull(4,java.sql.Types.INTEGER);
0370       }
0371       else {
0372         pstmt.setLong(4,_docStartOffset.longValue());
0373       }
0374       //do we have end offset?
0375       if (null==_docEndOffset) {
0376         pstmt.setNull(5,java.sql.Types.INTEGER);
0377       }
0378       else {
0379         pstmt.setLong(5,_docEndOffset.longValue());
0380       }
0381 
0382       pstmt.setBoolean(6,_docIsMarkupAware.booleanValue());
0383 
0384       //is the document part of a corpus?
0385       if (null == _corpusID) {
0386         pstmt.setNull(7,java.sql.Types.BIGINT);
0387       }
0388       else {
0389         pstmt.setLong(7,_corpusID.longValue());
0390       }
0391 
0392       pstmt.execute();
0393       rset = pstmt.getResultSet();
0394       if (false == rset.next()) {
0395         throw new PersistenceException("empty result set");
0396       }
0397 
0398       docID = new Long(rset.getLong(1));
0399 
0400       return docID;
0401 
0402     }
0403     catch(SQLException sqle) {
0404       throw new PersistenceException("can't create document [step 4] in DB: ["+ sqle.getMessage()+"]");
0405     }
0406     finally {
0407       DBHelper.cleanup(rset);
0408       DBHelper.cleanup(pstmt);
0409     }
0410 
0411   }
0412 
0413 
0414   /** creates an entry for annotation set in the database */
0415   protected void createAnnotationSet(Long lrID, AnnotationSet aset)
0416     throws PersistenceException {
0417 
0418     //1. create a-set
0419     String asetName = aset.getName();
0420     Long asetID = null;
0421 
0422     //DB stuff
0423     PreparedStatement pstmt = null;
0424     ResultSet rs = null;
0425 
0426     try {
0427       String sql = "select persist_create_annotation_set(?,?)";
0428       pstmt = this.jdbcConn.prepareStatement(sql);
0429 
0430       pstmt.setLong(1,lrID.longValue());
0431       if (null == asetName) {
0432         pstmt.setNull(2,java.sql.Types.VARCHAR);
0433       }
0434       else {
0435         pstmt.setString(2,asetName);
0436       }
0437       pstmt.execute();
0438       rs = pstmt.getResultSet();
0439 
0440       if (false == rs.next()) {
0441         throw new PersistenceException("empty result set");
0442       }
0443 
0444       asetID = new Long(rs.getLong(1));
0445     }
0446     catch(SQLException sqle) {
0447       throw new PersistenceException("can't create a-set [step 1] in DB: ["+ sqle.getMessage()+"]");
0448     }
0449     finally {
0450       DBHelper.cleanup(rs);
0451       DBHelper.cleanup(pstmt);
0452     }
0453 
0454 
0455     //2. insert annotations/nodes for DEFAULT a-set
0456     //for now use a stupid cycle
0457     //TODO: pass all the data with one DB call (?)
0458 
0459     try {
0460       String sql = "select persist_create_annotation(?,?,?,?,?,?,?,?) ";
0461       pstmt = this.jdbcConn.prepareStatement(sql);
0462 
0463 
0464       Iterator itAnnotations = aset.iterator();
0465 
0466       while (itAnnotations.hasNext()) {
0467         Annotation ann = (Annotation)itAnnotations.next();
0468         Node start = (Node)ann.getStartNode();
0469         Node end = (Node)ann.getEndNode();
0470         String type = ann.getType();
0471 
0472         //DB stuff
0473         Long annGlobalID = null;
0474         pstmt.setLong(1,lrID.longValue());
0475         pstmt.setLong(2,ann.getId().longValue());
0476         pstmt.setLong(3,asetID.longValue());
0477         pstmt.setLong(4,start.getId().longValue());
0478         pstmt.setLong(5,start.getOffset().longValue());
0479         pstmt.setLong(6,end.getId().longValue());
0480         pstmt.setLong(7,end.getOffset().longValue());
0481         pstmt.setString(8,type);
0482         pstmt.execute();
0483         rs = pstmt.getResultSet();
0484 
0485         if (false == rs.next()) {
0486           throw new PersistenceException("empty result set");
0487         }
0488 
0489         annGlobalID = new Long(rs.getLong(1));
0490         DBHelper.cleanup(rs);
0491 
0492         //2.1. set annotation features
0493         FeatureMap features = ann.getFeatures();
0494         Assert.assertNotNull(features);
0495         createFeatures(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
0496 //        createFeaturesBulk(annGlobalID,DBHelper.FEATURE_OWNER_ANNOTATION,features);
0497       //while
0498     }//try
0499     catch(SQLException sqle) {
0500 
0501       switch(sqle.getErrorCode()) {
0502 
0503         case DBHelper.X_ORACLE_INVALID_ANNOTATION_TYPE:
0504           throw new PersistenceException(
0505                               "can't create annotation in DB, [invalid annotation type]");
0506         default:
0507           throw new PersistenceException(
0508                 "can't create annotation in DB: ["+ sqle.getMessage()+"]");
0509       }//switch
0510     }//catch
0511     finally {
0512       DBHelper.cleanup(pstmt);
0513     }
0514   }
0515 
0516   /**
0517    *  updates the content of the document if it is binary or a long string
0518    *  (that does not fit into VARCHAR2)
0519    */
0520   protected void updateDocumentContent(Long docID,DocumentContent content)
0521     throws PersistenceException {
0522 
0523     //1. get LOB locators from DB
0524     PreparedStatement pstmt = null;
0525     try {
0526       String sql =  " update  t_doc_content "      +
0527                     " set     dc_character_content = ?,  " +
0528                     "         dc_content_type = ? " +
0529                     " where   dc_id = (select doc_content_id " +
0530                     "                   from t_document " +
0531                     "                   where doc_id = ?) ";
0532 
0533       pstmt = this.jdbcConn.prepareStatement(sql);
0534       pstmt.setString(1,content.toString());
0535       pstmt.setInt(2,DBHelper.CHARACTER_CONTENT);
0536       pstmt.setLong(3,docID.longValue());
0537       pstmt.executeUpdate();
0538     }
0539     catch(SQLException sqle) {
0540       throw new PersistenceException("can't update document content in DB : ["+
0541                                       sqle.getMessage()+"]");
0542     }
0543     finally {
0544       DBHelper.cleanup(pstmt);
0545     }
0546 
0547   }
0548 
0549 
0550 
0551 
0552   /**
0553    *  creates a feature with the specified type/key/value for the specified entity
0554    *  entitties are either LRs ot Annotations
0555    *  valid values are: boolean,
0556    *                    int,
0557    *                    long,
0558    *                    string,
0559    *                    float,
0560    *                    Object,
0561    *                    boolean List,
0562    *                    int List,
0563    *                    long List,
0564    *                    string List,
0565    *                    float List,
0566    *                    Object List
0567    *
0568    */
0569 
0570   private void createFeature(Long entityID, int entityType,String key, Object value, PreparedStatement pstmt)
0571     throws PersistenceException {
0572 
0573     //1. what kind of feature value is this?
0574     int valueType = findFeatureType(value);
0575 
0576     //2. how many elements do we store?
0577     Vector elementsToStore = new Vector();
0578 
0579     switch(valueType) {
0580       case DBHelper.VALUE_TYPE_NULL:
0581       case DBHelper.VALUE_TYPE_BINARY:
0582       case DBHelper.VALUE_TYPE_BOOLEAN:
0583       case DBHelper.VALUE_TYPE_FLOAT:
0584       case DBHelper.VALUE_TYPE_INTEGER:
0585       case DBHelper.VALUE_TYPE_LONG:
0586       case DBHelper.VALUE_TYPE_STRING:
0587         elementsToStore.add(value);
0588         break;
0589 
0590       default:
0591         //arrays
0592         List arr = (List)value;
0593         Iterator itValues = arr.iterator();
0594 
0595         while (itValues.hasNext()) {
0596           elementsToStore.add(itValues.next());
0597         }
0598 
0599         //normalize , i.e. ignore arrays
0600         if (valueType == DBHelper.VALUE_TYPE_BINARY_ARR)
0601           valueType = DBHelper.VALUE_TYPE_BINARY;
0602         else if (valueType == DBHelper.VALUE_TYPE_BOOLEAN_ARR)
0603           valueType = DBHelper.VALUE_TYPE_BOOLEAN;
0604         else if (valueType == DBHelper.VALUE_TYPE_FLOAT_ARR)
0605           valueType = DBHelper.VALUE_TYPE_FLOAT;
0606         else if (valueType == DBHelper.VALUE_TYPE_INTEGER_ARR)
0607           valueType = DBHelper.VALUE_TYPE_INTEGER;
0608         else if (valueType == DBHelper.VALUE_TYPE_LONG_ARR)
0609           valueType = DBHelper.VALUE_TYPE_LONG;
0610         else if (valueType == DBHelper.VALUE_TYPE_STRING_ARR)
0611           valueType = DBHelper.VALUE_TYPE_STRING;
0612     }
0613 
0614     //3. for all elements:
0615     for (int i=0; i< elementsToStore.size(); i++) {
0616 
0617         Object currValue = elementsToStore.elementAt(i);
0618 
0619         //3.1. create a dummy feature [LOB hack]
0620         Long featID = _createFeature(entityID,entityType,key,currValue,valueType,pstmt);
0621     }
0622 
0623   }
0624 
0625 
0626 
0627   /**
0628    *  helper metod
0629    *  iterates a FeatureMap and creates all its features in the database
0630    */
0631   protected void createFeatures(Long entityID, int entityType, FeatureMap features)
0632     throws PersistenceException {
0633 
0634     //0. prepare statement ad use it for all features
0635     PreparedStatement pstmt = null;
0636 
0637     try {
0638       String sql = "select persist_create_feature(?,?,?,?,?,?,?,?) ";
0639       pstmt = this.jdbcConn.prepareStatement(sql);
0640     }
0641     catch (SQLException sqle) {
0642       throw new PersistenceException(sqle);
0643     }
0644 
0645     /* when some day Java has macros, this will be a macro */
0646     Set entries = features.entrySet();
0647     Iterator itFeatures = entries.iterator();
0648     while (itFeatures.hasNext()) {
0649       Map.Entry entry = (Map.Entry)itFeatures.next();
0650       String key = (String)entry.getKey();
0651       Object value = entry.getValue();
0652       createFeature(entityID,entityType,key,value,pstmt);
0653     }
0654 
0655     //3. cleanup
0656     DBHelper.cleanup(pstmt);
0657   }
0658 
0659   protected void createFeaturesBulk(Long entityID, int entityType, FeatureMap features)
0660     throws PersistenceException {
0661 
0662     throw new MethodNotImplementedException();
0663   }
0664 
0665   /**
0666    *  creates a feature of the specified type/value/valueType/key for the specified entity
0667    *  Entity is one of: LR, Annotation
0668    *  Value types are: boolean, int, long, string, float, Object
0669    */
0670   private Long _createFeature(Long entityID,
0671                               int entityType,
0672                               String key,
0673                               Object value,
0674                               int valueType,
0675                               PreparedStatement pstmt)
0676     throws PersistenceException {
0677 
0678     //1. store in DB
0679     Long featID = null;
0680     ResultSet rs = null;
0681 
0682     try {
0683 
0684       //1.1 set known values + NULLs
0685       pstmt.setLong(1,entityID.longValue());
0686       pstmt.setInt(2,entityType);
0687       pstmt.setString(3,key);
0688       pstmt.setNull(4,java.sql.Types.BIGINT);
0689       pstmt.setNull(5,java.sql.Types.DOUBLE);
0690       pstmt.setNull(6,java.sql.Types.LONGVARCHAR);
0691       pstmt.setNull(7,java.sql.Types.LONGVARBINARY);
0692       pstmt.setInt(8,valueType);
0693 
0694       //1.2 set proper data
0695       switch(valueType) {
0696 
0697         case DBHelper.VALUE_TYPE_NULL:
0698           break;
0699 
0700         case DBHelper.VALUE_TYPE_BOOLEAN:
0701 
0702           boolean b = ((Boolean)value).booleanValue();
0703           pstmt.setLong(4, b ? DBHelper.TRUE : DBHelper.FALSE);
0704           break;
0705 
0706         case DBHelper.VALUE_TYPE_INTEGER:
0707 
0708           pstmt.setLong(4,((Integer)value).intValue());
0709           break;
0710 
0711         case DBHelper.VALUE_TYPE_LONG:
0712 
0713           pstmt.setLong(4,((Long)value).longValue());
0714           break;
0715 
0716         case DBHelper.VALUE_TYPE_FLOAT:
0717 
0718           Double d = (Double)value;
0719           pstmt.setDouble(5,d.doubleValue());
0720           break;
0721 
0722         case DBHelper.VALUE_TYPE_BINARY:
0723           //we serialize the value (object) in the DB
0724           ByteArrayOutputStream baos = new ByteArrayOutputStream();
0725           ObjectOutputStream oos = new ObjectOutputStream(baos);
0726           oos.writeObject(value);
0727           oos.close();
0728           baos.close();
0729           byte[] buff = baos.toByteArray();
0730           ByteArrayInputStream bais = new ByteArrayInputStream(buff);
0731           pstmt.setBinaryStream(7,bais,buff.length);
0732           bais.close();
0733           break;
0734 
0735         case DBHelper.VALUE_TYPE_STRING:
0736 
0737           String s = (String)value;
0738           //does it fin into a varchar2?
0739           pstmt.setString(6,s);
0740           break;
0741 
0742         default:
0743           throw new IllegalArgumentException("unsuppoeted feature type");
0744       }
0745 
0746       pstmt.execute();
0747       rs = pstmt.getResultSet();
0748 
0749       if (false == rs.next()) {
0750         throw new PersistenceException("empty result set");
0751       }
0752 
0753       featID = new Long(rs.getLong(1));
0754     }
0755     catch(IOException ioe) {
0756       throw new PersistenceException("can't write binary data ["+ioe.getMessage()+"]");
0757     }
0758     catch(SQLException sqle) {
0759 
0760       switch(sqle.getErrorCode()) {
0761         case DBHelper.X_ORACLE_INVALID_FEATURE_TYPE:
0762           throw new PersistenceException("can't create feature [step 1],"+
0763                       "[invalid feature type] in DB: ["+ sqle.getMessage()+"]");
0764         default:
0765           throw new PersistenceException("can't create feature [step 1] in DB: ["+
0766                                                       sqle.getMessage()+"]");
0767       }
0768     }
0769     finally {
0770       DBHelper.cleanup(rs);
0771 //      DBHelper.cleanup(stmt);
0772     }
0773 
0774     return featID;
0775   }
0776 
0777 
0778   /**
0779    *  updates the value of a feature where the value is string (>4000 bytes, stored as CLOB)
0780    *  or Object (stored as BLOB)
0781    */
0782 /*  private void _updateFeatureLOB(Long featID,Object value, int valueType)
0783     throws PersistenceException {
0784 
0785     throw new MethodNotImplementedException();
0786   }
0787 */
0788   /** helper for sync() - saves a Corpus in the database */
0789 /*  protected void syncCorpus(Corpus corp)
0790     throws PersistenceException,SecurityException {
0791 
0792     throw new MethodNotImplementedException();
0793   }
0794 */
0795 
0796   /**
0797    *  helper for sync()
0798    *  NEVER call directly
0799    */
0800   protected void _syncLR(LanguageResource lr)
0801     throws PersistenceException,SecurityException {
0802 
0803     //0.preconditions
0804     Assert.assertTrue(lr instanceof DatabaseDocumentImpl ||
0805                       lr instanceof DatabaseCorpusImpl);;
0806     Assert.assertNotNull(lr.getLRPersistenceId());
0807 
0808     PreparedStatement pstmt = null;
0809 
0810     try {
0811       pstmt = this.jdbcConn.prepareStatement("select persist_update_lr(?,?,?)");
0812       pstmt.setLong(1,((Long)lr.getLRPersistenceId()).longValue());
0813       pstmt.setString(2,lr.getName());
0814       //do we have a parent resource?
0815       if (lr instanceof Document &&
0816           null != lr.getParent()) {
0817         pstmt.setLong(3,((Long)lr.getParent().getLRPersistenceId()).longValue());
0818       }
0819       else {
0820         pstmt.setNull(3,java.sql.Types.BIGINT);
0821       }
0822 
0823       pstmt.execute();
0824     }
0825     catch(SQLException sqle) {
0826 
0827       switch(sqle.getErrorCode()) {
0828         case DBHelper.X_ORACLE_INVALID_LR:
0829           throw new PersistenceException("can't set LR name in DB: [invalid LR ID]");
0830         default:
0831           throw new PersistenceException(
0832                 "can't set LR name in DB: ["+ sqle.getMessage()+"]");
0833       }
0834 
0835     }
0836     finally {
0837       DBHelper.cleanup(pstmt);
0838     }
0839 
0840   }
0841 
0842   /** helper for sync() - never call directly */
0843   protected void _syncDocumentHeader(Document doc)
0844     throws PersistenceException {
0845 
0846     Long lrID = (Long)doc.getLRPersistenceId();
0847 
0848     PreparedStatement pstmt = null;
0849 
0850     try {
0851       pstmt = this.jdbcConn.prepareStatement("select persist_update_document(?,?,?,?,?)");
0852       pstmt.setLong(1,lrID.longValue());
0853       if(doc.getSourceUrl() != null){
0854         pstmt.setString(2, doc.getSourceUrl().toString());
0855       }else{
0856         pstmt.setString(2"");
0857       }
0858       //do we have start offset?
0859       if (null==doc.getSourceUrlStartOffset()) {
0860         pstmt.setNull(3,java.sql.Types.INTEGER);
0861       }
0862       else {
0863         pstmt.setLong(3,doc.getSourceUrlStartOffset().longValue());
0864       }
0865       //do we have end offset?
0866       if (null==doc.getSourceUrlEndOffset()) {
0867         pstmt.setNull(4,java.sql.Types.INTEGER);
0868       }
0869       else {
0870         pstmt.setLong(4,doc.getSourceUrlEndOffset().longValue());
0871       }
0872 
0873       pstmt.setBoolean(5,doc.getMarkupAware().booleanValue());
0874       pstmt.execute();
0875     }
0876     catch(SQLException sqle) {
0877 
0878       switch(sqle.getErrorCode()) {
0879         case DBHelper.X_ORACLE_INVALID_LR :
0880           throw new PersistenceException("invalid LR supplied: no such document: ["+
0881                                                             sqle.getMessage()+"]");
0882         default:
0883           throw new PersistenceException("can't change document data: ["+
0884                                                             sqle.getMessage()+"]");
0885       }
0886     }
0887     finally {
0888       DBHelper.cleanup(pstmt);
0889     }
0890 
0891   }
0892 
0893   /** helper for sync() - never call directly */
0894   protected void _syncDocumentContent(Document doc)
0895     throws PersistenceException {
0896 
0897     //0.
0898     Assert.assertNotNull(doc);
0899     Assert.assertNotNull(doc.getLRPersistenceId());
0900     Assert.assertTrue(doc instanceof DatabaseDocumentImpl);
0901 
0902     PreparedStatement pstmt = null;
0903     //1.
0904     try {
0905       pstmt = this.jdbcConn.prepareStatement("select persist_update_document_content(?,?)");
0906       pstmt.setLong(1,((Long)doc.getLRPersistenceId()).longValue());
0907 
0908       DocumentContent dc = doc.getContent();
0909       if (dc.size().longValue() 0) {
0910         pstmt.setString(2,dc.toString());
0911       }
0912       else {
0913         pstmt.setNull(2,java.sql.Types.LONGVARCHAR);
0914       }
0915 
0916       pstmt.execute();
0917     }
0918     catch(SQLException sqle) {
0919       throw new PersistenceException("Cannot update document content ["+
0920                                       sqle.getMessage()+"]");
0921     }
0922     finally {
0923       DBHelper.cleanup(pstmt);
0924     }
0925   }
0926 
0927   /** helper for sync() - never call directly */
0928   protected void _syncFeatures(LanguageResource lr)
0929     throws PersistenceException {
0930 
0931     //0. preconditions
0932     Assert.assertNotNull(lr);
0933     Assert.assertNotNull(lr.getLRPersistenceId());
0934     Assert.assertEquals(((DatabaseDataStore)lr.getDataStore()).getDatabaseID(),
0935                       this.getDatabaseID());
0936     Assert.assertTrue(lr instanceof Document || lr instanceof Corpus);
0937     //we have to be in the context of transaction
0938 
0939     //1, get ID  in the DB
0940     Long lrID = (Long)lr.getLRPersistenceId();
0941     int  entityType;
0942 
0943     //2. delete features
0944     PreparedStatement pstmt = null;
0945     try {
0946       Assert.assertTrue(false == this.jdbcConn.getAutoCommit());
0947       pstmt = this.jdbcConn.prepareStatement("select persist_delete_features(?,?) ");
0948       pstmt.setLong(1,lrID.longValue());
0949 
0950       if (lr instanceof Document) {
0951         entityType = DBHelper.FEATURE_OWNER_DOCUMENT;
0952       }
0953       else if (lr instanceof Corpus) {
0954         entityType = DBHelper.FEATURE_OWNER_CORPUS;
0955       }
0956       else {
0957         throw new IllegalArgumentException();
0958       }
0959 
0960       pstmt.setInt(2,entityType);
0961       pstmt.execute();
0962     }
0963     catch(SQLException sqle) {
0964       throw new PersistenceException("can't delete features in DB: ["+ sqle.getMessage()+"]");
0965     }
0966     finally {
0967       DBHelper.cleanup(pstmt);
0968     }
0969 
0970     //3. recreate them
0971     createFeatures(lrID,entityType, lr.getFeatures());
0972   }
0973 
0974   /** helper for sync() - never call directly */
0975 /*  protected void _syncAnnotationSets(Document doc,Collection removedSets,Collection addedSets)
0976     throws PersistenceException {
0977 
0978     throw new MethodNotImplementedException();
0979   }
0980 */
0981 
0982   /** helper for sync() - never call directly */
0983 /*  protected void _syncAddedAnnotations(Document doc, AnnotationSet as, Collection changes)
0984     throws PersistenceException {
0985 
0986     throw new MethodNotImplementedException();
0987   }
0988 */
0989 
0990   /** helper for sync() - never call directly */
0991 /*  protected void _syncRemovedAnnotations(Document doc,AnnotationSet as, Collection changes)
0992     throws PersistenceException {
0993 
0994     throw new MethodNotImplementedException();
0995   }
0996 */
0997   /** helper for sync() - never call directly */
0998 /*  protected void _syncChangedAnnotations(Document doc,AnnotationSet as, Collection changes)
0999     throws PersistenceException {
1000 
1001     throw new MethodNotImplementedException();
1002   }
1003 */
1004 
1005   /**
1006    *  reads the features of an entity
1007    *  entities are of type LR or Annotation
1008    */
1009   protected FeatureMap readFeatures(Long entityID, int entityType)
1010     throws PersistenceException {
1011 
1012     //0. preconditions
1013     Assert.assertNotNull(entityID);
1014     Assert.assertTrue(entityType == DBHelper.FEATURE_OWNER_ANNOTATION ||
1015                   entityType == DBHelper.FEATURE_OWNER_CORPUS ||
1016                   entityType == DBHelper.FEATURE_OWNER_DOCUMENT);
1017 
1018 
1019     PreparedStatement pstmt = null;
1020     ResultSet rs = null;
1021     FeatureMap fm = new SimpleFeatureMapImpl();
1022 
1023     //1. read from DB
1024     try {
1025       String sql = " select ftkey.fk_string, " +
1026                    "        ft.ft_value_type, " +
1027                    "        ft.ft_int_value, " +
1028                    "        ft.ft_float_value, " +
1029                    "        ft.ft_binary_value, " +
1030                    "        ft.ft_character_value " +
1031                    " from   t_feature ft, " +
1032                    "        t_feature_key ftkey " +
1033                    " where  ft.ft_entity_id = ? " +
1034                    "        and ft.ft_entity_type = ? " +
1035                    "        and ft.ft_key_id = ftkey.fk_id " +
1036                    " order by ftkey.fk_string,ft.ft_id";
1037 
1038       pstmt = this.jdbcConn.prepareStatement(sql);
1039       pstmt.setLong(1,entityID.longValue());
1040       pstmt.setLong(2,entityType);
1041       pstmt.execute();
1042       rs = pstmt.getResultSet();
1043 
1044       //3. fill feature map
1045       Vector arrFeatures = new Vector();
1046       String prevKey = null;
1047       String currKey = null;
1048       Object currFeature = null;
1049 
1050 
1051       while (rs.next()) {
1052         //NOTE: because there are LOBs in the resulset
1053         //the columns should be read in the order they appear
1054         //in the query
1055         currKey = rs.getString("fk_string");
1056 
1057         Long valueType = new Long(rs.getLong("ft_value_type"));
1058 
1059         //we don't quite know what is the type of the NUMBER
1060         //stored in DB
1061         Object numberValue = null;
1062 
1063         //for all numeric types + boolean -> read from DB as appropriate
1064         //Java object
1065         switch(valueType.intValue()) {
1066 
1067           case DBHelper.VALUE_TYPE_BOOLEAN:
1068             numberValue = new Boolean(rs.getBoolean("ft_int_value"));
1069             break;
1070 
1071           case DBHelper.VALUE_TYPE_FLOAT:
1072             numberValue = new Double(rs.getDouble("ft_float_value"));
1073             break;
1074 
1075           case DBHelper.VALUE_TYPE_INTEGER:
1076             numberValue = new Integer(rs.getInt("ft_int_value"));
1077             break;
1078 
1079           case DBHelper.VALUE_TYPE_LONG:
1080             numberValue = new Long(rs.getLong("ft_int_value"));
1081             break;
1082         }
1083 
1084         //don't forget to read the rest of the current row
1085         InputStream blobValue = rs.getBinaryStream("ft_binary_value");
1086         String stringValue = rs.getString("ft_character_value");
1087 
1088         switch(valueType.intValue()) {
1089 
1090           case DBHelper.VALUE_TYPE_NULL:
1091             currFeature = null;
1092             break;
1093 
1094           case DBHelper.VALUE_TYPE_BOOLEAN:
1095           case DBHelper.VALUE_TYPE_FLOAT:
1096           case DBHelper.VALUE_TYPE_INTEGER:
1097           case DBHelper.VALUE_TYPE_LONG:
1098             currFeature = numberValue;
1099             break;
1100 
1101           case DBHelper.VALUE_TYPE_BINARY:
1102             //deserialize a java object
1103             ObjectInputStream ois = new ObjectInputStream(blobValue);
1104             currFeature = ois.readObject();
1105             ois.close();
1106             blobValue.close();
1107             break;
1108 
1109           case DBHelper.VALUE_TYPE_STRING:
1110             currFeature = stringValue;
1111             break;
1112 
1113           default:
1114             throw new PersistenceException("Invalid feature type found in DB, type is ["+valueType.intValue()+"]");
1115         }//switch
1116 
1117         //new feature or part of an array?
1118         if (currKey.equals(prevKey&& prevKey != null) {
1119           //part of array
1120           arrFeatures.add(currFeature);
1121         }
1122         else {
1123           //add prev feature to feature map
1124 
1125           //is the prev feature an array or a single object?
1126           if (arrFeatures.size() 1) {
1127             //put a clone, because this is a temp array that will
1128             //be cleared in few lines
1129             fm.put(prevKey, new Vector(arrFeatures));
1130           }
1131           else if (arrFeatures.size() == 1) {
1132             fm.put(prevKey,arrFeatures.elementAt(0));
1133           }
1134           else {
1135             //do nothing, this is the dummy feature
1136             ;
1137           }//if
1138 
1139           //now clear the array from previous fesature(s) and put the new
1140           //one there
1141           arrFeatures.clear();
1142 
1143           prevKey = currKey;
1144           arrFeatures.add(currFeature);
1145         }//if
1146       }//while
1147 
1148       //add the last feature
1149       if (arrFeatures.size() 1) {
1150         fm.put(currKey,arrFeatures);
1151       }
1152       else if (arrFeatures.size() == 1) {
1153         fm.put(currKey,arrFeatures.elementAt(0));
1154       }
1155     }//try
1156     catch(SQLException sqle) {
1157       throw new PersistenceException("can't read features from DB: ["+ sqle.getMessage()+"]");
1158     }
1159     catch(IOException ioe) {
1160       throw new PersistenceException("can't read features from DB: ["+ ioe.getMessage()+"]");
1161     }
1162     catch(ClassNotFoundException cnfe) {
1163       throw new PersistenceException("can't read features from DB: ["+ cnfe.getMessage()+"]");
1164     }
1165     finally {
1166       DBHelper.cleanup(rs);
1167       DBHelper.cleanup(pstmt);
1168     }
1169 
1170     return fm;
1171   }
1172 
1173 
1174   /**
1175    *  helper method for delete()
1176    *  never call it directly beause proper events will not be fired
1177    */
1178   protected void deleteDocument(Long lrId)
1179   throws PersistenceException {
1180     //0. preconditions
1181     Assert.assertNotNull(lrId);
1182 
1183     PreparedStatement pstmt = null;
1184 
1185     //1. delete from DB
1186     try {
1187       pstmt = this.jdbcConn.prepareStatement("select persist_delete_document(?) ");
1188       pstmt.setLong(1,lrId.longValue());
1189       pstmt.execute();
1190     }
1191     catch(SQLException sqle) {
1192       throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]");
1193     }
1194     finally {
1195       DBHelper.cleanup(pstmt);
1196     }
1197   }
1198 
1199   /**
1200    *  helper method for delete()
1201    *  never call it directly beause proper events will not be fired
1202    */
1203   protected void deleteCorpus(Long lrId)
1204     throws PersistenceException {
1205 
1206     Long ID = (Long)lrId;
1207 
1208     PreparedStatement pstmt = null;
1209 
1210     try {
1211       pstmt = this.jdbcConn.prepareStatement("select persist_delete_corpus(?)");
1212       pstmt.setLong(1,ID.longValue());
1213       pstmt.execute();
1214     }
1215     catch(SQLException sqle) {
1216       throw new PersistenceException("can't delete LR from DB: ["+ sqle.getMessage()+"]");
1217     }
1218     finally {
1219       DBHelper.cleanup(pstmt);
1220     }
1221   }
1222 
1223 
1224   /** helper for sync() - never call directly */
1225   protected void _syncRemovedDocumentsFromCorpus(List docLRIDs, Long corpLRID)
1226     throws PersistenceException {
1227 
1228     //0.preconditions
1229     Assert.assertNotNull(docLRIDs);
1230     Assert.assertNotNull(corpLRID);
1231     Assert.assertTrue(docLRIDs.size() 0);
1232 
1233     PreparedStatement pstmt = null;
1234 
1235     try {
1236       pstmt = this.jdbcConn.prepareStatement("select persist_remove_doc_from_corpus(?,?)");
1237 
1238       Iterator it = docLRIDs.iterator();
1239       while (it.hasNext()) {
1240         Long currLRID = (Long)it.next();
1241         pstmt.setLong(1,currLRID.longValue());
1242         pstmt.setLong(2,corpLRID.longValue());
1243         pstmt.execute();
1244       }
1245     }
1246     catch(SQLException sqle) {
1247 
1248       switch(sqle.getErrorCode()) {
1249         case DBHelper.X_ORACLE_INVALID_LR :
1250           throw new PersistenceException("invalid LR supplied: no such document: ["+
1251                                                             sqle.getMessage()+"]");
1252         default:
1253           throw new PersistenceException("can't change document data: ["+
1254                                                             sqle.getMessage()+"]");
1255       }
1256     }
1257     finally {
1258       DBHelper.cleanup(pstmt);
1259     }
1260 
1261   }
1262 
1263   /**
1264    *   adds document to corpus in the database
1265    *   if the document is already part of the corpus nothing
1266    *   changes
1267    */
1268   protected void addDocumentToCorpus(Long docID,Long corpID)
1269   throws PersistenceException,SecurityException {
1270 
1271     //0. preconditions
1272     Assert.assertNotNull(docID);
1273     Assert.assertNotNull(corpID);
1274 
1275     //1. check session
1276     if (null == this.session) {
1277       throw new SecurityException("session not set");
1278     }
1279 
1280     if (false == this.ac.isValidSession(this.session)) {
1281       throw new SecurityException("invalid session supplied");
1282     }
1283 
1284     //2. check permissions
1285     if (false == canWriteLR(corpID)) {
1286       throw new SecurityException("no write access granted to the user");
1287     }
1288 
1289     if (false == canWriteLR(docID)) {
1290       throw new SecurityException("no write access granted to the user");
1291     }
1292 
1293     //3. database
1294     PreparedStatement pstmt = null;
1295 
1296     try {
1297       pstmt = this.jdbcConn.prepareStatement("select persist_add_document_to_corpus(?,?) ");
1298       pstmt.setLong(1,docID.longValue());
1299       pstmt.setLong(2,corpID.longValue());
1300       pstmt.execute();
1301     }
1302     catch(SQLException sqle) {
1303 
1304       switch(sqle.getErrorCode()) {
1305         case DBHelper.X_ORACLE_INVALID_LR:
1306           throw new PersistenceException("invalid LR ID supplied ["+sqle.getMessage()+"]");
1307         default:
1308           throw new PersistenceException(
1309                 "can't add document to corpus : ["+ sqle.getMessage()+"]");
1310       }
1311     }
1312     finally {
1313       DBHelper.cleanup(pstmt);
1314     }
1315   }
1316 
1317 
1318 }