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