/*
 * $Id: GenericDBImpl.java,v 1.388 2007/11/29 11:25:37 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: GenericDBImpl.java,v $
 * Revision 1.388  2007/11/29 11:25:37  marbobo
 * + Bugfix
 *
 * Revision 1.387  2006/11/25 20:58:09  vkc
 * + Bugfix beim Kopieren von Kategorien/Projekten/Projektordnern
 *
 * Revision 1.386  2006/11/11 22:04:31  marbobo
 * + Beim Kopieren/Verschieben von Kategorien/Projekten/Projektordnern werden in Abhängigkeit des Zielordners die entsprechenden Umkonvertierungen vorgenommen
 *
 * Revision 1.385  2006/10/09 12:19:26  marbobo
 * + Beim erhöhen der Zugriffsanzahl wird nicht mehr saveDocument aufgerufen
 *
 * Revision 1.384  2006/07/12 12:21:51  marbobo
 * + Bugfix
 *
 * Revision 1.383  2006/07/11 23:13:56  marbobo
 * + Dateien im Papierkorb werden nicht mehr im Quotaberechnung berücksichtigt
 * + Dokumentversionen werden nun beim aktivierten Papierkorb nicht mehr endgültig gelöscht
 *
 * Revision 1.382  2006/03/27 15:24:16  marbobo
 * + Bugfix
 *
 * Revision 1.381  2005/09/22 11:04:35  marbobo
 * + Beim kopieren von Dokumenten Zugriffszähler wieder auf 0 setzen
 *
 * Revision 1.380  2005/09/22 01:19:39  marbobo
 * + Beim kopieren von Dokumenten wird die Publizierungsreihenfolge beibehaltan
 * + Bugfix
 *
 * Revision 1.379  2005/09/13 00:15:04  marbobo
 * + Beim Wiederherstellen aus dem Papierkorb werden alle Dokumente nun korrekt Indiziert
 *
 * Revision 1.378  2005/09/05 22:44:17  marbobo
 * - Unnötige Methode entfernt
 *
 * Revision 1.377  2005/09/01 16:33:31  jonyp
 * added createUserTrashCategory in delete-Method
 *
 * Revision 1.376  2005/08/26 14:52:56  jonyp
 * move2Trash functionality
 *
 * Revision 1.375  2005/08/19 15:10:46  jonyp
 * Trash Functionality
 *
 * Revision 1.374  2005/06/14 10:37:50  marbobo
 * + MAX_FIELD_LENGTH auf maximum gesetzt
 * + refactoring
 *
 * Revision 1.373  2005/03/21 20:22:29  marbobo
 * + DocSize auf Type Long umgestellt
 *
 * Revision 1.372  2005/02/08 16:16:07  bigmac
 * Quotabehandlung überarbeitet
 *
 * Revision 1.371  2005/01/06 16:09:23  jonyp
 * - commented out specifics for oracle db in getConnection
 * - whole system works if only prepared statements are used !!!
 *
 * Revision 1.370  2004/12/23 00:57:42  koppy
 * move-Methode in Absprache mit Johnny geändert:
 * Bei existierendem Kategorienamen in der Zielkategorie
 * wird die zu verschiebende Kategorie automatisch
 * umbennant in "GleicherName (1)", "GleicherName (2)", usw..
 *
 * Revision 1.369  2004/12/17 01:16:07  marbobo
 * + Weitere Umstellungen und Optimierungen bzgl. PreparedStatements
 * + PreparedStatement-Cache-Größe auf 500 gesetzt
 *
 * Revision 1.368  2004/12/15 14:27:20  marbobo
 * + Beim Kopieren von Dokumenten kleine Ergänzung bzgl. dyn. Attributen
 *
 * Revision 1.367  2004/12/15 14:13:30  marbobo
 * + Bugfix (Beim Kopieren von Dokumenten werden dyn. Attribute nun richtig Kopiert)
 *
 * Revision 1.366  2004/12/09 14:27:48  marbobo
 * - DBHelper.execQuery() entfernt
 *
 * Revision 1.365  2004/12/09 12:39:29  lawern
 * vergessene PreparedStatements geschlossen
 *
 * Revision 1.364  2004/12/07 21:37:04  marbobo
 * + Statement auf PreparedStatement umgestellt
 *
 * Revision 1.363  2004/12/06 10:54:56  jonyp
 * added oracle specific constants for altering a session inside getConnection()-method
 *
 * Revision 1.362  2004/11/29 08:59:34  lawern
 * Property vkc.db.pool.prepStmtMaxSize eingefügt.
 *
 * Revision 1.361  2004/11/26 13:00:07  lawern
 * PreparedStatement-Pooling eingebaut.
 *
 * Revision 1.360  2004/11/26 10:56:45  jonyp
 * code cleaning
 *
 * Revision 1.359  2004/11/25 17:02:04  jonyp
 * added support for checks on idle connections
 *
 * Revision 1.358  2004/11/25 14:13:45  lawern
 * WhenExhaustedAction auf WHEN_EXHAUSTED_BLOCK gesetzt
 *
 * Revision 1.357  2004/11/24 16:35:40  jonyp
 * deleted setUserDocMailProcessed method
 *
 * Revision 1.356  2004/11/24 16:32:29  jonyp
 * fixed static warning: exhausted
 *
 * Revision 1.355  2004/11/24 13:39:03  lawern
 * Property vkc.db.connection.pool_size nach vkc.db.pool_size umbenannt.
 *
 * Revision 1.354  2004/11/24 13:01:14  lawern
 * Global "conn.setAutoCommit( false );" enfernt, weil überflüssig.
 *
 * Revision 1.353  2004/11/24 12:19:54  lawern
 * Autocommit für alle DBs auf false gesetzt.
 *
 * Revision 1.352  2004/11/24 09:25:52  lawern
 * Hibernate ist VKC-Geschichte!
 *
 * Revision 1.351  2004/11/19 10:32:24  jonyp
 * deleted code for "alter session" with cached_cursors
 *
 * Revision 1.350  2004/11/18 19:38:23  marbobo
 * + changed order of close-stmt to resultset/connect/session  in finally-blocks
 *
 * Revision 1.349  2004/11/18 17:01:23  jonyp
 * added code in "getConnection" to increase simulteanously opened cursors per session! (preliminary!)
 *
 * Revision 1.348  2004/11/10 21:38:21  marbobo
 * - Owner-Rechte beim Verschieben entfernt
 *
 * Revision 1.347  2004/11/10 15:02:52  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.346  2004/11/09 14:35:38  marbobo
 * + Umstellung von authorId auf publisherId
 * + Hinzufügen von AuthorName für Dokumente
 * + Nötwendige Änderungen betreffend AuthorName und PublisherId
 *
 * Revision 1.345  2004/11/05 01:56:48  marbobo
 * - hasPublishedDocuments()
 * + Einige Select-Statements korrigiert
 *
 * Revision 1.344  2004/10/31 18:19:35  marbobo
 * + Bei den Kopier- und Verschiebefunktionen   Benutzerrechte berücksichtigen
 * + Der Benutzer der Kopier- und Verschiebefunktionen ausführt bekommt standardmäßig alle Rechte
 *
 * Revision 1.343  2004/10/31 02:58:30  marbobo
 * + Bei den Kopierfunktionen (unvollständig)  Benutzerrechte berücksichtigen
 *
 * Revision 1.342  2004/10/25 13:24:42  koppy
 * Änderungen bzgl. Rauswurf von CONVERSION_STATUS_ID
 * aus DOCUMENTS-Tabelle. Stattdessen entsprechende
 * DOCUMENTID in der CONVERSIONSTATI-Tabelle
 *
 * Revision 1.341  2004/10/25 09:40:30  jonyp
 * changed getConnection to work with oracle-db. preliminary !!!
 *
 * Revision 1.340  2004/10/21 23:49:18  marbobo
 * + CURRENT_TIMESTAMP ersetzt mit java-Anweisung
 *
 * Revision 1.339  2004/10/06 12:33:36  marbobo
 * + Suche nach Globalen Dokument-Attributen korrigiert
 *
 * Revision 1.338  2004/10/05 17:59:46  marbobo
 * + Suche nach Globalen Dokument-Attributen überarbeitet
 *
 * Revision 1.337  2004/08/05 07:06:14  koppy
 * + getDbUrl()
 *
 * Revision 1.336  2004/07/27 14:56:41  jonyp
 * added new parameter for deletion of categories (definite = true)
 *
 * Revision 1.335  2004/07/20 15:15:53  jonyp
 * changed due to move of VKCQuotaInfo from util to view.bean
 *
 * Revision 1.334  2004/07/09 00:16:24  marbobo
 * + Beim Kopieren von Kategorien werden nun auch die
 *    entsprechenden virtuellen Queries mit kopiert
 *
 * Revision 1.333  2004/06/27 17:20:24  marbobo
 * + Quellcode maintenance
 *
 * Revision 1.332  2004/06/24 23:25:12  marbobo
 * Quellcode maintenance
 *
 * Revision 1.331  2004/06/22 13:20:20  lawern
 * Classify auf das Interface Classifier umgestellt.
 *
 * Revision 1.330  2004/06/20 21:40:02  marbobo
 * - Unnötige flush-Anweisungen entfernt
 * + Bugfixes
 *
 * Revision 1.329  2004/06/15 20:23:15  marbobo
 * Hibernate für VKCUser rausgenommen
 *
 * Revision 1.328  2004/04/16 08:31:28  jonyp
 * quotaInfo: compute only published documents
 *
 * Revision 1.327  2004/04/08 11:25:10  marbobo
 * getQuotaInfo() angepasst
 *
 * Revision 1.326  2004/04/07 15:48:30  koppy
 * Beim Löschen mehrerer Dokumente nur ein "updateCategory"
 *
 * Revision 1.325  2004/04/06 00:03:30  marbobo
 * + getQuotaInfos() und getQuotaInfo() ( wegen Percormance-Speedup
 * beim sortieren nach Quota und Anzah Pub. Dokumente )
 *
 * Revision 1.324  2004/03/31 13:51:29  bigmac
 * *** empty log message ***
 *
 * Revision 1.323  2004/03/29 14:37:14  bigmac
 * Bug beim Kopieren von Dokumenten behoben
 *
 * Revision 1.322  2004/03/24 13:09:22  bigmac
 * Property aclId enfernt
 *
 * Revision 1.321  2004/03/18 14:22:43  koppy
 * PersistentObjects transaktionsfähig gemacht
 *
 * Revision 1.320  2004/03/16 17:06:51  jonyp
 * copyDocument: ConverterInfo makePersistent with session parameter
 *
 * Revision 1.319  2004/03/16 16:01:42  teng
 * Funktionen bzgl. Mailing sind weg
 *
 * Revision 1.318  2004/03/15 17:07:23  jonyp
 * copyDocument: create new convesrionstati-entries during copying of documents
 *
 * Revision 1.317  2004/03/15 13:40:02  bigmac
 * Anpassungen an PermissionManager und weitere Refactorings
 *
 * Revision 1.316  2004/03/15 10:35:56  bigmac
 * Weitere Refactorings ...
 *
 * Revision 1.315  2004/03/13 20:46:14  bigmac
 * Weitere Refactorings ...
 *
 * Revision 1.314  2004/03/12 15:44:16  bigmac
 * Methoden für (lokale) zusätzliche Attribute aus VKCDynAttributeSerializer
 * nach VKCAddAttributeSerializer verschoben
 *
 * Unbenutze private Methoden projektweit entsorgt
 *
 * Revision 1.313  2004/03/12 12:56:57  bigmac
 * Anpassungen an VKCKeywordSerializer und VKCDynaAttributeSerializer
 * und generelle Refactorings durchgeführt
 *
 * Revision 1.312  2004/03/11 20:17:18  bigmac
 * Anpassungen an VKCUserSerializer
 *
 * Revision 1.311  2004/03/10 14:45:18  bigmac
 * populateUserDocMails() erzeugt nur noch ein Statement-Objekt
 * category.setAclId( null ) in copyCategory !!!
 *
 * Revision 1.310  2004/03/10 13:18:22  bigmac
 * saveAdditionalDocumentFormats() verschoben nach VKCDocumentFormatSerializer
 *
 * Revision 1.309  2004/03/09 18:40:35  bigmac
 * Anpassungen an VKCSysParameterSerializer
 *
 * Revision 1.308  2004/03/08 18:21:16  bigmac
 * saveDefaultAcl() und getDefaultCalAcl()  nach VKCAclSerializer verschoben
 * Anpassungen an VKCLanguageSerializer und VKCLanguagePackSerializer
 *
 * Revision 1.307  2004/03/08 16:20:47  teng
 * getTemplateURL() wird geloescht
 *
 * Revision 1.306  2004/03/07 22:34:08  marbobo
 * VKCCatProjectSteps bezogene methoden in die entsprechende serializer verschoben
 * und entsprechend angepasst - hybernate-Eintrag gelöscht
 *
 * Revision 1.305  2004/03/07 20:21:26  bigmac
 * Überflüssig gewordene Hibernate Mapping-Files gelöscht
 *
 * Revision 1.304  2004/03/07 20:05:56  bigmac
 * einige Methoden aus der VKCDataBase in die entsprechenden
 * Serializer verschoben, ein paar Refactorings durchgeführt und
 * tote Code-Teile entfernt
 *
 * Revision 1.303  2004/03/07 16:53:21  bigmac
 * Anpassungen an VKCAclSerializer
 *
 * Revision 1.302  2004/03/07 13:45:45  bigmac
 * Anpassungen an VKCDocumentTypeSerializer, VKCDocumentClassSerializer, VKCDocumentStatusSerializer
 *
 * Revision 1.301  2004/03/04 10:33:54  lawern
 * Ausgabe des StackTrace bei falschem Usernamen entfernt.
 *
 * Revision 1.300  2004/03/04 10:22:39  jonyp
 * added deleteAcl
 *
 * Revision 1.299  2004/03/03 16:28:02  jonyp
 * delete some entries of hibernate mapping
 *
 * Revision 1.298  2004/03/03 08:38:58  bigmac
 * Indizierung von Dokumenten wird innerhalb einer Session durchgeführt
 *
 * Revision 1.297  2004/03/02 23:05:43  bigmac
 * Anpassungen an VKCDocumentSerializer und VKCCategorySerializer
 *
 * Revision 1.296  2004/03/02 17:43:50  bigmac
 * Anpassungen an VKCGroupSerializer
 *
 * Revision 1.295  2004/03/02 00:51:59  marbobo
 * + Dyn. Attribute von Dokumenten speichern und löschen
 *
 * Revision 1.294  2004/02/26 08:43:04  bigmac
 * NPE in copyDocument() bei Notizen und Links behoben
 *
 * Revision 1.293  2004/02/25 11:30:17  bigmac
 * Anpassungen an vkc.utils.CategoryUtils
 *
 * Revision 1.292  2004/02/24 16:08:00  vchris
 * Bugfix in copyCategoryAsProject
 *
 * Revision 1.291  2004/02/24 14:15:05  lawern
 * Löschen von Kategorien optimiert.
 *
 * Revision 1.290  2004/02/24 13:31:21  bigmac
 * Signatur von saveProxyUsers() erweitert
 *
 * Revision 1.289  2004/02/24 09:09:38  jonyp
 * *** empty log message ***
 *
 * Revision 1.287  2004/02/20 16:35:18  bigmac
 * Anpassungen bzgl. Dokumentstati und Dokumentklassen
 *
 * Revision 1.286  2004/02/19 10:12:22  jonyp
 * *** empty log message ***
 *
 * Revision 1.284  2004/02/17 17:06:52  vchris
 * Änderungen bzgl. Projektmgmt
 *
 * Revision 1.283  2004/02/11 14:59:35  bigmac
 * getProjectInfos() auf neuen View umgestellt
 *
 * Revision 1.282  2004/02/10 16:25:14  vchris
 * Erweiterungen für Projektmanagement
 *
 * Revision 1.281  2004/02/10 15:09:06  marbobo
 * + retrieveAddDocAttributes()
 *
 * Revision 1.280  2004/02/09 21:21:23  bigmac
 * Laden von Projektschritten angepasst
 *
 * Revision 1.279  2004/02/09 17:29:55  vchris
 * Erweiterungen Projektmgmt
 *
 * Revision 1.277  2004/02/09 13:17:03  bigmac
 * + getUserInfos(VKCCategory category, String permTokens)
 *
 * Revision 1.276  2004/02/03 01:39:28  koppy
 * Converter Exceptionhandling eingeführt
 *
 */
package vkc.db;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory;
import org.apache.commons.dbcp.DriverConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;

import vkc.db.common.ConversionStatus;

import vkc.db.entities.VKCAclType;
import vkc.db.entities.VKCCategory;
import vkc.db.entities.VKCDocument;
import vkc.db.entities.VKCDynaAttribute;
import vkc.db.entities.VKCSearchQuery;
import vkc.db.entities.VKCSubCategory;
import vkc.db.entities.VKCUser;

import vkc.db.helper.DBHelper;
import vkc.db.serializer.VKCCategorySerializer;
import vkc.db.serializer.VKCDocumentClassSerializer;
import vkc.db.serializer.VKCDocumentSerializer;
import vkc.db.serializer.VKCDocumentStatusSerializer;
import vkc.db.serializer.VKCDocumentTypeSerializer;
import vkc.db.serializer.VKCKeywordSerializer;
import vkc.db.serializer.VKCLanguagePackSerializer;
import vkc.db.serializer.VKCLanguageSerializer;
import vkc.db.serializer.VKCSysParameterSerializer;
import vkc.db.serializer.VKCTemplateSerializer;
import vkc.db.serializer.VKCUserSerializer;

import vkc.exception.AclException;
import vkc.exception.DBException;

import vkc.misc.VKCLogManager;
import vkc.procoba.project.entities.VKCProject;
import vkc.procoba.project.entities.VKCProjectSubfolder;

import vkc.search.Search;
import vkc.security.PermissionManager;

import vkc.server.archive.DataStoreException;

import vkc.util.CategoryUtils;
import vkc.util.DocumentUtils;
import vkc.util.ProjectUtils;
import vkc.view.bean.VKCQuotaInfo;

import vkc.util.sort.DocumentSortFactory;
import vkc.util.treeview.TreeView;
import vkc.util.treeview.TreeViewItem;
import vkc.util.treeview.TreeViewManager;

import vkc.view.WebShare;
import vkc.view.bean.UserInfo;

import java.io.IOException;

import java.sql.PreparedStatement;
import java.sql.Timestamp;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

/**
 * Implementation der VKCDatenbank für die <i>allgemeine</i> Datenbank
 *
 * @author koppy
 * @version 1.0
 *
 * @see VKCDatabase
 */
public class GenericDBImpl implements VKCDatabase {
    private static long structureChangedAt = 0;

    private static GenericDBImpl instance = null;
    private static GenericObjectPool genericPool = null;

    private DataSource dataSource = null;

    private String dbUrl = null;
    private String driverClass = null;

    /*    
     * Konstruktor
     */
    private GenericDBImpl(Properties properties) throws Exception {
        dbUrl = properties.getProperty("vkc.db.url");
        driverClass = properties.getProperty("vkc.db.driver_class");
        WebShare.database = this;

        dataSource = buildDataSource(properties);

        VKCDocumentTypeSerializer.initDocumentTypes();
        VKCDocumentClassSerializer.initDocumentClasses();
        VKCDocumentStatusSerializer.initDocumentStati();
        VKCLanguagePackSerializer.initLanguagePacks();
        VKCLanguageSerializer.initLanguages();
        VKCTemplateSerializer.initTemplates();
    }

    /**
     * Gibt DIE Instanz der GenericDB zurück
     *
     * @return DOCUMENT ME!
     *
     * @throws DBException
     *             DOCUMENT ME!
     */
    public static synchronized VKCDatabase getInstance(Properties properties) throws DBException {
        if (instance != null) {
            return instance;
        }

        try {
            return instance = new GenericDBImpl(properties);
        } catch ( Exception e ) {
            VKCLogManager.errorLog(GenericDBImpl.class, "1", "Database implementation could not be instantiated!", e);
            throw new DBException(e);
        }
    }

    /**
     * Erzeugt über DBCP einen Connection-Pool
     *
     * @param props
     * @return
     */
    public static DataSource buildDataSource(Properties props) {
        String driverClassName = props.getProperty("vkc.db.driver_class");

        String connectURL = props.getProperty("vkc.db.url");
        String user = props.getProperty("vkc.db.username");
        String passwd = props.getProperty("vkc.db.password");
        String isolation = props.getProperty("vkc.db.isolation");
        String poolMaxSize = props.getProperty("vkc.db.pool.maxSize", "-1");
        String prepStmtMaxSize = props.getProperty("vkc.db.pool.prepStmtMaxSize", "500");
        String poolValidationQuery = props.getProperty("vkc.db.pool.validationQuery", null);
        String poolTestOnBorrow = props.getProperty("vkc.db.pool.testOnBorrow", "false");
        String poolTestOnReturn = props.getProperty("vkc.db.pool.testOnReturn", "false");
        String poolTestWhileIdle = props.getProperty("vkc.db.pool.testWhileIdle", "false");
        String poolTimeBetweenEvictionRunsMillis = props.getProperty("vkc.db.pool.timeBetweenEvictionRunsMillis", "5000");
        int maxConnections = -1;

        Driver driver = null;
        Class driverClass = null;

        try {
            driverClass = Class.forName(driverClassName);
        } catch (ClassNotFoundException e) {
            VKCLogManager.errorLog(GenericDBImpl.class, "1", "Can't load database driver!", e);
        }

        try {
            driver = (Driver) driverClass.newInstance();
        } catch (Exception e) {
            VKCLogManager.errorLog(GenericDBImpl.class, "2", "Can't load database driver!", e);
        }

        try {
            maxConnections = Integer.parseInt(poolMaxSize);
        } catch (NumberFormatException e) {
            VKCLogManager.errorLog(GenericDBImpl.class, "3", "Can't read poolMaxSize!", e);
        }

        Properties conProps = new Properties();
        conProps.setProperty("user", user);
        conProps.setProperty("password", passwd);

        try {
            if (driver.acceptsURL(connectURL) == false) {
                VKCLogManager.errorLog(GenericDBImpl.class, "4", "The URL \""
                        + connectURL + "\" is invalid for the JDBC driver class "
                        + driverClass.getName() + ".");
            }
        } catch (SQLException e) {
            VKCLogManager.errorLog(GenericDBImpl.class, "5", "The URL \""
                    + connectURL + "\" is invalid for the JDBC driver class "
                    + driverClass.getName() + ".", e);
        }

        DriverConnectionFactory connFactory = new DriverConnectionFactory(driver, connectURL, conProps);

        genericPool = new GenericObjectPool(null, maxConnections);

        genericPool.setTestOnBorrow(false);
        genericPool.setTestOnReturn(false);
        genericPool.setTestWhileIdle(false);

        if (poolValidationQuery != null) {
            // check, if connection is alive on borrow
            genericPool.setTestOnBorrow(poolTestOnBorrow.toLowerCase().equals("true"));

            // check, if connection is alive on return
            genericPool.setTestOnReturn(poolTestOnReturn.toLowerCase().equals("true"));

            // check, if connection is alive when idle
            genericPool.setTestWhileIdle(poolTestWhileIdle.toLowerCase().equals("true"));

            if (genericPool.getTestWhileIdle()) {
                long timeBetweenEvictionRunsMillis = 5000;

                try {
                    timeBetweenEvictionRunsMillis = Long.parseLong(poolTimeBetweenEvictionRunsMillis);
                } catch (NumberFormatException e) {
                    VKCLogManager.errorLog(GenericDBImpl.class, "6", "Can't read poolTimeBetweenEvictionRunsMillis!", e);
                }

                genericPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            }
        }

        genericPool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);

        //PreparedStatement-Pool-Factory erzeugen
        StackKeyedObjectPoolFactory keyedObjectPoolFactory = null;

        try {
            keyedObjectPoolFactory = new StackKeyedObjectPoolFactory(Integer.parseInt(prepStmtMaxSize));
        } catch (NumberFormatException e) {
            VKCLogManager.errorLog(GenericDBImpl.class, "7", "Can't read prepStmtMaxSize!", e);
            keyedObjectPoolFactory = new StackKeyedObjectPoolFactory();
        }

        if (isolation == null) {
            new PoolableConnectionFactory(connFactory, genericPool, keyedObjectPoolFactory, poolValidationQuery, false, false);
        } else {
            int isolationLevel;

            try {
                isolationLevel = Integer.parseInt(isolation);
                new PoolableConnectionFactory(connFactory, genericPool, keyedObjectPoolFactory, poolValidationQuery, false, false, isolationLevel);
            } catch (NumberFormatException e) {
                VKCLogManager.errorLog(GenericDBImpl.class, "8", "Can't read isolation level!", e);
                new PoolableConnectionFactory(connFactory, genericPool, keyedObjectPoolFactory, poolValidationQuery, false, false);
            }
        }

        PoolingDataSource dataSource = new PoolingDataSource(genericPool);

        return dataSource;
    }

    public DataSource getDataSource(){
        return dataSource;
    }

    public String getDbUrl(){
        return dbUrl;
    }

    public String getDriverClass(){
        return driverClass;
    }

    public Connection getConnection() throws DBException {
        try {
            Connection conn = dataSource.getConnection();

            //TODO Im Produktivsystem Debug-Ausgabe entfernen!
            //System.out.println( "connection-pool-size: (" + genericPool.getNumActive() + " active) (" + genericPool.getNumIdle() + " idle)" );

            return conn;
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "1", "Error while getting connection", e);
            throw new DBException(e);
        }
    }

    public void buildTree(TreeView tree) throws DBException {
        Connection conn = null;
        PreparedStatement pStmt = null;
        PreparedStatement pSubCatsStmt = null;
        ResultSet rs = null;

        try {
            conn = getConnection();

            pSubCatsStmt = conn.prepareStatement(
                    "SELECT Categories.categoryId, Categories.name FROM Categories " +
                    "WHERE parentCategoryId = ? order by name asc");

            // Get the root category
            pStmt = conn.prepareStatement("SELECT Categories.name, Categories.aclId FROM Categories WHERE categoryId = " + VKCCategory.ROOT_CATEGORY_ID);
            rs = pStmt.executeQuery();

            rs.next();

            TreeViewItem rootNode = new TreeViewItem(VKCCategory.ROOT_CATEGORY_ID + "", null, rs.getString("name"), null);
            tree.setRootNode(rootNode);
            DBHelper.close(rs);
            rs = null;

            // Iterate over all categories
            List tmpCatIds = new ArrayList();
            List tmpNodes = new ArrayList();
            tmpCatIds.add(new Integer(VKCCategory.ROOT_CATEGORY_ID));
            tmpNodes.add(rootNode);

            Integer categoryId;

            while (!tmpCatIds.isEmpty()) {
                categoryId = (Integer) tmpCatIds.remove(0);
                rootNode = (TreeViewItem) tmpNodes.remove(0);

                DBHelper.close(rs);
                // Subcategories
                pSubCatsStmt.setInt(1, categoryId.intValue());
                rs = pSubCatsStmt.executeQuery();

                while (rs.next()) {
                    int subCatId = rs.getInt("categoryId");
                    String subCatName = rs.getString("name");

                    TreeViewItem childNode = new TreeViewItem(subCatId + "", null, subCatName, null);

                    // Add as child
                    tmpCatIds.add(new Integer(subCatId));
                    tmpNodes.add(childNode);
                    rootNode.addChild(childNode);
                }
            }
        } catch (Exception e) {
            throw new DBException(e);
        } finally {
            DBHelper.close(rs);
            DBHelper.close(pStmt);
            DBHelper.close(pSubCatsStmt);
            DBHelper.close(conn);
        }
    }

    /**
     * @see vkc.db.VKCDatabase#move(vkc.db.entities.VKCCategory, java.util.Set,
     *      java.util.Set, vkc.db.entities.VKCCategory)
     */
    public void move(VKCCategory source, VKCCategory target, Set categoryIds, Set documentIds, VKCUser user) throws DBException {
        Connection conn = null;

        try {
        	boolean moveAsProject = target.getCategoryId().intValue() == VKCSysParameterSerializer.getProjectsRootCategoryId();
            boolean moveAsProjectSubFolder = target instanceof VKCProject || target instanceof VKCProjectSubfolder;
            conn = getConnection();

            // if move comes from trash, we have to change flags: 
            // for categories from FLAG_DELETED to FLAG_ACTIVE 
            // and documents from FLAG_DELETED to FLAG_PUBLISHED 
            boolean recoverFromTrash = CategoryUtils.isInTrash(source.getCategoryId());
            
            // Kategorien umhängen
            if (categoryIds != null && !categoryIds.isEmpty()) {
                structureChangedAt = System.currentTimeMillis();

                PreparedStatement pStmt = conn.prepareStatement("UPDATE Categories SET parentCategoryId = ? WHERE categoryId = ?");

                PreparedStatement nameStmt = conn.prepareStatement("SELECT Name FROM Categories WHERE categoryId = ?");
                PreparedStatement updateNameStmt = conn.prepareStatement("UPDATE Categories SET Name = ? WHERE categoryId = ?");

                for (Iterator it = categoryIds.iterator(); it.hasNext();) {
                    Integer id = (Integer) it.next();

                    // In eine "ausgeschnittene" Kategorie kann nicht eingefügt werden
                    if (id.equals(target.getCategoryId())) {
                        it.remove();

                        continue;
                    }
                    
                    nameStmt.setInt(1, id.intValue());

                    ResultSet rs = nameStmt.executeQuery();

                    if (rs.next()) {
                        // Den Kategorienamen ggf. umbennenen
                        String name = rs.getString("Name");
                        String newName = name;
                        int count = 1;
                        
                        while (CategoryUtils.isContained(newName, target.getSubCategories())) {
                            newName = name + " (" + (count++) + ")";
                        }

                        if (!newName.equals(name)) {
                            // Namen aktualisieren
                            updateNameStmt.setString(1, newName);
                            updateNameStmt.setInt(2, id.intValue());
                            updateNameStmt.execute();
                        }
                    }

                    rs.close();

                    pStmt.setInt(1, target.getCategoryId().intValue());
                    pStmt.setInt(2, id.intValue());
                    pStmt.execute();
                    
                    if (recoverFromTrash) {
                    	Date now = new Date(System.currentTimeMillis());
                    	Set docIds = DocumentUtils.getAllDocumentIdsRecursiv(id, VKCDocument.FLAG_DELETED, conn);
                        VKCCategorySerializer.setFlag(id, VKCCategory.FLAG_ACTIVE, VKCDocument.FLAG_PUBLISHED, true, conn);
                        
                        for (Iterator iter = docIds.iterator(); iter.hasNext();) {
							Integer docId = (Integer) iter.next();
							VKCDocument doc = VKCDocumentSerializer.retrieveDocument(docId, conn);
							
							if (doc.getPublished().before(now)) {
								Search.addDocument(doc, true);
							} else {
								doc.setFlag(VKCDocument.FLAG_TOBEPUBLISHED);
								VKCDocumentSerializer.saveDocument(doc, conn, false);
							}
						}
                    }
                    
                    if (moveAsProject) {
                    	ProjectUtils.convertToProject(id, conn);
                    } else if (moveAsProjectSubFolder) {
                    	if (target instanceof VKCProject) {
                        	ProjectUtils.convertToProjectSubfolder(id, target.getCategoryId(), conn);
                    	} else {
                        	ProjectUtils.convertToProjectSubfolder(id, ((VKCProjectSubfolder)target).getProjectId(), conn);
                    	}
                    }
                }


                DBHelper.close(pStmt);
                DBHelper.close(nameStmt);
                DBHelper.close(updateNameStmt);
            }

            // Dokumente umhängen
            if (documentIds != null && !documentIds.isEmpty()) {
                PreparedStatement pStmt = conn.prepareStatement("UPDATE CategoryDocuments SET categoryId = ? WHERE documentId = ?");
                
                for (Iterator it = documentIds.iterator(); it.hasNext();) {
                    Integer documentId = (Integer) it.next();
                    
                    pStmt.setInt(1, target.getCategoryId().intValue());
                    pStmt.setInt(2,  documentId.intValue());
                    pStmt.execute();                
                    
                    if (recoverFromTrash) {
                    	Date now = new Date(System.currentTimeMillis());
						VKCDocument doc = VKCDocumentSerializer.retrieveDocument(documentId, conn);
						
						if (doc.getPublished().before(now)) {
		                    VKCDocumentSerializer.setFlag(documentId, VKCDocument.FLAG_PUBLISHED, true, conn);
							doc.setCategoryId(target.getCategoryId());
		                    doc.setFlag(VKCDocument.FLAG_PUBLISHED);
		                    Search.addDocument(doc, true);
						} else {
							doc.setCategoryId(target.getCategoryId());
							doc.setFlag(VKCDocument.FLAG_TOBEPUBLISHED);
							VKCDocumentSerializer.saveDocument(doc, conn, false);
						}
                    }
                }
                
                DBHelper.close(pStmt);
            }

            //Search-Index aktualisieren
            if (documentIds != null && !documentIds.isEmpty() && !recoverFromTrash) {
                for (Iterator it = documentIds.iterator(); it.hasNext();) {
                    Search.updateDocument(VKCDocumentSerializer.retrieveDocument((Integer) it.next(), conn), true);
                }
            }

            //Classify-Index aktualisieren
            WebShare.classifier.updateCategory(VKCCategorySerializer.retrieveCategory(source.getCategoryId(), conn)); // Source
            WebShare.classifier.updateCategory(VKCCategorySerializer.retrieveCategory(target.getCategoryId(), conn)); // Target
            VKCKeywordSerializer.saveKeywords(conn, source.getKeywords(), "CategoryKeywords", "categoryId", source.getCategoryId());
            VKCKeywordSerializer.saveKeywords(conn, target.getKeywords(), "CategoryKeywords", "categoryId", target.getCategoryId());

            conn.commit();

            TreeViewManager.getInstance().move(categoryIds, target.getCategoryId().toString());
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "2", "move-Operation could not be performed!", e);
            DBHelper.rollBack(conn);
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }

    /**
     * @see vkc.db.VKCDatabase#copy(vkc.db.entities.VKCCategory, java.util.Set,
     *      java.util.Set, vkc.db.entities.VKCCategory)
     */
    public void copy(VKCCategory target, Set categoryIds, Set documentIds, VKCUser user) throws DBException {
        if (target == null || user == null) {
            return;
        }

        Connection conn = null;

        try {
            conn = getConnection();

            Set accessibleCategoryIds = CategoryUtils.getReadableCategoryIds(user);

            // Kategorien rekursiv incl. Doks kopieren
            List copiedCats = new ArrayList();

            if (categoryIds != null && !categoryIds.isEmpty()) {
                for (Iterator it = categoryIds.iterator(); it.hasNext();) {
                    Integer catId = (Integer) it.next();

                    if (accessibleCategoryIds.contains(catId)) {
                        VKCCategory c = copyCategory(target, conn, catId, null, true, user);

                        if (c != null) {
                            copiedCats.add(c);
                        }
                    }
                }
            }

            VKCCategory category;

            while (!copiedCats.isEmpty()) {
                category = (VKCCategory) copiedCats.remove(0);

                Collection subCats = category.getSubCategories();

                for (Iterator it = subCats.iterator(); it.hasNext();) {
                    VKCSubCategory subCat = (VKCSubCategory) it.next();

                    if (accessibleCategoryIds.contains(subCat.getSubCategoryId())) {
                        VKCCategory c = copyCategory(category, conn, subCat.getSubCategoryId(), null, false, user);

                        if (c != null) {
                            copiedCats.add(c);
                        }
                    }
                }
            }

            // Dokumente kopieren
            if (documentIds != null && !documentIds.isEmpty()) {
                Integer[] docIds = (Integer[])documentIds.toArray(new Integer[0]);
                
                if (docIds.length == 1) {
                    copyDocument(conn, target, docIds[0], null, true, user);
                } else {
                	docIds = VKCDocumentSerializer.getDocumentIdsAscendingByPublishingDate(docIds, conn);
                	
                	for (int i = 0; i < docIds.length; i++) {
                        copyDocument(conn, target, docIds[i], null, (i + 1) == docIds.length, user);
					}
                }
            }

            conn.commit();
        } catch (Exception e) {
            if (!QUOTA_EXCEEDED.equals(e.getMessage())) {
                VKCLogManager.errorLog(this, "2", "copy-Operation could not be performed!", e);
            }

            DBHelper.rollBack(conn);
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }

    /**
     * @see vkc.db.VKCDatabase#delete(vkc.db.entities.VKCCategory,
     *      java.util.Set, java.util.Set)
     */
    public void delete(Integer sourceCategoryId, Set categoryIds, Set documentIds, VKCUser deletor) throws DBException {
        Connection conn = null;

        try {
            conn = getConnection();
            Integer userTrashCategoryId = null;

            // if this operation is done inside trash, then delete definitely
            boolean definite = CategoryUtils.isInTrash(sourceCategoryId);
            
            // if definite is false, check weather Trash is enabled
            if (definite == false) {
                if (WebShare.isTrashEnabled()) {
                    // create a common trash category for this user
                    userTrashCategoryId = CategoryUtils.createUserTrashCategory(deletor, conn).getCategoryId();
                } else {
                    // trash is not enabled, delete all things definitely
                    definite = true;
                }
            }
             
            // delete all given categories
            if (categoryIds != null && !categoryIds.isEmpty()) {
                Iterator catIter = categoryIds.iterator();

                while (catIter.hasNext()) {
                    VKCCategorySerializer.deleteCategory(deletor, (Integer)catIter.next(), definite, userTrashCategoryId, conn);
                }
            }

            // delete all given documents
            if (documentIds != null && !documentIds.isEmpty()) {
                Iterator docIter = documentIds.iterator();
                int docCount = 0;

                while (docIter.hasNext()) {
                    VKCDocumentSerializer.deleteDocument(deletor, (Integer)docIter.next(), definite, userTrashCategoryId, ++docCount == documentIds.size(), conn);
                }
            }

            conn.commit();
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "2", "delete-Operation could not be performed!", e);
            DBHelper.rollBack(conn);
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }

    private Vector copyDynaAttributes(Vector dynAttrs) {
        Vector newDynAttrs = new Vector();

        for (int i = 0; i < dynAttrs.size(); i++) {
            VKCDynaAttribute a = (VKCDynaAttribute)dynAttrs.get(i);
            VKCDynaAttribute aNew = new VKCDynaAttribute();

            aNew.setDynaAttrId(new Integer(-(i + 1)));
            aNew.setDynaEntryId(new Integer(-(i + 1)));
            aNew.setName(a.getName());
            aNew.setValue(a.getValue());

            newDynAttrs.add(aNew);
        }

        return newDynAttrs;
    }

    /**
     * @see vkc.db.VKCDatabase#copyCategory(vkc.db.entities.VKCCategory, Integer, String)
     */
    public VKCCategory copyCategory(VKCCategory target,
                                    Integer catId,
                                    String newName,
                                    boolean rekursiv,
                                    VKCUser user)
    throws DBException {
        Connection conn = null;

        try {
            conn = getConnection();
            VKCCategory newCategory = copyCategory(conn, target, catId, newName, rekursiv, user);
            conn.commit();

            return newCategory;
        } catch (Exception e) {
            DBHelper.rollBack(conn);
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }

    public VKCCategory copyCategory(Connection conn,
                                    VKCCategory target,
                                    Integer catId,
                                    String  newName,
                                    boolean rekursiv,
                                    VKCUser user)
    throws DBException {
        try {
            VKCCategory category = copyCategory(target, conn, catId, newName, true, user);

            if (rekursiv && category != null) {
                List copiedCats = new ArrayList();
                copiedCats.add(category);

                while (!copiedCats.isEmpty()) {
                    VKCCategory tmpCat = (VKCCategory) copiedCats.remove(0);
                    Collection subCats = tmpCat.getSubCategories();

                    for (Iterator it = subCats.iterator(); it.hasNext();) {
                        VKCSubCategory subCat = (VKCSubCategory) it.next();
                        VKCCategory c = copyCategory(tmpCat, conn, subCat.getSubCategoryId(), null, false, user);

                        if (c != null) {
                            copiedCats.add(c);
                        }
                    }
                }
            }
            
            return category;
        } catch (Exception ex) {
            // Mögliche Änderungen am Treeview zurücksetzen
            TreeViewManager.getInstance().requestUpdate();
            throw new DBException(ex);
        }
    }

    /**
     * @param target
     * @param conn
     * @param catId
     *
     * @return die neu Kategorie
     * @throws HibernateException
     * @throws SQLException
     * @throws DBException
     * @throws AclException
     */
    private VKCCategory copyCategory(VKCCategory target,
                                     Connection conn,
                                     Integer catId,
                                     String newName,
                                     boolean lookForDuplicateEntries,
                                     VKCUser user)
    throws SQLException, DBException, AclException {
    	boolean copyAsProject = target.getCategoryId().intValue() == VKCSysParameterSerializer.getProjectsRootCategoryId();
        boolean copyAsProjectSubFolder = target instanceof VKCProject || target instanceof VKCProjectSubfolder;
    	VKCCategory cat = VKCCategorySerializer.retrieveCategory(catId, conn);
        VKCSearchQuery query = VKCCategorySerializer.retrieveSearchQuery(cat.getCategoryId(), conn);

        if (copyAsProject) {
        	if (!(cat instanceof VKCProject)) {
            	cat = convertToProject(cat);
        	}
        } else if (copyAsProjectSubFolder) {
        	if (!(cat instanceof VKCProjectSubfolder)) {
            	cat = convertToProjectSubFolder(cat);
        	}

        	if (target instanceof VKCProject) {
                ((VKCProjectSubfolder) cat).setProjectId(target.getCategoryId());
            } else {
                ((VKCProjectSubfolder) cat).setProjectId(((VKCProjectSubfolder) target).getProjectId());
            }
        }
        
        
        if (lookForDuplicateEntries) {
            if (newName == null) {
                if (CategoryUtils.isContained(cat.getName(), target.getSubCategories())) {
                    return null;
                }
            } else {
                String tmp = new String(newName);

                for (int i = 1; CategoryUtils.isContained(newName, target.getSubCategories()); i++) {
                    newName = tmp + " (" + i + ")";
                }
            }
        }

        PermissionManager.setCategoryOwnerPermissions(new Integer(VKCAclType.USER_TYPE), user.getUserId(), user.getUserId(), cat.getAcl());
        cat.getAcl().setAclId(null);
        cat.setCategoryId(new Integer(-1));
        cat.setParentCategoryId(target.getCategoryId());
        cat.setLastChanged(new Timestamp(System.currentTimeMillis()));

        if (newName != null) {
            cat.setName(newName);
        }

        // Dynamische Attribute kopieren
        cat.setDynaAttrsId(null);
        cat.setDynaAttributes(copyDynaAttributes(new Vector(cat.getDynaAttributes())));

        VKCCategorySerializer.saveCategory(cat, conn);
        VKCCategorySerializer.saveSearchQuery(query, cat.getCategoryId(), conn);
        DocumentSortFactory.sort(cat.getDocuments(), DocumentSortFactory.SORT_BY_PUBLISH_DATE_UP);
        
        List documents = cat.getDocuments();
        cat.setDocuments(new ArrayList());

        for (int i = 0; i < documents.size(); i++) {
        	VKCDocument doc = (VKCDocument)documents.get(i);
        	boolean updateCategory = (i + 1) == documents.size() ? true : false;
            copyDocument(conn, cat, doc.getDocumentId(), null, updateCategory, user);
        }

        return cat;
    }

    private VKCProjectSubfolder convertToProjectSubFolder(VKCCategory cat) throws DBException {
		VKCProjectSubfolder projectSubfolder = new VKCProjectSubfolder();
		projectSubfolder.setAbsPathDescr(cat.getAbsPathDescr());
		projectSubfolder.setAccessCount(cat.getAccessCount());
		projectSubfolder.setAcl(cat.getAcl());
		projectSubfolder.setAddAttributes(cat.getAddAttributes());
		projectSubfolder.setAllowedDocumentTypes(cat.getAllowedDocumentTypes());
		projectSubfolder.setCategoryId(cat.getCategoryId());
		projectSubfolder.setCreated(cat.getCreated());
		projectSubfolder.setCreatorId(cat.getCreatorId());
		projectSubfolder.setDescription(cat.getDescription());
		projectSubfolder.setDocuments(cat.getDocuments());
		projectSubfolder.setDynaAttributes(cat.getDynaAttributes());
		projectSubfolder.setDynaAttrsId(cat.getDynaAttrsId());
		projectSubfolder.setFlag(cat.getFlag());
		projectSubfolder.setKeywords(cat.getKeywords());
		projectSubfolder.setKeywordsByString(cat.getKeywordsAsString());
		projectSubfolder.setLastAccessed(cat.getLastAccessed());
		projectSubfolder.setLastAccessorId(cat.getLastAccessorId());
		projectSubfolder.setLastChanged(cat.getLastChanged());
		projectSubfolder.setLastChangerId(cat.getLastChangerId());
		projectSubfolder.setName(cat.getName());
		projectSubfolder.setParentCategoryId(cat.getParentCategoryId());
		projectSubfolder.setShowDescription(cat.getShowDescription());
		projectSubfolder.setStatus(cat.getStatus());
		projectSubfolder.getSubCategories().addAll(cat.getSubCategories());
		
		return projectSubfolder;
	}

	private VKCProject convertToProject(VKCCategory cat) throws DBException {
		VKCProject project = new VKCProject();
		project.setAbsPathDescr(cat.getAbsPathDescr());
		project.setAccessCount(cat.getAccessCount());
		project.setAcl(cat.getAcl());
		project.setAddAttributes(cat.getAddAttributes());
		project.setAllowedDocumentTypes(cat.getAllowedDocumentTypes());
		project.setCategoryId(cat.getCategoryId());
		project.setCreated(cat.getCreated());
		project.setCreatorId(cat.getCreatorId());
		project.setDescription(cat.getDescription());
		project.setDocuments(cat.getDocuments());
		project.setDynaAttributes(cat.getDynaAttributes());
		project.setDynaAttrsId(cat.getDynaAttrsId());
		project.setFlag(cat.getFlag());
		project.setKeywords(cat.getKeywords());
		project.setKeywordsByString(cat.getKeywordsAsString());
		project.setLastAccessed(cat.getLastAccessed());
		project.setLastAccessorId(cat.getLastAccessorId());
		project.setLastChanged(cat.getLastChanged());
		project.setLastChangerId(cat.getLastChangerId());
		project.setName(cat.getName());
		project.setParentCategoryId(cat.getParentCategoryId());
		project.setShowDescription(cat.getShowDescription());
		project.setStatus(cat.getStatus());
		project.getSubCategories().addAll(cat.getSubCategories());
		
		return project;
	}

	public void copyDocument(VKCCategory target, Integer docId, String newName, VKCUser user) throws DBException {
        Connection conn = null;

        try {
            conn = getConnection();
            copyDocument(conn, target, docId, newName, true, user);
            conn.commit();
        } catch (Exception e) {
            DBHelper.rollBack(conn);
            // Mögliche Änderungen am Treeview zurücksetzen
            TreeViewManager.getInstance().requestUpdate();
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }

    /**
     * Kopiert ein Dokument in die Ziel-Kategorie
     *
     * @param target
     * @param conn
     * @param docId
     * @param newName Hier kann ein Name angegeben werden, wenn das Dokument gleichzeitig umbenannt werden soll.
     *
     * @throws DBException
     * @throws DataStoreException
     * @throws Exception
     */
    private void copyDocument(Connection conn,
                              VKCCategory target,
                              Integer docId,
                              String newName,
                              boolean updateSearchIndex,
                              VKCUser user)
    throws DBException {
        VKCDocument doc = VKCDocumentSerializer.retrieveDocument(docId);
        doc.setDocumentId(new Integer(-1));
        doc.setCategoryId(target.getCategoryId());
        doc.setPublished(new Date(System.currentTimeMillis()));
        doc.setLastChanged(new Date(System.currentTimeMillis()));
        doc.setPreDocId(null); // Kopiertes Dokument besitzt keine älteren Versionen
        doc.setVersionNo(new Integer(1));
        doc.setVersionHigh(new Integer(1));
        doc.setVersionLow(new Integer(0));
        doc.setVersionDescr("");
        doc.setAccessCount(new Integer(0));
        doc.setPublisherId(user.getUserId());

        //Dyn. Attribute kopieren
        doc.setDynaAttrsId(null);
        doc.setDynaAttributes(copyDynaAttributes(new Vector(doc.getDynaAttributes())));

        if (newName != null) {
            doc.setName(newName);
        }

        if (!"0".equals(doc.getArchiveId())) { // Abfrage wegen Links und Notes
            try {
                String archiveId = WebShare.archive.store(doc.getInputStream());
                doc.setArchiveId(archiveId);
            } catch (DataStoreException e) {
                throw new DBException(e);
            }
        }

        VKCDocumentSerializer.saveDocument(doc, conn, true);

        // Erzeugung der zusätzlichen Dokumentformate anstossen

        if (doc.getDocumentType().isConvertible()) {
            try {
                ConversionStatus s = new ConversionStatus();
                s.setDocumentId(doc.getDocumentId());
                s.makePersistent(conn);
            } catch (Exception e) {
                VKCLogManager.errorLog(this, "", e.getMessage(), e);
                throw new DBException(e);
            }
        }

        if (!target.getDocuments().contains(doc)) {
            target.addDocument(doc);
        }

        if (updateSearchIndex) {
            try {
                // Classify-Index aktualisieren
                WebShare.classifier.updateCategory(target);

                //Veränderte Keywords wegschreiben
                VKCKeywordSerializer.saveKeywords(conn, target.getKeywords(), "CategoryKeywords", "categoryId", target.getCategoryId());
            } catch (IOException ioe) {
                VKCLogManager.errorLog(this, "1", "Classifyindex could not be updated!", ioe);
            }
        }
    }

    public HashMap getQuotaInfos() throws DBException {
        Connection conn = null;
        List userInfos = new ArrayList();
        HashMap quotas = new HashMap();

        try {
            conn = getConnection();
            userInfos = VKCUserSerializer.retrieveUserInfos();

            for (int i = 0; i < userInfos.size(); i++) {
                UserInfo u = (UserInfo)userInfos.get(i);
                quotas.put(u.getUserId(), getQuotaInfo(u.getUserId(), conn));
            }

            return quotas;
        } catch (Exception e) {
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }

    public VKCQuotaInfo getQuotaInfo(Integer userId) throws DBException {
    	return getQuotaInfo(userId, false);
    }

    public VKCQuotaInfo getQuotaInfo(Integer userId, boolean force) throws DBException {
    	Connection conn = null;

        try {
            conn = getConnection();
            
            return getQuotaInfo(userId, force, conn);
        } catch (Exception e) {
            throw new DBException(e);
        } finally {
            DBHelper.close(conn);
        }
    }


    public VKCQuotaInfo getQuotaInfo(Integer userId, Connection conn) throws DBException {
    	return getQuotaInfo(userId, false, conn);
    }

    public VKCQuotaInfo getQuotaInfo(Integer userId, 
    								 boolean force, 
									 Connection conn)
    throws DBException {
        PreparedStatement pStmt1 = null;
        PreparedStatement pStmt2 = null;
        PreparedStatement pStmt3 = null;
        ResultSet rs1 = null;
        ResultSet rs2 = null;
        ResultSet rs3 = null;

        // do not compute quota info, if it is not wanted
        if (!VKCSysParameterSerializer.isQuotaCheckingEnabled() && !force) {
            return null;
        }

        try {
            VKCQuotaInfo quotaInfo = new VKCQuotaInfo();
            quotaInfo.setUserId(userId);

            // get quota
            pStmt1 = conn.prepareStatement("SELECT quota FROM users WHERE userId = ?");
            pStmt1.setInt(1, userId.intValue());
            rs1 = pStmt1.executeQuery();
            rs1.next();

            Integer quota = new Integer(rs1.getInt("quota"));
            
            if (rs1.wasNull()) { // check if quota is null
                quotaInfo.setQuota(null);
            } else {
                quotaInfo.setQuota(quota);
            }
            
            String statement = "SELECT SUM(docsize) AS SUM_DOCSIZE FROM documents WHERE publisherId = ?";
            
            if (WebShare.isTrashEnabled()) {
            	statement += " AND flag <> " + VKCDocument.FLAG_DELETED;
            }

            // count of all regular documents and its amount
            pStmt2 = conn.prepareStatement(statement);
            pStmt2.setInt(1, userId.intValue());
            rs2 = pStmt2.executeQuery();
            rs2.next();
            quotaInfo.setAmountPubDocs(rs2.getLong("SUM_DOCSIZE"));
            statement = 
                " SELECT SUM(additionaldocumentformats.docsize) AS SUM_ADDDOCSIZE" +
                " FROM additionaldocumentformats, documents" +
                " WHERE additionaldocumentformats.documentId = documents.documentId" +
                " AND documents.publisherId = ?";

            if (WebShare.isTrashEnabled()) {
            	statement += " AND documents.flag <> " + VKCDocument.FLAG_DELETED;
            }

            // all additional document formats
            pStmt3 = conn.prepareStatement(statement);
            pStmt3.setInt(1, userId.intValue());
            rs3 = pStmt3.executeQuery();
            rs3.next();
            quotaInfo.setAmountAddDocFormats(rs3.getLong("SUM_ADDDOCSIZE"));

            return quotaInfo;
        } catch (Exception e) {
            throw new DBException(e);
        } finally {
            DBHelper.close(rs1);
            DBHelper.close(rs2);
            DBHelper.close(rs3);
            DBHelper.close(pStmt1);
            DBHelper.close(pStmt2);
            DBHelper.close(pStmt3);
        }
    }

    /**
     * Gibt den Zeitpunkt zurück an dem die Struktur-Pfade zuletzt modifiziert
     * wurden.
     *
     * @return Zeitpunkt an dem die Struktur-Pfade zuletzt modifiziert wurden
     */
    public synchronized long getStructureChangedAt() {
        return structureChangedAt;
    }

    public synchronized void setStructureChangedAt(long time) {
        structureChangedAt = time;
    }
}

//class MyStackKeyedObjectPoolFactory extends StackKeyedObjectPoolFactory
//{
//
//	public MyStackKeyedObjectPoolFactory(int max) {
//        super((KeyedPoolableObjectFactory)null, max, 4);
//    }
//
//
//	public KeyedObjectPool createPool() {
//        return new MyStackKeyedObjectPool(_factory,_maxSleeping,_initCapacity);
//    }
//}
//class MyStackKeyedObjectPool extends StackKeyedObjectPool {
//	public MyStackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) {
//		super(factory, max, init);
//	}
//
//	public Object borrowObject(Object key) throws Exception {
//		Object back = super.borrowObject(key);
//		System.out.println("borrow prepared statement:" + back);
//
//		return back;
//	}
//}