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