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