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