001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.db.generic;
029
030import org.opencms.configuration.CmsConfigurationManager;
031import org.opencms.configuration.CmsParameterConfiguration;
032import org.opencms.db.CmsDbConsistencyException;
033import org.opencms.db.CmsDbContext;
034import org.opencms.db.CmsDbEntryNotFoundException;
035import org.opencms.db.CmsDbSqlException;
036import org.opencms.db.CmsDriverManager;
037import org.opencms.db.CmsResourceState;
038import org.opencms.db.I_CmsDriver;
039import org.opencms.db.I_CmsHistoryDriver;
040import org.opencms.db.I_CmsVfsDriver;
041import org.opencms.file.CmsDataAccessException;
042import org.opencms.file.CmsFile;
043import org.opencms.file.CmsFolder;
044import org.opencms.file.CmsProject;
045import org.opencms.file.CmsProperty;
046import org.opencms.file.CmsPropertyDefinition;
047import org.opencms.file.CmsResource;
048import org.opencms.file.CmsUser;
049import org.opencms.file.CmsVfsResourceNotFoundException;
050import org.opencms.file.history.CmsHistoryFile;
051import org.opencms.file.history.CmsHistoryFolder;
052import org.opencms.file.history.CmsHistoryPrincipal;
053import org.opencms.file.history.CmsHistoryProject;
054import org.opencms.file.history.I_CmsHistoryResource;
055import org.opencms.main.CmsLog;
056import org.opencms.security.CmsOrganizationalUnit;
057import org.opencms.security.I_CmsPrincipal;
058import org.opencms.util.CmsStringUtil;
059import org.opencms.util.CmsUUID;
060
061import java.sql.Connection;
062import java.sql.PreparedStatement;
063import java.sql.ResultSet;
064import java.sql.SQLException;
065import java.util.ArrayList;
066import java.util.HashMap;
067import java.util.Iterator;
068import java.util.List;
069import java.util.Map;
070import java.util.Set;
071
072import org.apache.commons.logging.Log;
073
074/**
075 * Generic (ANSI-SQL) database server implementation of the history driver methods.<p>
076 *
077 * @since 6.9.1
078 */
079public class CmsHistoryDriver implements I_CmsDriver, I_CmsHistoryDriver {
080
081    /** The log object for this class. */
082    private static final Log LOG = CmsLog.getLog(org.opencms.db.generic.CmsHistoryDriver.class);
083
084    /** The driver manager instance. */
085    protected CmsDriverManager m_driverManager;
086
087    /** The SQL manager instance. */
088    protected CmsSqlManager m_sqlManager;
089
090    /**
091     * @see org.opencms.db.I_CmsHistoryDriver#createPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String, org.opencms.file.CmsPropertyDefinition.CmsPropertyType)
092     */
093    public CmsPropertyDefinition createPropertyDefinition(
094        CmsDbContext dbc,
095        String name,
096        CmsPropertyDefinition.CmsPropertyType type) throws CmsDataAccessException {
097
098        Connection conn = null;
099        PreparedStatement stmt = null;
100
101        try {
102            conn = m_sqlManager.getConnection(dbc);
103            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTYDEF_CREATE_HISTORY");
104            stmt.setString(1, new CmsUUID().toString());
105            stmt.setString(2, name);
106            stmt.setInt(3, type.getMode());
107            stmt.executeUpdate();
108        } catch (SQLException e) {
109            throw new CmsDbSqlException(
110                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
111                e);
112
113        } finally {
114            m_sqlManager.closeAll(dbc, conn, stmt, null);
115        }
116
117        return readPropertyDefinition(dbc, name);
118    }
119
120    /**
121     * @see org.opencms.db.I_CmsHistoryDriver#deleteEntries(CmsDbContext, I_CmsHistoryResource, int, long)
122     */
123    public int deleteEntries(CmsDbContext dbc, I_CmsHistoryResource resource, int versionsToKeep, long time)
124    throws CmsDataAccessException {
125
126        Connection conn = null;
127        PreparedStatement stmt = null;
128        ResultSet res = null;
129
130        try {
131            conn = m_sqlManager.getConnection(dbc);
132
133            int maxVersion = -1;
134            // get the maximal version number for this resource
135            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_MAXVER");
136            stmt.setString(1, resource.getStructureId().toString());
137            res = stmt.executeQuery();
138            boolean noHistoryStructure = false;
139            if (res.next()) {
140                maxVersion = res.getInt(1);
141                noHistoryStructure |= res.wasNull();
142                while (res.next()) {
143                    // do nothing only move through all rows because of mssql odbc driver
144                }
145            } else {
146                // make sure the connection is closed
147                m_sqlManager.closeAll(dbc, conn, stmt, res);
148                // nothing to delete
149                internalCleanup(dbc, resource);
150                return 0;
151            }
152            m_sqlManager.closeAll(dbc, conn, stmt, res);
153
154            if (time >= 0) {
155                int maxVersionByTime = -1;
156                conn = m_sqlManager.getConnection(dbc);
157                // get the maximal version to keep for this resource based on the time parameter
158                stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_MAXVER_BYTIME");
159                stmt.setString(1, resource.getStructureId().toString());
160                stmt.setLong(2, time);
161                res = stmt.executeQuery();
162                if (res.next()) {
163                    maxVersionByTime = res.getInt(1);
164                    while (res.next()) {
165                        // do nothing only move through all rows because of mssql odbc driver
166                    }
167                }
168                m_sqlManager.closeAll(dbc, conn, stmt, res);
169                if (maxVersionByTime > 0) {
170                    if (versionsToKeep < 0) {
171                        versionsToKeep = (maxVersion - maxVersionByTime);
172                    } else {
173                        versionsToKeep = Math.min(versionsToKeep, (maxVersion - maxVersionByTime));
174                    }
175                }
176            }
177            int structureVersions = 0;
178            // get the minimal structure publish tag to keep for this sibling
179            conn = m_sqlManager.getConnection(dbc);
180            if (!noHistoryStructure) {
181                if ((versionsToKeep == -1) || ((maxVersion - versionsToKeep) <= 0)) {
182                    // nothing to delete
183                    internalCleanup(dbc, resource);
184                    return 0;
185                }
186
187                // get the minimal structure publish tag to keep for this sibling
188                int minStrPublishTagToKeep = -1;
189
190                stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_MAXTAG_FOR_VERSION");
191                stmt.setString(1, resource.getStructureId().toString());
192                stmt.setInt(2, (1 + maxVersion) - versionsToKeep);
193                res = stmt.executeQuery();
194                if (res.next()) {
195                    minStrPublishTagToKeep = res.getInt(1);
196                    while (res.next()) {
197                        // do nothing only move through all rows because of mssql odbc driver
198                    }
199                } else {
200                    // make sure the statement and the result is closed
201                    m_sqlManager.closeAll(dbc, conn, stmt, res);
202                    // nothing to delete
203                    internalCleanup(dbc, resource);
204                    return 0;
205                }
206                m_sqlManager.closeAll(dbc, conn, stmt, res);
207                if (minStrPublishTagToKeep < 1) {
208                    // nothing to delete
209                    internalCleanup(dbc, resource);
210                    return 0;
211                }
212                minStrPublishTagToKeep++;
213
214                // delete the properties
215                conn = m_sqlManager.getConnection(dbc);
216                stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_DELETE");
217                stmt.setString(1, resource.getStructureId().toString());
218                stmt.setInt(2, minStrPublishTagToKeep);
219                stmt.executeUpdate();
220                m_sqlManager.closeAll(dbc, null, stmt, null);
221
222                // delete the structure entries
223                stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_DELETE");
224                stmt.setString(1, resource.getStructureId().toString());
225                stmt.setInt(2, minStrPublishTagToKeep);
226                structureVersions = stmt.executeUpdate();
227                m_sqlManager.closeAll(dbc, null, stmt, null);
228            }
229
230            // get the minimal resource publish tag to keep,
231            // all entries with publish tag less than this will be deleted
232            int minResPublishTagToKeep = -1;
233            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_MIN_USED_TAG");
234            stmt.setString(1, resource.getResourceId().toString());
235            res = stmt.executeQuery();
236            if (res.next()) {
237                minResPublishTagToKeep = res.getInt(1);
238                if (res.wasNull()) {
239                    // the database will return a row with a single NULL column if there are no rows at all for the given
240                    // resource id. This means that we want to clean up all resource history and content history entries
241                    // for this resource id, and we achieve this by comparing their publish tag with the maximum integer.
242                    minResPublishTagToKeep = Integer.MAX_VALUE;
243                }
244                while (res.next()) {
245                    // do nothing only move through all rows because of mssql odbc driver
246                }
247            }
248            m_sqlManager.closeAll(dbc, conn, stmt, res);
249
250            // delete the resource entries
251            conn = m_sqlManager.getConnection(dbc);
252            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_DELETE");
253            stmt.setString(1, resource.getResourceId().toString());
254            stmt.setInt(2, minResPublishTagToKeep);
255            int resourceVersions = stmt.executeUpdate();
256            m_sqlManager.closeAll(dbc, null, stmt, null);
257
258            // delete the content entries
259            stmt = m_sqlManager.getPreparedStatement(conn, "C_CONTENT_HISTORY_DELETE");
260            stmt.setString(1, resource.getResourceId().toString());
261            stmt.setInt(2, minResPublishTagToKeep);
262            stmt.executeUpdate();
263
264            // make sure the statement and the result is closed
265            m_sqlManager.closeAll(dbc, conn, stmt, res);
266            internalCleanup(dbc, resource);
267            return Math.max(structureVersions, resourceVersions);
268        } catch (SQLException e) {
269            throw new CmsDbSqlException(
270                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
271                e);
272        } finally {
273            m_sqlManager.closeAll(dbc, conn, stmt, res);
274        }
275    }
276
277    /**
278     * @see org.opencms.db.I_CmsHistoryDriver#deletePropertyDefinition(org.opencms.db.CmsDbContext, org.opencms.file.CmsPropertyDefinition)
279     */
280    public void deletePropertyDefinition(CmsDbContext dbc, CmsPropertyDefinition metadef)
281    throws CmsDataAccessException {
282
283        Connection conn = null;
284        PreparedStatement stmt = null;
285
286        try {
287            if ((internalCountProperties(dbc, metadef, CmsProject.ONLINE_PROJECT_ID) != 0)
288                || (internalCountProperties(dbc, metadef, CmsUUID.getOpenCmsUUID()) != 0)) { // HACK: to get an offline project
289
290                throw new CmsDbConsistencyException(
291                    Messages.get().container(Messages.ERR_ERROR_DELETING_PROPERTYDEF_1, metadef.getName()));
292            }
293
294            // delete the historical property definition
295            conn = m_sqlManager.getConnection(dbc);
296            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTYDEF_DELETE_HISTORY");
297            stmt.setString(1, metadef.getId().toString());
298            stmt.executeUpdate();
299        } catch (SQLException e) {
300            throw new CmsDbSqlException(
301                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
302                e);
303        } finally {
304            m_sqlManager.closeAll(dbc, conn, stmt, null);
305        }
306    }
307
308    /**
309     * @see org.opencms.db.I_CmsHistoryDriver#destroy()
310     */
311    public void destroy() throws Throwable {
312
313        m_sqlManager = null;
314        m_driverManager = null;
315
316        if (CmsLog.INIT.isInfoEnabled()) {
317            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_DRIVER_1, getClass().getName()));
318        }
319    }
320
321    /**
322     * @see org.opencms.db.I_CmsHistoryDriver#getAllDeletedEntries(org.opencms.db.CmsDbContext)
323     */
324    public List<I_CmsHistoryResource> getAllDeletedEntries(CmsDbContext dbc) throws CmsDataAccessException {
325
326        Connection conn = null;
327        PreparedStatement stmt = null;
328        ResultSet res = null;
329
330        Map<CmsUUID, Integer> tmpEntrieis = new HashMap<CmsUUID, Integer>();
331        List<I_CmsHistoryResource> entries = new ArrayList<I_CmsHistoryResource>();
332        try {
333            conn = m_sqlManager.getConnection(dbc);
334            // get all not-deleted historical entries that may come in question
335            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_READ_DELETED");
336            res = stmt.executeQuery();
337            while (res.next()) {
338                CmsUUID structureId = new CmsUUID(res.getString(1));
339                int version = res.getInt(2);
340                tmpEntrieis.put(structureId, Integer.valueOf(version));
341            }
342        } catch (SQLException e) {
343            throw new CmsDbSqlException(
344                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
345                e);
346        } finally {
347            m_sqlManager.closeAll(dbc, conn, stmt, res);
348        }
349        for (Map.Entry<CmsUUID, Integer> entry : tmpEntrieis.entrySet()) {
350            entries.add(readResource(dbc, entry.getKey(), entry.getValue().intValue()));
351        }
352        return entries;
353    }
354
355    /**
356     * @see org.opencms.db.I_CmsHistoryDriver#getAllNotDeletedEntries(org.opencms.db.CmsDbContext)
357     */
358    public List<I_CmsHistoryResource> getAllNotDeletedEntries(CmsDbContext dbc) throws CmsDataAccessException {
359
360        Connection conn = null;
361        PreparedStatement stmt = null;
362        ResultSet res = null;
363
364        Map<CmsUUID, Integer> tmpEntrieis = new HashMap<CmsUUID, Integer>();
365
366        List<I_CmsHistoryResource> entries = new ArrayList<I_CmsHistoryResource>();
367        try {
368            conn = m_sqlManager.getConnection(dbc);
369
370            // get all not-deleted historical entries that may come in question
371            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_READ_NOTDELETED");
372            res = stmt.executeQuery();
373            while (res.next()) {
374                CmsUUID structureId = new CmsUUID(res.getString(1));
375                int version = res.getInt(2);
376                tmpEntrieis.put(structureId, Integer.valueOf(version));
377            }
378        } catch (SQLException e) {
379            throw new CmsDbSqlException(
380                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
381                e);
382        } finally {
383            m_sqlManager.closeAll(dbc, conn, stmt, res);
384        }
385
386        for (Map.Entry<CmsUUID, Integer> entry : tmpEntrieis.entrySet()) {
387            entries.add(readResource(dbc, entry.getKey(), entry.getValue().intValue()));
388        }
389        return entries;
390    }
391
392    /**
393     * @see org.opencms.db.I_CmsHistoryDriver#getSqlManager()
394     */
395    public CmsSqlManager getSqlManager() {
396
397        return m_sqlManager;
398    }
399
400    /**
401     * @see org.opencms.db.I_CmsDriver#init(org.opencms.db.CmsDbContext, org.opencms.configuration.CmsConfigurationManager, java.util.List, org.opencms.db.CmsDriverManager)
402     */
403    public void init(
404        CmsDbContext dbc,
405        CmsConfigurationManager configurationManager,
406        List<String> successiveDrivers,
407        CmsDriverManager driverManager) {
408
409        CmsParameterConfiguration configuration = configurationManager.getConfiguration();
410
411        String poolUrl;
412        if (configuration.get("db.history.pool") != null) {
413            poolUrl = configuration.get("db.history.pool").toString();
414        } else {
415            // TODO: deprecated, remove as soon as possible
416            poolUrl = configuration.get("db.backup.pool").toString();
417        }
418
419        String classname;
420        if (configuration.get("db.history.sqlmanager") != null) {
421            classname = configuration.get("db.history.sqlmanager").toString();
422        } else {
423            // TODO: deprecated, remove as soon as possible
424            classname = configuration.get("db.backup.sqlmanager").toString();
425        }
426
427        m_sqlManager = initSqlManager(classname);
428        m_sqlManager.init(I_CmsHistoryDriver.DRIVER_TYPE_ID, poolUrl);
429
430        m_driverManager = driverManager;
431
432        if (CmsLog.INIT.isInfoEnabled()) {
433            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ASSIGNED_POOL_1, poolUrl));
434        }
435
436        if ((successiveDrivers != null) && !successiveDrivers.isEmpty()) {
437            if (LOG.isWarnEnabled()) {
438                LOG.warn(
439                    Messages.get().getBundle().key(
440                        Messages.LOG_SUCCESSIVE_DRIVERS_UNSUPPORTED_1,
441                        getClass().getName()));
442            }
443        }
444    }
445
446    /**
447     * @see org.opencms.db.I_CmsHistoryDriver#initSqlManager(String)
448     */
449    public org.opencms.db.generic.CmsSqlManager initSqlManager(String classname) {
450
451        return CmsSqlManager.getInstance(classname);
452    }
453
454    /**
455     * @see org.opencms.db.I_CmsHistoryDriver#readAllAvailableVersions(CmsDbContext, CmsUUID)
456     */
457    public List<I_CmsHistoryResource> readAllAvailableVersions(CmsDbContext dbc, CmsUUID structureId)
458    throws CmsDataAccessException {
459
460        ResultSet res = null;
461        List<I_CmsHistoryResource> result = new ArrayList<I_CmsHistoryResource>();
462        PreparedStatement stmt = null;
463        Connection conn = null;
464
465        try {
466            conn = m_sqlManager.getConnection(dbc);
467
468            // get all direct versions (where the structure entry has been written)
469            // sorted from the NEWEST to the OLDEST version (publish tag descendant)
470            List<I_CmsHistoryResource> historyResources = new ArrayList<I_CmsHistoryResource>();
471            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_ALL_VERSIONS");
472            stmt.setString(1, structureId.toString());
473            res = stmt.executeQuery();
474            while (res.next()) {
475                historyResources.add(internalCreateResource(res));
476            }
477            m_sqlManager.closeAll(dbc, null, stmt, res);
478
479            if (!historyResources.isEmpty()) {
480                // look for newer versions
481                // this is the NEWEST version, with the HIGHEST publish tag
482                I_CmsHistoryResource histRes = historyResources.get(0);
483
484                // look for later resource entries
485                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_NEW_VERSIONS");
486                stmt.setString(1, histRes.getResourceId().toString());
487                stmt.setInt(2, histRes.getPublishTag());
488                res = stmt.executeQuery();
489
490                I_CmsHistoryResource lastHistRes = histRes;
491                // these are sorted from the oldest to the newest version (publish tag ascendent)
492                while (res.next()) {
493                    int resVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
494                    if (resVersion == lastHistRes.getResourceVersion()) {
495                        // skip not interesting versions
496                        continue;
497                    }
498                    I_CmsHistoryResource newHistRes = internalMergeResource(histRes, res, 0);
499                    // add interesting versions, in the right order
500                    result.add(0, newHistRes);
501                    lastHistRes = newHistRes;
502                }
503                m_sqlManager.closeAll(dbc, null, stmt, res);
504            }
505            // iterate from the NEWEST to the OLDEST versions (publish tag descendant)
506            for (int i = 0; i < historyResources.size(); i++) {
507                I_CmsHistoryResource histRes = historyResources.get(i);
508                result.add(histRes);
509                if (i < (historyResources.size() - 1)) {
510                    // this is one older direct version than histRes (histRes.getPublishTag() > histRes2.getPublishTag())
511                    I_CmsHistoryResource histRes2 = historyResources.get(i + 1);
512
513                    // look for resource changes in between of the direct versions in ascendent order
514                    stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_BTW_VERSIONS");
515                    stmt.setString(1, histRes.getResourceId().toString());
516                    stmt.setInt(2, histRes2.getPublishTag()); // lower limit
517                    stmt.setInt(3, histRes.getPublishTag()); // upper limit
518                    res = stmt.executeQuery();
519
520                    int pos = result.size();
521                    I_CmsHistoryResource lastHistRes = histRes2;
522                    while (res.next()) {
523                        int resVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
524                        if (resVersion == lastHistRes.getResourceVersion()) {
525                            // skip not interesting versions
526                            continue;
527                        }
528                        I_CmsHistoryResource newHistRes = internalMergeResource(histRes2, res, 0);
529                        // add interesting versions, in the right order
530                        result.add(pos, newHistRes);
531                        lastHistRes = newHistRes;
532                    }
533                    m_sqlManager.closeAll(dbc, null, stmt, res);
534                }
535            }
536            if (!result.isEmpty()) {
537                // get the oldest version
538                I_CmsHistoryResource histRes = result.get(result.size() - 1);
539
540                if (histRes.getVersion() > 1) {
541                    // look for older resource versions, in descendant order
542                    stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_OLD_VERSIONS");
543                    stmt.setString(1, histRes.getResourceId().toString());
544                    stmt.setInt(2, histRes.getPublishTag());
545                    res = stmt.executeQuery();
546
547                    int offset = (histRes.getStructureVersion() > 0 ? 1 : 0);
548
549                    I_CmsHistoryResource lastHistRes = histRes;
550                    while (res.next()) {
551                        I_CmsHistoryResource newHistRes = internalMergeResource(histRes, res, offset);
552                        if (newHistRes.getResourceVersion() != lastHistRes.getResourceVersion()) {
553                            // only add interesting versions
554                            if (offset == 1) {
555                                if (histRes != lastHistRes) {
556                                    result.add(lastHistRes);
557                                }
558                            } else {
559                                result.add(newHistRes);
560                            }
561                        }
562                        lastHistRes = newHistRes;
563                    }
564                    // add the last one if there is one
565                    if ((offset == 1) && (lastHistRes != histRes)) {
566                        result.add(lastHistRes);
567                    }
568                    m_sqlManager.closeAll(dbc, null, stmt, res);
569                }
570            }
571        } catch (SQLException e) {
572            throw new CmsDbSqlException(
573                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
574                e);
575        } finally {
576            m_sqlManager.closeAll(dbc, conn, stmt, res);
577        }
578
579        return result;
580    }
581
582    /**
583     * @see org.opencms.db.I_CmsHistoryDriver#readContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
584     */
585    public byte[] readContent(CmsDbContext dbc, CmsUUID resourceId, int publishTag) throws CmsDataAccessException {
586
587        Connection conn = null;
588        PreparedStatement stmt = null;
589        ResultSet res = null;
590        byte[] content = null;
591
592        try {
593            conn = m_sqlManager.getConnection(dbc);
594            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_CONTENT");
595            stmt.setString(1, resourceId.toString());
596            stmt.setInt(2, publishTag);
597            stmt.setInt(3, publishTag);
598            res = stmt.executeQuery();
599
600            if (res.next()) {
601                content = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
602                while (res.next()) {
603                    // do nothing only move through all rows because of mssql odbc driver
604                }
605            }
606        } catch (SQLException e) {
607            throw new CmsDbSqlException(
608                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
609                e);
610        } finally {
611            m_sqlManager.closeAll(dbc, conn, stmt, res);
612        }
613        return content;
614    }
615
616    /**
617     * @see org.opencms.db.I_CmsHistoryDriver#readDeletedResources(CmsDbContext, CmsUUID, CmsUUID)
618     */
619    public List<I_CmsHistoryResource> readDeletedResources(CmsDbContext dbc, CmsUUID structureId, CmsUUID userId)
620    throws CmsDataAccessException {
621
622        Connection conn = null;
623        PreparedStatement stmt = null;
624        ResultSet res = null;
625        List<I_CmsHistoryResource> result = new ArrayList<I_CmsHistoryResource>();
626        List<I_CmsHistoryResource> tmpHistRes = new ArrayList<I_CmsHistoryResource>();
627
628        try {
629            conn = m_sqlManager.getConnection(dbc);
630            if (userId == null) {
631                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED");
632            } else {
633                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED_RESTRICTED");
634            }
635            stmt.setString(1, structureId.toString());
636            if (userId != null) {
637                stmt.setString(2, userId.toString());
638            }
639            res = stmt.executeQuery();
640            while (res.next()) {
641                // store the result into a temporary list
642                tmpHistRes.add(internalCreateResource(res));
643            }
644        } catch (SQLException e) {
645            throw new CmsDbSqlException(
646                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
647                e);
648        } finally {
649            m_sqlManager.closeAll(dbc, conn, stmt, res);
650        }
651        I_CmsVfsDriver vfsDriver = m_driverManager.getVfsDriver(dbc);
652        for (I_CmsHistoryResource histRes : tmpHistRes) {
653            if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(), histRes.getStructureId())) {
654                // only add resources that are really deleted
655                continue;
656            }
657            result.add(histRes);
658        }
659
660        if (!result.isEmpty()
661            || (dbc.getRequestContext() == null)
662            || (dbc.getRequestContext().getAttribute("ATTR_RESOURCE_NAME") == null)) {
663            return result;
664        }
665        try {
666            conn = m_sqlManager.getConnection(dbc);
667            if (userId == null) {
668                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED_NAME");
669            } else {
670                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_DELETED_NAME_RESTRICTED");
671            }
672            String path = dbc.getRequestContext().getAttribute("ATTR_RESOURCE_NAME").toString();
673            stmt.setString(1, path + '%');
674            stmt.setString(2, path);
675            if (userId != null) {
676                stmt.setString(3, userId.toString());
677            }
678            res = stmt.executeQuery();
679            // clear the temporary list
680            tmpHistRes.clear();
681            while (res.next()) {
682                // store the result into a temporary list
683                tmpHistRes.add(internalCreateResource(res));
684            }
685        } catch (SQLException e) {
686            throw new CmsDbSqlException(
687                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
688                e);
689        } finally {
690            m_sqlManager.closeAll(dbc, conn, stmt, res);
691        }
692        for (I_CmsHistoryResource histRes : tmpHistRes) {
693            if (vfsDriver.validateStructureIdExists(dbc, dbc.currentProject().getUuid(), histRes.getStructureId())) {
694                // only add resources that are really deleted
695                continue;
696            }
697            result.add(histRes);
698        }
699        return result;
700    }
701
702    /**
703     * @see org.opencms.db.I_CmsHistoryDriver#readLastVersion(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
704     */
705    public int readLastVersion(CmsDbContext dbc, CmsUUID structureId) throws CmsDataAccessException {
706
707        PreparedStatement stmt = null;
708        Connection conn = null;
709        ResultSet res = null;
710        int lastVersion = 0;
711
712        try {
713            conn = m_sqlManager.getConnection(dbc);
714            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_MAXVER");
715            stmt.setString(1, structureId.toString());
716            res = stmt.executeQuery();
717
718            if (res.next()) {
719                lastVersion = res.getInt(1);
720                while (res.next()) {
721                    // do nothing only move through all rows because of mssql odbc driver
722                }
723            } else {
724                lastVersion = 0;
725            }
726        } catch (SQLException e) {
727            throw new CmsDbSqlException(
728                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
729                e);
730        } finally {
731            m_sqlManager.closeAll(dbc, conn, stmt, res);
732        }
733
734        return lastVersion;
735    }
736
737    /**
738     * @see org.opencms.db.I_CmsHistoryDriver#readMaxPublishTag(CmsDbContext, CmsUUID)
739     */
740    public int readMaxPublishTag(CmsDbContext dbc, CmsUUID resourceId) throws CmsDataAccessException {
741
742        PreparedStatement stmt = null;
743        Connection conn = null;
744        ResultSet res = null;
745        int result = 0;
746
747        try {
748            conn = m_sqlManager.getConnection(dbc);
749            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_READ_MAX_PUBLISH_TAG");
750            stmt.setString(1, resourceId.toString());
751            res = stmt.executeQuery();
752
753            if (res.next()) {
754                result = res.getInt(1);
755                while (res.next()) {
756                    // do nothing only move through all rows because of mssql odbc driver
757                }
758            }
759        } catch (SQLException e) {
760            throw new CmsDbSqlException(
761                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
762                e);
763        } finally {
764            m_sqlManager.closeAll(dbc, conn, stmt, res);
765        }
766
767        return result;
768    }
769
770    /**
771     * @see org.opencms.db.I_CmsHistoryDriver#readNextPublishTag(org.opencms.db.CmsDbContext)
772     */
773    public int readNextPublishTag(CmsDbContext dbc) {
774
775        PreparedStatement stmt = null;
776        Connection conn = null;
777        ResultSet res = null;
778        int projectPublishTag = 1;
779        int resourcePublishTag = 1;
780
781        try {
782            // get the max publish tag from project history
783            conn = m_sqlManager.getConnection(dbc);
784            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_MAXTAG");
785            res = stmt.executeQuery();
786
787            if (res.next()) {
788                projectPublishTag = res.getInt(1) + 1;
789                while (res.next()) {
790                    // do nothing only move through all rows because of mssql odbc driver
791                }
792            }
793        } catch (SQLException exc) {
794            LOG.error(Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), exc);
795        } finally {
796            m_sqlManager.closeAll(dbc, null, stmt, res);
797        }
798
799        try {
800            // get the max publish tag from resource history
801            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_MAXTAG");
802            res = stmt.executeQuery();
803
804            if (res.next()) {
805                resourcePublishTag = res.getInt(1) + 1;
806                while (res.next()) {
807                    // do nothing only move through all rows because of mssql odbc driver
808                }
809            }
810        } catch (SQLException exc) {
811            LOG.error(Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), exc);
812        } finally {
813            m_sqlManager.closeAll(dbc, null, stmt, res);
814        }
815
816        // keep the biggest
817        if (resourcePublishTag > projectPublishTag) {
818            projectPublishTag = resourcePublishTag;
819        }
820
821        try {
822            // get the max publish tag from contents
823            stmt = m_sqlManager.getPreparedStatement(conn, "C_CONTENT_PUBLISH_MAXTAG");
824            res = stmt.executeQuery();
825
826            if (res.next()) {
827                resourcePublishTag = res.getInt(1) + 1;
828                while (res.next()) {
829                    // do nothing only move through all rows because of mssql odbc driver
830                }
831            }
832        } catch (SQLException exc) {
833            LOG.error(Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), exc);
834        } finally {
835            m_sqlManager.closeAll(dbc, conn, stmt, res);
836        }
837
838        // return the biggest
839        if (resourcePublishTag > projectPublishTag) {
840            projectPublishTag = resourcePublishTag;
841        }
842
843        return projectPublishTag;
844    }
845
846    /**
847     * @see org.opencms.db.I_CmsHistoryDriver#readPrincipal(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID)
848     */
849    public CmsHistoryPrincipal readPrincipal(CmsDbContext dbc, CmsUUID principalId) throws CmsDataAccessException {
850
851        Connection conn = null;
852        PreparedStatement stmt = null;
853        ResultSet res = null;
854        CmsHistoryPrincipal historyPrincipal = null;
855
856        try {
857            conn = m_sqlManager.getConnection(dbc);
858            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_PRINCIPAL_READ");
859            stmt.setString(1, principalId.toString());
860            res = stmt.executeQuery();
861            if (res.next()) {
862                String userName = res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_NAME"));
863                String ou = CmsOrganizationalUnit.removeLeadingSeparator(
864                    res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_OU")));
865                historyPrincipal = new CmsHistoryPrincipal(
866                    principalId,
867                    ou + userName,
868                    res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_DESCRIPTION")),
869                    res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_EMAIL")),
870                    res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_TYPE")),
871                    new CmsUUID(res.getString(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_USERDELETED"))),
872                    res.getLong(m_sqlManager.readQuery("C_PRINCIPALS_HISTORY_DATEDELETED")));
873                while (res.next()) {
874                    // do nothing only move through all rows because of mssql odbc driver
875                }
876            } else {
877                throw new CmsDbEntryNotFoundException(
878                    Messages.get().container(Messages.ERR_HISTORY_PRINCIPAL_NOT_FOUND_1, principalId));
879            }
880        } catch (SQLException e) {
881            throw new CmsDbSqlException(
882                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
883                e);
884        } finally {
885            m_sqlManager.closeAll(dbc, conn, stmt, res);
886        }
887        return historyPrincipal;
888    }
889
890    /**
891     * @see org.opencms.db.I_CmsHistoryDriver#readProject(org.opencms.db.CmsDbContext, CmsUUID)
892     */
893    public CmsHistoryProject readProject(CmsDbContext dbc, CmsUUID projectId) throws CmsDataAccessException {
894
895        PreparedStatement stmt = null;
896        CmsHistoryProject project = null;
897        ResultSet res = null;
898        Connection conn = null;
899
900        int tmpTag;
901
902        try {
903            conn = m_sqlManager.getConnection(dbc);
904            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ_BYID");
905
906            stmt.setString(1, projectId.toString());
907            res = stmt.executeQuery();
908
909            if (res.next()) {
910                tmpTag = res.getInt(m_sqlManager.readQuery("C_PROJECTS_PUBLISH_TAG_0"));
911                project = internalCreateProject(res, null);
912                while (res.next()) {
913                    // do nothing only move through all rows because of mssql odbc driver
914                }
915            } else {
916                throw new CmsDbEntryNotFoundException(
917                    Messages.get().container(Messages.ERR_NO_HISTORY_PROJECT_WITH_ID_1, projectId));
918            }
919        } catch (SQLException e) {
920            throw new CmsDbSqlException(
921                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
922                e);
923        } finally {
924            m_sqlManager.closeAll(dbc, conn, stmt, res);
925        }
926
927        List<String> projectresources = readProjectResources(dbc, tmpTag);
928        project.setProjectResources(projectresources);
929
930        return project;
931    }
932
933    /**
934     * @see org.opencms.db.I_CmsHistoryDriver#readProject(org.opencms.db.CmsDbContext, int)
935     */
936    public CmsHistoryProject readProject(CmsDbContext dbc, int publishTag) throws CmsDataAccessException {
937
938        PreparedStatement stmt = null;
939        CmsHistoryProject project = null;
940        ResultSet res = null;
941        Connection conn = null;
942        try {
943            conn = m_sqlManager.getConnection(dbc);
944            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ");
945
946            stmt.setInt(1, publishTag);
947            res = stmt.executeQuery();
948
949            if (res.next()) {
950                project = internalCreateProject(res, null);
951                while (res.next()) {
952                    // do nothing only move through all rows because of mssql odbc driver
953                }
954            } else {
955                throw new CmsDbEntryNotFoundException(
956                    Messages.get().container(Messages.ERR_NO_HISTORY_PROJECT_WITH_TAG_ID_1, Integer.valueOf(publishTag)));
957            }
958        } catch (SQLException e) {
959            throw new CmsDbSqlException(
960                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
961                e);
962        } finally {
963            m_sqlManager.closeAll(dbc, conn, stmt, res);
964        }
965
966        List<String> projectresources = readProjectResources(dbc, publishTag);
967        project.setProjectResources(projectresources);
968
969        return project;
970    }
971
972    /**
973     * @see org.opencms.db.I_CmsHistoryDriver#readProjectResources(org.opencms.db.CmsDbContext, int)
974     */
975    public List<String> readProjectResources(CmsDbContext dbc, int publishTag) throws CmsDataAccessException {
976
977        PreparedStatement stmt = null;
978        Connection conn = null;
979        ResultSet res = null;
980        List<String> projectResources = new ArrayList<String>();
981
982        try {
983            conn = m_sqlManager.getConnection(dbc);
984            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTRESOURCES_HISTORY_READ");
985            stmt.setInt(1, publishTag);
986            res = stmt.executeQuery();
987            while (res.next()) {
988                projectResources.add(res.getString("RESOURCE_PATH"));
989            }
990        } catch (SQLException e) {
991            throw new CmsDbSqlException(
992                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
993                e);
994        } finally {
995            m_sqlManager.closeAll(dbc, conn, stmt, res);
996        }
997
998        return projectResources;
999    }
1000
1001    /**
1002     * @see org.opencms.db.I_CmsHistoryDriver#readProjects(org.opencms.db.CmsDbContext)
1003     */
1004    public List<CmsHistoryProject> readProjects(CmsDbContext dbc) throws CmsDataAccessException {
1005
1006        List<CmsHistoryProject> projects = new ArrayList<CmsHistoryProject>();
1007        ResultSet res = null;
1008        PreparedStatement stmt = null;
1009        Connection conn = null;
1010
1011        Map<Integer, CmsHistoryProject> tmpProjects = new HashMap<Integer, CmsHistoryProject>();
1012
1013        try {
1014            // create the statement
1015            conn = m_sqlManager.getConnection(dbc);
1016            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ_ALL");
1017            res = stmt.executeQuery();
1018
1019            // this is not really efficient
1020            // but it is overriden in all db specific implementations, including mysql
1021            int i = 0;
1022            int max = 300;
1023
1024            while (res.next() && (i < max)) {
1025                tmpProjects.put(Integer.valueOf(res.getInt("PUBLISH_TAG")), internalCreateProject(res, null));
1026                i++;
1027            }
1028        } catch (SQLException e) {
1029            throw new CmsDbSqlException(
1030                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1031                e);
1032        } finally {
1033            m_sqlManager.closeAll(dbc, conn, stmt, res);
1034        }
1035
1036        for (Map.Entry<Integer, CmsHistoryProject> entry : tmpProjects.entrySet()) {
1037            List<String> resources = readProjectResources(dbc, entry.getKey().intValue());
1038            entry.getValue().setProjectResources(resources);
1039            projects.add(entry.getValue());
1040        }
1041
1042        return projects;
1043    }
1044
1045    /**
1046     * @see org.opencms.db.I_CmsHistoryDriver#readProperties(org.opencms.db.CmsDbContext, org.opencms.file.history.I_CmsHistoryResource)
1047     */
1048    public List<CmsProperty> readProperties(CmsDbContext dbc, I_CmsHistoryResource resource)
1049    throws CmsDataAccessException {
1050
1051        ResultSet res = null;
1052        PreparedStatement stmt = null;
1053        Connection conn = null;
1054
1055        Map<String, CmsProperty> propertyMap = new HashMap<String, CmsProperty>();
1056
1057        try {
1058            conn = m_sqlManager.getConnection(dbc);
1059
1060            // get the latest properties for this sibling
1061            int pubTag = -1;
1062            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_READ_PUBTAG");
1063            stmt.setString(1, resource.getStructureId().toString());
1064            stmt.setInt(2, resource.getPublishTag());
1065            res = stmt.executeQuery();
1066            if (res.next()) {
1067                pubTag = res.getInt(1);
1068                while (res.next()) {
1069                    // do nothing only move through all rows because of mssql odbc driver
1070                }
1071            }
1072            m_sqlManager.closeAll(dbc, null, stmt, res);
1073
1074            if (pubTag > 0) {
1075                // add the siblings props
1076                stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_READALL_STR");
1077                stmt.setString(1, resource.getStructureId().toString());
1078                stmt.setInt(2, pubTag);
1079                res = stmt.executeQuery();
1080
1081                while (res.next()) {
1082                    String propertyKey = res.getString(1);
1083                    String propertyValue = res.getString(2);
1084                    int mappingType = res.getInt(3);
1085
1086                    internalAddToPropMap(propertyMap, resource, propertyKey, propertyValue, mappingType);
1087                }
1088                m_sqlManager.closeAll(dbc, null, stmt, res);
1089            }
1090
1091            if (pubTag != resource.getPublishTag()) {
1092                // check if there were newer shared properties modifications
1093                stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_READALL_RES");
1094                stmt.setString(1, resource.getStructureId().toString());
1095                stmt.setInt(2, resource.getPublishTag());
1096                res = stmt.executeQuery();
1097
1098                while (res.next()) {
1099                    String propertyKey = res.getString(1);
1100                    String propertyValue = res.getString(2);
1101                    int mappingType = res.getInt(3);
1102
1103                    internalAddToPropMap(propertyMap, resource, propertyKey, propertyValue, mappingType);
1104                }
1105                m_sqlManager.closeAll(dbc, null, stmt, res);
1106            }
1107        } catch (SQLException e) {
1108            throw new CmsDbSqlException(
1109                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1110                e);
1111        } finally {
1112            m_sqlManager.closeAll(dbc, conn, stmt, res);
1113        }
1114
1115        return new ArrayList<CmsProperty>(propertyMap.values());
1116    }
1117
1118    /**
1119     * @see org.opencms.db.I_CmsHistoryDriver#readPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String)
1120     */
1121    public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name) throws CmsDataAccessException {
1122
1123        CmsPropertyDefinition propDef = null;
1124        ResultSet res = null;
1125        PreparedStatement stmt = null;
1126        Connection conn = null;
1127
1128        try {
1129            conn = m_sqlManager.getConnection(dbc);
1130            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTYDEF_READ_HISTORY");
1131            stmt.setString(1, name);
1132            res = stmt.executeQuery();
1133
1134            if (res.next()) {
1135                propDef = new CmsPropertyDefinition(
1136                    new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_ID"))),
1137                    res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_NAME")),
1138                    CmsPropertyDefinition.CmsPropertyType.valueOf(
1139                        res.getInt(m_sqlManager.readQuery("C_PROPERTYDEF_TYPE"))));
1140                while (res.next()) {
1141                    // do nothing only move through all rows because of mssql odbc driver
1142                }
1143            } else {
1144                throw new CmsDbEntryNotFoundException(
1145                    Messages.get().container(Messages.ERR_NO_PROPERTYDEF_WITH_NAME_1, name));
1146            }
1147        } catch (SQLException e) {
1148            throw new CmsDbSqlException(
1149                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1150                e);
1151        } finally {
1152            m_sqlManager.closeAll(dbc, conn, stmt, res);
1153        }
1154
1155        return propDef;
1156    }
1157
1158    /**
1159     * @see org.opencms.db.I_CmsHistoryDriver#readPublishTag(org.opencms.db.CmsDbContext, long)
1160     */
1161    public int readPublishTag(CmsDbContext dbc, long maxdate) throws CmsDataAccessException {
1162
1163        ResultSet res = null;
1164        PreparedStatement stmt = null;
1165        Connection conn = null;
1166        int maxVersion = 0;
1167
1168        try {
1169            conn = m_sqlManager.getConnection(dbc);
1170            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_READ_TAG_FOR_DATE");
1171            stmt.setLong(1, maxdate);
1172            res = stmt.executeQuery();
1173            if (res.next()) {
1174                maxVersion = res.getInt(1);
1175                while (res.next()) {
1176                    // do nothing only move through all rows because of mssql odbc driver
1177                }
1178            }
1179        } catch (SQLException e) {
1180            throw new CmsDbSqlException(
1181                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1182                e);
1183        } finally {
1184            m_sqlManager.closeAll(dbc, conn, stmt, res);
1185        }
1186        return maxVersion;
1187    }
1188
1189    /**
1190     * @see org.opencms.db.I_CmsHistoryDriver#readResource(CmsDbContext, CmsUUID, int)
1191     */
1192    public I_CmsHistoryResource readResource(CmsDbContext dbc, CmsUUID structureId, int version)
1193    throws CmsDataAccessException {
1194
1195        I_CmsHistoryResource resource = null;
1196        PreparedStatement stmt = null;
1197        ResultSet res = null;
1198        Connection conn = null;
1199
1200        try {
1201            conn = m_sqlManager.getConnection(dbc);
1202            stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_READ_VERSION");
1203            stmt.setString(1, structureId.toString());
1204            stmt.setInt(2, version);
1205            res = stmt.executeQuery();
1206            if (res.next()) {
1207                resource = internalCreateResource(res);
1208                while (res.next()) {
1209                    // do nothing only move through all rows because of mssql odbc driver
1210                }
1211            } else {
1212                throw new CmsVfsResourceNotFoundException(
1213                    Messages.get().container(Messages.ERR_HISTORY_FILE_NOT_FOUND_1, structureId));
1214            }
1215        } catch (SQLException e) {
1216            throw new CmsDbSqlException(
1217                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1218                e);
1219        } finally {
1220            m_sqlManager.closeAll(dbc, conn, stmt, res);
1221        }
1222        return resource;
1223    }
1224
1225    /**
1226     * @see org.opencms.db.I_CmsHistoryDriver#setDriverManager(org.opencms.db.CmsDriverManager)
1227     */
1228    public void setDriverManager(CmsDriverManager driverManager) {
1229
1230        m_driverManager = driverManager;
1231    }
1232
1233    /**
1234     * @see org.opencms.db.I_CmsHistoryDriver#setSqlManager(org.opencms.db.CmsSqlManager)
1235     */
1236    public void setSqlManager(org.opencms.db.CmsSqlManager sqlManager) {
1237
1238        m_sqlManager = (CmsSqlManager)sqlManager;
1239    }
1240
1241    /**
1242     * @see org.opencms.db.I_CmsHistoryDriver#writePrincipal(CmsDbContext, org.opencms.security.I_CmsPrincipal)
1243     */
1244    public void writePrincipal(CmsDbContext dbc, I_CmsPrincipal principal) throws CmsDataAccessException {
1245
1246        Connection conn = null;
1247        PreparedStatement stmt = null;
1248
1249        try {
1250            // check if the principal was already saved
1251            readPrincipal(dbc, principal.getId());
1252            return;
1253        } catch (CmsDbEntryNotFoundException e) {
1254            // ok
1255        }
1256        try {
1257            conn = m_sqlManager.getConnection(dbc);
1258            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_PRINCIPAL_CREATE");
1259            stmt.setString(1, principal.getId().toString());
1260            stmt.setString(2, principal.getSimpleName());
1261            String desc = principal.getDescription();
1262            desc = CmsStringUtil.isEmptyOrWhitespaceOnly(desc) ? "-" : desc;
1263            stmt.setString(3, desc);
1264            stmt.setString(4, CmsOrganizationalUnit.SEPARATOR + principal.getOuFqn());
1265            if (principal instanceof CmsUser) {
1266                String email = ((CmsUser)principal).getEmail();
1267                email = CmsStringUtil.isEmptyOrWhitespaceOnly(email) ? "-" : email;
1268                stmt.setString(5, email);
1269                stmt.setString(6, I_CmsPrincipal.PRINCIPAL_USER);
1270            } else {
1271                stmt.setString(5, "-");
1272                stmt.setString(6, I_CmsPrincipal.PRINCIPAL_GROUP);
1273            }
1274            stmt.setString(7, dbc.currentUser().getId().toString());
1275            stmt.setLong(8, System.currentTimeMillis());
1276
1277            stmt.executeUpdate();
1278        } catch (SQLException e) {
1279            throw new CmsDbSqlException(
1280                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1281                e);
1282        } finally {
1283            m_sqlManager.closeAll(dbc, conn, stmt, null);
1284        }
1285    }
1286
1287    /**
1288     * @see org.opencms.db.I_CmsHistoryDriver#writeProject(org.opencms.db.CmsDbContext, int, long)
1289     */
1290    public void writeProject(CmsDbContext dbc, int publishTag, long publishDate) throws CmsDataAccessException {
1291
1292        CmsProject currentProject = dbc.currentProject();
1293        CmsUser currentUser = dbc.currentUser();
1294
1295        List<String> projectresources = m_driverManager.getProjectDriver(dbc).readProjectResources(dbc, currentProject);
1296
1297        // write historical project to the database
1298        Connection conn = null;
1299        PreparedStatement stmt = null;
1300        try {
1301            conn = m_sqlManager.getConnection(dbc);
1302
1303            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTS_HISTORY_CREATE");
1304            // first write the project
1305            stmt.setInt(1, publishTag);
1306            stmt.setString(2, currentProject.getUuid().toString());
1307            stmt.setString(3, currentProject.getSimpleName());
1308            stmt.setLong(4, publishDate);
1309            stmt.setString(5, currentUser.getId().toString());
1310            stmt.setString(6, currentProject.getOwnerId().toString());
1311            stmt.setString(7, currentProject.getGroupId().toString());
1312            stmt.setString(8, currentProject.getManagerGroupId().toString());
1313            stmt.setString(9, currentProject.getDescription());
1314            stmt.setLong(10, currentProject.getDateCreated());
1315            stmt.setInt(11, currentProject.getType().getMode());
1316            stmt.setString(12, CmsOrganizationalUnit.SEPARATOR + currentProject.getOuFqn());
1317            stmt.executeUpdate();
1318
1319            m_sqlManager.closeAll(dbc, null, stmt, null);
1320
1321            // now write the projectresources
1322            stmt = m_sqlManager.getPreparedStatement(conn, "C_PROJECTRESOURCES_HISTORY_CREATE");
1323            Iterator<String> i = projectresources.iterator();
1324            while (i.hasNext()) {
1325                stmt.setInt(1, publishTag);
1326                stmt.setString(2, currentProject.getUuid().toString());
1327                stmt.setString(3, i.next());
1328                stmt.executeUpdate();
1329                stmt.clearParameters();
1330            }
1331        } catch (SQLException e) {
1332            throw new CmsDbSqlException(
1333                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1334                e);
1335        } finally {
1336            m_sqlManager.closeAll(dbc, conn, stmt, null);
1337        }
1338    }
1339
1340    /**
1341     * @see org.opencms.db.I_CmsHistoryDriver#writeProperties(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, java.util.List, int)
1342     */
1343    public void writeProperties(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties, int publishTag)
1344    throws CmsDataAccessException {
1345
1346        Connection conn = null;
1347        PreparedStatement stmt = null;
1348
1349        Map<CmsProperty, CmsPropertyDefinition> propDefs = new HashMap<CmsProperty, CmsPropertyDefinition>();
1350
1351        try {
1352            for (CmsProperty property : properties) {
1353                CmsPropertyDefinition propDef = null;
1354                try {
1355                    propDef = readPropertyDefinition(dbc, property.getName());
1356                } catch (CmsDbEntryNotFoundException e) {
1357                    // create if missing
1358                    propDef = createPropertyDefinition(dbc, property.getName(), CmsPropertyDefinition.TYPE_NORMAL);
1359                }
1360                propDefs.put(property, propDef);
1361            }
1362        } catch (CmsDataAccessException e) {
1363            throw new CmsDbSqlException(
1364                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1365                e);
1366        } finally {
1367            m_sqlManager.closeAll(dbc, conn, stmt, null);
1368        }
1369
1370        try {
1371            conn = m_sqlManager.getConnection(dbc);
1372            for (Map.Entry<CmsProperty, CmsPropertyDefinition> entry : propDefs.entrySet()) {
1373
1374                for (int i = 0; i < 2; i++) {
1375                    int mappingType;
1376                    String value;
1377                    CmsUUID id;
1378                    if (i == 0) {
1379                        // write the structure value on the first cycle
1380                        value = entry.getKey().getStructureValue();
1381                        mappingType = CmsProperty.STRUCTURE_RECORD_MAPPING;
1382                        id = resource.getStructureId();
1383                        if (CmsStringUtil.isEmpty(value)) {
1384                            continue;
1385                        }
1386                    } else {
1387                        // write the resource value on the second cycle
1388                        value = entry.getKey().getResourceValue();
1389                        mappingType = CmsProperty.RESOURCE_RECORD_MAPPING;
1390                        id = resource.getResourceId();
1391                        if (CmsStringUtil.isEmpty(value)) {
1392                            break;
1393                        }
1394                    }
1395
1396                    stmt = m_sqlManager.getPreparedStatement(conn, "C_PROPERTIES_HISTORY_CREATE");
1397
1398                    stmt.setString(1, resource.getStructureId().toString());
1399                    stmt.setString(2, entry.getValue().getId().toString());
1400                    stmt.setString(3, id.toString());
1401                    stmt.setInt(4, mappingType);
1402                    stmt.setString(5, m_sqlManager.validateEmpty(value));
1403                    stmt.setInt(6, publishTag);
1404
1405                    stmt.executeUpdate();
1406                    m_sqlManager.closeAll(dbc, null, stmt, null);
1407                }
1408            }
1409        } catch (SQLException e) {
1410            throw new CmsDbSqlException(
1411                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1412                e);
1413        } finally {
1414            m_sqlManager.closeAll(dbc, conn, stmt, null);
1415        }
1416    }
1417
1418    /**
1419     * @see org.opencms.db.I_CmsHistoryDriver#writeResource(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, java.util.List, int)
1420     */
1421    public void writeResource(CmsDbContext dbc, CmsResource resource, List<CmsProperty> properties, int publishTag)
1422    throws CmsDataAccessException {
1423
1424        Connection conn = null;
1425        PreparedStatement stmt = null;
1426
1427        try {
1428            boolean valResource = internalValidateResource(dbc, resource, publishTag);
1429            int sibCount = resource.getSiblingCount();
1430
1431            // if deleted
1432            if (resource.getState().isDeleted()) {
1433                // if it is a file
1434                if (resource instanceof CmsFile) {
1435                    if (!valResource) {
1436                        if (sibCount < 2) {
1437                            // copy from offline content to content tables
1438                            // so that the history contains the last state of the file
1439                            m_driverManager.getVfsDriver(dbc).createOnlineContent(
1440                                dbc,
1441                                resource.getResourceId(),
1442                                ((CmsFile)resource).getContents(),
1443                                publishTag,
1444                                false,
1445                                true);
1446                        } else {
1447                            @SuppressWarnings("unchecked")
1448                            Set<CmsUUID> changedAndDeleted = (Set<CmsUUID>)dbc.getAttribute(
1449                                CmsDriverManager.KEY_CHANGED_AND_DELETED);
1450                            if ((changedAndDeleted == null) || !changedAndDeleted.contains(resource.getResourceId())) {
1451                                // put the content definitively in the history if no sibling is left
1452                                // (unless another sibling with status "changed" or "new" is published)
1453                                m_driverManager.getVfsDriver(dbc).createOnlineContent(
1454                                    dbc,
1455                                    resource.getResourceId(),
1456                                    ((CmsFile)resource).getContents(),
1457                                    publishTag,
1458                                    true,
1459                                    false);
1460                            }
1461                        }
1462                    }
1463                }
1464
1465                // update version numbers
1466                m_driverManager.getVfsDriver(dbc).publishVersions(dbc, resource, !valResource);
1467            }
1468
1469            // read the version numbers
1470            Map<String, Integer> versions = m_driverManager.getVfsDriver(dbc).readVersions(
1471                dbc,
1472                CmsProject.ONLINE_PROJECT_ID,
1473                resource.getResourceId(),
1474                resource.getStructureId());
1475            int structureVersion = (versions.get("structure")).intValue();
1476            int resourceVersion = (versions.get("resource")).intValue();
1477
1478            CmsUUID parentId = CmsUUID.getNullUUID();
1479            CmsFolder parent = m_driverManager.getVfsDriver(dbc).readParentFolder(
1480                dbc,
1481                CmsProject.ONLINE_PROJECT_ID,
1482                resource.getStructureId());
1483            if (parent != null) {
1484                parentId = parent.getStructureId();
1485            }
1486
1487            conn = m_sqlManager.getConnection(dbc);
1488            if (!valResource) {
1489                // write the resource
1490                stmt = m_sqlManager.getPreparedStatement(conn, "C_RESOURCES_HISTORY_WRITE");
1491                stmt.setString(1, resource.getResourceId().toString());
1492                stmt.setInt(2, resource.getTypeId());
1493                stmt.setInt(3, resource.getFlags());
1494                stmt.setLong(4, resource.getDateCreated());
1495                stmt.setString(5, resource.getUserCreated().toString());
1496                stmt.setLong(6, resource.getDateLastModified());
1497                stmt.setString(7, resource.getUserLastModified().toString());
1498                stmt.setInt(8, resource.getState().getState());
1499                stmt.setInt(9, resource.getLength());
1500                stmt.setLong(10, resource.getDateContent());
1501                stmt.setString(11, dbc.currentProject().getUuid().toString());
1502                stmt.setInt(12, resource.getSiblingCount());
1503                stmt.setInt(13, resourceVersion);
1504                stmt.setInt(14, publishTag);
1505                stmt.executeUpdate();
1506                m_sqlManager.closeAll(dbc, null, stmt, null);
1507            }
1508            // write the structure
1509            stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_WRITE");
1510            stmt.setString(1, resource.getStructureId().toString());
1511            stmt.setString(2, resource.getResourceId().toString());
1512            stmt.setString(3, resource.getRootPath());
1513            stmt.setInt(4, resource.getState().getState());
1514            stmt.setLong(5, resource.getDateReleased());
1515            stmt.setLong(6, resource.getDateExpired());
1516            stmt.setInt(7, structureVersion);
1517            stmt.setString(8, parentId.toString());
1518            stmt.setInt(9, publishTag);
1519            stmt.setInt(10, resource.getVersion());
1520            stmt.executeUpdate();
1521        } catch (SQLException e) {
1522            throw new CmsDbSqlException(
1523                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1524                e);
1525        } finally {
1526            m_sqlManager.closeAll(dbc, conn, stmt, null);
1527        }
1528
1529        writeProperties(dbc, resource, properties, publishTag);
1530    }
1531
1532    /**
1533     * Updates the property map for the given resource with the given property data.<p>
1534     *
1535     * @param propertyMap the map to update
1536     * @param resource the resource the properties belong to
1537     * @param propertyKey the property key
1538     * @param propertyValue the property value
1539     * @param mappingType the mapping type
1540     *
1541     * @throws CmsDbConsistencyException if the mapping type is wrong
1542     */
1543    protected void internalAddToPropMap(
1544        Map<String, CmsProperty> propertyMap,
1545        I_CmsHistoryResource resource,
1546        String propertyKey,
1547        String propertyValue,
1548        int mappingType) throws CmsDbConsistencyException {
1549
1550        CmsProperty property = propertyMap.get(propertyKey);
1551        if (property != null) {
1552            // there exists already a property for this key in the result
1553            switch (mappingType) {
1554                case CmsProperty.STRUCTURE_RECORD_MAPPING:
1555                    // this property value is mapped to a structure record
1556                    property.setStructureValue(propertyValue);
1557                    break;
1558                case CmsProperty.RESOURCE_RECORD_MAPPING:
1559                    // this property value is mapped to a resource record
1560                    property.setResourceValue(propertyValue);
1561                    break;
1562                default:
1563                    throw new CmsDbConsistencyException(
1564                        Messages.get().container(
1565                            Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
1566                            resource.getRootPath(),
1567                            Integer.valueOf(mappingType),
1568                            propertyKey));
1569            }
1570        } else {
1571            // there doesn't exist a property for this key yet
1572            property = new CmsProperty();
1573            property.setName(propertyKey);
1574
1575            switch (mappingType) {
1576                case CmsProperty.STRUCTURE_RECORD_MAPPING:
1577                    // this property value is mapped to a structure record
1578                    property.setStructureValue(propertyValue);
1579                    property.setResourceValue(null);
1580                    break;
1581                case CmsProperty.RESOURCE_RECORD_MAPPING:
1582                    // this property value is mapped to a resource record
1583                    property.setStructureValue(null);
1584                    property.setResourceValue(propertyValue);
1585                    break;
1586                default:
1587                    throw new CmsDbConsistencyException(
1588                        Messages.get().container(
1589                            Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
1590                            resource.getRootPath(),
1591                            Integer.valueOf(mappingType),
1592                            propertyKey));
1593            }
1594            propertyMap.put(propertyKey, property);
1595        }
1596    }
1597
1598    /**
1599     * Deletes all historical entries of subresources of a folder without any historical netry left.<p>
1600     *
1601     * @param dbc the current database context
1602     * @param resource the resource to check
1603     *
1604     * @throws CmsDataAccessException if something goes wrong
1605     */
1606    protected void internalCleanup(CmsDbContext dbc, I_CmsHistoryResource resource) throws CmsDataAccessException {
1607
1608        Connection conn = null;
1609        PreparedStatement stmt = null;
1610        ResultSet res = null;
1611        Map<CmsUUID, Integer> tmpSubResources = new HashMap<CmsUUID, Integer>();
1612
1613        // if is folder and if no versions left
1614        boolean isFolderAndNoVersionLeft = resource.getRootPath().endsWith("/")
1615            && (readLastVersion(dbc, resource.getStructureId()) == 0);
1616
1617        // if the resource is a folder
1618        if (isFolderAndNoVersionLeft) {
1619            try {
1620                conn = m_sqlManager.getConnection(dbc);
1621                // get all direct subresources
1622                stmt = m_sqlManager.getPreparedStatement(conn, "C_STRUCTURE_HISTORY_READ_SUBRESOURCES");
1623                stmt.setString(1, resource.getStructureId().toString());
1624                res = stmt.executeQuery();
1625                while (res.next()) {
1626                    CmsUUID structureId = new CmsUUID(res.getString(1));
1627                    int version = res.getInt(2);
1628                    tmpSubResources.put(structureId, Integer.valueOf(version));
1629                }
1630            } catch (SQLException e) {
1631                throw new CmsDbSqlException(
1632                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1633                    e);
1634            } finally {
1635                m_sqlManager.closeAll(dbc, conn, stmt, res);
1636            }
1637        }
1638        // delete all subresource versions
1639        for (Map.Entry<CmsUUID, Integer> entry : tmpSubResources.entrySet()) {
1640            I_CmsHistoryResource histResource = readResource(dbc, entry.getKey(), entry.getValue().intValue());
1641            deleteEntries(dbc, histResource, 0, -1);
1642        }
1643    }
1644
1645    /**
1646     * Returns the amount of properties for a propertydefinition.<p>
1647     *
1648     * @param dbc the current database context
1649     * @param metadef the propertydefinition to test
1650     * @param projectId the ID of the current project
1651     *
1652     * @return the amount of properties for a propertydefinition
1653     * @throws CmsDataAccessException if something goes wrong
1654     */
1655    protected int internalCountProperties(CmsDbContext dbc, CmsPropertyDefinition metadef, CmsUUID projectId)
1656    throws CmsDataAccessException {
1657
1658        ResultSet res = null;
1659        PreparedStatement stmt = null;
1660        Connection conn = null;
1661
1662        int returnValue;
1663        try {
1664            // create statement
1665            conn = m_sqlManager.getConnection(dbc);
1666            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_READALL_COUNT");
1667            stmt.setString(1, metadef.getId().toString());
1668            res = stmt.executeQuery();
1669
1670            if (res.next()) {
1671                returnValue = res.getInt(1);
1672                while (res.next()) {
1673                    // do nothing only move through all rows because of mssql odbc driver
1674                }
1675            } else {
1676                throw new CmsDbConsistencyException(
1677                    Messages.get().container(Messages.ERR_NO_PROPERTIES_FOR_PROPERTYDEF_1, metadef.getName()));
1678            }
1679        } catch (SQLException e) {
1680            throw new CmsDbSqlException(
1681                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1682                e);
1683        } finally {
1684            m_sqlManager.closeAll(dbc, conn, stmt, res);
1685        }
1686        return returnValue;
1687    }
1688
1689    /**
1690     * Creates a historical project from the given result set and resources.<p>
1691     * @param res the resource set
1692     * @param resources the historical resources
1693     *
1694     * @return the historical project
1695     *
1696     * @throws SQLException if something goes wrong
1697     */
1698    protected CmsHistoryProject internalCreateProject(ResultSet res, List<String> resources) throws SQLException {
1699
1700        String ou = CmsOrganizationalUnit.removeLeadingSeparator(
1701            res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_OU_0")));
1702        CmsUUID publishedById = new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECT_PUBLISHED_BY_0")));
1703        CmsUUID userId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_USER_ID_0")));
1704        return new CmsHistoryProject(
1705            res.getInt(m_sqlManager.readQuery("C_PROJECTS_PUBLISH_TAG_0")),
1706            new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_ID_0"))),
1707            ou + res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_NAME_0")),
1708            res.getString(m_sqlManager.readQuery("C_PROJECTS_PROJECT_DESCRIPTION_0")),
1709            userId,
1710            new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_GROUP_ID_0"))),
1711            new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROJECTS_MANAGERGROUP_ID_0"))),
1712            res.getLong(m_sqlManager.readQuery("C_PROJECTS_DATE_CREATED_0")),
1713            CmsProject.CmsProjectType.valueOf(res.getInt(m_sqlManager.readQuery("C_PROJECTS_PROJECT_TYPE_0"))),
1714            res.getLong(m_sqlManager.readQuery("C_PROJECT_PUBLISHDATE_0")),
1715            publishedById,
1716            resources);
1717    }
1718
1719    /**
1720     * Creates a valid {@link I_CmsHistoryResource} instance from a JDBC ResultSet.<p>
1721     *
1722     * @param res the JDBC result set
1723     *
1724     * @return the new historical resource instance
1725     *
1726     * @throws SQLException if a requested attribute was not found in the result set
1727     */
1728    protected I_CmsHistoryResource internalCreateResource(ResultSet res) throws SQLException {
1729
1730        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
1731        int structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
1732        int tagId = res.getInt(m_sqlManager.readQuery("C_RESOURCES_PUBLISH_TAG"));
1733        CmsUUID structureId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_ID")));
1734        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
1735        String resourcePath = res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_PATH"));
1736        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
1737        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
1738        CmsUUID projectLastModified = new CmsUUID(
1739            res.getString(m_sqlManager.readQuery("C_RESOURCES_PROJECT_LASTMODIFIED")));
1740        int state = Math.max(
1741            res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE")),
1742            res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE")));
1743        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
1744        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
1745        long dateReleased = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_RELEASED"));
1746        long dateExpired = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_EXPIRED"));
1747        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
1748        CmsUUID userLastModified = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
1749        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
1750        CmsUUID parentId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_HISTORY_PARENTID")));
1751        long dateContent = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));
1752
1753        boolean isFolder = resourcePath.endsWith("/");
1754        if (isFolder) {
1755            return new CmsHistoryFolder(
1756                tagId,
1757                structureId,
1758                resourceId,
1759                resourcePath,
1760                resourceType,
1761                resourceFlags,
1762                projectLastModified,
1763                CmsResourceState.valueOf(state),
1764                dateCreated,
1765                userCreated,
1766                dateLastModified,
1767                userLastModified,
1768                dateReleased,
1769                dateExpired,
1770                resourceVersion + structureVersion,
1771                parentId,
1772                resourceVersion,
1773                structureVersion);
1774        } else {
1775            return new CmsHistoryFile(
1776                tagId,
1777                structureId,
1778                resourceId,
1779                resourcePath,
1780                resourceType,
1781                resourceFlags,
1782                projectLastModified,
1783                CmsResourceState.valueOf(state),
1784                dateCreated,
1785                userCreated,
1786                dateLastModified,
1787                userLastModified,
1788                dateReleased,
1789                dateExpired,
1790                resourceSize,
1791                dateContent,
1792                resourceVersion + structureVersion,
1793                parentId,
1794                null,
1795                resourceVersion,
1796                structureVersion);
1797        }
1798    }
1799
1800    /**
1801     * Merges an historical entry for a sibling, based on the structure data from the given historical resource
1802     * and result set for the resource entry.<p>
1803     *
1804     * @param histRes the original historical entry
1805     * @param res the result set of the resource entry
1806     * @param versionOffset the offset for the structure version
1807     *
1808     * @return a merged historical entry for the sibling
1809     *
1810     * @throws SQLException if something goes wrong
1811     */
1812    protected I_CmsHistoryResource internalMergeResource(I_CmsHistoryResource histRes, ResultSet res, int versionOffset)
1813    throws SQLException {
1814
1815        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
1816        int structureVersion = histRes.getStructureVersion() - versionOffset;
1817        int tagId = res.getInt(m_sqlManager.readQuery("C_RESOURCES_PUBLISH_TAG"));
1818        CmsUUID structureId = histRes.getStructureId();
1819        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
1820        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
1821        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
1822        CmsUUID projectLastModified = new CmsUUID(
1823            res.getString(m_sqlManager.readQuery("C_RESOURCES_PROJECT_LASTMODIFIED")));
1824        int state = histRes.getState().getState(); // may be we have to compute something here?
1825        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
1826        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
1827        long dateReleased = histRes.getDateReleased();
1828        long dateExpired = histRes.getDateExpired();
1829        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
1830        CmsUUID userLastModified = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
1831        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
1832        // here we could use the path/parent id for the sibling where the modification really occurred
1833        String resourcePath = histRes.getRootPath();
1834        CmsUUID parentId = histRes.getParentId();
1835        long dateContent = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));
1836
1837        if (histRes.isFolder()) {
1838            return new CmsHistoryFolder(
1839                tagId,
1840                structureId,
1841                resourceId,
1842                resourcePath,
1843                resourceType,
1844                resourceFlags,
1845                projectLastModified,
1846                CmsResourceState.valueOf(state),
1847                dateCreated,
1848                userCreated,
1849                dateLastModified,
1850                userLastModified,
1851                dateReleased,
1852                dateExpired,
1853                resourceVersion + structureVersion,
1854                parentId,
1855                resourceVersion,
1856                structureVersion);
1857        } else {
1858            return new CmsHistoryFile(
1859                tagId,
1860                structureId,
1861                resourceId,
1862                resourcePath,
1863                resourceType,
1864                resourceFlags,
1865                projectLastModified,
1866                CmsResourceState.valueOf(state),
1867                dateCreated,
1868                userCreated,
1869                dateLastModified,
1870                userLastModified,
1871                dateReleased,
1872                dateExpired,
1873                resourceSize,
1874                dateContent,
1875                resourceVersion + structureVersion,
1876                parentId,
1877                null,
1878                resourceVersion,
1879                structureVersion);
1880        }
1881    }
1882
1883    /**
1884     * Tests if a history resource does exist.<p>
1885     *
1886     * @param dbc the current database context
1887     * @param resource the resource to test
1888     * @param publishTag the publish tag of the resource to test
1889     *
1890     * @return <code>true</code> if the resource already exists, <code>false</code> otherwise
1891     *
1892     * @throws CmsDataAccessException if something goes wrong
1893     */
1894    protected boolean internalValidateResource(CmsDbContext dbc, CmsResource resource, int publishTag)
1895    throws CmsDataAccessException {
1896
1897        Connection conn = null;
1898        PreparedStatement stmt = null;
1899        ResultSet res = null;
1900        boolean exists = false;
1901
1902        try {
1903            conn = m_sqlManager.getConnection(dbc);
1904            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_EXISTS_RESOURCE");
1905            stmt.setString(1, resource.getResourceId().toString());
1906            stmt.setInt(2, publishTag);
1907            res = stmt.executeQuery();
1908
1909            exists = res.next();
1910        } catch (SQLException e) {
1911            throw new CmsDbSqlException(
1912                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1913                e);
1914        } finally {
1915            m_sqlManager.closeAll(dbc, conn, stmt, res);
1916        }
1917        return exists;
1918    }
1919
1920}