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