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