org.opencms.db.jpa.CmsHistoryDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.opencms.db.jpa.CmsHistoryDriver.java

Source

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.db.jpa;

import org.opencms.configuration.CmsConfigurationManager;
import org.opencms.configuration.CmsParameterConfiguration;
import org.opencms.db.CmsDbConsistencyException;
import org.opencms.db.CmsDbContext;
import org.opencms.db.CmsDbEntryNotFoundException;
import org.opencms.db.CmsDbSqlException;
import org.opencms.db.CmsDriverManager;
import org.opencms.db.CmsResourceState;
import org.opencms.db.I_CmsDriver;
import org.opencms.db.I_CmsHistoryDriver;
import org.opencms.db.I_CmsVfsDriver;
import org.opencms.db.jpa.persistence.CmsDAOContents;
import org.opencms.db.jpa.persistence.CmsDAOHistoryPrincipals;
import org.opencms.db.jpa.persistence.CmsDAOHistoryProjectResources;
import org.opencms.db.jpa.persistence.CmsDAOHistoryProjects;
import org.opencms.db.jpa.persistence.CmsDAOHistoryProperties;
import org.opencms.db.jpa.persistence.CmsDAOHistoryPropertyDef;
import org.opencms.db.jpa.persistence.CmsDAOHistoryResources;
import org.opencms.db.jpa.persistence.CmsDAOHistoryStructure;
import org.opencms.file.CmsDataAccessException;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsFolder;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsPropertyDefinition.CmsPropertyType;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsUser;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.history.CmsHistoryFile;
import org.opencms.file.history.CmsHistoryFolder;
import org.opencms.file.history.CmsHistoryPrincipal;
import org.opencms.file.history.CmsHistoryProject;
import org.opencms.file.history.I_CmsHistoryResource;
import org.opencms.main.CmsLog;
import org.opencms.security.CmsOrganizationalUnit;
import org.opencms.security.I_CmsPrincipal;
import org.opencms.util.CmsDataTypeUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.apache.commons.logging.Log;

/**
 * JPA database server implementation of the history driver methods.<p>
 * 
 * @since 8.0.0 
 */
public class CmsHistoryDriver implements I_CmsDriver, I_CmsHistoryDriver {

    /** Query key. */
    private static final String C_CONTENT_HISTORY_DELETE = "C_CONTENT_HISTORY_DELETE";

    /** Query key. */
    private static final String C_CONTENT_PUBLISH_MAXTAG = "C_CONTENT_PUBLISH_MAXTAG";

    /** Query key. */
    private static final String C_FILES_READ_HISTORY_BYID = "C_FILES_READ_HISTORY_BYID";

    /** Query key. */
    private static final String C_HISTORY_EXISTS_RESOURCE = "C_HISTORY_EXISTS_RESOURCE";

    /** Query key. */
    private static final String C_HISTORY_PRINCIPAL_READ = "C_HISTORY_PRINCIPAL_READ";

    /** Query key. */
    private static final String C_HISTORY_READ_CONTENT = "C_HISTORY_READ_CONTENT";

    /** Query key. */
    private static final String C_HISTORY_READ_MAXTAG_FOR_VERSION = "C_HISTORY_READ_MAXTAG_FOR_VERSION";

    /** Query key. */
    private static final String C_HISTORY_READ_MIN_USED_TAG = "C_HISTORY_READ_MIN_USED_TAG";

    /** Query key. */
    private static final String C_PROJECTRESOURCES_HISTORY_READ = "C_PROJECTRESOURCES_HISTORY_READ";

    /** Query key. */
    private static final String C_PROJECTS_HISTORY_MAXTAG = "C_PROJECTS_HISTORY_MAXTAG";

    /** Query key. */
    private static final String C_PROJECTS_HISTORY_READ = "C_PROJECTS_HISTORY_READ";

    /** Query key. */
    private static final String C_PROJECTS_HISTORY_READ_ALL = "C_PROJECTS_HISTORY_READ_ALL";

    /** Query key. */
    private static final String C_PROJECTS_HISTORY_READ_BYID = "C_PROJECTS_HISTORY_READ_BYID";

    /** Query key. */
    private static final String C_PROJECTS_HISTORY_READ_TAG_FOR_DATE = "C_PROJECTS_HISTORY_READ_TAG_FOR_DATE";

    /** Query key. */
    private static final String C_PROPERTIES_HISTORY_DELETE = "C_PROPERTIES_HISTORY_DELETE";

    /** Query key. */
    private static final String C_PROPERTIES_HISTORY_READ_PUBTAG = "C_PROPERTIES_HISTORY_READ_PUBTAG";

    /** Query key. */
    private static final String C_PROPERTIES_HISTORY_READALL_RES = "C_PROPERTIES_HISTORY_READALL_RES";

    /** Query key. */
    private static final String C_PROPERTIES_HISTORY_READALL_STR = "C_PROPERTIES_HISTORY_READALL_STR";

    /** Query key. */
    private static final String C_PROPERTIES_READALL_COUNT = "C_PROPERTIES_READALL_COUNT";

    /** Query key. */
    private static final String C_PROPERTYDEF_DELETE_HISTORY = "C_PROPERTYDEF_DELETE_HISTORY";

    /** Query key. */
    private static final String C_PROPERTYDEF_READ_HISTORY = "C_PROPERTYDEF_READ_HISTORY";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_DELETE = "C_RESOURCES_HISTORY_DELETE";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_MAXTAG = "C_RESOURCES_HISTORY_MAXTAG";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_ALL_VERSIONS = "C_RESOURCES_HISTORY_READ_ALL_VERSIONS";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_BTW_VERSIONS = "C_RESOURCES_HISTORY_READ_BTW_VERSIONS";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_DELETED = "C_RESOURCES_HISTORY_READ_DELETED";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_DELETED_NAME = "C_RESOURCES_HISTORY_READ_DELETED_NAME";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_DELETED_NAME_RESTRICTED = "C_RESOURCES_HISTORY_READ_DELETED_NAME_RESTRICTED";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_DELETED_RESTRICTED = "C_RESOURCES_HISTORY_READ_DELETED_RESTRICTED";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_NEW_VERSIONS = "C_RESOURCES_HISTORY_READ_NEW_VERSIONS";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_OLD_VERSIONS = "C_RESOURCES_HISTORY_READ_OLD_VERSIONS";

    /** Query key. */
    private static final String C_RESOURCES_HISTORY_READ_VERSION = "C_RESOURCES_HISTORY_READ_VERSION";

    /** Query key. */
    private static final String C_RESOURCES_READ_MAX_PUBLISH_TAG = "C_RESOURCES_READ_MAX_PUBLISH_TAG";

    /** Query key. */
    private static final String C_STRUCTURE_HISTORY_DELETE = "C_STRUCTURE_HISTORY_DELETE";

    /** Query key. */
    private static final String C_STRUCTURE_HISTORY_MAXVER = "C_STRUCTURE_HISTORY_MAXVER";

    /** Query key. */
    private static final String C_STRUCTURE_HISTORY_MAXVER_BYTIME = "C_STRUCTURE_HISTORY_MAXVER_BYTIME";

    /** Query key. */
    private static final String C_STRUCTURE_HISTORY_READ_DELETED = "C_STRUCTURE_HISTORY_READ_DELETED";

    /** Query key. */
    private static final String C_STRUCTURE_HISTORY_READ_NOTDELETED = "C_STRUCTURE_HISTORY_READ_NOTDELETED";

    /** Query key. */
    private static final String C_STRUCTURE_HISTORY_READ_SUBRESOURCES = "C_STRUCTURE_HISTORY_READ_SUBRESOURCES";

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(org.opencms.db.jpa.CmsHistoryDriver.class);

    /** The driver manager instance. */
    protected CmsDriverManager m_driverManager;

    /** The SQL manager instance. */
    protected CmsSqlManager m_sqlManager;

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#createPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String, org.opencms.file.CmsPropertyDefinition.CmsPropertyType)
     */
    public CmsPropertyDefinition createPropertyDefinition(CmsDbContext dbc, String name, CmsPropertyType type)
            throws CmsDataAccessException {

        try {
            CmsDAOHistoryPropertyDef chpd = new CmsDAOHistoryPropertyDef();
            chpd.setPropertyDefId(new CmsUUID().toString());
            chpd.setPropertyDefName(name);
            chpd.setPropertyDefType(type.getMode());
            m_sqlManager.persist(dbc, chpd);
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE), e);
        }
        return readPropertyDefinition(dbc, name);
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#deleteEntries(org.opencms.db.CmsDbContext, org.opencms.file.history.I_CmsHistoryResource, int, long)
     */
    public int deleteEntries(CmsDbContext dbc, I_CmsHistoryResource resource, int versionsToKeep, long time)
            throws CmsDataAccessException {

        try {
            int maxVersion = -1;
            // get the maximal version number for this resource
            Query q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_MAXVER);
            q.setParameter(1, resource.getStructureId().toString());
            try {
                maxVersion = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }

            if (time >= 0) {
                int maxVersionByTime = -1;
                // get the maximal version to keep for this resource based on the time parameter
                q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_MAXVER_BYTIME);
                q.setParameter(1, resource.getStructureId().toString());
                q.setParameter(2, Long.valueOf(time));

                try {
                    maxVersionByTime = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
                } catch (NoResultException e) {
                    // do nothing
                }

                if (maxVersionByTime > 0) {
                    if (versionsToKeep < 0) {
                        versionsToKeep = (maxVersion - maxVersionByTime);
                    } else {
                        versionsToKeep = Math.min(versionsToKeep, (maxVersion - maxVersionByTime));
                    }
                }
            }

            if ((maxVersion - versionsToKeep) <= 0) {
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }

            // get the minimal structure publish tag to keep for this sibling
            int minStrPublishTagToKeep = -1;
            q = m_sqlManager.createQuery(dbc, C_HISTORY_READ_MAXTAG_FOR_VERSION);
            q.setParameter(1, resource.getStructureId().toString());
            q.setParameter(2, Integer.valueOf((1 + maxVersion) - versionsToKeep));
            try {
                minStrPublishTagToKeep = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }

            if (minStrPublishTagToKeep < 1) {
                // nothing to delete
                internalCleanup(dbc, resource);
                return 0;
            }
            minStrPublishTagToKeep++;

            // delete the properties
            q = m_sqlManager.createQuery(dbc, C_PROPERTIES_HISTORY_DELETE);
            q.setParameter(1, resource.getStructureId().toString());
            q.setParameter(2, Integer.valueOf(minStrPublishTagToKeep));
            @SuppressWarnings("unchecked")
            List<CmsDAOHistoryProperties> hisProps = q.getResultList();
            for (CmsDAOHistoryProperties hp : hisProps) {
                m_sqlManager.remove(dbc, hp);
            }

            // delete the structure entries
            q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_DELETE);
            q.setParameter(1, resource.getStructureId().toString());
            q.setParameter(2, Integer.valueOf(minStrPublishTagToKeep));
            @SuppressWarnings("unchecked")
            List<CmsDAOHistoryStructure> structureEntries = q.getResultList();
            int structureVersions = 0;
            for (CmsDAOHistoryStructure hs : structureEntries) {
                m_sqlManager.remove(dbc, hs);
                structureVersions++;
            }

            // get the minimal resource publish tag to keep, 
            // all entries with publish tag less than this will be deleted
            int minResPublishTagToKeep = -1;
            q = m_sqlManager.createQuery(dbc, C_HISTORY_READ_MIN_USED_TAG);
            q.setParameter(1, resource.getResourceId().toString());

            try {
                Object numObj = q.getSingleResult();
                if (numObj == null) {
                    minResPublishTagToKeep = Integer.MAX_VALUE;
                } else {
                    minResPublishTagToKeep = CmsDataTypeUtil.numberToInt((Number) numObj);
                }
            } catch (NoResultException e) {
                internalCleanup(dbc, resource);
                return structureVersions;
            }

            // delete the resource entries
            q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_DELETE);
            q.setParameter(1, resource.getResourceId().toString());
            q.setParameter(2, Integer.valueOf(minResPublishTagToKeep));
            int resourceVersions = 0;
            @SuppressWarnings("unchecked")
            List<CmsDAOHistoryResources> resourceEntries = q.getResultList();
            for (CmsDAOHistoryResources hr : resourceEntries) {
                m_sqlManager.remove(dbc, hr);
                resourceVersions++;
            }

            // delete the content entries
            q = m_sqlManager.createQuery(dbc, C_CONTENT_HISTORY_DELETE);
            q.setParameter(1, resource.getResourceId().toString());
            q.setParameter(2, Integer.valueOf(minResPublishTagToKeep));
            @SuppressWarnings("unchecked")
            List<CmsDAOContents> contentEntries = q.getResultList();
            for (CmsDAOContents c : contentEntries) {
                m_sqlManager.remove(dbc, c);
            }

            internalCleanup(dbc, resource);

            return Math.max(structureVersions, resourceVersions);
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE), e);
        }
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#deletePropertyDefinition(org.opencms.db.CmsDbContext, org.opencms.file.CmsPropertyDefinition)
     */
    public void deletePropertyDefinition(CmsDbContext dbc, CmsPropertyDefinition metadef)
            throws CmsDataAccessException {

        try {
            if ((internalCountProperties(dbc, metadef, CmsProject.ONLINE_PROJECT_ID) != 0)
                    || (internalCountProperties(dbc, metadef, CmsUUID.getOpenCmsUUID()) != 0)) { // HACK: to get an offline project

                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_ERROR_DELETING_PROPERTYDEF_1, metadef.getName()));
            }

            // delete the historical property definition
            Query q = m_sqlManager.createQuery(dbc, C_PROPERTYDEF_DELETE_HISTORY);
            q.setParameter(1, metadef.getId().toString());
            @SuppressWarnings("unchecked")
            List<CmsDAOHistoryPropertyDef> res = q.getResultList();
            for (CmsDAOHistoryPropertyDef hpd : res) {
                m_sqlManager.remove(dbc, hpd);
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(
                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, Messages.ERR_JPA_PERSITENCE), e);
        }
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#destroy()
     */
    public void destroy() throws Throwable {

        m_sqlManager = null;
        m_driverManager = null;

        if (CmsLog.INIT.isInfoEnabled()) {
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_DRIVER_1, getClass().getName()));
        }
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#getAllDeletedEntries(org.opencms.db.CmsDbContext)
     */
    public List<I_CmsHistoryResource> getAllDeletedEntries(CmsDbContext dbc) throws CmsDataAccessException {

        Query q = null;
        List<I_CmsHistoryResource> entries = new ArrayList<I_CmsHistoryResource>();
        try {
            // get all not-deleted historical entries that may come in question
            q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_READ_DELETED);
            @SuppressWarnings("unchecked")
            List<Object[]> res = q.getResultList();
            for (Object[] obj : res) {
                CmsUUID structureId = new CmsUUID((String) obj[0]);
                int version = CmsDataTypeUtil.numberToInt((Integer) obj[1]);
                entries.add(readResource(dbc, structureId, version));
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return entries;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#getAllNotDeletedEntries(CmsDbContext)
     */
    public List<I_CmsHistoryResource> getAllNotDeletedEntries(CmsDbContext dbc) throws CmsDataAccessException {

        List<I_CmsHistoryResource> entries = new ArrayList<I_CmsHistoryResource>();
        try {
            // get all not-deleted historical entries that may come in question
            Query q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_READ_NOTDELETED);
            @SuppressWarnings("unchecked")
            List<Object[]> res = q.getResultList();
            for (Object[] o : res) {
                CmsUUID structureId = new CmsUUID((String) o[0]);
                int version = CmsDataTypeUtil.numberToInt((Number) o[1]);
                entries.add(readResource(dbc, structureId, version));
            }
        } catch (PersistenceException e) {
            throw new CmsDbSqlException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return entries;
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#getSqlManager()
     */
    public org.opencms.db.CmsSqlManager getSqlManager() {

        return m_sqlManager;
    }

    /** 
     * @see org.opencms.db.I_CmsDriver#init(org.opencms.db.CmsDbContext, org.opencms.configuration.CmsConfigurationManager, java.util.List, org.opencms.db.CmsDriverManager)
     */
    public void init(CmsDbContext dbc, CmsConfigurationManager configurationManager, List<String> successiveDrivers,
            CmsDriverManager driverManager) {

        CmsParameterConfiguration config = configurationManager.getConfiguration();

        String poolUrl = config.get("db.history.pool");
        String classname = config.get("db.history.sqlmanager");

        m_sqlManager = initSqlManager(classname);

        m_driverManager = driverManager;

        if (CmsLog.INIT.isInfoEnabled()) {
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ASSIGNED_POOL_1, poolUrl));
        }

        if ((successiveDrivers != null) && !successiveDrivers.isEmpty()) {
            if (LOG.isWarnEnabled()) {
                LOG.warn(Messages.get().getBundle().key(Messages.LOG_SUCCESSIVE_DRIVERS_UNSUPPORTED_1,
                        getClass().getName()));
            }
        }

    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#initSqlManager(java.lang.String)
     */
    public CmsSqlManager initSqlManager(String classname) {

        return CmsSqlManager.getInstance(classname);
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readAllAvailableVersions(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
     */

    public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsUUID structureId)
            throws CmsDataAccessException {

        List<I_CmsHistoryResource> result = new ArrayList<I_CmsHistoryResource>();
        Query q = null;
        try {
            // get all direct versions (where the structure entry has been written)
            // sorted from the NEWEST to the OLDEST version (publish tag descendant)
            List<I_CmsHistoryResource> historyResources = new ArrayList<I_CmsHistoryResource>();
            q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_ALL_VERSIONS);
            q.setParameter(1, structureId.toString());
            @SuppressWarnings("unchecked")
            List<Object[]> res = q.getResultList();
            for (Object[] o : res) {
                historyResources.add(internalCreateResource(o));
            }
            res = null;

            if (!historyResources.isEmpty()) {
                // look for newer versions
                // this is the NEWEST version, with the HIGHEST publish tag
                I_CmsHistoryResource histRes = historyResources.get(0);

                // look for later resource entries

                q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_NEW_VERSIONS);
                q.setParameter(1, histRes.getResourceId().toString());
                q.setParameter(2, Integer.valueOf(histRes.getPublishTag()));
                @SuppressWarnings("unchecked")
                List<CmsDAOHistoryResources> lResources = q.getResultList();

                I_CmsHistoryResource lastHistRes = histRes;
                // these are sorted from the oldest to the newest version (publish tag ascendent)
                for (CmsDAOHistoryResources hr : lResources) {
                    int resVersion = hr.getResourceVersion();
                    if (resVersion == lastHistRes.getResourceVersion()) {
                        // skip not interesting versions
                        continue;
                    }
                    I_CmsHistoryResource newHistRes = internalMergeResource(histRes, hr, 0);
                    // add interesting versions, in the right order
                    result.add(0, newHistRes);
                    lastHistRes = newHistRes;
                }
            }
            // iterate from the NEWEST to the OLDEST versions (publish tag descendant)
            for (int i = 0; i < historyResources.size(); i++) {
                I_CmsHistoryResource histRes = historyResources.get(i);
                result.add(histRes);
                if (i < (historyResources.size() - 1)) {
                    // this is one older direct version than histRes (histRes.getPublishTag() > histRes2.getPublishTag())
                    I_CmsHistoryResource histRes2 = historyResources.get(i + 1);

                    // look for resource changes in between of the direct versions in ascendent order                    
                    q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_BTW_VERSIONS);
                    q.setParameter(1, histRes.getResourceId().toString());
                    q.setParameter(2, Integer.valueOf(histRes2.getPublishTag())); // lower limit
                    q.setParameter(3, Integer.valueOf(histRes.getPublishTag())); // upper limit
                    @SuppressWarnings("unchecked")
                    List<CmsDAOHistoryResources> lResources = q.getResultList();

                    int pos = result.size();
                    I_CmsHistoryResource lastHistRes = histRes2;
                    for (CmsDAOHistoryResources hr : lResources) {
                        int resVersion = hr.getResourceVersion();
                        if (resVersion == lastHistRes.getResourceVersion()) {
                            // skip not interesting versions
                            continue;
                        }
                        I_CmsHistoryResource newHistRes = internalMergeResource(histRes2, hr, 0);
                        // add interesting versions, in the right order
                        result.add(pos, newHistRes);
                        lastHistRes = newHistRes;
                    }
                    lResources = null;
                }
            }
            if (!result.isEmpty()) {
                // get the oldest version
                I_CmsHistoryResource histRes = result.get(result.size() - 1);

                if (histRes.getVersion() > 1) {
                    // look for older resource versions, in descendant order
                    q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_OLD_VERSIONS);
                    q.setParameter(1, String.valueOf(histRes.getResourceId()));
                    q.setParameter(2, Integer.valueOf(histRes.getPublishTag()));
                    @SuppressWarnings("unchecked")
                    List<CmsDAOHistoryResources> lResources = q.getResultList();

                    int offset = (histRes.getStructureVersion() > 0 ? 1 : 0);

                    I_CmsHistoryResource lastHistRes = histRes;
                    for (CmsDAOHistoryResources hr : lResources) {
                        I_CmsHistoryResource newHistRes = internalMergeResource(histRes, hr, offset);
                        if (newHistRes.getResourceVersion() != lastHistRes.getResourceVersion()) {
                            // only add interesting versions
                            if (offset == 1) {
                                if (histRes != lastHistRes) {
                                    result.add(lastHistRes);
                                }
                            } else {
                                result.add(newHistRes);
                            }
                        }
                        lastHistRes = newHistRes;
                    }
                    // add the last one if there is one
                    if ((offset == 1) && (lastHistRes != histRes)) {
                        result.add(lastHistRes);
                    }
                }
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return result;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
     */
    public byte[] readContent(CmsDbContext dbc, CmsUUID resourceId, int publishTag) throws CmsDataAccessException {

        byte[] content = null;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_HISTORY_READ_CONTENT);
            q.setParameter(1, resourceId.toString());
            q.setParameter(2, Integer.valueOf(publishTag));
            q.setParameter(3, Integer.valueOf(publishTag));

            try {
                content = ((CmsDAOContents) q.getSingleResult()).getFileContent();
            } catch (NoResultException e) {
                // do nothing
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return content;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readDeletedResources(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, org.opencms.util.CmsUUID)
     */
    public List<I_CmsHistoryResource> readDeletedResources(CmsDbContext dbc, CmsUUID structureId, CmsUUID userId)
            throws CmsDataAccessException {

        List<I_CmsHistoryResource> result = new ArrayList<I_CmsHistoryResource>();
        Query q = null;
        I_CmsVfsDriver vfsDriver = m_driverManager.getVfsDriver(dbc);

        try {
            if (userId == null) {
                q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_DELETED);
            } else {
                q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_DELETED_RESTRICTED);
            }
            q.setParameter(1, structureId.toString());
            if (userId != null) {
                q.setParameter(2, userId.toString());
            }
            @SuppressWarnings("unchecked")
            List<Object[]> res = q.getResultList();
            for (Object[] o : res) {
                I_CmsHistoryResource histRes = internalCreateResource(o);
                if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(),
                        histRes.getStructureId())) {
                    // only add resources that are really deleted
                    continue;
                }
                result.add(histRes);
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        if (!result.isEmpty() || (dbc.getRequestContext() == null)
                || (dbc.getRequestContext().getAttribute("ATTR_RESOURCE_NAME") == null)) {
            return result;
        }
        try {
            if (userId == null) {
                q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_DELETED_NAME);
            } else {
                q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_DELETED_NAME_RESTRICTED);
            }
            String path = dbc.getRequestContext().getAttribute("ATTR_RESOURCE_NAME").toString();
            q.setParameter(1, CmsVfsDriver.escapeDbWildcard(path + '%'));
            q.setParameter(2, path);
            if (userId != null) {
                q.setParameter(3, userId.toString());
            }
            @SuppressWarnings("unchecked")
            List<Object[]> res = q.getResultList();
            for (Object[] o : res) {
                I_CmsHistoryResource histRes = internalCreateResource(o);
                if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(),
                        histRes.getStructureId())) {
                    // only add resources that are really deleted
                    continue;
                }
                result.add(histRes);
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return result;
    }

    /**
     * Possibly there is no need for this method.<p>
     * 
     * TODO: check if this method is used somewhere
     * TODO: remove this method
     * 
     * @param dbc the db context 
     * @param structureId the structure id
     * @param tagId the tag id
     * 
     * @return the historical resource
     *  
     * @throws CmsDataAccessException if something goes wrong
     */
    @Deprecated
    public I_CmsHistoryResource readFile(CmsDbContext dbc, CmsUUID structureId, int tagId)
            throws CmsDataAccessException {

        I_CmsHistoryResource file = null;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_FILES_READ_HISTORY_BYID);
            q.setParameter(1, structureId.toString());
            q.setParameter(2, Integer.valueOf(tagId));
            Object[] res = (Object[]) q.getSingleResult();
            file = internalCreateResource(res);
        } catch (NoResultException e) {
            throw new CmsVfsResourceNotFoundException(
                    Messages.get().container(Messages.ERR_HISTORY_FILE_NOT_FOUND_1, structureId));
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        ((CmsFile) file).setContents(readContent(dbc, file.getResourceId(), file.getPublishTag()));
        return file;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readLastVersion(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
     */
    public int readLastVersion(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException {

        int lastVersion = 0;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_MAXVER);
            q.setParameter(1, structureId.toString());
            try {
                lastVersion = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                // do nothing
            } catch (NullPointerException e) {
                lastVersion = 0;
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return lastVersion;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readMaxPublishTag(CmsDbContext, CmsUUID)
     */
    public int readMaxPublishTag(CmsDbContext dbc, CmsUUID resourceId) throws CmsDataAccessException {

        int result = 0;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_RESOURCES_READ_MAX_PUBLISH_TAG);
            q.setParameter(1, resourceId.toString());
            try {
                result = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                // do nothing
            }

        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return result;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readNextPublishTag(org.opencms.db.CmsDbContext)
     */
    public int readNextPublishTag(CmsDbContext dbc) {

        int projectPublishTag = 1;
        int resourcePublishTag = 1;
        Query q;

        try {
            // get the max publish tag from project history 
            q = m_sqlManager.createQuery(dbc, C_PROJECTS_HISTORY_MAXTAG);
            try {
                projectPublishTag = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult()) + 1;
            } catch (NoResultException e) {
                // do nothing
            }

        } catch (PersistenceException e) {
            LOG.error(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        try {
            // get the max publish tag from resource history 
            q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_MAXTAG);
            try {
                resourcePublishTag = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult()) + 1;
            } catch (NoResultException e) {
                // do nothing
            }
        } catch (PersistenceException e) {
            LOG.error(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        // keep the biggest 
        if (resourcePublishTag > projectPublishTag) {
            projectPublishTag = resourcePublishTag;
        }

        try {
            // get the max publish tag from contents 
            q = m_sqlManager.createQuery(dbc, C_CONTENT_PUBLISH_MAXTAG);
            try {
                resourcePublishTag = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult()) + 1;
            } catch (NoResultException e) {
                // do nothing
            }
        } catch (PersistenceException e) {
            // do nothing
        }
        // return the biggest 
        if (resourcePublishTag > projectPublishTag) {
            projectPublishTag = resourcePublishTag;
        }

        return projectPublishTag;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readPrincipal(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
     */
    public CmsHistoryPrincipal readPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsDataAccessException {

        CmsHistoryPrincipal historyPrincipal = null;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_HISTORY_PRINCIPAL_READ);
            q.setParameter(1, principalId.toString());
            try {
                CmsDAOHistoryPrincipals hp = (CmsDAOHistoryPrincipals) q.getSingleResult();
                String userName = hp.getPrincipalName();
                String ou = CmsOrganizationalUnit.removeLeadingSeparator(hp.getPrincipalOu());
                historyPrincipal = new CmsHistoryPrincipal(principalId, ou + userName, hp.getPrincipalDescription(),
                        hp.getPrincipalEmail(), hp.getPrincipalType(), new CmsUUID(hp.getPrincipalUserDeleted()),
                        hp.getPrincipalDateDeleted());
            } catch (NoResultException e) {
                throw new CmsDbEntryNotFoundException(
                        Messages.get().container(Messages.ERR_HISTORY_PRINCIPAL_NOT_FOUND_1, principalId));
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return historyPrincipal;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProject(org.opencms.db.CmsDbContext, CmsUUID)
     */
    public CmsHistoryProject readProject(CmsDbContext dbc, CmsUUID projectId) throws CmsDataAccessException {

        CmsHistoryProject project = null;
        try {
            Query q = m_sqlManager.createQuery(dbc, C_PROJECTS_HISTORY_READ_BYID);

            q.setParameter(1, projectId.toString());
            try {
                CmsDAOHistoryProjects hp = (CmsDAOHistoryProjects) q.getSingleResult();
                int tag = hp.getPublishTag();
                List<String> projectresources = readProjectResources(dbc, tag);
                project = internalCreateProject(hp, projectresources);
            } catch (NoResultException e) {
                throw new CmsDbEntryNotFoundException(
                        Messages.get().container(Messages.ERR_NO_HISTORY_PROJECT_WITH_ID_1, projectId));
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return project;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProject(org.opencms.db.CmsDbContext, int)
     */
    public CmsHistoryProject readProject(CmsDbContext dbc, int publishTag) throws CmsDataAccessException {

        CmsHistoryProject project = null;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_PROJECTS_HISTORY_READ);

            q.setParameter(1, Integer.valueOf(publishTag));
            try {
                CmsDAOHistoryProjects hp = (CmsDAOHistoryProjects) q.getSingleResult();
                List<String> projectresources = readProjectResources(dbc, publishTag);
                project = internalCreateProject(hp, projectresources);
            } catch (NoResultException e) {
                throw new CmsDbEntryNotFoundException(Messages.get()
                        .container(Messages.ERR_NO_HISTORY_PROJECT_WITH_TAG_ID_1, new Integer(publishTag)));
            }

        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return project;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProjectResources(org.opencms.db.CmsDbContext, int)
     */
    public List<String> readProjectResources(CmsDbContext dbc, int publishTag) throws CmsDataAccessException {

        List<String> projectResources = new ArrayList<String>();
        try {
            Query q = m_sqlManager.createQuery(dbc, C_PROJECTRESOURCES_HISTORY_READ);
            q.setParameter(1, Integer.valueOf(publishTag));
            @SuppressWarnings("unchecked")
            List<String> res = q.getResultList();
            for (String s : res) {
                projectResources.add(s);
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return projectResources;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readProjects(org.opencms.db.CmsDbContext)
     */
    public List<CmsHistoryProject> readProjects(CmsDbContext dbc) throws CmsDataAccessException {

        List<CmsHistoryProject> projects = new ArrayList<CmsHistoryProject>();

        try {
            Query q = m_sqlManager.createQuery(dbc, C_PROJECTS_HISTORY_READ_ALL);
            q.setMaxResults(300);
            @SuppressWarnings("unchecked")
            List<CmsDAOHistoryProjects> res = q.getResultList();

            for (CmsDAOHistoryProjects hp : res) {
                List<String> resources = readProjectResources(dbc, hp.getPublishTag());
                projects.add(internalCreateProject(hp, resources));
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return (projects);
    }

    /** 
     * @see org.opencms.db.I_CmsHistoryDriver#readProperties(org.opencms.db.CmsDbContext, org.opencms.file.history.I_CmsHistoryResource)
     */
    public List<CmsProperty> readProperties(CmsDbContext dbc, I_CmsHistoryResource resource)
            throws CmsDataAccessException {

        Map<String, CmsProperty> propertyMap = new HashMap<String, CmsProperty>();

        try {
            // get the latest properties for this sibling
            int pubTag = -1;
            Query q = m_sqlManager.createQuery(dbc, C_PROPERTIES_HISTORY_READ_PUBTAG);
            q.setParameter(1, String.valueOf(resource.getStructureId()));
            q.setParameter(2, Integer.valueOf(resource.getPublishTag()));
            try {
                pubTag = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                // do nothing
            }

            if (pubTag > 0) {
                // add the siblings props
                q = m_sqlManager.createQuery(dbc, C_PROPERTIES_HISTORY_READALL_STR);
                q.setParameter(1, resource.getStructureId().toString());
                q.setParameter(2, Integer.valueOf(pubTag));

                @SuppressWarnings("unchecked")
                List<Object[]> res = q.getResultList();
                for (Object[] o : res) {
                    String propertyKey = (String) o[0];
                    String propertyValue = (String) o[1];
                    int mappingType = CmsDataTypeUtil.numberToInt((Number) o[2]);

                    internalAddToPropMap(propertyMap, resource, propertyKey, propertyValue, mappingType);
                }
            }

            if (pubTag != resource.getPublishTag()) {
                // check if there were newer shared properties modifications
                q = m_sqlManager.createQuery(dbc, C_PROPERTIES_HISTORY_READALL_RES);
                q.setParameter(1, resource.getStructureId().toString());
                q.setParameter(2, Integer.valueOf(resource.getPublishTag()));
                @SuppressWarnings("unchecked")
                List<Object[]> res = q.getResultList();

                for (Object[] o : res) {
                    String propertyKey = (String) o[0];
                    String propertyValue = (String) o[1];
                    int mappingType = CmsDataTypeUtil.numberToInt((Number) o[2]);

                    internalAddToPropMap(propertyMap, resource, propertyKey, propertyValue, mappingType);
                }
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return new ArrayList<CmsProperty>(propertyMap.values());
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String)
     */
    public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name)
            throws CmsDataAccessException {

        CmsPropertyDefinition propDef = null;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_PROPERTYDEF_READ_HISTORY);
            q.setParameter(1, name);
            try {
                CmsDAOHistoryPropertyDef hpd = (CmsDAOHistoryPropertyDef) q.getSingleResult();
                propDef = new CmsPropertyDefinition(new CmsUUID(hpd.getPropertyDefId()), hpd.getPropertyDefName(),
                        CmsPropertyDefinition.CmsPropertyType.valueOf(hpd.getPropertyDefType()));
            } catch (NoResultException e) {
                throw new CmsDbEntryNotFoundException(
                        Messages.get().container(Messages.ERR_NO_PROPERTYDEF_WITH_NAME_1, name));
            }

        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return propDef;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readPublishTag(org.opencms.db.CmsDbContext, long)
     */
    public int readPublishTag(CmsDbContext dbc, long maxdate) throws CmsDataAccessException {

        int maxVersion = 0;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_PROJECTS_HISTORY_READ_TAG_FOR_DATE);
            q.setParameter(1, Long.valueOf(maxdate));
            try {
                maxVersion = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                // do nothing
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return maxVersion;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#readResource(CmsDbContext, CmsUUID, int)
     */
    public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsUUID structureId, int version)
            throws CmsDataAccessException {

        I_CmsHistoryResource resource = null;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_RESOURCES_HISTORY_READ_VERSION);
            q.setParameter(1, structureId.toString());
            q.setParameter(2, Integer.valueOf(version));
            try {
                resource = internalCreateResource((Object[]) q.getSingleResult());
            } catch (NoResultException e) {
                throw new CmsVfsResourceNotFoundException(
                        Messages.get().container(Messages.ERR_HISTORY_FILE_NOT_FOUND_1, structureId));
            }

        } catch (PersistenceException e) {
            throw new CmsDbSqlException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return resource;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#setDriverManager(org.opencms.db.CmsDriverManager) 
     */
    public void setDriverManager(CmsDriverManager driverManager) {

        m_driverManager = driverManager;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#setSqlManager(org.opencms.db.CmsSqlManager) 
     */
    public void setSqlManager(org.opencms.db.CmsSqlManager sqlManager) {

        m_sqlManager = (CmsSqlManager) sqlManager;
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writePrincipal(CmsDbContext, org.opencms.security.I_CmsPrincipal)
     */
    public void writePrincipal(CmsDbContext dbc, I_CmsPrincipal principal) throws CmsDataAccessException {

        try {
            // check if the principal was already saved
            readPrincipal(dbc, principal.getId());
            return;
        } catch (CmsDbEntryNotFoundException e) {
            // ok
        }
        try {
            CmsDAOHistoryPrincipals hp = new CmsDAOHistoryPrincipals();
            hp.setPrincipalId(principal.getId().toString());
            hp.setPrincipalName(principal.getSimpleName());
            String desc = principal.getDescription();
            desc = CmsStringUtil.isEmptyOrWhitespaceOnly(desc) ? "-" : desc;
            hp.setPrincipalDescription(desc);
            hp.setPrincipalOu(CmsOrganizationalUnit.SEPARATOR + principal.getOuFqn());
            if (principal instanceof CmsUser) {
                String email = ((CmsUser) principal).getEmail();
                email = CmsStringUtil.isEmptyOrWhitespaceOnly(email) ? "-" : email;
                hp.setPrincipalEmail(email);
                hp.setPrincipalType(I_CmsPrincipal.PRINCIPAL_USER);
            } else {
                hp.setPrincipalEmail("-");
                hp.setPrincipalType(I_CmsPrincipal.PRINCIPAL_GROUP);
            }
            hp.setPrincipalUserDeleted(dbc.currentUser().getId().toString());
            hp.setPrincipalDateDeleted(System.currentTimeMillis());
            m_sqlManager.persist(dbc, hp);
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writeProject(org.opencms.db.CmsDbContext, int, long)
     */
    public void writeProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException {

        CmsProject currentProject = dbc.currentProject();
        CmsUser currentUser = dbc.currentUser();

        List<String> projectresources = m_driverManager.getProjectDriver(dbc).readProjectResources(dbc,
                currentProject);

        // write historical project to the database
        try {
            CmsDAOHistoryProjects hp = new CmsDAOHistoryProjects();

            hp.setPublishTag(publishTag);
            hp.setProjectId(currentProject.getUuid().toString());
            hp.setProjectName(currentProject.getSimpleName());
            hp.setProjectPublishDate(publishDate);
            hp.setProjectPublishedBy(currentUser.getId().toString());
            hp.setUserId(currentProject.getOwnerId().toString());
            hp.setGroupId(currentProject.getGroupId().toString());
            hp.setManagerGroupId(currentProject.getManagerGroupId().toString());
            hp.setProjectDescription(currentProject.getDescription());
            hp.setDateCreated(currentProject.getDateCreated());
            hp.setProjectType(currentProject.getType().getMode());
            hp.setProjectOu(CmsOrganizationalUnit.SEPARATOR + currentProject.getOuFqn());

            m_sqlManager.persist(dbc, hp);

            // now write the projectresources
            CmsDAOHistoryProjectResources hpr;
            for (String projectResource : projectresources) {
                hpr = new CmsDAOHistoryProjectResources();
                hpr.setPublishTag(publishTag);
                hpr.setProjectId(currentProject.getUuid().toString());
                hpr.setResourcePath(projectResource);
                m_sqlManager.persist(dbc, hpr);
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writeProperties(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, java.util.List, int)
     */
    public void writeProperties(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties,
            int publishTag) throws CmsDataAccessException {

        CmsDAOHistoryProperties hp;

        try {
            Iterator<CmsProperty> dummy = properties.iterator();
            while (dummy.hasNext()) {
                CmsProperty property = dummy.next();
                CmsPropertyDefinition propDef = null;
                try {
                    propDef = readPropertyDefinition(dbc, property.getName());
                } catch (CmsDbEntryNotFoundException e) {
                    // create if missing
                    propDef = createPropertyDefinition(dbc, property.getName(), CmsPropertyDefinition.TYPE_NORMAL);
                }

                for (int i = 0; i < 2; i++) {
                    int mappingType;
                    String value;
                    CmsUUID id;
                    if (i == 0) {
                        // write the structure value on the first cycle
                        value = property.getStructureValue();
                        mappingType = CmsProperty.STRUCTURE_RECORD_MAPPING;
                        id = resource.getStructureId();
                        if (CmsStringUtil.isEmpty(value)) {
                            continue;
                        }
                    } else {
                        // write the resource value on the second cycle
                        value = property.getResourceValue();
                        mappingType = CmsProperty.RESOURCE_RECORD_MAPPING;
                        id = resource.getResourceId();
                        if (CmsStringUtil.isEmpty(value)) {
                            break;
                        }
                    }

                    hp = new CmsDAOHistoryProperties();

                    hp.setStructureId(resource.getStructureId().toString());
                    hp.setPropertyDefId(propDef.getId().toString());
                    hp.setPropertyMappingId(id.toString());
                    hp.setPropertyMappingType(mappingType);
                    hp.setPropertyValue(m_sqlManager.validateEmpty(value));
                    hp.setPublishTag(publishTag);

                    m_sqlManager.persist(dbc, hp);
                }
            }
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
    }

    /**
     * @see org.opencms.db.I_CmsHistoryDriver#writeResource(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, java.util.List, int)
     */
    public void writeResource(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties, int publishTag)
            throws CmsDataAccessException {

        try {
            int sibCount = resource.getSiblingCount();

            boolean valResource = internalValidateResource(dbc, resource, publishTag);

            // if deleted
            if (resource.getState().isDeleted()) {
                // if it is a file
                if (resource instanceof CmsFile) {
                    if (!valResource) {
                        if (sibCount < 2) {
                            // copy from offline content to content tables
                            // so that the history contains the last state of the file
                            m_driverManager.getVfsDriver(dbc).createOnlineContent(dbc, resource.getResourceId(),
                                    ((CmsFile) resource).getContents(), publishTag, false, true);
                        } else {
                            @SuppressWarnings("unchecked")
                            Set<CmsUUID> changedAndDeleted = (Set<CmsUUID>) dbc
                                    .getAttribute(CmsDriverManager.KEY_CHANGED_AND_DELETED);
                            if ((changedAndDeleted == null)
                                    || !changedAndDeleted.contains(resource.getResourceId())) {

                                // put the content definitively in the history if no sibling is left
                                m_driverManager.getVfsDriver(dbc).createOnlineContent(dbc, resource.getResourceId(),
                                        ((CmsFile) resource).getContents(), publishTag, true, false);
                            }
                        }
                    }
                }

                // update version numbers
                m_driverManager.getVfsDriver(dbc).publishVersions(dbc, resource, !valResource);
            }

            // read the version numbers
            Map<String, Integer> versions = m_driverManager.getVfsDriver(dbc).readVersions(dbc,
                    CmsProject.ONLINE_PROJECT_ID, resource.getResourceId(), resource.getStructureId());
            int structureVersion = CmsDataTypeUtil.numberToInt(versions.get("structure"));
            int resourceVersion = CmsDataTypeUtil.numberToInt(versions.get("resource"));

            if (!valResource) {
                // write the resource
                CmsDAOHistoryResources hs = new CmsDAOHistoryResources();

                hs.setResourceId(resource.getResourceId().toString());
                hs.setResourceType(resource.getTypeId());
                hs.setResourceFlags(resource.getFlags());
                hs.setDateCreated(resource.getDateCreated());
                hs.setUserCreated(resource.getUserCreated().toString());
                hs.setDateLastModified(resource.getDateLastModified());
                hs.setUserLastModified(resource.getUserLastModified().toString());
                hs.setResourceState(resource.getState().getState());
                hs.setResourceSize(resource.getLength());
                hs.setDateContent(resource.getDateContent());
                hs.setProjectLastModified(dbc.currentProject().getUuid().toString());
                hs.setSiblingCount(resource.getSiblingCount());
                hs.setResourceVersion(resourceVersion);
                hs.setPublishTag(publishTag);

                m_sqlManager.persist(dbc, hs);
            }
            CmsUUID parentId = CmsUUID.getNullUUID();
            CmsFolder parent = m_driverManager.getVfsDriver(dbc).readParentFolder(dbc, CmsProject.ONLINE_PROJECT_ID,
                    resource.getStructureId());
            if (parent != null) {
                parentId = parent.getStructureId();
            }
            // write the structure
            CmsDAOHistoryStructure hstr = new CmsDAOHistoryStructure();

            hstr.setStructureId(resource.getStructureId().toString());
            hstr.setResourceId(resource.getResourceId().toString());
            hstr.setResourcePath(resource.getRootPath());
            hstr.setStructureState(resource.getState().getState());
            hstr.setDateReleased(resource.getDateReleased());
            hstr.setDateExpired(resource.getDateExpired());
            hstr.setStructureVersion(structureVersion);
            hstr.setParentId(parentId.toString());
            hstr.setPublishTag(publishTag);
            hstr.setVersion(resource.getVersion());

            m_sqlManager.persist(dbc, hstr);
        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        writeProperties(dbc, resource, properties, publishTag);
    }

    /**
     * Updates the property map for the given resource with the given property data.<p>
     * 
     * @param propertyMap the map to update
     * @param resource the resource the properties belong to
     * @param propertyKey the property key
     * @param propertyValue the property value
     * @param mappingType the mapping type
     * 
     * @throws CmsDbConsistencyException if the mapping type is wrong
     */
    protected void internalAddToPropMap(Map<String, CmsProperty> propertyMap, I_CmsHistoryResource resource,
            String propertyKey, String propertyValue, int mappingType) throws CmsDbConsistencyException {

        CmsProperty property = propertyMap.get(propertyKey);
        if (property != null) {
            // there exists already a property for this key in the result
            switch (mappingType) {
            case CmsProperty.STRUCTURE_RECORD_MAPPING:
                // this property value is mapped to a structure record
                property.setStructureValue(propertyValue);
                break;
            case CmsProperty.RESOURCE_RECORD_MAPPING:
                // this property value is mapped to a resource record
                property.setResourceValue(propertyValue);
                break;
            default:
                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
                                resource.getRootPath(), new Integer(mappingType), propertyKey));
            }
        } else {
            // there doesn't exist a property for this key yet
            property = new CmsProperty();
            property.setName(propertyKey);

            switch (mappingType) {
            case CmsProperty.STRUCTURE_RECORD_MAPPING:
                // this property value is mapped to a structure record
                property.setStructureValue(propertyValue);
                property.setResourceValue(null);
                break;
            case CmsProperty.RESOURCE_RECORD_MAPPING:
                // this property value is mapped to a resource record
                property.setStructureValue(null);
                property.setResourceValue(propertyValue);
                break;
            default:
                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
                                resource.getRootPath(), new Integer(mappingType), propertyKey));
            }
            propertyMap.put(propertyKey, property);
        }
    }

    /**
     * Deletes all historical entries of subresources of a folder without any historical netry left.<p>
     * 
     * @param dbc the current database context
     * @param resource the resource to check
     * 
     * @throws CmsDataAccessException if something goes wrong
     */
    protected void internalCleanup(CmsDbContext dbc, I_CmsHistoryResource resource) throws CmsDataAccessException {

        boolean isFolder = resource.getRootPath().endsWith("/");
        List<I_CmsHistoryResource> subResources = new ArrayList<I_CmsHistoryResource>();
        // if the resource is a folder
        if (isFolder) {
            // and if no versions left
            if (readLastVersion(dbc, resource.getStructureId()) == 0) {
                // get all direct subresources                    

                try {
                    Query q = m_sqlManager.createQuery(dbc, C_STRUCTURE_HISTORY_READ_SUBRESOURCES);
                    q.setParameter(1, resource.getStructureId().toString());
                    @SuppressWarnings("unchecked")
                    List<Object[]> res = q.getResultList();
                    for (Object[] obj : res) {
                        CmsUUID structureId = new CmsUUID((String) obj[0]);
                        int version = CmsDataTypeUtil.numberToInt((Integer) obj[1]);
                        subResources.add(readResource(dbc, structureId, version));
                    }
                } catch (PersistenceException e) {
                    throw new CmsDbSqlException(
                            Messages.get().container(Messages.ERR_GENERIC_SQL_1, Messages.ERR_JPA_PERSITENCE), e);
                }
            }
        }

        // delete all sub resource versions
        for (I_CmsHistoryResource histResource : subResources) {
            deleteEntries(dbc, histResource, 0, -1);
        }
    }

    /**
     * Returns the amount of properties for a propertydefinition.<p>
     * 
     * @param dbc the current database context
     * @param metadef the propertydefinition to test
     * @param projectId the ID of the current project
     * 
     * @return the amount of properties for a propertydefinition
     * @throws CmsDataAccessException if something goes wrong
     */
    protected int internalCountProperties(CmsDbContext dbc, CmsPropertyDefinition metadef, CmsUUID projectId)
            throws CmsDataAccessException {

        int returnValue;
        try {
            // create statement
            Query q = m_sqlManager.createQuery(dbc, projectId, C_PROPERTIES_READALL_COUNT);
            q.setParameter(1, metadef.getId().toString());
            try {
                returnValue = CmsDataTypeUtil.numberToInt((Number) q.getSingleResult());
            } catch (NoResultException e) {
                throw new CmsDbConsistencyException(
                        Messages.get().container(Messages.ERR_NO_PROPERTIES_FOR_PROPERTYDEF_1, metadef.getName()));
            }

        } catch (PersistenceException e) {
            throw new CmsDataAccessException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }
        return returnValue;
    }

    /**
     * Creates a historical project from the given result set and resources.<p>
     * @param hp the CmsDAOHistoryProjects instance
     * @param resources the historical resources
     * 
     * @return the historical project
     *  
     * @throws PersistenceException if something goes wrong
     */
    protected CmsHistoryProject internalCreateProject(CmsDAOHistoryProjects hp, List<String> resources)
            throws PersistenceException {

        String ou = CmsOrganizationalUnit.removeLeadingSeparator(hp.getProjectOu());
        CmsUUID publishedById = new CmsUUID(hp.getProjectPublishedBy());
        CmsUUID userId = new CmsUUID(hp.getUserId());
        return new CmsHistoryProject(hp.getPublishTag(), new CmsUUID(hp.getProjectId()), ou + hp.getProjectName(),
                hp.getProjectDescription(), userId, new CmsUUID(hp.getGroupId()),
                new CmsUUID(hp.getManagerGroupId()), hp.getDateCreated(),
                CmsProject.CmsProjectType.valueOf(hp.getProjectType()), hp.getProjectPublishDate(), publishedById,
                resources);
    }

    /**
     * Creates a valid {@link I_CmsHistoryResource} instance from a JDBC ResultSet.<p>
     * 
     * @param res the JDBC result set
     * 
     * @return the new historical resource instance
     * 
     */
    protected I_CmsHistoryResource internalCreateResource(Object[] res) {

        CmsDAOHistoryStructure hs = (CmsDAOHistoryStructure) res[0];
        CmsDAOHistoryResources hr = (CmsDAOHistoryResources) res[1];

        int resourceVersion = hr.getResourceVersion();
        int structureVersion = hs.getStructureVersion();
        int tagId = hr.getPublishTag();
        CmsUUID structureId = new CmsUUID(hs.getStructureId());
        CmsUUID resourceId = new CmsUUID(hr.getResourceId());
        String resourcePath = hs.getResourcePath();
        int resourceType = hr.getResourceType();
        int resourceFlags = hr.getResourceFlags();
        CmsUUID projectLastModified = new CmsUUID(hr.getProjectLastModified());
        int state = Math.max(hr.getResourceState(), hs.getStructureState());
        long dateCreated = hr.getDateCreated();
        long dateLastModified = hr.getDateLastModified();
        long dateReleased = hs.getDateReleased();
        long dateExpired = hs.getDateExpired();
        int resourceSize = hr.getResourceSize();
        CmsUUID userLastModified = new CmsUUID(hr.getUserLastModified());
        CmsUUID userCreated = new CmsUUID(hr.getUserCreated());
        CmsUUID parentId = new CmsUUID(hs.getParentId());
        long dateContent = hr.getDateContent();

        boolean isFolder = resourcePath.endsWith("/");
        if (isFolder) {
            return new CmsHistoryFolder(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired,
                    resourceVersion + structureVersion, parentId, resourceVersion, structureVersion);
        } else {
            return new CmsHistoryFile(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired, resourceSize, dateContent,
                    resourceVersion + structureVersion, parentId, null, resourceVersion, structureVersion);
        }
    }

    /**
     * Merges an historical entry for a sibling, based on the structure data from the given historical resource
     * and result set for the resource entry.<p>
     * 
     * @param histRes the original historical entry
     * @param hr the CmsDAOHistoryResources instance of the resource entry
     * @param versionOffset the offset for the structure version
     * 
     * @return a merged historical entry for the sibling
     * 
     */
    protected I_CmsHistoryResource internalMergeResource(I_CmsHistoryResource histRes, CmsDAOHistoryResources hr,
            int versionOffset) {

        int resourceVersion = hr.getResourceVersion();
        int structureVersion = histRes.getStructureVersion() - versionOffset;
        int tagId = hr.getPublishTag();
        CmsUUID structureId = histRes.getStructureId();
        CmsUUID resourceId = new CmsUUID(hr.getResourceId());
        int resourceType = hr.getResourceType();
        int resourceFlags = hr.getResourceFlags();
        CmsUUID projectLastModified = new CmsUUID(hr.getProjectLastModified());
        int state = histRes.getState().getState(); // may be we have to compute something here?
        long dateCreated = hr.getDateCreated();
        long dateLastModified = hr.getDateLastModified();
        long dateReleased = histRes.getDateReleased();
        long dateExpired = histRes.getDateExpired();
        int resourceSize = hr.getResourceSize();
        CmsUUID userLastModified = new CmsUUID(hr.getUserLastModified());
        CmsUUID userCreated = new CmsUUID(hr.getUserCreated());
        // here we could use the path/parent id for the sibling where the modification really occurred
        String resourcePath = histRes.getRootPath();
        CmsUUID parentId = histRes.getParentId();
        long dateContent = hr.getDateContent();

        if (histRes.isFolder()) {
            return new CmsHistoryFolder(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired,
                    resourceVersion + structureVersion, parentId, resourceVersion, structureVersion);
        } else {
            return new CmsHistoryFile(tagId, structureId, resourceId, resourcePath, resourceType, resourceFlags,
                    projectLastModified, CmsResourceState.valueOf(state), dateCreated, userCreated,
                    dateLastModified, userLastModified, dateReleased, dateExpired, resourceSize, dateContent,
                    resourceVersion + structureVersion, parentId, null, resourceVersion, structureVersion);
        }
    }

    /**
     * Tests if a history resource does exist.<p>
     * 
     * @param dbc the current database context
     * @param resource the resource to test
     * @param publishTag the publish tag of the resource to test
     * 
     * @return <code>true</code> if the resource already exists, <code>false</code> otherwise
     * 
     * @throws CmsDataAccessException if something goes wrong
     */
    protected boolean internalValidateResource(CmsDbContext dbc, CmsResource resource, int publishTag)
            throws CmsDataAccessException {

        boolean exists = false;

        try {
            Query q = m_sqlManager.createQuery(dbc, C_HISTORY_EXISTS_RESOURCE);
            q.setParameter(1, resource.getResourceId().toString());
            q.setParameter(2, Integer.valueOf(publishTag));
            try {
                q.getSingleResult();
                exists = true;
            } catch (NoResultException e) {
                //do nothing
            }
        } catch (PersistenceException e) {
            throw new CmsDbSqlException(Messages.get().container(Messages.ERR_JPA_PERSITENCE, e), e);
        }

        return exists;
    }
}