extraTables initialized with null if not present instead of emptyString
[mir.git] / source / mir / storage / Database.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30 package mir.storage;
31
32 import java.io.IOException;
33 import java.io.InputStreamReader;
34 import java.sql.Connection;
35 import java.sql.PreparedStatement;
36 import java.sql.ResultSet;
37 import java.sql.ResultSetMetaData;
38 import java.sql.SQLException;
39 import java.sql.Statement;
40 import java.sql.Timestamp;
41 import java.text.ParseException;
42 import java.text.SimpleDateFormat;
43 import java.util.ArrayList;
44 import java.util.Calendar;
45 import java.util.GregorianCalendar;
46 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.TimeZone;
51 import java.util.Vector;
52
53 import mir.config.MirPropertiesConfiguration;
54 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;
55 import mir.entity.Entity;
56 import mir.entity.EntityList;
57 import mir.entity.StorableObjectEntity;
58 import mir.log.LoggerWrapper;
59 import mir.misc.StringUtil;
60 import mir.storage.store.ObjectStore;
61 import mir.storage.store.StorableObject;
62 import mir.storage.store.StoreContainerType;
63 import mir.storage.store.StoreIdentifier;
64 import mir.storage.store.StoreUtil;
65 import mir.util.JDBCStringRoutines;
66
67 import com.codestudio.util.SQLManager;
68
69
70 /**
71  * Diese Klasse implementiert die Zugriffsschicht auf die Datenbank.
72  * Alle Projektspezifischen Datenbankklassen erben von dieser Klasse.
73  * In den Unterklassen wird im Minimalfall nur die Tabelle angegeben.
74  * Im Konfigurationsfile findet sich eine Verweis auf den verwendeten
75  * Treiber, Host, User und Passwort, ueber den der Zugriff auf die
76  * Datenbank erfolgt.
77  *
78  * @version $Id: Database.java,v 1.44.2.19 2003/11/28 17:16:37 rk Exp $
79  * @author rk
80  *
81  */
82 public class Database implements StorageObject {
83   private static Class GENERIC_ENTITY_CLASS = mir.entity.StorableObjectEntity.class;
84   private static Class STORABLE_OBJECT_ENTITY_CLASS = mir.entity.StorableObjectEntity.class;
85
86
87   private static Map POPUP_EMPTYLINE = new HashMap();
88   protected static final ObjectStore o_store = ObjectStore.getInstance();
89   private static final int _millisPerHour = 60 * 60 * 1000;
90   private static final int _millisPerMinute = 60 * 1000;
91
92   static {
93     // always same object saves a little space
94     POPUP_EMPTYLINE.put("key", "");
95     POPUP_EMPTYLINE.put("value", "--");
96   }
97
98   protected LoggerWrapper logger;
99   protected MirPropertiesConfiguration configuration;
100   protected String theTable;
101   protected String theCoreTable = null;
102   protected String thePKeyName = "id";
103   protected int thePKeyType;
104   protected int thePKeyIndex;
105   protected boolean evaluatedMetaData = false;
106   protected ArrayList metadataFields;
107   protected ArrayList metadataLabels;
108   protected ArrayList metadataNotNullFields;
109   protected int[] metadataTypes;
110   protected Class theEntityClass;
111   protected List popupCache = null;
112   protected boolean hasPopupCache = false;
113   protected Map hashCache = null;
114   protected boolean hasTimestamp = true;
115   private String database_driver;
116   private String database_url;
117   private int defaultLimit;
118
119   TimeZone timezone;
120   SimpleDateFormat internalDateFormat;
121   SimpleDateFormat userInputDateFormat;
122 /*
123   private SimpleDateFormat _dateFormatterOut;
124   private SimpleDateFormat _dateFormatterIn;
125   _dateFormatterOut = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
126   _dateFormatterIn = new SimpleDateFormat("yyyy-MM-dd HH:mm");
127 */
128
129   /**
130    * Kontruktor bekommt den Filenamen des Konfigurationsfiles ?bergeben.
131    * Aus diesem file werden <code>Database.Logfile</code>,
132    * <code>Database.Username</code>,<code>Database.Password</code>,
133    * <code>Database.Host</code> und <code>Database.Adaptor</code>
134    * ausgelesen und ein Broker f?r die Verbindugen zur Datenbank
135    * erzeugt.
136    *
137    * @param   String confFilename Dateiname der Konfigurationsdatei
138    */
139   public Database() throws StorageObjectFailure {
140     try {
141       configuration = MirPropertiesConfiguration.instance();
142     }
143     catch (PropertiesConfigExc e) {
144       throw new StorageObjectFailure(e);
145     }
146     logger = new LoggerWrapper("Database");
147     timezone = TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone"));
148     internalDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
149     internalDateFormat.setTimeZone(timezone);
150
151     userInputDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
152     userInputDateFormat.setTimeZone(timezone);
153
154
155     String theAdaptorName = configuration.getString("Database.Adaptor");
156     defaultLimit = Integer.parseInt(configuration.getString("Database.Limit"));
157
158     try {
159       theEntityClass = GENERIC_ENTITY_CLASS;
160     }
161     catch (Throwable e) {
162       logger.error("Error in Database() constructor with " + theAdaptorName + " -- " + e.getMessage());
163       throw new StorageObjectFailure("Error in Database() constructor.", e);
164     }
165   }
166
167   /**
168    * Liefert die Entity-Klasse zur?ck, in der eine Datenbankzeile gewrappt
169    * wird. Wird die Entity-Klasse durch die erbende Klasse nicht ?berschrieben,
170    * wird eine mir.entity.GenericEntity erzeugt.
171    *
172    * @return Class-Objekt der Entity
173    */
174   public java.lang.Class getEntityClass() {
175     return theEntityClass;
176   }
177
178   /**
179    * Liefert die Standardbeschr?nkung von select-Statements zur?ck, also
180    * wieviel Datens?tze per Default selektiert werden.
181    *
182    * @return Standard-Anzahl der Datens?tze
183    */
184   public int getLimit() {
185     return defaultLimit;
186   }
187
188   /**
189    * Liefert den Namen des Primary-Keys zur?ck. Wird die Variable nicht von
190    * der erbenden Klasse ?berschrieben, so ist der Wert <code>PKEY</code>
191    * @return Name des Primary-Keys
192    */
193   public String getIdName() {
194     return thePKeyName;
195   }
196
197   /**
198    * Liefert den Namen der Tabelle, auf das sich das Datenbankobjekt bezieht.
199    *
200    * @return Name der Tabelle
201    */
202   public String getTableName() {
203     return theTable;
204   }
205
206   /*
207   *   Dient dazu vererbte Tabellen bei objectrelationalen DBMS
208   *   zu speichern, wenn die id einer Tabelle in der parenttabelle verwaltet
209   *   wird.
210   *   @return liefert theCoreTabel als String zurueck, wenn gesetzt, sonst
211   *    the Table
212    */
213   public String getCoreTable() {
214     if (theCoreTable != null) {
215       return theCoreTable;
216     }
217     else {
218       return theTable;
219     }
220   }
221
222   /**
223    * Liefert Feldtypen der Felder der Tabelle zurueck (s.a. java.sql.Types)
224    * @return int-Array mit den Typen der Felder
225    * @exception StorageObjectException
226    */
227   public int[] getTypes() throws StorageObjectFailure {
228     if (metadataTypes == null) {
229       get_meta_data();
230     }
231
232     return metadataTypes;
233   }
234
235   /**
236    * Liefert eine Liste der Labels der Tabellenfelder
237    * @return ArrayListe mit Labeln
238    * @exception StorageObjectException
239    */
240   public List getLabels() throws StorageObjectFailure {
241     if (metadataLabels == null) {
242       get_meta_data();
243     }
244
245     return metadataLabels;
246   }
247
248   /**
249    * Liefert eine Liste der Felder der Tabelle
250    * @return ArrayList mit Feldern
251    * @exception StorageObjectException
252    */
253   public List getFields() throws StorageObjectFailure {
254     if (metadataFields == null) {
255       get_meta_data();
256     }
257
258     return metadataFields;
259   }
260
261   /**
262    *   Gets value out of ResultSet according to type and converts to String
263    *   @param rs  ResultSet.
264    *   @param aType  a type from java.sql.Types.*
265    *   @param index  index in ResultSet
266    *   @return returns the value as String. If no conversion is possible
267    *                             /unsupported value/ is returned
268    */
269   private String getValueAsString(ResultSet rs, int valueIndex, int aType)
270     throws StorageObjectFailure {
271     String outValue = null;
272
273     if (rs != null) {
274       try {
275         switch (aType) {
276           case java.sql.Types.BIT:
277             outValue = (rs.getBoolean(valueIndex) == true) ? "1" : "0";
278
279             break;
280
281           case java.sql.Types.INTEGER:
282           case java.sql.Types.SMALLINT:
283           case java.sql.Types.TINYINT:
284           case java.sql.Types.BIGINT:
285
286             int out = rs.getInt(valueIndex);
287
288             if (!rs.wasNull()) {
289               outValue = new Integer(out).toString();
290             }
291
292             break;
293
294           case java.sql.Types.NUMERIC:
295
296             /** @todo Numeric can be float or double depending upon
297              *  metadata.getScale() / especially with oracle */
298             long outl = rs.getLong(valueIndex);
299
300             if (!rs.wasNull()) {
301               outValue = new Long(outl).toString();
302             }
303
304             break;
305
306           case java.sql.Types.REAL:
307
308             float tempf = rs.getFloat(valueIndex);
309
310             if (!rs.wasNull()) {
311               tempf *= 10;
312               tempf += 0.5;
313
314               int tempf_int = (int) tempf;
315               tempf = (float) tempf_int;
316               tempf /= 10;
317               outValue = "" + tempf;
318               outValue = outValue.replace('.', ',');
319             }
320
321             break;
322
323           case java.sql.Types.DOUBLE:
324
325             double tempd = rs.getDouble(valueIndex);
326
327             if (!rs.wasNull()) {
328               tempd *= 10;
329               tempd += 0.5;
330
331               int tempd_int = (int) tempd;
332               tempd = (double) tempd_int;
333               tempd /= 10;
334               outValue = "" + tempd;
335               outValue = outValue.replace('.', ',');
336             }
337
338             break;
339
340           case java.sql.Types.CHAR:
341           case java.sql.Types.VARCHAR:
342           case java.sql.Types.LONGVARCHAR:
343             outValue = rs.getString(valueIndex);
344
345             break;
346
347           case java.sql.Types.LONGVARBINARY:
348             outValue = rs.getString(valueIndex);
349
350             break;
351
352           case java.sql.Types.TIMESTAMP:
353
354             // it's important to use Timestamp here as getting it
355             // as a string is undefined and is only there for debugging
356             // according to the API. we can make it a string through formatting.
357             // -mh
358             Timestamp timestamp = (rs.getTimestamp(valueIndex));
359
360             if (!rs.wasNull()) {
361               java.util.Date date = new java.util.Date(timestamp.getTime());
362
363               Calendar calendar = new GregorianCalendar();
364               calendar.setTime(date);
365               calendar.setTimeZone(timezone);
366               outValue = internalDateFormat.format(date);
367
368               int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
369               String tzOffset = StringUtil.zeroPaddingNumber(Math.abs(offset) / _millisPerHour, 2, 2);
370
371               if (offset<0)
372                 outValue = outValue + "-";
373               else
374                 outValue = outValue + "+";
375               outValue = outValue + tzOffset;
376             }
377
378             break;
379
380           default:
381             outValue = "<unsupported value>";
382             logger.warn("Unsupported Datatype: at " + valueIndex + " (" + aType + ")");
383         }
384       } catch (SQLException e) {
385         throw new StorageObjectFailure("Could not get Value out of Resultset -- ",
386           e);
387       }
388     }
389
390     return outValue;
391   }
392
393   /**
394    *   select-Operator um einen Datensatz zu bekommen.
395    *   @param id Primaerschluessel des Datensatzes.
396    *   @return liefert EntityObject des gefundenen Datensatzes oder null.
397    */
398   public Entity selectById(String id) throws StorageObjectExc {
399     if ((id == null) || id.equals("")) {
400       throw new StorageObjectExc("Database.selectById: Missing id");
401     }
402
403     // ask object store for object
404     if (StoreUtil.extendsStorableEntity(theEntityClass)) {
405       String uniqueId = id;
406
407       if (theEntityClass.equals(StorableObjectEntity.class)) {
408         uniqueId += ("@" + theTable);
409       }
410
411       StoreIdentifier search_sid = new StoreIdentifier(theEntityClass, uniqueId);
412       logger.debug("CACHE: (dbg) looking for sid " + search_sid.toString());
413
414       Entity hit = (Entity) o_store.use(search_sid);
415
416       if (hit != null) {
417         return hit;
418       }
419     }
420
421     Statement stmt = null;
422     Connection con = getPooledCon();
423     Entity returnEntity = null;
424
425     try {
426       ResultSet rs;
427
428       /** @todo better prepared statement */
429       String selectSql =
430         "select * from " + theTable + " where " + thePKeyName + "=" + id;
431       stmt = con.createStatement();
432       rs = executeSql(stmt, selectSql);
433
434       if (rs != null) {
435         if (evaluatedMetaData == false) {
436           evalMetaData(rs.getMetaData());
437         }
438
439         if (rs.next()) {
440           returnEntity = makeEntityFromResultSet(rs);
441         }
442         else {
443           logger.debug("No data for id: " + id + " in table " + theTable);
444         }
445
446         rs.close();
447       }
448       else {
449         logger.debug("No Data for Id " + id + " in Table " + theTable);
450       }
451     }
452     catch (SQLException sqe) {
453       throwSQLException(sqe, "selectById");
454       return null;
455     }
456     catch (NumberFormatException e) {
457       logger.error("ID is no number: " + id);
458     }
459     finally {
460       freeConnection(con, stmt);
461     }
462
463     return returnEntity;
464   }
465
466   /**
467    * This method makes it possible to make selects across multiple tables
468    * 
469    * @param mainTablePrefix prefix for the mainTable
470    * @param extraTables a vector of tables for relational select
471    * @param aWhereClause whereClause
472    * @return EntityList of selected Objects
473    * @throws StorageObjectFailure
474    */
475
476   public EntityList selectByWhereClauseWithExtraTables(String mainTablePrefix, 
477                                                 List extraTables, String aWhereClause )
478    throws StorageObjectFailure {
479         return selectByWhereClause( mainTablePrefix, extraTables, aWhereClause, "", 0, defaultLimit);
480   }
481
482   /**
483    *   select-Operator um Datensaetze zu bekommen, die key = value erfuellen.
484    *   @param key  Datenbankfeld der Bedingung.
485    *   @param value  Wert die der key anehmen muss.
486    *   @return EntityList mit den gematchten Entities
487    */
488   public EntityList selectByFieldValue(String aField, String aValue) throws StorageObjectFailure {
489     return selectByFieldValue(aField, aValue, 0);
490   }
491
492   /**
493    *   select-Operator um Datensaetze zu bekommen, die key = value erfuellen.
494    *   @param key  Datenbankfeld der Bedingung.
495    *   @param value  Wert die der key anehmen muss.
496    *   @param offset  Gibt an ab welchem Datensatz angezeigt werden soll.
497    *   @return EntityList mit den gematchten Entities
498    */
499   public EntityList selectByFieldValue(String aField, String aValue, int offset) throws StorageObjectFailure {
500     return selectByWhereClause(aField + "=" + aValue, offset);
501   }
502
503   /**
504    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.
505    * Also offset wird der erste Datensatz genommen.
506    *
507    * @param wc where-Clause
508    * @return EntityList mit den gematchten Entities
509    * @exception StorageObjectException
510    */
511   public EntityList selectByWhereClause(String where) throws StorageObjectFailure {
512     return selectByWhereClause(where, 0);
513   }
514
515   /**
516    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.
517    * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.
518    *
519    * @param wc where-Clause
520    * @param offset ab welchem Datensatz.
521    * @return EntityList mit den gematchten Entities
522    * @exception StorageObjectException
523    */
524   public EntityList selectByWhereClause(String whereClause, int offset) throws StorageObjectFailure {
525     return selectByWhereClause(whereClause, null, offset);
526   }
527
528   /**
529    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.
530    * Also offset wird der erste Datensatz genommen.
531    * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.
532    *
533    * @param wc where-Clause
534    * @param ob orderBy-Clause
535    * @return EntityList mit den gematchten Entities
536    * @exception StorageObjectException
537    */
538   public EntityList selectByWhereClause(String where, String order) throws StorageObjectFailure {
539     return selectByWhereClause(where, order, 0);
540   }
541
542   public EntityList selectByWhereClause(String mainTablePrefix, List extraTables, String where, String order) throws StorageObjectFailure {
543     return selectByWhereClause(mainTablePrefix, extraTables, where, order, 0, defaultLimit);
544   }
545
546   /**
547    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.
548    * Als maximale Anzahl wird das Limit auf der Konfiguration genommen.
549    *
550    * @param wc where-Clause
551    * @param ob orderBy-Clause
552    * @param offset ab welchem Datensatz
553    * @return EntityList mit den gematchten Entities
554    * @exception StorageObjectException
555    */
556   public EntityList selectByWhereClause(String whereClause, String orderBy, int offset) throws StorageObjectFailure {
557     return selectByWhereClause(whereClause, orderBy, offset, defaultLimit);
558   }
559
560   /**
561    * select-Operator returns EntityList with matching rows in Database.
562    * @param aWhereClause where-Clause
563    * @param anOrderByClause orderBy-Clause
564    * @param offset ab welchem Datensatz
565    * @param limit wieviele Datens?tze
566    * @return EntityList mit den gematchten Entities
567    * @exception StorageObjectException
568    */
569   public EntityList selectByWhereClause(String aWhereClause, String anOrderByClause,
570             int offset, int limit) throws StorageObjectFailure {
571     return selectByWhereClause("", null, aWhereClause, anOrderByClause, offset, limit);              
572   }
573
574
575   /**
576    * select-Operator returns EntityList with matching rows in Database.
577    * @param aWhereClause where-Clause
578    * @param anOrderByClause orderBy-Clause
579    * @param offset ab welchem Datensatz
580    * @param limit wieviele Datens?tze
581    * @return EntityList mit den gematchten Entities
582    * @exception StorageObjectException
583    */
584   public EntityList selectByWhereClause(String mainTablePrefix, List extraTables,
585       String aWhereClause, String anOrderByClause,
586                         int offset, int limit) throws StorageObjectFailure {
587     
588     // TODO get rid of emtpy Strings in extraTables
589     // make extraTables null, if single empty String in it
590     // cause StringUtil.splitString puts in emptyString        
591     if (extraTables != null && ((String)extraTables.get(0)).trim().equals(""))
592       {
593         logger.debug("+++ made extraTables to null!");
594         extraTables=null;
595       }
596     
597       String useTable = theTable;
598           String selectStar = "*";
599           if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) {
600             useTable+=" "+mainTablePrefix;
601             selectStar=mainTablePrefix.trim() + ".*";
602           }
603     
604     // check o_store for entitylist
605     // only if no relational select
606     if (extraTables==null) {
607       if (StoreUtil.extendsStorableEntity(theEntityClass)) {
608          StoreIdentifier searchSid = new StoreIdentifier(theEntityClass,
609                StoreContainerType.STOC_TYPE_ENTITYLIST,
610                StoreUtil.getEntityListUniqueIdentifierFor(theTable, 
611                 aWhereClause, anOrderByClause, offset, limit));
612          EntityList hit = (EntityList) o_store.use(searchSid);
613
614          if (hit != null) {
615             return hit;
616          }
617       }
618     }
619
620     // local
621     EntityList theReturnList = null;
622     Connection con = null;
623     Statement stmt = null;
624     ResultSet rs;
625     int offsetCount = 0;
626     int count = 0;
627
628     // build sql-statement
629
630     if ((aWhereClause != null) && (aWhereClause.trim().length() == 0)) {
631       aWhereClause = null;
632     }
633
634     StringBuffer countSql =
635       new StringBuffer("select count(*) from ").append(useTable);
636     StringBuffer selectSql =
637       new StringBuffer("select "+selectStar+" from ").append(useTable);
638  
639     // append extratables, if necessary
640     if (extraTables!=null) {
641       for (int i=0;i < extraTables.size();i++) {
642         if (!extraTables.get(i).equals("")) {        
643           countSql.append( ", " + extraTables.get(i));
644           selectSql.append( ", " + extraTables.get(i));
645         }
646       }
647     }
648     
649     if (aWhereClause != null) {
650       selectSql.append(" where ").append(aWhereClause);
651       countSql.append(" where ").append(aWhereClause);
652     }
653
654     if ((anOrderByClause != null) && !(anOrderByClause.trim().length() == 0)) {
655       selectSql.append(" order by ").append(anOrderByClause);
656     }
657
658     if ((limit > -1) && (offset > -1)) {
659       selectSql.append(" LIMIT ").append(limit).append(" OFFSET ").append(offset);
660     }
661
662     // execute sql
663     try {
664       con = getPooledCon();
665       stmt = con.createStatement();
666
667       // selecting...
668       rs = executeSql(stmt, selectSql.toString());
669
670       if (rs != null) {
671         if (!evaluatedMetaData) {
672           evalMetaData(rs.getMetaData());
673         }
674
675         theReturnList = new EntityList();
676         Entity theResultEntity;
677         while (rs.next()) {
678           theResultEntity = makeEntityFromResultSet(rs);
679           theReturnList.add(theResultEntity);
680           offsetCount++;
681         }
682         rs.close();
683       }
684
685       // making entitylist infos
686       count = offsetCount;
687
688       if (theReturnList != null) {
689         // now we decide if we have to know an overall count...
690         count = offsetCount;
691
692         if ((limit > -1) && (offset > -1)) {
693           if (offsetCount == limit) {
694             rs = executeSql(stmt, countSql.toString());
695
696             if (rs != null) {
697               if (rs.next()) {
698                 count = rs.getInt(1);
699               }
700
701               rs.close();
702             }
703             else {
704               logger.error("Could not count: " + countSql);
705             }
706           }
707         }
708
709         theReturnList.setCount(count);
710         theReturnList.setOffset(offset);
711         theReturnList.setWhere(aWhereClause);
712         theReturnList.setOrder(anOrderByClause);
713         theReturnList.setStorage(this);
714         theReturnList.setLimit(limit);
715
716         if (offset >= limit) {
717           theReturnList.setPrevBatch(offset - limit);
718         }
719
720         if ((offset + offsetCount) < count) {
721           theReturnList.setNextBatch(offset + limit);
722         }
723
724         if (extraTables==null && StoreUtil.extendsStorableEntity(theEntityClass)) {
725           StoreIdentifier sid = theReturnList.getStoreIdentifier();
726           logger.debug("CACHE (add): " + sid.toString());
727           o_store.add(sid);
728         }
729       }
730     }
731     catch (SQLException sqe) {
732       throwSQLException(sqe, "selectByWhereClause");
733     }
734     finally {
735       try {
736         if (con != null) {
737           freeConnection(con, stmt);
738         }
739       } catch (Throwable t) {
740       }
741     }
742
743     return theReturnList;
744   }
745
746   /**
747    *  Bastelt aus einer Zeile der Datenbank ein EntityObjekt.
748    *
749    *  @param rs Das ResultSetObjekt.
750    *  @return Entity Die Entity.
751    */
752   private Entity makeEntityFromResultSet(ResultSet rs)
753     throws StorageObjectFailure {
754     Map theResultHash = new HashMap();
755     String theResult = null;
756     int theType;
757     Entity returnEntity = null;
758
759     try {
760       // ask object store for object @ thePKeyIndex
761       if (StoreUtil.extendsStorableEntity(theEntityClass)) {
762          StoreIdentifier searchSid = StorableObjectEntity.getStoreIdentifier(this,
763                theEntityClass, rs);
764          Entity hit = (Entity) o_store.use(searchSid);
765          if (hit != null) return hit;
766       }
767
768
769       int size = metadataFields.size();
770
771       for (int i = 0; i < size; i++) {
772         // alle durchlaufen bis nix mehr da
773         theType = metadataTypes[i];
774
775         if (theType == java.sql.Types.LONGVARBINARY) {
776           InputStreamReader is =
777             (InputStreamReader) rs.getCharacterStream(i + 1);
778
779           if (is != null) {
780             char[] data = new char[32768];
781             StringBuffer theResultString = new StringBuffer();
782             int len;
783
784             while ((len = is.read(data)) > 0) {
785               theResultString.append(data, 0, len);
786             }
787
788             is.close();
789             theResult = theResultString.toString();
790           } else {
791             theResult = null;
792           }
793         } else {
794           theResult = getValueAsString(rs, (i + 1), theType);
795         }
796
797         if (theResult != null) {
798           theResultHash.put(metadataFields.get(i), theResult);
799         }
800       }
801
802       if (theEntityClass != null) {
803         returnEntity = (Entity) theEntityClass.newInstance();
804         returnEntity.setStorage(this);
805         returnEntity.setValues(theResultHash);
806
807         if (returnEntity instanceof StorableObject) {
808           logger.debug("CACHE: ( in) " + returnEntity.getId() + " :" + theTable);
809           o_store.add(((StorableObject) returnEntity).getStoreIdentifier());
810         }
811       } else {
812         throwStorageObjectException("Internal Error: theEntityClass not set!");
813       }
814     }
815     catch (IllegalAccessException e) {
816       throwStorageObjectException("No access! -- " + e.getMessage());
817     }
818     catch (IOException e) {
819       throwStorageObjectException("IOException! -- " + e.getMessage());
820     }
821     catch (InstantiationException e) {
822       throwStorageObjectException("No Instatiation! -- " + e.getMessage());
823     }
824     catch (SQLException sqe) {
825       throwSQLException(sqe, "makeEntityFromResultSet");
826
827       return null;
828     }
829
830     return returnEntity;
831   }
832
833   /**
834    * Inserts an entity into the database.
835    *
836    * @param theEntity
837    * @return der Wert des Primary-keys der eingef?gten Entity
838    */
839   public String insert(Entity theEntity) throws StorageObjectFailure {
840     //cache
841     invalidatePopupCache();
842
843     // invalidating all EntityLists corresponding with theEntityClass
844     if (StoreUtil.extendsStorableEntity(theEntityClass)) {
845       StoreContainerType stoc_type =
846         StoreContainerType.valueOf(theEntityClass,
847           StoreContainerType.STOC_TYPE_ENTITYLIST);
848       o_store.invalidate(stoc_type);
849     }
850
851     String returnId = null;
852     Connection con = null;
853     PreparedStatement pstmt = null;
854
855     try {
856       List streamedInput = theEntity.streamedInput();
857       StringBuffer f = new StringBuffer();
858       StringBuffer v = new StringBuffer();
859       String aField;
860       String aValue;
861       boolean firstField = true;
862
863       // make sql-string
864       for (int i = 0; i < getFields().size(); i++) {
865         aField = (String) getFields().get(i);
866
867         if (!aField.equals(thePKeyName)) {
868           aValue = null;
869
870           // exceptions
871           if (!theEntity.hasValueForField(aField) && (
872               aField.equals("webdb_create") ||
873               aField.equals("webdb_lastchange"))) {
874             aValue = "NOW()";
875           }
876           else {
877             if ((streamedInput != null) && streamedInput.contains(aField)) {
878               aValue = "?";
879             }
880             else {
881               if (theEntity.hasValueForField(aField)) {
882                 aValue =
883                   "'" +
884                    JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(aField)) + "'";
885               }
886             }
887           }
888
889           // wenn Wert gegeben, dann einbauen
890           if (aValue != null) {
891             if (firstField == false) {
892               f.append(",");
893               v.append(",");
894             }
895             else {
896               firstField = false;
897             }
898
899             f.append(aField);
900             v.append(aValue);
901           }
902         }
903       }
904        // end for
905
906       // insert into db
907       StringBuffer sqlBuf =
908         new StringBuffer("insert into ").append(theTable).append("(").append(f)
909                                         .append(") values (").append(v).append(")");
910       String sql = sqlBuf.toString();
911
912       logger.debug("INSERT: " + sql);
913       con = getPooledCon();
914       con.setAutoCommit(false);
915       pstmt = con.prepareStatement(sql);
916
917       if (streamedInput != null) {
918         for (int i = 0; i < streamedInput.size(); i++) {
919           String inputString =
920             (String) theEntity.getValue((String) streamedInput.get(i));
921           pstmt.setBytes(i + 1, inputString.getBytes());
922         }
923       }
924
925       int ret = pstmt.executeUpdate();
926
927       if (ret == 0) {
928         //insert failed
929         return null;
930       }
931
932       pstmt = con.prepareStatement("select currval('" + getCoreTable() + "_id_seq')");
933
934       ResultSet rs = pstmt.executeQuery();
935       rs.next();
936       returnId = rs.getString(1);
937       theEntity.setId(returnId);
938     }
939     catch (SQLException sqe) {
940       throwSQLException(sqe, "insert");
941     }
942     finally {
943       try {
944         con.setAutoCommit(true);
945       }
946       catch (Exception e) {
947       }
948
949       freeConnection(con, pstmt);
950     }
951
952     /** @todo store entity in o_store */
953     return returnId;
954   }
955
956   /**
957    * Updates an entity in the database
958    *
959    * @param theEntity
960    */
961   public void update(Entity theEntity) throws StorageObjectFailure {
962     Connection con = null;
963     PreparedStatement pstmt = null;
964
965     /** @todo this is stupid: why do we prepare statement, when we
966      *  throw it away afterwards. should be regular statement
967      *  update/insert could better be one routine called save()
968      *  that chooses to either insert or update depending if we
969      *  have a primary key in the entity. i don't know if we
970      *  still need the streamed input fields. // rk  */
971     /** @todo extension: check if Entity did change, otherwise we don't need
972      *  the roundtrip to the database */
973     /** invalidating corresponding entitylists in o_store*/
974     if (StoreUtil.extendsStorableEntity(theEntityClass)) {
975       StoreContainerType stoc_type =
976         StoreContainerType.valueOf(theEntityClass,
977           StoreContainerType.STOC_TYPE_ENTITYLIST);
978       o_store.invalidate(stoc_type);
979     }
980
981     List streamedInput = theEntity.streamedInput();
982     String id = theEntity.getId();
983     String aField;
984     StringBuffer fv = new StringBuffer();
985     boolean firstField = true;
986
987     //cache
988     invalidatePopupCache();
989
990     // build sql statement
991     for (int i = 0; i < getFields().size(); i++) {
992       aField = (String) metadataFields.get(i);
993
994       // only normal cases
995       if (  !(aField.equals(thePKeyName) ||
996             aField.equals("webdb_create") ||
997             aField.equals("webdb_lastchange") ||
998             ((streamedInput != null) && streamedInput.contains(aField)))) {
999         if (theEntity.hasValueForField(aField)) {
1000           if (firstField == false) {
1001             fv.append(", ");
1002           }
1003           else {
1004             firstField = false;
1005           }
1006
1007           fv.append(aField).append("='").append(JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(aField))).append("'");
1008
1009           //              fv.append(aField).append("='").append(StringUtil.quote((String)theEntity.getValue(aField))).append("'");
1010         }
1011       }
1012     }
1013
1014     StringBuffer sql =
1015       new StringBuffer("update ").append(theTable).append(" set ").append(fv);
1016
1017     // exceptions
1018     if (metadataFields.contains("webdb_lastchange")) {
1019       sql.append(",webdb_lastchange=NOW()");
1020     }
1021
1022     // special case: the webdb_create requires the field in yyyy-mm-dd HH:mm
1023     // format so anything extra will be ignored. -mh
1024     if (metadataFields.contains("webdb_create") &&
1025         theEntity.hasValueForField("webdb_create")) {
1026       // minimum of 10 (yyyy-mm-dd)...
1027       if (theEntity.getValue("webdb_create").length() >= 10) {
1028         String dateString = theEntity.getValue("webdb_create");
1029
1030         // if only 10, then add 00:00 so it doesn't throw a ParseException
1031         if (dateString.length() == 10) {
1032           dateString = dateString + " 00:00";
1033         }
1034
1035         // TimeStamp stuff
1036         try {
1037           java.util.Date d = userInputDateFormat.parse(dateString);
1038 //          Timestamp tStamp = new Timestamp(d.getTime());
1039           sql.append(",webdb_create='" + JDBCStringRoutines.formatDate(d) + "'");
1040         }
1041         catch (ParseException e) {
1042           throw new StorageObjectFailure(e);
1043         }
1044       }
1045     }
1046
1047     if (streamedInput != null) {
1048       for (int i = 0; i < streamedInput.size(); i++) {
1049         sql.append(",").append(streamedInput.get(i)).append("=?");
1050       }
1051     }
1052
1053     sql.append(" where id=").append(id);
1054     logger.debug("UPDATE: " + sql);
1055
1056     try {
1057       con = getPooledCon();
1058       con.setAutoCommit(false);
1059       pstmt = con.prepareStatement(sql.toString());
1060
1061       if (streamedInput != null) {
1062         for (int i = 0; i < streamedInput.size(); i++) {
1063           String inputString =
1064             theEntity.getValue((String) streamedInput.get(i));
1065           pstmt.setBytes(i + 1, inputString.getBytes());
1066         }
1067       }
1068
1069       pstmt.executeUpdate();
1070     }
1071     catch (SQLException sqe) {
1072       throwSQLException(sqe, "update");
1073     }
1074     finally {
1075       try {
1076         con.setAutoCommit(true);
1077       }
1078       catch (Exception e) {
1079         ;
1080       }
1081
1082       freeConnection(con, pstmt);
1083     }
1084   }
1085
1086   /*
1087   *   delete-Operator
1088   *   @param id des zu loeschenden Datensatzes
1089   *   @return boolean liefert true zurueck, wenn loeschen erfolgreich war.
1090    */
1091   public boolean delete(String id) throws StorageObjectFailure {
1092     invalidatePopupCache();
1093
1094     // ostore send notification
1095     if (StoreUtil.extendsStorableEntity(theEntityClass)) {
1096       String uniqueId = id;
1097
1098       if (theEntityClass.equals(StorableObjectEntity.class)) {
1099         uniqueId += ("@" + theTable);
1100       }
1101
1102       logger.debug("CACHE: (del) " + id);
1103
1104       StoreIdentifier search_sid =
1105         new StoreIdentifier(theEntityClass,
1106           StoreContainerType.STOC_TYPE_ENTITY, uniqueId);
1107       o_store.invalidate(search_sid);
1108     }
1109
1110     /** @todo could be prepared Statement */
1111     Statement stmt = null;
1112     Connection con = null;
1113     int res = 0;
1114     String sql =
1115       "delete from " + theTable + " where " + thePKeyName + "='" + id + "'";
1116
1117     //theLog.printInfo("DELETE " + sql);
1118     try {
1119       con = getPooledCon();
1120       stmt = con.createStatement();
1121       res = stmt.executeUpdate(sql);
1122     } catch (SQLException sqe) {
1123       throwSQLException(sqe, "delete");
1124     } finally {
1125       freeConnection(con, stmt);
1126     }
1127
1128     return (res > 0) ? true : false;
1129   }
1130
1131   /**
1132    * Deletes entities based on a where clause
1133    *
1134    * @param aWhereClause
1135    * @return
1136    * @throws StorageObjectFailure
1137    */
1138   public int deleteByWhereClause(String aWhereClause) throws StorageObjectFailure {
1139     invalidatePopupCache();
1140     if (StoreUtil.extendsStorableEntity(theEntityClass)) {
1141       StoreContainerType stoc_type = StoreContainerType.valueOf(theEntityClass, StoreContainerType.STOC_TYPE_ENTITYLIST);
1142       o_store.invalidate(stoc_type);
1143     }
1144
1145     Statement stmt = null;
1146     Connection con = null;
1147     int res = 0;
1148     String sql =
1149       "delete from " + theTable + " where " + aWhereClause;
1150
1151     //theLog.printInfo("DELETE " + sql);
1152     try {
1153       con = getPooledCon();
1154       stmt = con.createStatement();
1155       res = stmt.executeUpdate(sql);
1156     }
1157     catch (SQLException sqe) {
1158       throwSQLException(sqe, "delete");
1159     }
1160     finally {
1161       freeConnection(con, stmt);
1162     }
1163
1164     return res;
1165   }
1166
1167   /* noch nicht implementiert.
1168   * @return immer false
1169    */
1170   public boolean delete(EntityList theEntityList) {
1171     invalidatePopupCache();
1172
1173     return false;
1174   }
1175
1176   /* invalidates the popupCache
1177    */
1178   protected void invalidatePopupCache() {
1179     /** @todo  invalidates toooo much */
1180     popupCache = null;
1181     hashCache = null;
1182   }
1183
1184   /**
1185    * Diese Methode fuehrt den Sqlstring <i>sql</i> aus und timed im Logfile.
1186    * @param stmt Statemnt
1187    * @param sql Sql-String
1188    * @return ResultSet
1189    * @exception StorageObjectException
1190    */
1191   public ResultSet executeSql(Statement stmt, String sql)
1192                             throws StorageObjectFailure, SQLException {
1193     ResultSet rs;
1194     long startTime = System.currentTimeMillis();
1195
1196     try {
1197       rs = stmt.executeQuery(sql);
1198
1199       logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1200     }
1201     catch (SQLException e) {
1202       logger.error(e.getMessage() +"\n" + (System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1203       throw e;
1204     }
1205
1206     return rs;
1207   }
1208 /*
1209   public ResultSet executeSql(String sql) throws StorageObjectFailure, SQLException {
1210     long startTime = System.currentTimeMillis();
1211     Connection connection = null;
1212     Statement statement = null;
1213
1214     try {
1215       connection = getPooledCon();
1216       statement = connection.createStatement();
1217       ResultSet result;
1218
1219       result = statement.executeQuery(sql);
1220
1221       logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1222       return result;
1223     }
1224     catch (Throwable e) {
1225       logger.error(e.getMessage() +"\n" + (System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1226       throw new StorageObjectFailure(e);
1227     }
1228     finally {
1229       if (connection!=null) {
1230         freeConnection(connection, statement);
1231       }
1232     }
1233   }
1234 */
1235   private Map processRow(ResultSet aResultSet) throws StorageObjectFailure, StorageObjectExc {
1236     try {
1237       Map result = new HashMap();
1238       ResultSetMetaData metaData = aResultSet.getMetaData();
1239       int nrColumns = metaData.getColumnCount();
1240       for (int i=0; i<nrColumns; i++) {
1241         result.put(metaData.getColumnName(i+1), getValueAsString(aResultSet, i+1, metaData.getColumnType(i+1)));
1242       }
1243
1244       return result;
1245     }
1246     catch (Throwable e) {
1247       throw new StorageObjectFailure(e);
1248     }
1249   }
1250
1251   public List executeFreeSql(String sql, int aLimit) throws StorageObjectFailure, StorageObjectExc {
1252     Connection connection = null;
1253     Statement statement = null;
1254     try {
1255       List result = new Vector();
1256       connection = getPooledCon();
1257       statement = connection.createStatement();
1258       ResultSet resultset = executeSql(statement, sql);
1259       try {
1260         while (resultset.next() && result.size() < aLimit) {
1261           result.add(processRow(resultset));
1262         }
1263       }
1264       finally {
1265         resultset.close();
1266       }
1267
1268       return result;
1269     }
1270     catch (Throwable e) {
1271       throw new StorageObjectFailure(e);
1272     }
1273     finally {
1274       if (connection!=null) {
1275         freeConnection(connection, statement);
1276       }
1277     }
1278   };
1279
1280   public Map executeFreeSingleRowSql(String anSqlStatement) throws StorageObjectFailure, StorageObjectExc {
1281     try {
1282       List resultList = executeFreeSql(anSqlStatement, 1);
1283       try {
1284         if (resultList.size()>0)
1285           return (Map) resultList.get(0);
1286         else
1287           return null;
1288       }
1289       finally {
1290       }
1291     }
1292     catch (Throwable t) {
1293       throw new StorageObjectFailure(t);
1294     }
1295   };
1296
1297   public String executeFreeSingleValueSql(String sql) throws StorageObjectFailure, StorageObjectExc {
1298     Map row = executeFreeSingleRowSql(sql);
1299
1300     if (row==null)
1301       return null;
1302
1303     Iterator i = row.values().iterator();
1304     if (i.hasNext())
1305       return (String) i.next();
1306     else
1307       return null;
1308   };
1309
1310   public int getSize(String where) throws SQLException, StorageObjectFailure {
1311     return getSize("", null, where);
1312   }
1313   /**
1314    * returns the number of rows in the table
1315    */
1316   public int getSize(String mainTablePrefix, List extraTables, String where) throws SQLException, StorageObjectFailure {
1317     
1318     long startTime = System.currentTimeMillis();
1319     
1320     String useTable = theTable;
1321     if (mainTablePrefix!=null && mainTablePrefix.trim().length()>0) {
1322           useTable+=" "+mainTablePrefix;
1323     }
1324     StringBuffer countSql =
1325       new StringBuffer("select count(*) from ").append(useTable);
1326         // append extratables, if necessary
1327       if (extraTables!=null) {
1328         for (int i=0;i < extraTables.size();i++) {
1329           if (!extraTables.get(i).equals("")) {        
1330             countSql.append( ", " + extraTables.get(i));
1331           }
1332         }
1333       }
1334
1335     if ((where != null) && (where.length() != 0)) {
1336       countSql.append( " where " + where);
1337     }
1338
1339     Connection con = null;
1340     Statement stmt = null;
1341     int result = 0;
1342
1343     try {
1344       con = getPooledCon();
1345       stmt = con.createStatement();
1346
1347       ResultSet rs = executeSql(stmt, countSql.toString());
1348
1349       while (rs.next()) {
1350         result = rs.getInt(1);
1351       }
1352     }
1353     catch (SQLException e) {
1354       logger.error("Database.getSize: " + e.getMessage());
1355     }
1356     finally {
1357       freeConnection(con, stmt);
1358     }
1359     logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + countSql);
1360
1361     return result;
1362   }
1363
1364   public int executeUpdate(Statement stmt, String sql)
1365     throws StorageObjectFailure, SQLException {
1366     int rs;
1367     long startTime = System.currentTimeMillis();
1368
1369     try {
1370       rs = stmt.executeUpdate(sql);
1371
1372       logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1373     }
1374     catch (SQLException e) {
1375       logger.error("Failed: " + (System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1376       throw e;
1377     }
1378
1379     return rs;
1380   }
1381
1382   public int executeUpdate(String sql)
1383     throws StorageObjectFailure, SQLException {
1384     int result = -1;
1385     long startTime = System.currentTimeMillis();
1386     Connection con = null;
1387     PreparedStatement pstmt = null;
1388
1389     try {
1390       con = getPooledCon();
1391       pstmt = con.prepareStatement(sql);
1392       result = pstmt.executeUpdate();
1393     }
1394     catch (Throwable e) {
1395       logger.error("Database.executeUpdate(" + sql + "): " + e.getMessage());
1396       throw new StorageObjectFailure("Database.executeUpdate(" + sql + "): " + e.getMessage(), e);
1397     }
1398     finally {
1399       freeConnection(con, pstmt);
1400     }
1401
1402     logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql);
1403     return result;
1404   }
1405
1406   /**
1407    * Wertet ResultSetMetaData aus und setzt interne Daten entsprechend
1408    * @param md ResultSetMetaData
1409    * @exception StorageObjectException
1410    */
1411   private void evalMetaData(ResultSetMetaData md) throws StorageObjectFailure {
1412     this.evaluatedMetaData = true;
1413     this.metadataFields = new ArrayList();
1414     this.metadataLabels = new ArrayList();
1415     this.metadataNotNullFields = new ArrayList();
1416
1417     try {
1418       int numFields = md.getColumnCount();
1419       this.metadataTypes = new int[numFields];
1420
1421       String aField;
1422       int aType;
1423
1424       for (int i = 1; i <= numFields; i++) {
1425         aField = md.getColumnName(i);
1426         metadataFields.add(aField);
1427         metadataLabels.add(md.getColumnLabel(i));
1428         aType = md.getColumnType(i);
1429         metadataTypes[i - 1] = aType;
1430
1431         if (aField.equals(thePKeyName)) {
1432           thePKeyType = aType;
1433           thePKeyIndex = i;
1434         }
1435
1436         if (md.isNullable(i) == ResultSetMetaData.columnNullable) {
1437           metadataNotNullFields.add(aField);
1438         }
1439       }
1440     }
1441     catch (SQLException e) {
1442       throwSQLException(e, "evalMetaData");
1443     }
1444   }
1445
1446   /**
1447    *  Wertet die Metadaten eines Resultsets fuer eine Tabelle aus,
1448    *  um die alle Columns und Typen einer Tabelle zu ermitteln.
1449    */
1450   private void get_meta_data() throws StorageObjectFailure {
1451     Connection con = null;
1452     PreparedStatement pstmt = null;
1453     String sql = "select * from " + theTable + " where 0=1";
1454
1455     try {
1456       con = getPooledCon();
1457       pstmt = con.prepareStatement(sql);
1458
1459       logger.debug("METADATA: " + sql);
1460       ResultSet rs = pstmt.executeQuery();
1461       evalMetaData(rs.getMetaData());
1462       rs.close();
1463     }
1464     catch (SQLException e) {
1465       throwSQLException(e, "get_meta_data");
1466     }
1467     finally {
1468       freeConnection(con, pstmt);
1469     }
1470   }
1471
1472   public Connection getPooledCon() throws StorageObjectFailure {
1473     Connection con = null;
1474
1475     try {
1476       con = SQLManager.getInstance().requestConnection();
1477     }
1478     catch (SQLException e) {
1479       logger.error("could not connect to the database " + e.getMessage());
1480
1481       throw new StorageObjectFailure("Could not connect to the database", e);
1482     }
1483
1484     return con;
1485   }
1486
1487   public void freeConnection(Connection con, Statement stmt)
1488     throws StorageObjectFailure {
1489     SQLManager.closeStatement(stmt);
1490     SQLManager.getInstance().returnConnection(con);
1491   }
1492
1493   /**
1494    * Wertet SQLException aus und wirft dannach eine StorageObjectException
1495    * @param sqe SQLException
1496    * @param wo Funktonsname, in der die SQLException geworfen wurde
1497    * @exception StorageObjectException
1498    */
1499   protected void throwSQLException(SQLException sqe, String aFunction) throws StorageObjectFailure {
1500     String state = "";
1501     String message = "";
1502     int vendor = 0;
1503
1504     if (sqe != null) {
1505       state = sqe.getSQLState();
1506       message = sqe.getMessage();
1507       vendor = sqe.getErrorCode();
1508     }
1509
1510     String information =
1511         "SQL Error: " +
1512         "state= " + state +
1513         ", vendor= " + vendor +
1514         ", message=" + message +
1515         ", function= " + aFunction;
1516
1517     logger.error(information);
1518
1519     throw new StorageObjectFailure(information, sqe);
1520   }
1521
1522   protected void _throwStorageObjectException(Exception e, String aFunction)
1523     throws StorageObjectFailure {
1524
1525     if (e != null) {
1526       logger.error(e.getMessage() + aFunction);
1527       throw new StorageObjectFailure(aFunction, e);
1528     }
1529   }
1530
1531   /**
1532    * Loggt Fehlermeldung mit dem Parameter Message und wirft dannach
1533    * eine StorageObjectException
1534    * @param message Nachricht mit dem Fehler
1535    * @exception StorageObjectException
1536    */
1537   void throwStorageObjectException(String aMessage) throws StorageObjectFailure {
1538     logger.error(aMessage);
1539     throw new StorageObjectFailure(aMessage, null);
1540   }
1541 }