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