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.CmsAlias;
033import org.opencms.db.CmsAliasFilter;
034import org.opencms.db.CmsDbConsistencyException;
035import org.opencms.db.CmsDbContext;
036import org.opencms.db.CmsDbEntryNotFoundException;
037import org.opencms.db.CmsDbSqlException;
038import org.opencms.db.CmsDbUtil;
039import org.opencms.db.CmsDriverManager;
040import org.opencms.db.CmsPreparedStatementIntParameter;
041import org.opencms.db.CmsPreparedStatementStringParameter;
042import org.opencms.db.CmsResourceState;
043import org.opencms.db.CmsRewriteAlias;
044import org.opencms.db.CmsRewriteAliasFilter;
045import org.opencms.db.CmsVfsOnlineResourceAlreadyExistsException;
046import org.opencms.db.I_CmsDriver;
047import org.opencms.db.I_CmsPreparedStatementParameter;
048import org.opencms.db.I_CmsProjectDriver;
049import org.opencms.db.I_CmsVfsDriver;
050import org.opencms.db.urlname.CmsUrlNameMappingEntry;
051import org.opencms.db.urlname.CmsUrlNameMappingFilter;
052import org.opencms.file.CmsDataAccessException;
053import org.opencms.file.CmsFile;
054import org.opencms.file.CmsFolder;
055import org.opencms.file.CmsProject;
056import org.opencms.file.CmsProperty;
057import org.opencms.file.CmsPropertyDefinition;
058import org.opencms.file.CmsResource;
059import org.opencms.file.CmsResourceFilter;
060import org.opencms.file.CmsVfsException;
061import org.opencms.file.CmsVfsResourceAlreadyExistsException;
062import org.opencms.file.CmsVfsResourceNotFoundException;
063import org.opencms.file.I_CmsResource;
064import org.opencms.file.history.I_CmsHistoryResource;
065import org.opencms.file.quota.CmsFolderSizeEntry;
066import org.opencms.file.quota.CmsFolderSizeOptions;
067import org.opencms.file.types.CmsResourceTypeJsp;
068import org.opencms.gwt.shared.alias.CmsAliasMode;
069import org.opencms.main.CmsEvent;
070import org.opencms.main.CmsException;
071import org.opencms.main.CmsLog;
072import org.opencms.main.I_CmsEventListener;
073import org.opencms.main.OpenCms;
074import org.opencms.relations.CmsRelation;
075import org.opencms.relations.CmsRelationFilter;
076import org.opencms.relations.CmsRelationType;
077import org.opencms.security.CmsOrganizationalUnit;
078import org.opencms.security.CmsPermissionSet;
079import org.opencms.util.CmsFileUtil;
080import org.opencms.util.CmsPair;
081import org.opencms.util.CmsStringUtil;
082import org.opencms.util.CmsUUID;
083
084import java.io.ByteArrayInputStream;
085import java.sql.Connection;
086import java.sql.PreparedStatement;
087import java.sql.ResultSet;
088import java.sql.SQLException;
089import java.util.ArrayList;
090import java.util.Collection;
091import java.util.Collections;
092import java.util.HashMap;
093import java.util.HashSet;
094import java.util.Iterator;
095import java.util.List;
096import java.util.Map;
097import java.util.Set;
098
099import org.apache.commons.logging.Log;
100
101/**
102 * Generic (ANSI-SQL) database server implementation of the VFS driver methods.<p>
103 *
104 * @since 6.0.0
105 */
106public class CmsVfsDriver implements I_CmsDriver, I_CmsVfsDriver {
107
108    /**
109     * Used to distinguish the different contexts in which relation conditions are assembled.
110     */
111    protected static enum RelationConditionMode {
112        /** For reading relations. */
113        read,
114        /** For deleting relations. */
115        delete
116    }
117
118    /** Contains the macro replacement value for the offline project. */
119    protected static final String OFFLINE = "OFFLINE";
120
121    /** Contains the macro replacement value for the online project. */
122    protected static final String ONLINE = "ONLINE";
123
124    /** The log object for this class. */
125    private static final Log LOG = CmsLog.getLog(org.opencms.db.generic.CmsVfsDriver.class);
126
127    /** The driver manager. */
128    protected CmsDriverManager m_driverManager;
129
130    /**
131     * This field is temporarily used to compute the versions during publishing.<p>
132     *
133     * @see #publishVersions(CmsDbContext, CmsResource, boolean)
134     */
135    protected List<CmsUUID> m_resOp = new ArrayList<CmsUUID>();
136
137    /** The sql manager. */
138    protected CmsSqlManager m_sqlManager;
139
140    /**
141     * This method prepares the SQL conditions for mapping entries for a given URL name mapping filter.<p>
142     *
143     * @param filter the filter from which the SQL conditions should be generated
144     *
145     * @return a pair consisting of an SQL string and a list of the prepared statement parameters for the SQL
146     */
147    public static CmsPair<String, List<I_CmsPreparedStatementParameter>> prepareUrlNameMappingConditions(
148        CmsUrlNameMappingFilter filter) {
149
150        List<String> sqlConditions = new ArrayList<String>();
151        List<I_CmsPreparedStatementParameter> parameters = new ArrayList<I_CmsPreparedStatementParameter>();
152        if (filter.getName() != null) {
153            sqlConditions.add("NAME = ?");
154            parameters.add(new CmsPreparedStatementStringParameter(filter.getName()));
155        }
156
157        if (filter.getStructureId() != null) {
158            sqlConditions.add("STRUCTURE_ID = ?");
159            parameters.add(new CmsPreparedStatementStringParameter(filter.getStructureId().toString()));
160        }
161
162        if (filter.getNamePattern() != null) {
163            sqlConditions.add(" NAME LIKE ? ");
164            parameters.add(new CmsPreparedStatementStringParameter(filter.getNamePattern()));
165        }
166
167        if ((filter.getStates() != null) && (filter.getStates().length > 0)) {
168            List<String> stateConditions = new ArrayList<String>();
169            stateConditions.add("1 = 0");
170            for (int i = 0; i < filter.getStates().length; i++) {
171                stateConditions.add("STATE = ?");
172                parameters.add(new CmsPreparedStatementIntParameter(filter.getStates()[i]));
173            }
174            sqlConditions.add("( " + CmsStringUtil.listAsString(stateConditions, " OR ") + ")");
175
176        }
177
178        if (filter.getRejectStructureId() != null) {
179            sqlConditions.add(" STRUCTURE_ID <> ? ");
180            parameters.add(new CmsPreparedStatementStringParameter(filter.getRejectStructureId().toString()));
181        }
182
183        if (filter.getLocale() != null) {
184            sqlConditions.add(" LOCALE = ? ");
185            parameters.add(new CmsPreparedStatementStringParameter(filter.getLocale()));
186        }
187
188        String conditionString = CmsStringUtil.listAsString(sqlConditions, " AND ");
189        return CmsPair.create(conditionString, parameters);
190    }
191
192    /**
193     * Escapes the database wildcards within the resource path.<p>
194     *
195     * This method is required to ensure chars in the resource path that have a special
196     * meaning in SQL (for example "_", which is the "any char" operator) are escaped.<p>
197     *
198     * It will escape the following chars:
199     * <ul>
200     * <li>"_" to "|_"</li>
201     * </ul>
202     *
203     * @param path the resource path
204     * @return the escaped resource path
205     */
206    protected static String escapeDbWildcard(String path) {
207
208        return CmsStringUtil.substitute(path, "_", "|_");
209    }
210
211    /**
212     * @see org.opencms.db.I_CmsVfsDriver#addUrlNameMappingEntry(org.opencms.db.CmsDbContext, boolean, org.opencms.db.urlname.CmsUrlNameMappingEntry)
213     */
214    public void addUrlNameMappingEntry(CmsDbContext dbc, boolean online, CmsUrlNameMappingEntry entry)
215    throws CmsDataAccessException {
216
217        Connection conn = null;
218        PreparedStatement stmt = null;
219        String query = m_sqlManager.readQuery("C_ADD_URLNAME_MAPPING");
220        query = replaceProject(query, online);
221        try {
222            conn = m_sqlManager.getConnection(dbc);
223            stmt = m_sqlManager.getPreparedStatementForSql(conn, query);
224            stmt.setString(1, entry.getName());
225            stmt.setString(2, entry.getStructureId().toString());
226            stmt.setInt(3, entry.getState());
227            stmt.setLong(4, entry.getDateChanged());
228            stmt.setString(5, entry.getLocale());
229            stmt.executeUpdate();
230        } catch (SQLException e) {
231            throw wrapException(stmt, e);
232        } finally {
233            m_sqlManager.closeAll(dbc, conn, stmt, null);
234        }
235    }
236
237    /**
238     * Counts the number of siblings of a resource.<p>
239     *
240     * @param dbc the current database context
241     * @param projectId the current project id
242     * @param resourceId the resource id to count the number of siblings from
243     *
244     * @return number of siblings
245     * @throws CmsDataAccessException if something goes wrong
246     */
247    public int countSiblings(CmsDbContext dbc, CmsUUID projectId, CmsUUID resourceId) throws CmsDataAccessException {
248
249        Connection conn = null;
250        PreparedStatement stmt = null;
251        ResultSet res = null;
252        int count = 0;
253
254        try {
255            conn = m_sqlManager.getConnection(dbc);
256
257            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_COUNT_SIBLINGS");
258            stmt.setString(1, resourceId.toString());
259            res = stmt.executeQuery();
260
261            if (res.next()) {
262                count = res.getInt(1);
263                while (res.next()) {
264                    // do nothing only move through all rows because of mssql odbc driver
265                }
266            }
267        } catch (SQLException e) {
268            throw new CmsDbSqlException(
269                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
270                e);
271        } finally {
272            m_sqlManager.closeAll(dbc, conn, stmt, res);
273        }
274
275        return count;
276    }
277
278    /**
279    * @see org.opencms.db.I_CmsVfsDriver#createContent(CmsDbContext, CmsUUID, CmsResource, byte[])
280    */
281    public void createContent(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, byte[] content)
282    throws CmsDataAccessException {
283
284        Connection conn = null;
285        PreparedStatement stmt = null;
286
287        try {
288            conn = m_sqlManager.getConnection(dbc);
289            // create new offline content
290            stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_CONTENTS_WRITE");
291            stmt.setString(1, resource.getResourceId().toString());
292            if (content.length < 2000) {
293                stmt.setBytes(2, content);
294            } else {
295                stmt.setBinaryStream(2, new ByteArrayInputStream(content), content.length);
296            }
297            stmt.executeUpdate();
298        } catch (SQLException e) {
299            throw new CmsDbSqlException(
300                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
301                e);
302        } finally {
303            m_sqlManager.closeAll(dbc, conn, stmt, null);
304        }
305    }
306
307    /**
308     * @see org.opencms.db.I_CmsVfsDriver#createFile(CmsDbContext, java.sql.ResultSet, CmsUUID)
309     */
310    public CmsFile createFile(CmsDbContext dbc, ResultSet res, CmsUUID projectId)
311    throws CmsDataAccessException, SQLException {
312
313        CmsUUID structureId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_ID")));
314        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
315        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
316        String resourcePath = res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_PATH"));
317        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
318        int resourceState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE"));
319        int structureState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE"));
320        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
321        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
322        long dateReleased = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_RELEASED"));
323        long dateExpired = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_EXPIRED"));
324        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
325        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
326        CmsUUID userLastModified = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
327        byte[] content = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
328        int siblingCount = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIBLING_COUNT"));
329        long dateContent = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));
330        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
331        int structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
332
333        // calculate the overall state
334        int newState = (structureState > resourceState) ? structureState : resourceState;
335
336        // in case of folder type ensure, that the root path has a trailing slash
337        if (CmsFolder.isFolderType(resourceType)) {
338            resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath);
339        }
340
341        return new CmsFile(
342            structureId,
343            resourceId,
344            resourcePath,
345            resourceType,
346            resourceFlags,
347            projectId,
348            CmsResourceState.valueOf(newState),
349            dateCreated,
350            userCreated,
351            dateLastModified,
352            userLastModified,
353            dateReleased,
354            dateExpired,
355            siblingCount,
356            resourceSize,
357            dateContent,
358            resourceVersion + structureVersion,
359            content);
360    }
361
362    /**
363     * @see org.opencms.db.I_CmsVfsDriver#createFile(CmsDbContext, java.sql.ResultSet, CmsUUID, boolean)
364     */
365    public CmsFile createFile(CmsDbContext dbc, ResultSet res, CmsUUID projectId, boolean hasFileContentInResultSet)
366    throws CmsDataAccessException, SQLException {
367
368        byte[] content = null;
369
370        CmsUUID resProjectId = null;
371
372        CmsUUID structureId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_ID")));
373        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
374        String resourcePath = res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_PATH"));
375        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
376        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
377        int resourceState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE"));
378        int structureState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE"));
379        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
380        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
381        long dateReleased = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_RELEASED"));
382        long dateExpired = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_EXPIRED"));
383        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
384        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
385        CmsUUID userLastModified = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
386        CmsUUID lockedInProject = new CmsUUID(res.getString("LOCKED_IN_PROJECT"));
387        int siblingCount = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIBLING_COUNT"));
388        long dateContent = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));
389        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
390        int structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
391
392        // in case of folder type ensure, that the root path has a trailing slash
393        if (CmsFolder.isFolderType(resourceType)) {
394            resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath);
395        }
396        if (hasFileContentInResultSet) {
397            content = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
398        }
399        resProjectId = lockedInProject;
400        int newState = (structureState > resourceState) ? structureState : resourceState;
401
402        return new CmsFile(
403            structureId,
404            resourceId,
405            resourcePath,
406            resourceType,
407            resourceFlags,
408            resProjectId,
409            CmsResourceState.valueOf(newState),
410            dateCreated,
411            userCreated,
412            dateLastModified,
413            userLastModified,
414            dateReleased,
415            dateExpired,
416            siblingCount,
417            resourceSize,
418            dateContent,
419            resourceVersion + structureVersion,
420            content);
421    }
422
423    /**
424     * @see org.opencms.db.I_CmsVfsDriver#createFolder(java.sql.ResultSet, CmsUUID, boolean)
425     */
426    public CmsFolder createFolder(ResultSet res, CmsUUID projectId, boolean hasProjectIdInResultSet)
427    throws SQLException {
428
429        CmsUUID structureId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_ID")));
430        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
431        String resourcePath = res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_PATH"));
432        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
433        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
434        int resourceState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE"));
435        int structureState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE"));
436        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
437        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
438        long dateReleased = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_RELEASED"));
439        long dateExpired = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_EXPIRED"));
440        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
441        CmsUUID userLastModified = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
442        CmsUUID resProjectId = new CmsUUID(res.getString("LOCKED_IN_PROJECT"));
443        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
444        int structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
445        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
446
447        // in case of folder type ensure, that the root path has a trailing slash
448        if (CmsFolder.isFolderSize(resourceSize)) {
449            resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath);
450        }
451
452        int newState = (structureState > resourceState) ? structureState : resourceState;
453
454        return new CmsFolder(
455            structureId,
456            resourceId,
457            resourcePath,
458            resourceType,
459            resourceFlags,
460            resProjectId,
461            CmsResourceState.valueOf(newState),
462            dateCreated,
463            userCreated,
464            dateLastModified,
465            userLastModified,
466            dateReleased,
467            dateExpired,
468            resourceVersion + structureVersion);
469    }
470
471    /**
472     * @see org.opencms.db.I_CmsVfsDriver#createOnlineContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, byte[], int, boolean, boolean)
473     */
474    public void createOnlineContent(
475        CmsDbContext dbc,
476        CmsUUID resourceId,
477        byte[] contents,
478        int publishTag,
479        boolean keepOnline,
480        boolean needToUpdateContent)
481    throws CmsDataAccessException {
482
483        Connection conn = null;
484        PreparedStatement stmt = null;
485
486        try {
487            conn = m_sqlManager.getConnection(dbc);
488            boolean dbcHasProjectId = (dbc.getProjectId() != null) && !dbc.getProjectId().isNullUUID();
489
490            if (needToUpdateContent || dbcHasProjectId) {
491                if (dbcHasProjectId || !OpenCms.getSystemInfo().isHistoryEnabled()) {
492                    // remove the online content for this resource id
493                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_DELETE");
494                    stmt.setString(1, resourceId.toString());
495                    stmt.executeUpdate();
496                    m_sqlManager.closeAll(dbc, null, stmt, null);
497                } else {
498                    // put the online content in the history, only if explicit requested
499                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_HISTORY");
500                    stmt.setString(1, resourceId.toString());
501                    stmt.executeUpdate();
502                    m_sqlManager.closeAll(dbc, null, stmt, null);
503                }
504
505                // create new online content
506                stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_WRITE");
507
508                stmt.setString(1, resourceId.toString());
509                if (contents.length < 2000) {
510                    stmt.setBytes(2, contents);
511                } else {
512                    stmt.setBinaryStream(2, new ByteArrayInputStream(contents), contents.length);
513                }
514                stmt.setInt(3, publishTag);
515                stmt.setInt(4, publishTag);
516                stmt.setInt(5, keepOnline ? 1 : 0);
517                stmt.executeUpdate();
518                m_sqlManager.closeAll(dbc, null, stmt, null);
519            } else {
520                // update old content entry
521                stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_CONTENTS_UPDATE");
522                stmt.setInt(1, publishTag);
523                stmt.setString(2, resourceId.toString());
524                stmt.executeUpdate();
525                m_sqlManager.closeAll(dbc, null, stmt, null);
526
527                if (!keepOnline) {
528                    // put the online content in the history
529                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_HISTORY");
530                    stmt.setString(1, resourceId.toString());
531                    stmt.executeUpdate();
532                    m_sqlManager.closeAll(dbc, null, stmt, null);
533                }
534            }
535        } catch (SQLException e) {
536            throw new CmsDbSqlException(
537                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
538                e);
539        } finally {
540            m_sqlManager.closeAll(dbc, conn, stmt, null);
541        }
542    }
543
544    /**
545     * @see org.opencms.db.I_CmsVfsDriver#createPropertyDefinition(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, java.lang.String, org.opencms.file.CmsPropertyDefinition.CmsPropertyType)
546     */
547    public CmsPropertyDefinition createPropertyDefinition(
548        CmsDbContext dbc,
549        CmsUUID projectId,
550        String name,
551        CmsPropertyDefinition.CmsPropertyType type)
552    throws CmsDataAccessException {
553
554        Connection conn = null;
555        PreparedStatement stmt = null;
556
557        try {
558            conn = m_sqlManager.getConnection(dbc);
559            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTYDEF_CREATE");
560            stmt.setString(1, new CmsUUID().toString());
561            stmt.setString(2, name);
562            stmt.setInt(3, type.getMode());
563            stmt.executeUpdate();
564        } catch (SQLException e) {
565            throw new CmsDbSqlException(
566                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
567                e);
568        } finally {
569            m_sqlManager.closeAll(dbc, conn, stmt, null);
570        }
571
572        return readPropertyDefinition(dbc, name, projectId);
573    }
574
575    /**
576     * @see org.opencms.db.I_CmsVfsDriver#createRelation(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.relations.CmsRelation)
577     */
578    public void createRelation(CmsDbContext dbc, CmsUUID projectId, CmsRelation relation)
579    throws CmsDataAccessException {
580
581        Connection conn = null;
582        PreparedStatement stmt = null;
583
584        try {
585            conn = m_sqlManager.getConnection(dbc);
586            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_CREATE_RELATION");
587            stmt.setString(1, relation.getSourceId().toString());
588            stmt.setString(2, relation.getSourcePath());
589            stmt.setString(3, relation.getTargetId().toString());
590            stmt.setString(4, relation.getTargetPath());
591            stmt.setInt(5, relation.getType().getId());
592
593            if (LOG.isDebugEnabled()) {
594                LOG.debug(
595                    Messages.get().getBundle().key(
596                        Messages.LOG_CREATE_RELATION_2,
597                        String.valueOf(projectId),
598                        relation));
599            }
600            stmt.executeUpdate();
601        } catch (SQLException e) {
602            throw new CmsDbSqlException(
603                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
604                e);
605        } finally {
606            m_sqlManager.closeAll(dbc, conn, stmt, null);
607        }
608
609        if (relation.getType().getId() == CmsRelationType.LOCALE_VARIANT.getId()) {
610            try {
611
612                // Normalizes locale relations after creating a relation.
613                // After creating a locale variant relation from A to B, this statment
614                // removes all locale variant relations which are either
615                //       - from A to somewhere else than B,
616                //       - from B to some other resource
617                //       - to A from some other resources
618                conn = m_sqlManager.getConnection(dbc);
619                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RELATIONS_NORMALIZE_LOCALE_RELATIONS");
620                stmt.setString(1, relation.getSourceId().toString());
621                stmt.setString(2, relation.getTargetId().toString());
622                stmt.setString(3, relation.getSourceId().toString());
623                stmt.setString(4, relation.getTargetId().toString());
624                if (LOG.isDebugEnabled()) {
625                    LOG.debug(
626                        Messages.get().getBundle().key(
627                            Messages.LOG_CREATE_RELATION_2,
628                            String.valueOf(projectId),
629                            relation));
630                }
631                stmt.executeUpdate();
632            } catch (SQLException e) {
633                throw new CmsDbSqlException(
634                    Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
635                    e);
636            } finally {
637                m_sqlManager.closeAll(dbc, conn, stmt, null);
638            }
639        }
640    }
641
642    /**
643     * @see org.opencms.db.I_CmsVfsDriver#createResource(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.file.CmsResource, byte[])
644     */
645    public CmsResource createResource(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, byte[] content)
646    throws CmsDataAccessException {
647
648        CmsUUID newStructureId = null;
649        Connection conn = null;
650        PreparedStatement stmt = null;
651
652        // check the resource path
653        String resourcePath = CmsFileUtil.removeTrailingSeparator(resource.getRootPath());
654        if (resourcePath.length() > CmsDriverManager.MAX_VFS_RESOURCE_PATH_LENGTH) {
655            throw new CmsDataAccessException(
656                Messages.get().container(
657                    Messages.ERR_RESOURCENAME_TOO_LONG_2,
658                    resourcePath,
659                    Integer.valueOf(CmsDriverManager.MAX_VFS_RESOURCE_PATH_LENGTH)));
660        }
661
662        // check if the parent folder of the resource exists and if is not deleted
663        if (!resource.getRootPath().equals("/")) {
664            String parentFolderName = CmsResource.getParentFolder(resource.getRootPath());
665            CmsFolder parentFolder = m_driverManager.getVfsDriver(dbc).readFolder(dbc, projectId, parentFolderName);
666            if (parentFolder.getState().isDeleted()) {
667                throw new CmsDbEntryNotFoundException(
668                    Messages.get().container(Messages.ERR_PARENT_FOLDER_DELETED_1, resource.getRootPath()));
669            }
670        }
671
672        // validate the resource length
673        internalValidateResourceLength(resource);
674
675        // set the resource state and modification dates
676        CmsResourceState newState;
677        long dateModified;
678        long dateCreated;
679        long dateContent = System.currentTimeMillis();
680
681        if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
682            newState = CmsResource.STATE_UNCHANGED;
683            dateCreated = resource.getDateCreated();
684            dateModified = resource.getDateLastModified();
685        } else {
686            newState = CmsResource.STATE_NEW;
687            if (resource.isTouched()) {
688                dateCreated = resource.getDateCreated();
689                dateModified = resource.getDateLastModified();
690            } else {
691                dateCreated = System.currentTimeMillis();
692                dateModified = dateCreated;
693            }
694        }
695
696        // check if the resource already exists
697        newStructureId = resource.getStructureId();
698
699        try {
700            CmsResource existingResource = m_driverManager.getVfsDriver(dbc).readResource(
701                dbc,
702                ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID()) ? projectId : dbc.getProjectId(),
703                resourcePath,
704                true);
705            if (existingResource.getState().isDeleted()) {
706                // if an existing resource is deleted, it will be finally removed now.
707                // but we have to reuse its id in order to avoid orphans in the online project
708                newStructureId = existingResource.getStructureId();
709                newState = CmsResource.STATE_CHANGED;
710
711                // remove the existing file and it's properties
712                List<CmsResource> modifiedResources = m_driverManager.getVfsDriver(
713                    dbc).readSiblings(dbc, projectId, existingResource, false);
714                int propertyDeleteOption = (existingResource.getSiblingCount() > 1)
715                ? CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES
716                : CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES;
717                deletePropertyObjects(dbc, projectId, existingResource, propertyDeleteOption);
718                removeFile(dbc, projectId, existingResource);
719
720                OpenCms.fireCmsEvent(
721                    new CmsEvent(
722                        I_CmsEventListener.EVENT_RESOURCES_MODIFIED,
723                        Collections.<String, Object> singletonMap(
724                            I_CmsEventListener.KEY_RESOURCES,
725                            modifiedResources)));
726                OpenCms.fireCmsEvent(
727                    new CmsEvent(
728                        I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
729                        Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, existingResource)));
730            } else {
731                // we have a collision: there exists already a resource with the same path/name which cannot be removed
732                throw new CmsVfsResourceAlreadyExistsException(
733                    Messages.get().container(
734                        Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
735                        dbc.removeSiteRoot(resource.getRootPath())));
736            }
737        } catch (CmsVfsResourceNotFoundException e) {
738            // that's what we want in the best case- anything else should be thrown
739        }
740
741        try {
742            // read the parent id
743            String parentId = internalReadParentId(dbc, projectId, resourcePath);
744
745            // use consistent version numbers if the file is being restored
746            int lastVersion = m_driverManager.getHistoryDriver(dbc).readLastVersion(dbc, newStructureId);
747            int newStrVersion = 0;
748            int newResVersion = 0;
749            if (lastVersion > 0) {
750                I_CmsHistoryResource histRes = m_driverManager.getHistoryDriver(dbc).readResource(
751                    dbc,
752                    newStructureId,
753                    lastVersion);
754                newStrVersion = histRes.getStructureVersion();
755                newResVersion = histRes.getResourceVersion();
756            }
757
758            // write the structure
759            conn = m_sqlManager.getConnection(dbc);
760            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_STRUCTURE_WRITE");
761            stmt.setString(1, newStructureId.toString());
762            stmt.setString(2, resource.getResourceId().toString());
763            stmt.setString(3, resourcePath);
764            stmt.setInt(4, newState.getState());
765            stmt.setLong(5, resource.getDateReleased());
766            stmt.setLong(6, resource.getDateExpired());
767            stmt.setString(7, parentId);
768            stmt.setInt(8, newStrVersion); // starting version number
769            stmt.executeUpdate();
770            m_sqlManager.closeAll(dbc, conn, stmt, null);
771
772            if (!validateResourceIdExists(dbc, projectId, resource.getResourceId())) {
773                try {
774                    // create the resource record
775                    conn = m_sqlManager.getConnection(dbc);
776                    stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_WRITE");
777                    stmt.setString(1, resource.getResourceId().toString());
778                    stmt.setInt(2, resource.getTypeId());
779                    stmt.setInt(3, resource.getFlags());
780                    stmt.setLong(4, dateCreated);
781                    stmt.setString(5, resource.getUserCreated().toString());
782                    stmt.setLong(6, dateModified);
783                    stmt.setString(7, resource.getUserLastModified().toString());
784                    stmt.setInt(8, newState.getState());
785                    stmt.setInt(9, resource.getLength());
786                    stmt.setLong(10, dateContent);
787                    stmt.setString(11, projectId.toString());
788                    stmt.setInt(12, 1); // sibling count
789                    stmt.setInt(13, newResVersion); // version number
790                    stmt.executeUpdate();
791                } finally {
792                    m_sqlManager.closeAll(dbc, conn, stmt, null);
793                }
794
795                if (resource.isFile() && (content != null)) {
796                    // create the file content
797                    createContent(dbc, projectId, resource, content);
798                }
799            } else {
800                if ((content != null) || !resource.getState().isKeep()) {
801                    CmsUUID projLastMod = projectId;
802                    CmsResourceState state = CmsResource.STATE_CHANGED;
803                    if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
804                        // in case a sibling is being published
805                        projLastMod = resource.getProjectLastModified();
806                        state = CmsResource.STATE_UNCHANGED;
807                    }
808                    // update the resource record only if state has changed or new content is provided
809                    int sibCount = countSiblings(dbc, projectId, resource.getResourceId());
810                    conn = m_sqlManager.getConnection(dbc);
811                    stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_RESOURCES");
812                    stmt.setInt(1, resource.getTypeId());
813                    stmt.setInt(2, resource.getFlags());
814                    stmt.setLong(3, dateModified);
815                    stmt.setString(4, resource.getUserLastModified().toString());
816                    stmt.setInt(5, state.getState());
817                    stmt.setInt(6, resource.getLength());
818                    stmt.setLong(7, resource.getDateContent());
819                    stmt.setString(8, projLastMod.toString());
820                    stmt.setInt(9, sibCount);
821                    stmt.setString(10, resource.getResourceId().toString());
822                    stmt.executeUpdate();
823
824                    m_sqlManager.closeAll(dbc, conn, stmt, null);
825                }
826
827                if (resource.isFile()) {
828                    if (content != null) {
829                        // update the file content
830                        writeContent(dbc, resource, content);
831                    } else if (resource.getState().isKeep()) {
832                        // special case sibling creation - update the link Count
833                        int sibCount = countSiblings(dbc, projectId, resource.getResourceId());
834                        conn = m_sqlManager.getConnection(dbc);
835                        stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_SIBLING_COUNT");
836                        stmt.setInt(1, sibCount);
837                        stmt.setString(2, resource.getResourceId().toString());
838                        stmt.executeUpdate();
839                        m_sqlManager.closeAll(dbc, null, stmt, null);
840
841                        // update the resource flags
842                        stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_FLAGS");
843                        stmt.setInt(1, resource.getFlags());
844                        stmt.setString(2, resource.getResourceId().toString());
845                        stmt.executeUpdate();
846                        m_sqlManager.closeAll(dbc, conn, stmt, null);
847                    }
848                }
849            }
850        } catch (SQLException e) {
851            throw new CmsDbSqlException(
852                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
853                e);
854        } finally {
855            m_sqlManager.closeAll(dbc, conn, stmt, null);
856        }
857        repairBrokenRelations(dbc, projectId, resource.getStructureId(), resource.getRootPath());
858        return readResource(dbc, projectId, newStructureId, false);
859    }
860
861    /**
862     * @see org.opencms.db.I_CmsVfsDriver#createResource(java.sql.ResultSet, CmsUUID)
863     */
864    public CmsResource createResource(ResultSet res, CmsUUID projectId) throws SQLException {
865
866        CmsUUID structureId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_ID")));
867        CmsUUID resourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_ID")));
868        String resourcePath = res.getString(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_PATH"));
869        int resourceType = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_TYPE"));
870        int resourceFlags = res.getInt(m_sqlManager.readQuery("C_RESOURCES_RESOURCE_FLAGS"));
871        CmsUUID resourceProjectLastModified = new CmsUUID(
872            res.getString(m_sqlManager.readQuery("C_RESOURCES_PROJECT_LASTMODIFIED")));
873        int resourceState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE"));
874        int structureState = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE"));
875        long dateCreated = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CREATED"));
876        long dateLastModified = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_LASTMODIFIED"));
877        long dateReleased = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_RELEASED"));
878        long dateExpired = res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_EXPIRED"));
879        int resourceSize = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIZE"));
880        boolean isFolder = CmsFolder.isFolderSize(resourceSize);
881        if (isFolder) {
882            // in case of folder type ensure, that the root path has a trailing slash
883            resourcePath = CmsFileUtil.addTrailingSeparator(resourcePath);
884        }
885        long dateContent = isFolder ? -1 : res.getLong(m_sqlManager.readQuery("C_RESOURCES_DATE_CONTENT"));
886        CmsUUID userCreated = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_CREATED")));
887        CmsUUID userLastModified = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RESOURCES_USER_LASTMODIFIED")));
888        int siblingCount = res.getInt(m_sqlManager.readQuery("C_RESOURCES_SIBLING_COUNT"));
889        int resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
890        int structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
891
892        int newState = (structureState > resourceState) ? structureState : resourceState;
893        // if there is a change increase the version number
894        int newVersion = resourceVersion + structureVersion + (newState > 0 ? 1 : 0);
895
896        CmsResource newResource = new CmsResource(
897            structureId,
898            resourceId,
899            resourcePath,
900            resourceType,
901            isFolder,
902            resourceFlags,
903            resourceProjectLastModified,
904            CmsResourceState.valueOf(newState),
905            dateCreated,
906            userCreated,
907            dateLastModified,
908            userLastModified,
909            dateReleased,
910            dateExpired,
911            siblingCount,
912            resourceSize,
913            dateContent,
914            newVersion);
915
916        return newResource;
917    }
918
919    /**
920     * @see org.opencms.db.I_CmsVfsDriver#createSibling(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource)
921     */
922    public void createSibling(CmsDbContext dbc, CmsProject project, CmsResource resource)
923    throws CmsDataAccessException {
924
925        if (!project.getUuid().equals(CmsProject.ONLINE_PROJECT_ID)) {
926            // this method is only intended to be used during publishing
927            return;
928        }
929
930        // check if the resource already exists
931        CmsResource existingSibling = null;
932        CmsUUID newStructureId = resource.getStructureId();
933
934        Connection conn = null;
935        PreparedStatement stmt = null;
936        try {
937            existingSibling = readResource(dbc, project.getUuid(), resource.getRootPath(), true);
938
939            if (existingSibling.getState().isDeleted()) {
940                // if an existing resource is deleted, it will be finally removed now.
941                // but we have to reuse its id in order to avoid orphans in the online project.
942                newStructureId = existingSibling.getStructureId();
943
944                // remove the existing file and it's properties
945                List<CmsResource> modifiedResources = readSiblings(dbc, project.getUuid(), existingSibling, false);
946                int propertyDeleteOption = (existingSibling.getSiblingCount() > 1)
947                ? CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES
948                : CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES;
949                deletePropertyObjects(dbc, project.getUuid(), existingSibling, propertyDeleteOption);
950                removeFile(dbc, project.getUuid(), existingSibling);
951
952                OpenCms.fireCmsEvent(
953                    new CmsEvent(
954                        I_CmsEventListener.EVENT_RESOURCES_MODIFIED,
955                        Collections.<String, Object> singletonMap(
956                            I_CmsEventListener.KEY_RESOURCES,
957                            modifiedResources)));
958                OpenCms.fireCmsEvent(
959                    new CmsEvent(
960                        I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED,
961                        Collections.<String, Object> singletonMap(I_CmsEventListener.KEY_RESOURCE, existingSibling)));
962            } else {
963                // we have a collision: there exists already a resource with the same path/name which could not be removed
964                throw new CmsVfsResourceAlreadyExistsException(
965                    Messages.get().container(
966                        Messages.ERR_RESOURCE_WITH_NAME_ALREADY_EXISTS_1,
967                        dbc.removeSiteRoot(resource.getRootPath())));
968            }
969        } catch (CmsVfsResourceNotFoundException e) {
970            // that's what we want in the best case- anything else should be thrown
971        }
972
973        // check if a resource with the specified ID already exists
974        if (!validateResourceIdExists(dbc, project.getUuid(), resource.getResourceId())) {
975            throw new CmsVfsResourceNotFoundException(
976                Messages.get().container(
977                    Messages.ERR_CREATE_SIBLING_FILE_NOT_FOUND_1,
978                    dbc.removeSiteRoot(resource.getRootPath())));
979        }
980
981        // write a new structure referring to the resource
982        try {
983            // use consistent version numbers if the file is being restored
984            int lastVersion = m_driverManager.getHistoryDriver(dbc).readLastVersion(dbc, newStructureId);
985            int newStrVersion = 0;
986            if (lastVersion > 0) {
987                I_CmsHistoryResource histRes = m_driverManager.getHistoryDriver(dbc).readResource(
988                    dbc,
989                    newStructureId,
990                    lastVersion);
991                newStrVersion = histRes.getStructureVersion();
992            }
993
994            // read the parent id
995            String parentId = internalReadParentId(dbc, project.getUuid(), resource.getRootPath());
996
997            conn = m_sqlManager.getConnection(dbc);
998
999            // write the structure
1000            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_STRUCTURE_WRITE");
1001            stmt.setString(1, newStructureId.toString());
1002            stmt.setString(2, resource.getResourceId().toString());
1003            stmt.setString(3, resource.getRootPath());
1004            stmt.setInt(4, CmsResource.STATE_UNCHANGED.getState());
1005            stmt.setLong(5, resource.getDateReleased());
1006            stmt.setLong(6, resource.getDateExpired());
1007            stmt.setString(7, parentId);
1008            stmt.setInt(8, newStrVersion); // initial structure version number
1009            stmt.executeUpdate();
1010            m_sqlManager.closeAll(dbc, conn, stmt, null);
1011
1012            int sibCount = countSiblings(dbc, project.getUuid(), resource.getResourceId());
1013            conn = m_sqlManager.getConnection(dbc);
1014
1015            // update the link Count
1016            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_SIBLING_COUNT");
1017            stmt.setInt(1, sibCount);
1018            stmt.setString(2, resource.getResourceId().toString());
1019            stmt.executeUpdate();
1020
1021            m_sqlManager.closeAll(dbc, null, stmt, null);
1022
1023            // update the project last modified and flags
1024            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_RESOURCE_PROJECT");
1025            stmt.setInt(1, resource.getFlags());
1026            stmt.setString(2, resource.getProjectLastModified().toString());
1027            stmt.setString(3, resource.getResourceId().toString());
1028            stmt.executeUpdate();
1029        } catch (SQLException e) {
1030            throw new CmsDbSqlException(
1031                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1032                e);
1033        } finally {
1034            m_sqlManager.closeAll(dbc, conn, stmt, null);
1035        }
1036        repairBrokenRelations(dbc, project.getUuid(), resource.getStructureId(), resource.getRootPath());
1037    }
1038
1039    /**
1040     * @see org.opencms.db.I_CmsVfsDriver#deleteAliases(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.db.CmsAliasFilter)
1041     */
1042    public void deleteAliases(CmsDbContext dbc, CmsProject project, CmsAliasFilter filter)
1043    throws CmsDataAccessException {
1044
1045        Connection conn = null;
1046        PreparedStatement stmt = null;
1047        ResultSet res = null;
1048        if (filter.isNullFilter()) {
1049            throw new IllegalArgumentException("Trivial filter is not allowed for deleting aliases.");
1050        }
1051        try {
1052            conn = m_sqlManager.getConnection(dbc);
1053            CmsPair<String, List<String>> filterData = buildAliasConditions(filter);
1054            String sql = "DELETE FROM CMS_ALIASES WHERE " + filterData.getFirst();
1055            stmt = m_sqlManager.getPreparedStatementForSql(conn, sql);
1056            List<String> conditionParams = filterData.getSecond();
1057            for (int i = 0; i < conditionParams.size(); i++) {
1058                stmt.setString(1 + i, conditionParams.get(i));
1059            }
1060            stmt.executeUpdate();
1061        } catch (SQLException e) {
1062            throw new CmsDbSqlException(
1063                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1064                e);
1065        } finally {
1066            m_sqlManager.closeAll(dbc, conn, stmt, res);
1067        }
1068    }
1069
1070    /**
1071     * @see org.opencms.db.I_CmsVfsDriver#deleteHistoryContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
1072     */
1073    public void deleteHistoryContent(CmsDbContext dbc, CmsUUID resourceId, int publishTagToKeep)
1074    throws CmsDataAccessException {
1075
1076        Connection conn = null;
1077        PreparedStatement stmt = null;
1078        try {
1079            conn = m_sqlManager.getConnection(dbc);
1080            stmt = m_sqlManager.getPreparedStatement(conn, "C_CONTENT_HISTORY_DELETE");
1081            stmt.setString(1, resourceId.toString());
1082            stmt.setInt(2, publishTagToKeep);
1083            stmt.executeUpdate();
1084        } catch (SQLException e) {
1085            throw new CmsDbSqlException(
1086                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1087                e);
1088        } finally {
1089            m_sqlManager.closeAll(dbc, conn, stmt, null);
1090        }
1091    }
1092
1093    /**
1094     * @see org.opencms.db.I_CmsVfsDriver#deletePropertyDefinition(org.opencms.db.CmsDbContext, org.opencms.file.CmsPropertyDefinition)
1095     */
1096    public void deletePropertyDefinition(CmsDbContext dbc, CmsPropertyDefinition metadef)
1097    throws CmsDataAccessException {
1098
1099        Connection conn = null;
1100        PreparedStatement stmt = null;
1101
1102        try {
1103            if ((internalCountProperties(dbc, metadef, CmsProject.ONLINE_PROJECT_ID) != 0)
1104                || (internalCountProperties(dbc, metadef, CmsUUID.getOpenCmsUUID()) != 0)) { // HACK: to get an offline project
1105
1106                throw new CmsDataAccessException(
1107                    Messages.get().container(Messages.ERR_DELETE_USED_PROPERTY_1, metadef.getName()));
1108            }
1109
1110            conn = m_sqlManager.getConnection(dbc);
1111
1112            for (int i = 0; i < 2; i++) {
1113                if (i == 0) {
1114                    // delete the offline property definition
1115                    stmt = m_sqlManager.getPreparedStatement(conn, CmsUUID.getOpenCmsUUID(), "C_PROPERTYDEF_DELETE"); // HACK: to get an offline project
1116                } else {
1117                    // delete the online property definition
1118                    stmt = m_sqlManager.getPreparedStatement(
1119                        conn,
1120                        CmsProject.ONLINE_PROJECT_ID,
1121                        "C_PROPERTYDEF_DELETE");
1122                }
1123
1124                stmt.setString(1, metadef.getId().toString());
1125                stmt.executeUpdate();
1126                m_sqlManager.closeAll(dbc, null, stmt, null);
1127            }
1128        } catch (SQLException e) {
1129            throw new CmsDbSqlException(
1130                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1131                e);
1132        } finally {
1133            m_sqlManager.closeAll(dbc, conn, stmt, null);
1134        }
1135    }
1136
1137    /**
1138     * @see org.opencms.db.I_CmsVfsDriver#deletePropertyObjects(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.file.CmsResource, int)
1139     */
1140    public void deletePropertyObjects(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, int deleteOption)
1141    throws CmsDataAccessException {
1142
1143        Connection conn = null;
1144        PreparedStatement stmt = null;
1145
1146        try {
1147            conn = m_sqlManager.getConnection(dbc);
1148
1149            if (deleteOption == CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_AND_RESOURCE_VALUES) {
1150                // delete both the structure and resource property values mapped to the specified resource
1151                stmt = m_sqlManager.getPreparedStatement(
1152                    conn,
1153                    projectId,
1154                    "C_PROPERTIES_DELETE_ALL_STRUCTURE_AND_RESOURCE_VALUES");
1155                stmt.setString(1, resource.getResourceId().toString());
1156                stmt.setInt(2, CmsProperty.RESOURCE_RECORD_MAPPING);
1157                stmt.setString(3, resource.getStructureId().toString());
1158                stmt.setInt(4, CmsProperty.STRUCTURE_RECORD_MAPPING);
1159            } else if (deleteOption == CmsProperty.DELETE_OPTION_DELETE_STRUCTURE_VALUES) {
1160                // delete the structure values mapped to the specified resource
1161                stmt = m_sqlManager.getPreparedStatement(
1162                    conn,
1163                    projectId,
1164                    "C_PROPERTIES_DELETE_ALL_VALUES_FOR_MAPPING_TYPE");
1165                stmt.setString(1, resource.getStructureId().toString());
1166                stmt.setInt(2, CmsProperty.STRUCTURE_RECORD_MAPPING);
1167            } else if (deleteOption == CmsProperty.DELETE_OPTION_DELETE_RESOURCE_VALUES) {
1168                // delete the resource property values mapped to the specified resource
1169                stmt = m_sqlManager.getPreparedStatement(
1170                    conn,
1171                    projectId,
1172                    "C_PROPERTIES_DELETE_ALL_VALUES_FOR_MAPPING_TYPE");
1173                stmt.setString(1, resource.getResourceId().toString());
1174                stmt.setInt(2, CmsProperty.RESOURCE_RECORD_MAPPING);
1175            } else {
1176                throw new CmsDataAccessException(Messages.get().container(Messages.ERR_INVALID_DELETE_OPTION_1));
1177            }
1178
1179            stmt.executeUpdate();
1180        } catch (SQLException e) {
1181            throw new CmsDbSqlException(
1182                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1183                e);
1184        } finally {
1185            m_sqlManager.closeAll(dbc, conn, stmt, null);
1186        }
1187    }
1188
1189    /**
1190     * @see org.opencms.db.I_CmsVfsDriver#deleteRelations(org.opencms.db.CmsDbContext, CmsUUID, CmsResource, org.opencms.relations.CmsRelationFilter)
1191     */
1192    public void deleteRelations(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, CmsRelationFilter filter)
1193    throws CmsDataAccessException {
1194
1195        Connection conn = null;
1196        PreparedStatement stmt = null;
1197
1198        try {
1199            conn = m_sqlManager.getConnection(dbc);
1200
1201            if (filter.isSource()) {
1202                List<Object> params = new ArrayList<Object>(7);
1203
1204                StringBuffer queryBuf = new StringBuffer(256);
1205                queryBuf.append(m_sqlManager.readQuery(projectId, "C_DELETE_RELATIONS"));
1206                queryBuf.append(
1207                    prepareRelationConditions(RelationConditionMode.delete, projectId, filter, resource, params, true));
1208
1209                stmt = m_sqlManager.getPreparedStatementForSql(conn, queryBuf.toString());
1210                for (int i = 0; i < params.size(); i++) {
1211                    if (params.get(i) instanceof Integer) {
1212                        stmt.setInt(i + 1, ((Integer)params.get(i)).intValue());
1213                    } else {
1214                        stmt.setString(i + 1, (String)params.get(i));
1215                    }
1216                }
1217                stmt.executeUpdate();
1218                m_sqlManager.closeAll(dbc, null, stmt, null);
1219            }
1220            if (filter.isTarget()) {
1221                List<Object> params = new ArrayList<Object>(7);
1222
1223                StringBuffer queryBuf = new StringBuffer(256);
1224                queryBuf.append(m_sqlManager.readQuery(projectId, "C_DELETE_RELATIONS"));
1225                queryBuf.append(
1226                    prepareRelationConditions(
1227                        RelationConditionMode.delete,
1228                        projectId,
1229                        filter,
1230                        resource,
1231                        params,
1232                        false));
1233
1234                stmt = m_sqlManager.getPreparedStatementForSql(conn, queryBuf.toString());
1235                for (int i = 0; i < params.size(); i++) {
1236                    if (params.get(i) instanceof Integer) {
1237                        stmt.setInt(i + 1, ((Integer)params.get(i)).intValue());
1238                    } else {
1239                        stmt.setString(i + 1, (String)params.get(i));
1240                    }
1241                }
1242                stmt.executeUpdate();
1243                m_sqlManager.closeAll(dbc, null, stmt, null);
1244            }
1245        } catch (SQLException e) {
1246            throw new CmsDbSqlException(
1247                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1248                e);
1249        } finally {
1250            m_sqlManager.closeAll(dbc, conn, stmt, null);
1251        }
1252        // update broken remaining relations
1253        updateBrokenRelations(dbc, projectId, resource.getRootPath());
1254    }
1255
1256    /**
1257     * @see org.opencms.db.I_CmsVfsDriver#deleteRewriteAliases(org.opencms.db.CmsDbContext, org.opencms.db.CmsRewriteAliasFilter)
1258     */
1259    public void deleteRewriteAliases(CmsDbContext dbc, CmsRewriteAliasFilter filter) throws CmsDataAccessException {
1260
1261        Connection conn = null;
1262        PreparedStatement stmt = null;
1263        try {
1264            conn = m_sqlManager.getConnection(dbc);
1265            CmsPair<String, List<Object>> conditionAndParams = prepareRewriteAliasConditions(filter);
1266            String condition = conditionAndParams.getFirst();
1267            List<Object> params = conditionAndParams.getSecond();
1268            String query = "DELETE FROM CMS_REWRITES WHERE " + condition;
1269            stmt = m_sqlManager.getPreparedStatementForSql(conn, query);
1270            CmsDbUtil.fillParameters(stmt, params);
1271            stmt.execute();
1272        } catch (SQLException e) {
1273            throw wrapException(stmt, e);
1274        } finally {
1275            m_sqlManager.closeAll(dbc, conn, stmt, null);
1276        }
1277    }
1278
1279    /**
1280     * @see org.opencms.db.I_CmsVfsDriver#deleteUrlNameMappingEntries(org.opencms.db.CmsDbContext, boolean, org.opencms.db.urlname.CmsUrlNameMappingFilter)
1281     */
1282    public void deleteUrlNameMappingEntries(CmsDbContext dbc, boolean online, CmsUrlNameMappingFilter filter)
1283    throws CmsDataAccessException {
1284
1285        Connection conn = null;
1286        PreparedStatement stmt = null;
1287        try {
1288            conn = m_sqlManager.getConnection(dbc);
1289            String query = m_sqlManager.readQuery("C_DELETE_URLNAME_MAPPINGS");
1290            query = replaceProject(query, online);
1291            stmt = getPreparedStatementForFilter(conn, query, filter);
1292            stmt.executeUpdate();
1293        } catch (SQLException e) {
1294            throw wrapException(stmt, e);
1295        } finally {
1296            m_sqlManager.closeAll(dbc, conn, stmt, null);
1297        }
1298    }
1299
1300    /**
1301     * @see org.opencms.db.I_CmsVfsDriver#destroy()
1302     */
1303    public void destroy() throws Throwable {
1304
1305        m_sqlManager = null;
1306        m_driverManager = null;
1307
1308        if (CmsLog.INIT.isInfoEnabled()) {
1309            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_DRIVER_1, getClass().getName()));
1310        }
1311    }
1312
1313    /**
1314     * Returns all organizational units for the given resource.<p>
1315     *
1316     * @param dbc the database context
1317     * @param projectId the id of the project
1318     * @param resource the resource
1319     *
1320     * @return a list of {@link org.opencms.security.CmsOrganizationalUnit} objects
1321     *
1322     * @throws CmsDbSqlException if something goes wrong
1323     */
1324    public List<CmsOrganizationalUnit> getResourceOus(CmsDbContext dbc, CmsUUID projectId, CmsResource resource)
1325    throws CmsDbSqlException {
1326
1327        List<CmsOrganizationalUnit> ous = new ArrayList<CmsOrganizationalUnit>();
1328        String resName = resource.getRootPath();
1329        if (resource.isFolder() && !resName.endsWith("/")) {
1330            resName += "/";
1331        }
1332
1333        Connection conn = null;
1334        PreparedStatement stmt = null;
1335        ResultSet res = null;
1336        List<CmsRelation> rels = new ArrayList<CmsRelation>();
1337
1338        try {
1339            conn = m_sqlManager.getConnection(dbc);
1340            stmt = m_sqlManager.getPreparedStatementForSql(
1341                conn,
1342                m_sqlManager.readQuery(projectId, "C_READ_RESOURCE_OUS"));
1343            stmt.setInt(1, CmsRelationType.OU_RESOURCE.getId());
1344            stmt.setString(2, resName);
1345            res = stmt.executeQuery();
1346            while (res.next()) {
1347                rels.add(internalReadRelation(res));
1348            }
1349        } catch (SQLException e) {
1350            throw new CmsDbSqlException(
1351                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1352                e);
1353        } finally {
1354            m_sqlManager.closeAll(dbc, conn, stmt, res);
1355        }
1356
1357        for (CmsRelation rel : rels) {
1358            try {
1359                ous.add(
1360                    m_driverManager.readOrganizationalUnit(
1361                        dbc,
1362                        rel.getSourcePath().substring(CmsUserDriver.ORGUNIT_BASE_FOLDER.length())));
1363            } catch (CmsException e) {
1364                // should never happen
1365                if (LOG.isErrorEnabled()) {
1366                    LOG.error(e.getLocalizedMessage(), e);
1367                }
1368            }
1369        }
1370        return ous;
1371    }
1372
1373    /**
1374     * @see org.opencms.db.I_CmsVfsDriver#getSqlManager()
1375     */
1376    public CmsSqlManager getSqlManager() {
1377
1378        return m_sqlManager;
1379    }
1380
1381    /**
1382     * @see org.opencms.db.I_CmsVfsDriver#incrementCounter(org.opencms.db.CmsDbContext, java.lang.String)
1383     */
1384    public int incrementCounter(CmsDbContext dbc, String name) throws CmsDataAccessException {
1385
1386        Integer counterObj = internalReadCounter(dbc, name);
1387        int result;
1388        if (counterObj == null) {
1389            internalCreateCounter(dbc, name, 1);
1390            result = 0;
1391        } else {
1392            result = counterObj.intValue();
1393            internalIncrementCounter(dbc, name);
1394        }
1395        return result;
1396    }
1397
1398    /**
1399     * @see org.opencms.db.I_CmsDriver#init(org.opencms.db.CmsDbContext, org.opencms.configuration.CmsConfigurationManager, java.util.List, org.opencms.db.CmsDriverManager)
1400     */
1401    public void init(
1402        CmsDbContext dbc,
1403        CmsConfigurationManager configurationManager,
1404        List<String> successiveDrivers,
1405        CmsDriverManager driverManager) {
1406
1407        CmsParameterConfiguration configuration = configurationManager.getConfiguration();
1408        String poolUrl = configuration.get("db.vfs.pool");
1409        String classname = configuration.get("db.vfs.sqlmanager");
1410        m_sqlManager = initSqlManager(classname, getAdditionalSqlQueryProperties(configuration));
1411        m_sqlManager.init(I_CmsVfsDriver.DRIVER_TYPE_ID, poolUrl);
1412
1413        m_driverManager = driverManager;
1414
1415        if (CmsLog.INIT.isInfoEnabled()) {
1416            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ASSIGNED_POOL_1, poolUrl));
1417        }
1418
1419        if ((successiveDrivers != null) && !successiveDrivers.isEmpty()) {
1420            if (LOG.isWarnEnabled()) {
1421                LOG.warn(
1422                    Messages.get().getBundle().key(
1423                        Messages.LOG_SUCCESSIVE_DRIVERS_UNSUPPORTED_1,
1424                        getClass().getName()));
1425            }
1426        }
1427    }
1428
1429    /**
1430     * @see org.opencms.db.I_CmsVfsDriver#initSqlManager(String)
1431     */
1432    public org.opencms.db.generic.CmsSqlManager initSqlManager(String classname) {
1433
1434        return CmsSqlManager.getInstance(classname);
1435    }
1436
1437    /**
1438     * Initializes the SQL manager for this driver and loads additional SQL query property files.<p>
1439     *
1440     * @param classname the class name of the SQL manager
1441     * @param additionalQueryProperties the additional SQL query property files to load
1442     *
1443     * @return the SQL manager for this driver
1444     */
1445    public org.opencms.db.generic.CmsSqlManager initSqlManager(
1446        String classname,
1447        List<String> additionalQueryProperties) {
1448
1449        return CmsSqlManager.getInstance(classname, additionalQueryProperties);
1450    }
1451
1452    /**
1453     * @see org.opencms.db.I_CmsVfsDriver#insertAlias(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.db.CmsAlias)
1454     */
1455    public void insertAlias(CmsDbContext dbc, CmsProject project, CmsAlias alias) throws CmsDataAccessException {
1456
1457        Connection conn = null;
1458        PreparedStatement stmt = null;
1459        ResultSet res = null;
1460        try {
1461            conn = m_sqlManager.getConnection(dbc);
1462            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_ALIAS_ADD_4");
1463            stmt.setString(1, alias.getSiteRoot());
1464            stmt.setString(2, alias.getAliasPath());
1465            stmt.setInt(3, alias.getMode().toInt());
1466            stmt.setString(4, alias.getStructureId().toString());
1467            stmt.executeUpdate();
1468
1469        } catch (SQLException e) {
1470            throw new CmsDbSqlException(
1471                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1472                e);
1473        } finally {
1474            m_sqlManager.closeAll(dbc, conn, stmt, res);
1475        }
1476    }
1477
1478    /**
1479     * @see org.opencms.db.I_CmsVfsDriver#insertRewriteAliases(org.opencms.db.CmsDbContext, java.util.Collection)
1480     */
1481    public void insertRewriteAliases(CmsDbContext dbc, Collection<CmsRewriteAlias> rewriteAliases)
1482    throws CmsDataAccessException {
1483
1484        Connection conn = null;
1485        PreparedStatement stmt = null;
1486        ResultSet res = null;
1487        if (!rewriteAliases.isEmpty()) {
1488            try {
1489                conn = m_sqlManager.getConnection(dbc);
1490                stmt = m_sqlManager.getPreparedStatement(conn, dbc.currentProject(), "C_REWRITE_ALIAS_INSERT_5");
1491                for (CmsRewriteAlias alias : rewriteAliases) {
1492                    stmt.setString(1, alias.getId().toString());
1493                    stmt.setString(2, alias.getSiteRoot());
1494                    stmt.setString(3, alias.getPatternString());
1495                    stmt.setString(4, alias.getReplacementString());
1496                    stmt.setInt(5, alias.getMode().toInt());
1497                    stmt.addBatch();
1498                }
1499                stmt.executeBatch();
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, res);
1506            }
1507        }
1508    }
1509
1510    /**
1511     * @see org.opencms.db.I_CmsVfsDriver#moveResource(CmsDbContext, CmsUUID, CmsResource, String)
1512     */
1513    public void moveResource(CmsDbContext dbc, CmsUUID projectId, CmsResource source, String destinationPath)
1514    throws CmsDataAccessException {
1515
1516        if ((dbc.getRequestContext() != null)
1517            && (dbc.getRequestContext().getAttribute(REQ_ATTR_CHECK_PERMISSIONS) != null)) {
1518            // only check write permissions
1519            checkWritePermissionsInFolder(dbc, source);
1520            return;
1521        }
1522
1523        // determine destination folder
1524        String destinationFoldername = CmsResource.getParentFolder(destinationPath);
1525
1526        // read the destination folder (will also check read permissions)
1527        CmsFolder destinationFolder = m_driverManager.readFolder(dbc, destinationFoldername, CmsResourceFilter.ALL);
1528
1529        if (!projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
1530            // check online resource
1531            try {
1532                CmsResource onlineResource = m_driverManager.getVfsDriver(
1533                    dbc).readResource(dbc, CmsProject.ONLINE_PROJECT_ID, destinationPath, true);
1534
1535                if (!onlineResource.getStructureId().equals(source.getStructureId())) {
1536                    // source resource has been moved and it is not the
1537                    // same as the resource that is being trying to move back
1538                    CmsResource offlineResource = null;
1539                    try {
1540                        // read new location in offline project
1541                        offlineResource = readResource(
1542                            dbc,
1543                            dbc.getRequestContext().getCurrentProject().getUuid(),
1544                            onlineResource.getStructureId(),
1545                            true);
1546                    } catch (CmsException e) {
1547                        // should never happen
1548                        if (LOG.isErrorEnabled()) {
1549                            LOG.error(e.getMessage(), e);
1550                        }
1551                    }
1552
1553                    throw new CmsVfsOnlineResourceAlreadyExistsException(
1554                        Messages.get().container(
1555                            Messages.ERR_OVERWRITE_MOVED_RESOURCE_3,
1556                            dbc.removeSiteRoot(source.getRootPath()),
1557                            dbc.removeSiteRoot(destinationPath),
1558                            dbc.removeSiteRoot(offlineResource == null ? "__ERROR__" : offlineResource.getRootPath())));
1559                }
1560            } catch (CmsVfsResourceNotFoundException e) {
1561                // ok, no online resource
1562            }
1563        }
1564
1565        Connection conn = null;
1566        PreparedStatement stmt = null;
1567        ResultSet res = null;
1568
1569        try {
1570            conn = m_sqlManager.getConnection(dbc);
1571
1572            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_MOVE");
1573            stmt.setString(1, CmsFileUtil.removeTrailingSeparator(destinationPath)); // must remove trailing slash
1574            stmt.setString(2, destinationFolder.getStructureId().toString());
1575            stmt.setString(3, source.getStructureId().toString());
1576            stmt.executeUpdate();
1577        } catch (SQLException e) {
1578            throw new CmsDbSqlException(
1579                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1580                e);
1581        } finally {
1582            m_sqlManager.closeAll(dbc, conn, stmt, res);
1583        }
1584
1585        moveRelations(dbc, projectId, source.getStructureId(), destinationPath);
1586        repairBrokenRelations(dbc, projectId, source.getStructureId(), destinationPath);
1587        // repair project resources
1588        if (!projectId.equals(CmsProject.ONLINE_PROJECT_ID) && (dbc.getRequestContext() != null)) {
1589            String deletedResourceRootPath = source.getRootPath();
1590            dbc.getRequestContext().setAttribute(CmsProjectDriver.DBC_ATTR_READ_PROJECT_FOR_RESOURCE, Boolean.TRUE);
1591            I_CmsProjectDriver projectDriver = m_driverManager.getProjectDriver(dbc);
1592            Iterator<CmsProject> itProjects = projectDriver.readProjects(dbc, deletedResourceRootPath).iterator();
1593            while (itProjects.hasNext()) {
1594                CmsProject project = itProjects.next();
1595                projectDriver.deleteProjectResource(dbc, project.getUuid(), deletedResourceRootPath);
1596                projectDriver.createProjectResource(dbc, project.getUuid(), destinationPath);
1597            }
1598        }
1599    }
1600
1601    /**
1602     * @see org.opencms.db.I_CmsVfsDriver#publishResource(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource, org.opencms.file.CmsResource)
1603     */
1604    public void publishResource(
1605        CmsDbContext dbc,
1606        CmsProject onlineProject,
1607        CmsResource onlineResource,
1608        CmsResource offlineResource)
1609    throws CmsDataAccessException {
1610
1611        Connection conn = null;
1612        PreparedStatement stmt = null;
1613
1614        // validate the resource length
1615        internalValidateResourceLength(offlineResource);
1616        int resourceSize = offlineResource.getLength();
1617
1618        String resourcePath = CmsFileUtil.removeTrailingSeparator(offlineResource.getRootPath());
1619
1620        try {
1621            int sibCount = countSiblings(dbc, onlineProject.getUuid(), onlineResource.getResourceId());
1622            boolean resourceExists = validateResourceIdExists(
1623                dbc,
1624                onlineProject.getUuid(),
1625                offlineResource.getResourceId());
1626            conn = m_sqlManager.getConnection(dbc);
1627            if (resourceExists) {
1628                // the resource record exists online already
1629                // update the online resource record
1630                stmt = m_sqlManager.getPreparedStatement(conn, onlineProject, "C_RESOURCES_UPDATE_RESOURCES");
1631                stmt.setInt(1, offlineResource.getTypeId());
1632                stmt.setInt(2, offlineResource.getFlags());
1633                stmt.setLong(3, offlineResource.getDateLastModified());
1634                stmt.setString(4, offlineResource.getUserLastModified().toString());
1635                stmt.setInt(5, CmsResource.STATE_UNCHANGED.getState());
1636                stmt.setInt(6, resourceSize);
1637                stmt.setLong(7, offlineResource.getDateContent());
1638                stmt.setString(8, offlineResource.getProjectLastModified().toString());
1639                stmt.setInt(9, sibCount);
1640                stmt.setString(10, offlineResource.getResourceId().toString());
1641                stmt.executeUpdate();
1642                m_sqlManager.closeAll(dbc, conn, stmt, null);
1643            } else {
1644                // the resource record does NOT exist online yet
1645                // create the resource record online
1646                stmt = m_sqlManager.getPreparedStatement(conn, onlineProject, "C_RESOURCES_WRITE");
1647                stmt.setString(1, offlineResource.getResourceId().toString());
1648                stmt.setInt(2, offlineResource.getTypeId());
1649                stmt.setInt(3, offlineResource.getFlags());
1650                stmt.setLong(4, offlineResource.getDateCreated());
1651                stmt.setString(5, offlineResource.getUserCreated().toString());
1652                stmt.setLong(6, offlineResource.getDateLastModified());
1653                stmt.setString(7, offlineResource.getUserLastModified().toString());
1654                stmt.setInt(8, CmsResource.STATE_UNCHANGED.getState());
1655                stmt.setInt(9, resourceSize);
1656                stmt.setLong(10, offlineResource.getDateContent());
1657                stmt.setString(11, offlineResource.getProjectLastModified().toString());
1658                stmt.setInt(12, 1); // initial siblings count
1659                stmt.setInt(13, 1); // initial resource version
1660                stmt.executeUpdate();
1661                m_sqlManager.closeAll(dbc, conn, stmt, null);
1662            }
1663
1664            // read the parent id
1665            String parentId = internalReadParentId(dbc, onlineProject.getUuid(), resourcePath);
1666            boolean structureExists = validateStructureIdExists(
1667                dbc,
1668                onlineProject.getUuid(),
1669                offlineResource.getStructureId());
1670            conn = m_sqlManager.getConnection(dbc);
1671            if (structureExists) {
1672                // update the online structure record
1673                stmt = m_sqlManager.getPreparedStatement(conn, onlineProject, "C_RESOURCES_UPDATE_STRUCTURE");
1674                stmt.setString(1, offlineResource.getResourceId().toString());
1675                stmt.setString(2, resourcePath);
1676                stmt.setInt(3, CmsResource.STATE_UNCHANGED.getState());
1677                stmt.setLong(4, offlineResource.getDateReleased());
1678                stmt.setLong(5, offlineResource.getDateExpired());
1679                stmt.setString(6, parentId);
1680                stmt.setString(7, offlineResource.getStructureId().toString());
1681                stmt.executeUpdate();
1682                m_sqlManager.closeAll(dbc, null, stmt, null);
1683            } else {
1684                // create the structure record online
1685                stmt = m_sqlManager.getPreparedStatement(conn, onlineProject, "C_STRUCTURE_WRITE");
1686                stmt.setString(1, offlineResource.getStructureId().toString());
1687                stmt.setString(2, offlineResource.getResourceId().toString());
1688                stmt.setString(3, resourcePath);
1689                stmt.setInt(4, CmsResource.STATE_UNCHANGED.getState());
1690                stmt.setLong(5, offlineResource.getDateReleased());
1691                stmt.setLong(6, offlineResource.getDateExpired());
1692                stmt.setString(7, parentId);
1693                stmt.setInt(8, resourceExists ? 1 : 0); // new resources start with 0, new siblings with 1
1694                stmt.executeUpdate();
1695                m_sqlManager.closeAll(dbc, null, stmt, null);
1696            }
1697        } catch (SQLException e) {
1698            throw new CmsDbSqlException(
1699                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1700                e);
1701        } finally {
1702            m_sqlManager.closeAll(dbc, conn, stmt, null);
1703        }
1704
1705        try (Connection conn2 = m_sqlManager.getConnection(dbc)) {
1706            stmt = m_sqlManager.getPreparedStatement(conn2, onlineProject, "C_RELATIONS_REPAIR_BROKEN");
1707            stmt.setString(1, offlineResource.getStructureId().toString());
1708            stmt.setString(2, offlineResource.getRootPath());
1709            stmt.executeUpdate();
1710        } catch (SQLException e) {
1711            throw new CmsDbSqlException(
1712                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1713                e);
1714        }
1715    }
1716
1717    /**
1718     * @see org.opencms.db.I_CmsVfsDriver#publishVersions(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, boolean)
1719     */
1720    public void publishVersions(CmsDbContext dbc, CmsResource resource, boolean firstSibling)
1721    throws CmsDataAccessException {
1722
1723        // if resource is null just flush the internal cache
1724        if (resource == null) {
1725            m_resOp.clear();
1726            return;
1727        }
1728
1729        if (!dbc.getProjectId().isNullUUID() || dbc.currentProject().isOnlineProject()) {
1730            // this method is supposed to be used only in the offline project
1731            return;
1732        }
1733
1734        if (firstSibling) {
1735            // reset the resource operation flag
1736            m_resOp.remove(resource.getResourceId());
1737        }
1738
1739        boolean resOp = false; // assume structure operation
1740
1741        CmsResourceState resState = internalReadResourceState(dbc, dbc.currentProject().getUuid(), resource);
1742        CmsResourceState strState = internalReadStructureState(dbc, dbc.currentProject().getUuid(), resource);
1743
1744        if (!resState.isUnchanged()) {
1745            if (strState.isDeleted()) {
1746                resOp = (resState.isDeleted()
1747                    || (resource.getSiblingCount() == 1)
1748                    || (countSiblings(dbc, dbc.currentProject().getUuid(), resource.getResourceId()) == 1));
1749            } else {
1750                resOp = true;
1751            }
1752        }
1753
1754        if (!firstSibling) {
1755            if (resOp) {
1756                return;
1757            }
1758            if (m_resOp.contains(resource.getResourceId())) {
1759                return;
1760            }
1761        }
1762
1763        // read the offline version numbers
1764        Map<String, Integer> versions = readVersions(
1765            dbc,
1766            dbc.currentProject().getUuid(),
1767            resource.getResourceId(),
1768            resource.getStructureId());
1769        int strVersion = versions.get("structure").intValue();
1770        int resVersion = versions.get("resource").intValue();
1771
1772        if (resOp) {
1773            if (resource.getSiblingCount() > 1) {
1774                m_resOp.add(resource.getResourceId());
1775            }
1776            resVersion++;
1777        }
1778        if (!resOp) {
1779            strVersion++;
1780        }
1781
1782        Connection conn = null;
1783        PreparedStatement stmt = null;
1784        ResultSet res = null;
1785
1786        try {
1787            conn = m_sqlManager.getConnection(dbc);
1788
1789            if (resOp) {
1790                // update the resource version
1791                stmt = m_sqlManager.getPreparedStatement(
1792                    conn,
1793                    CmsProject.ONLINE_PROJECT_ID,
1794                    "C_RESOURCES_UPDATE_RESOURCE_VERSION");
1795                stmt.setInt(1, resVersion);
1796                stmt.setString(2, resource.getResourceId().toString());
1797                stmt.executeUpdate();
1798                m_sqlManager.closeAll(dbc, null, stmt, null);
1799            }
1800            if (!resOp || strState.isNew()) {
1801                // update the structure version
1802                stmt = m_sqlManager.getPreparedStatement(
1803                    conn,
1804                    CmsProject.ONLINE_PROJECT_ID,
1805                    "C_RESOURCES_UPDATE_STRUCTURE_VERSION");
1806                stmt.setInt(1, strVersion);
1807                stmt.setString(2, resource.getStructureId().toString());
1808                stmt.executeUpdate();
1809                m_sqlManager.closeAll(dbc, null, stmt, null);
1810            }
1811        } catch (SQLException e) {
1812            throw new CmsDbSqlException(
1813                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1814                e);
1815        } finally {
1816            m_sqlManager.closeAll(dbc, conn, stmt, res);
1817        }
1818    }
1819
1820    /**
1821     * @see org.opencms.db.I_CmsVfsDriver#readAliases(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.db.CmsAliasFilter)
1822     */
1823    public List<CmsAlias> readAliases(CmsDbContext dbc, CmsProject project, CmsAliasFilter filter)
1824    throws CmsDataAccessException {
1825
1826        Connection conn = null;
1827        PreparedStatement stmt = null;
1828        ResultSet res = null;
1829        try {
1830            conn = m_sqlManager.getConnection(dbc);
1831            CmsPair<String, List<String>> conditionPair = buildAliasConditions(filter);
1832            String conditionString = conditionPair.getFirst();
1833            List<String> conditionParams = conditionPair.getSecond();
1834            String sql = "SELECT site_root, path, alias_mode, structure_id FROM CMS_ALIASES WHERE " + conditionString;
1835            stmt = m_sqlManager.getPreparedStatementForSql(conn, sql);
1836            for (int i = 0; i < conditionParams.size(); i++) {
1837                stmt.setString(1 + i, conditionParams.get(i));
1838            }
1839            res = stmt.executeQuery();
1840            List<CmsAlias> result = new ArrayList<CmsAlias>();
1841            while (res.next()) {
1842                CmsAlias alias = internalReadAlias(res);
1843                result.add(alias);
1844            }
1845            return result;
1846        } catch (SQLException e) {
1847            throw new CmsDbSqlException(
1848                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1849                e);
1850        } finally {
1851            m_sqlManager.closeAll(dbc, conn, stmt, res);
1852        }
1853
1854    }
1855
1856    /**
1857     * @see org.opencms.db.I_CmsVfsDriver#readChildResources(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource, boolean, boolean)
1858     */
1859    public List<CmsResource> readChildResources(
1860        CmsDbContext dbc,
1861        CmsProject currentProject,
1862        CmsResource resource,
1863        boolean getFolders,
1864        boolean getFiles)
1865    throws CmsDataAccessException {
1866
1867        List<CmsResource> result = new ArrayList<CmsResource>();
1868        CmsUUID projectId = currentProject.getUuid();
1869
1870        String resourceTypeClause;
1871        if (getFolders && getFiles) {
1872            resourceTypeClause = null;
1873        } else if (getFolders) {
1874            resourceTypeClause = m_sqlManager.readQuery(projectId, "C_RESOURCES_GET_SUBRESOURCES_GET_FOLDERS");
1875        } else {
1876            resourceTypeClause = m_sqlManager.readQuery(projectId, "C_RESOURCES_GET_SUBRESOURCES_GET_FILES");
1877        }
1878        StringBuffer query = new StringBuffer();
1879        query.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_GET_SUBRESOURCES"));
1880        if (resourceTypeClause != null) {
1881            query.append(' ');
1882            query.append(resourceTypeClause);
1883        }
1884
1885        String sizeColumn = m_sqlManager.readQuery("C_RESOURCES_SIZE");
1886
1887        Connection conn = null;
1888        PreparedStatement stmt = null;
1889        ResultSet res = null;
1890        try {
1891            conn = m_sqlManager.getConnection(dbc);
1892            stmt = m_sqlManager.getPreparedStatementForSql(conn, query.toString());
1893            stmt.setString(1, resource.getStructureId().toString());
1894            res = stmt.executeQuery();
1895
1896            while (res.next()) {
1897                long size = res.getInt(sizeColumn);
1898                if (CmsFolder.isFolderSize(size)) {
1899                    result.add(createFolder(res, projectId, false));
1900                } else {
1901                    result.add(createFile(dbc, res, projectId, false));
1902                }
1903            }
1904        } catch (SQLException e) {
1905            throw new CmsDbSqlException(
1906                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1907                e);
1908        } finally {
1909            m_sqlManager.closeAll(dbc, conn, stmt, res);
1910        }
1911
1912        // sort result in memory, this is to avoid DB dependencies in the result order
1913        Collections.sort(result, I_CmsResource.COMPARE_ROOT_PATH_IGNORE_CASE_FOLDERS_FIRST);
1914        return result;
1915    }
1916
1917    /**
1918     * @see org.opencms.db.I_CmsVfsDriver#readContent(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID)
1919     */
1920    public byte[] readContent(CmsDbContext dbc, CmsUUID projectId, CmsUUID resourceId) throws CmsDataAccessException {
1921
1922        PreparedStatement stmt = null;
1923        ResultSet res = null;
1924        Connection conn = null;
1925        byte[] byteRes = null;
1926
1927        try {
1928            conn = m_sqlManager.getConnection(dbc);
1929            if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
1930                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ONLINE_FILES_CONTENT");
1931            } else {
1932                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_OFFLINE_FILES_CONTENT");
1933            }
1934            stmt.setString(1, resourceId.toString());
1935            res = stmt.executeQuery();
1936
1937            if (res.next()) {
1938                //query to read Array of bytes for the attribute FILE_CONTENT
1939                byteRes = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
1940                while (res.next()) {
1941                    // do nothing only move through all rows because of mssql odbc driver
1942                }
1943            } else {
1944                throw new CmsVfsResourceNotFoundException(
1945                    Messages.get().container(
1946                        Messages.ERR_READ_CONTENT_WITH_RESOURCE_ID_2,
1947                        resourceId,
1948                        Boolean.valueOf(projectId.equals(CmsProject.ONLINE_PROJECT_ID))));
1949            }
1950        } catch (SQLException e) {
1951            throw new CmsDbSqlException(
1952                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1953                e);
1954        } finally {
1955            m_sqlManager.closeAll(dbc, conn, stmt, res);
1956        }
1957        return byteRes;
1958    }
1959
1960    /**
1961     * @see org.opencms.db.I_CmsVfsDriver#readFolder(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID)
1962     */
1963    public CmsFolder readFolder(CmsDbContext dbc, CmsUUID projectId, CmsUUID folderId) throws CmsDataAccessException {
1964
1965        CmsFolder folder = null;
1966        ResultSet res = null;
1967        PreparedStatement stmt = null;
1968        Connection conn = null;
1969
1970        try {
1971            conn = m_sqlManager.getConnection(dbc);
1972            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READBYID");
1973            stmt.setString(1, folderId.toString());
1974            res = stmt.executeQuery();
1975
1976            if (res.next()) {
1977                folder = createFolder(res, projectId, true);
1978                while (res.next()) {
1979                    // do nothing only move through all rows because of mssql odbc driver
1980                }
1981            } else {
1982                throw new CmsVfsResourceNotFoundException(
1983                    Messages.get().container(Messages.ERR_READ_FOLDER_WITH_ID_1, folderId));
1984            }
1985        } catch (SQLException e) {
1986            throw new CmsDbSqlException(
1987                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
1988                e);
1989        } finally {
1990            m_sqlManager.closeAll(dbc, conn, stmt, res);
1991        }
1992
1993        return folder;
1994    }
1995
1996    /**
1997     * @see org.opencms.db.I_CmsVfsDriver#readFolder(org.opencms.db.CmsDbContext, CmsUUID, java.lang.String)
1998     */
1999    public CmsFolder readFolder(CmsDbContext dbc, CmsUUID projectId, String folderPath) throws CmsDataAccessException {
2000
2001        CmsFolder folder = null;
2002        ResultSet res = null;
2003        PreparedStatement stmt = null;
2004        Connection conn = null;
2005
2006        folderPath = CmsFileUtil.removeTrailingSeparator(folderPath);
2007        try {
2008            conn = m_sqlManager.getConnection(dbc);
2009            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ");
2010
2011            stmt.setString(1, folderPath);
2012            res = stmt.executeQuery();
2013
2014            if (res.next()) {
2015                folder = createFolder(res, projectId, true);
2016                while (res.next()) {
2017                    // do nothing only move through all rows because of mssql odbc driver
2018                }
2019            } else {
2020                throw new CmsVfsResourceNotFoundException(
2021                    Messages.get().container(Messages.ERR_READ_FOLDER_1, dbc.removeSiteRoot(folderPath)));
2022            }
2023        } catch (SQLException e) {
2024            throw new CmsDbSqlException(
2025                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2026                e);
2027        } finally {
2028            m_sqlManager.closeAll(dbc, conn, stmt, res);
2029        }
2030
2031        return folder;
2032
2033    }
2034
2035    public List<CmsFolderSizeEntry> readFolderSizeStats(CmsDbContext dbc, boolean online, CmsFolderSizeOptions options)
2036    throws CmsDataAccessException {
2037
2038        PreparedStatement stmt = null;
2039        List<CmsFolderSizeEntry> result = new ArrayList<>();
2040        try (Connection conn = m_sqlManager.getConnection(dbc)) {
2041            ResultSet res;
2042            if (options.isTree()) {
2043                String query = m_sqlManager.readQuery("C_FOLDER_SIZE_STATS");
2044                query = replaceProject(query, online);
2045                stmt = m_sqlManager.getPreparedStatementForSql(conn, query);
2046                stmt.setString(1, escapeDbWildcard(options.getRootPath()) + "%");
2047                res = stmt.executeQuery();
2048            } else {
2049                String query = m_sqlManager.readQuery("C_FOLDER_SIZE_STATS_SINGLE");
2050                query = replaceProject(query, online);
2051                stmt = m_sqlManager.getPreparedStatementForSql(conn, query);
2052                String path = options.getRootPath();
2053                if (!"/".equals(path)) {
2054                    path = CmsFileUtil.removeTrailingSeparator(path);
2055                }
2056                stmt.setString(1, path);
2057                res = stmt.executeQuery();
2058            }
2059            while (res.next()) {
2060                long size = res.getLong(1);
2061                String path = res.getString(2);
2062                String idStr = res.getString(3);
2063                int type = res.getInt(4);
2064                if (CmsUUID.isValidUUID(idStr)) {
2065                    CmsUUID id = new CmsUUID(idStr);
2066                    result.add(new CmsFolderSizeEntry(id, path, size, type));
2067                }
2068            }
2069            return Collections.unmodifiableList(result);
2070        } catch (SQLException e) {
2071            throw new CmsDbSqlException(
2072                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2073                e);
2074        }
2075    }
2076
2077    /**
2078     * @see org.opencms.db.I_CmsVfsDriver#readHistoryContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
2079     */
2080    public byte[] readHistoryContent(CmsDbContext dbc, CmsUUID resourceId, int publishTag)
2081    throws CmsDataAccessException {
2082
2083        Connection conn = null;
2084        PreparedStatement stmt = null;
2085        ResultSet res = null;
2086        byte[] content = null;
2087
2088        try {
2089            conn = m_sqlManager.getConnection(dbc);
2090            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_CONTENT");
2091            stmt.setString(1, resourceId.toString());
2092            stmt.setInt(2, publishTag);
2093            stmt.setInt(3, publishTag);
2094            res = stmt.executeQuery();
2095
2096            if (res.next()) {
2097                content = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
2098                while (res.next()) {
2099                    // do nothing only move through all rows because of mssql odbc driver
2100                }
2101            }
2102        } catch (SQLException e) {
2103            throw new CmsDbSqlException(
2104                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2105                e);
2106        } finally {
2107            m_sqlManager.closeAll(dbc, conn, stmt, res);
2108        }
2109        return content;
2110    }
2111
2112    /**
2113     * @see org.opencms.db.I_CmsVfsDriver#readParentFolder(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID)
2114     */
2115    public CmsFolder readParentFolder(CmsDbContext dbc, CmsUUID projectId, CmsUUID structureId)
2116    throws CmsDataAccessException {
2117
2118        CmsFolder parent = null;
2119        ResultSet res = null;
2120        PreparedStatement stmt = null;
2121        Connection conn = null;
2122        try {
2123            conn = m_sqlManager.getConnection(dbc);
2124            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ_PARENT_BY_ID");
2125            stmt.setString(1, structureId.toString());
2126            res = stmt.executeQuery();
2127
2128            if (res.next()) {
2129                parent = new CmsFolder(createResource(res, projectId));
2130                while (res.next()) {
2131                    // do nothing only move through all rows because of mssql odbc driver
2132                }
2133            }
2134        } catch (SQLException e) {
2135            throw new CmsDbSqlException(
2136                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2137                e);
2138        } finally {
2139            m_sqlManager.closeAll(dbc, conn, stmt, res);
2140        }
2141        return parent;
2142    }
2143
2144    /**
2145     * @see org.opencms.db.I_CmsVfsDriver#readPropertyDefinition(org.opencms.db.CmsDbContext, java.lang.String, CmsUUID)
2146     */
2147    public CmsPropertyDefinition readPropertyDefinition(CmsDbContext dbc, String name, CmsUUID projectId)
2148    throws CmsDataAccessException {
2149
2150        CmsPropertyDefinition propDef = null;
2151        ResultSet res = null;
2152        PreparedStatement stmt = null;
2153        Connection conn = null;
2154
2155        try {
2156            conn = m_sqlManager.getConnection(dbc);
2157            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTYDEF_READ");
2158            stmt.setString(1, name);
2159            res = stmt.executeQuery();
2160
2161            // if result set exists - return it
2162            if (res.next()) {
2163                propDef = new CmsPropertyDefinition(
2164                    new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_ID"))),
2165                    res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_NAME")),
2166                    CmsPropertyDefinition.CmsPropertyType.valueOf(
2167                        res.getInt(m_sqlManager.readQuery("C_PROPERTYDEF_TYPE"))));
2168                while (res.next()) {
2169                    // do nothing only move through all rows because of mssql odbc driver
2170                }
2171            } else {
2172                throw new CmsDbEntryNotFoundException(
2173                    Messages.get().container(Messages.ERR_NO_PROPERTYDEF_WITH_NAME_1, name));
2174            }
2175        } catch (SQLException e) {
2176            throw new CmsDbSqlException(
2177                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2178                e);
2179        } finally {
2180            m_sqlManager.closeAll(dbc, conn, stmt, res);
2181        }
2182
2183        return propDef;
2184    }
2185
2186    /**
2187     * @see org.opencms.db.I_CmsVfsDriver#readPropertyDefinitions(org.opencms.db.CmsDbContext, CmsUUID)
2188     */
2189    public List<CmsPropertyDefinition> readPropertyDefinitions(CmsDbContext dbc, CmsUUID projectId)
2190    throws CmsDataAccessException {
2191
2192        ArrayList<CmsPropertyDefinition> propertyDefinitions = new ArrayList<CmsPropertyDefinition>();
2193        ResultSet res = null;
2194        PreparedStatement stmt = null;
2195        Connection conn = null;
2196
2197        try {
2198            conn = m_sqlManager.getConnection(dbc);
2199            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTYDEF_READALL");
2200
2201            res = stmt.executeQuery();
2202            while (res.next()) {
2203                propertyDefinitions.add(
2204                    new CmsPropertyDefinition(
2205                        new CmsUUID(res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_ID"))),
2206                        res.getString(m_sqlManager.readQuery("C_PROPERTYDEF_NAME")),
2207                        CmsPropertyDefinition.CmsPropertyType.valueOf(
2208                            res.getInt(m_sqlManager.readQuery("C_PROPERTYDEF_TYPE")))));
2209            }
2210        } catch (SQLException e) {
2211            throw new CmsDbSqlException(
2212                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2213                e);
2214        } finally {
2215            m_sqlManager.closeAll(dbc, conn, stmt, res);
2216        }
2217        return propertyDefinitions;
2218    }
2219
2220    /**
2221     * @see org.opencms.db.I_CmsVfsDriver#readPropertyObject(org.opencms.db.CmsDbContext, java.lang.String, org.opencms.file.CmsProject, org.opencms.file.CmsResource)
2222     */
2223    public CmsProperty readPropertyObject(CmsDbContext dbc, String key, CmsProject project, CmsResource resource)
2224    throws CmsDataAccessException {
2225
2226        CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID())
2227        ? project.getUuid()
2228        : dbc.getProjectId();
2229
2230        ResultSet res = null;
2231        PreparedStatement stmt = null;
2232        Connection conn = null;
2233        String propertyValue = null;
2234        int mappingType = -1;
2235        CmsProperty property = null;
2236        int resultSize = 0;
2237
2238        try {
2239            conn = m_sqlManager.getConnection(dbc);
2240            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_READ");
2241
2242            stmt.setString(1, key);
2243            stmt.setString(2, resource.getStructureId().toString());
2244            stmt.setString(3, resource.getResourceId().toString());
2245            res = stmt.executeQuery();
2246
2247            while (res.next()) {
2248                if (resultSize >= 2) {
2249                    throw new CmsDbConsistencyException(
2250                        Messages.get().container(
2251                            Messages.ERR_TOO_MANY_PROPERTIES_3,
2252                            key,
2253                            resource.getRootPath(),
2254                            Integer.valueOf(resultSize)));
2255                }
2256
2257                if (property == null) {
2258                    property = new CmsProperty();
2259                    property.setName(key);
2260                }
2261
2262                propertyValue = res.getString(1);
2263                mappingType = res.getInt(2);
2264
2265                if (mappingType == CmsProperty.STRUCTURE_RECORD_MAPPING) {
2266                    property.setStructureValue(propertyValue);
2267                } else if (mappingType == CmsProperty.RESOURCE_RECORD_MAPPING) {
2268                    property.setResourceValue(propertyValue);
2269                } else {
2270                    throw new CmsDbConsistencyException(
2271                        Messages.get().container(
2272                            Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
2273                            resource.getRootPath(),
2274                            Integer.valueOf(mappingType),
2275                            key));
2276                }
2277
2278                resultSize++;
2279            }
2280        } catch (SQLException e) {
2281            throw new CmsDbSqlException(
2282                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2283                e);
2284        } finally {
2285            m_sqlManager.closeAll(dbc, conn, stmt, res);
2286        }
2287
2288        return (property != null) ? property : CmsProperty.getNullProperty();
2289    }
2290
2291    /**
2292     * @see org.opencms.db.I_CmsVfsDriver#readPropertyObjects(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource)
2293     */
2294    public List<CmsProperty> readPropertyObjects(CmsDbContext dbc, CmsProject project, CmsResource resource)
2295    throws CmsDataAccessException {
2296
2297        CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID())
2298        ? project.getUuid()
2299        : dbc.getProjectId();
2300
2301        ResultSet res = null;
2302        PreparedStatement stmt = null;
2303        Connection conn = null;
2304        int mappingType = -1;
2305        Map<String, CmsProperty> propertyMap = new HashMap<String, CmsProperty>();
2306
2307        String propertyKey;
2308        String propertyValue;
2309        CmsProperty property;
2310
2311        try {
2312            conn = m_sqlManager.getConnection(dbc);
2313            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_READALL");
2314            stmt.setString(1, resource.getStructureId().toString());
2315            stmt.setString(2, resource.getResourceId().toString());
2316            res = stmt.executeQuery();
2317
2318            while (res.next()) {
2319                propertyKey = null;
2320                propertyValue = null;
2321                mappingType = -1;
2322
2323                propertyKey = res.getString(1);
2324                propertyValue = res.getString(2);
2325                mappingType = res.getInt(3);
2326
2327                property = propertyMap.get(propertyKey);
2328                if (property == null) {
2329                    // there doesn't exist a property object for this key yet
2330                    property = new CmsProperty();
2331                    property.setName(propertyKey);
2332                    propertyMap.put(propertyKey, property);
2333                }
2334
2335                if (mappingType == CmsProperty.STRUCTURE_RECORD_MAPPING) {
2336                    // this property value is mapped to a structure record
2337                    property.setStructureValue(propertyValue);
2338                } else if (mappingType == CmsProperty.RESOURCE_RECORD_MAPPING) {
2339                    // this property value is mapped to a resource record
2340                    property.setResourceValue(propertyValue);
2341                } else {
2342                    throw new CmsDbConsistencyException(
2343                        Messages.get().container(
2344                            Messages.ERR_UNKNOWN_PROPERTY_VALUE_MAPPING_3,
2345                            resource.getRootPath(),
2346                            Integer.valueOf(mappingType),
2347                            propertyKey));
2348                }
2349                property.setOrigin(resource.getRootPath());
2350            }
2351        } catch (SQLException e) {
2352            throw new CmsDbSqlException(
2353                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2354                e);
2355        } finally {
2356            m_sqlManager.closeAll(dbc, conn, stmt, res);
2357        }
2358
2359        return new ArrayList<CmsProperty>(propertyMap.values());
2360    }
2361
2362    /**
2363     * @see org.opencms.db.I_CmsVfsDriver#readRelations(org.opencms.db.CmsDbContext, CmsUUID, CmsResource, org.opencms.relations.CmsRelationFilter)
2364     */
2365    public List<CmsRelation> readRelations(
2366        CmsDbContext dbc,
2367        CmsUUID projectId,
2368        CmsResource resource,
2369        CmsRelationFilter filter)
2370    throws CmsDataAccessException {
2371
2372        Set<CmsRelation> relations = new HashSet<CmsRelation>();
2373
2374        Connection conn = null;
2375        PreparedStatement stmt = null;
2376        ResultSet res = null;
2377
2378        try {
2379            conn = m_sqlManager.getConnection(dbc);
2380            if (filter.isSource()) {
2381                List<Object> params = new ArrayList<Object>(7);
2382
2383                StringBuffer queryBuf = new StringBuffer(256);
2384                queryBuf.append(m_sqlManager.readQuery(projectId, "C_READ_RELATIONS"));
2385                queryBuf.append(
2386                    prepareRelationConditions(RelationConditionMode.read, projectId, filter, resource, params, true));
2387                if (LOG.isDebugEnabled()) {
2388                    LOG.debug(queryBuf.toString());
2389                }
2390
2391                stmt = m_sqlManager.getPreparedStatementForSql(conn, queryBuf.toString());
2392                for (int i = 0; i < params.size(); i++) {
2393                    if (params.get(i) instanceof Integer) {
2394                        stmt.setInt(i + 1, ((Integer)params.get(i)).intValue());
2395                    } else {
2396                        stmt.setString(i + 1, (String)params.get(i));
2397                    }
2398                }
2399                res = stmt.executeQuery();
2400                while (res.next()) {
2401                    relations.add(internalReadRelation(res));
2402                }
2403                m_sqlManager.closeAll(dbc, null, stmt, res);
2404            }
2405
2406            if (filter.isTarget()) {
2407                List<Object> params = new ArrayList<Object>(7);
2408
2409                StringBuffer queryBuf = new StringBuffer(256);
2410                queryBuf.append(m_sqlManager.readQuery(projectId, "C_READ_RELATIONS"));
2411                queryBuf.append(
2412                    prepareRelationConditions(RelationConditionMode.read, projectId, filter, resource, params, false));
2413                if (LOG.isDebugEnabled()) {
2414                    LOG.debug(queryBuf.toString());
2415                }
2416
2417                stmt = m_sqlManager.getPreparedStatementForSql(conn, queryBuf.toString());
2418                for (int i = 0; i < params.size(); i++) {
2419                    if (params.get(i) instanceof Integer) {
2420                        stmt.setInt(i + 1, ((Integer)params.get(i)).intValue());
2421                    } else {
2422                        stmt.setString(i + 1, (String)params.get(i));
2423                    }
2424                }
2425                res = stmt.executeQuery();
2426                while (res.next()) {
2427                    relations.add(internalReadRelation(res));
2428                }
2429                m_sqlManager.closeAll(dbc, null, stmt, res);
2430            }
2431        } catch (SQLException e) {
2432            throw new CmsDbSqlException(
2433                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2434                e);
2435        } finally {
2436            m_sqlManager.closeAll(dbc, conn, stmt, res);
2437        }
2438
2439        List<CmsRelation> result = new ArrayList<CmsRelation>(relations);
2440        Collections.sort(result, CmsRelation.COMPARATOR);
2441        return result;
2442    }
2443
2444    /**
2445     * @see org.opencms.db.I_CmsVfsDriver#readResource(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID, boolean)
2446     */
2447    public CmsResource readResource(CmsDbContext dbc, CmsUUID projectId, CmsUUID structureId, boolean includeDeleted)
2448    throws CmsDataAccessException {
2449
2450        CmsResource resource = null;
2451        ResultSet res = null;
2452        PreparedStatement stmt = null;
2453        Connection conn = null;
2454
2455        try {
2456            conn = m_sqlManager.getConnection(dbc);
2457            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READBYID");
2458
2459            stmt.setString(1, structureId.toString());
2460            res = stmt.executeQuery();
2461
2462            if (res.next()) {
2463                resource = createResource(res, projectId);
2464                while (res.next()) {
2465                    // do nothing only move through all rows because of mssql odbc driver
2466                }
2467            } else {
2468                throw new CmsVfsResourceNotFoundException(
2469                    Messages.get().container(Messages.ERR_READ_RESOURCE_WITH_ID_1, structureId));
2470            }
2471        } catch (SQLException e) {
2472            throw new CmsDbSqlException(
2473                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2474                e);
2475        } finally {
2476            m_sqlManager.closeAll(dbc, conn, stmt, res);
2477        }
2478
2479        // check if this resource is marked as deleted and if we are allowed to return a deleted resource
2480        if ((resource != null) && resource.getState().isDeleted() && !includeDeleted) {
2481            throw new CmsVfsResourceNotFoundException(
2482                Messages.get().container(
2483                    Messages.ERR_READ_DELETED_RESOURCE_1,
2484                    dbc.removeSiteRoot(resource.getRootPath())));
2485        }
2486
2487        return resource;
2488    }
2489
2490    /**
2491     * @see org.opencms.db.I_CmsVfsDriver#readResource(org.opencms.db.CmsDbContext, CmsUUID, java.lang.String, boolean)
2492     */
2493    public CmsResource readResource(CmsDbContext dbc, CmsUUID projectId, String path, boolean includeDeleted)
2494    throws CmsDataAccessException {
2495
2496        CmsResource resource = null;
2497        ResultSet res = null;
2498        PreparedStatement stmt = null;
2499        Connection conn = null;
2500
2501        // must remove trailing slash
2502        int len = path.length();
2503        path = CmsFileUtil.removeTrailingSeparator(path);
2504        boolean endsWithSlash = (len != path.length());
2505
2506        try {
2507            conn = m_sqlManager.getConnection(dbc);
2508            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ");
2509
2510            stmt.setString(1, path);
2511            res = stmt.executeQuery();
2512
2513            if (res.next()) {
2514                resource = createResource(res, projectId);
2515
2516                // check if the resource is a file, it is not allowed to end with a "/" then
2517                if (endsWithSlash && resource.isFile()) {
2518                    throw new CmsVfsResourceNotFoundException(
2519                        Messages.get().container(Messages.ERR_READ_RESOURCE_1, dbc.removeSiteRoot(path + "/")));
2520                }
2521
2522                while (res.next()) {
2523                    // do nothing only move through all rows because of mssql odbc driver
2524                }
2525            } else {
2526                throw new CmsVfsResourceNotFoundException(
2527                    Messages.get().container(Messages.ERR_READ_RESOURCE_1, dbc.removeSiteRoot(path)));
2528            }
2529        } catch (SQLException e) {
2530            throw new CmsDbSqlException(
2531                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2532                e);
2533        } finally {
2534            m_sqlManager.closeAll(dbc, conn, stmt, res);
2535        }
2536
2537        // check if this resource is marked as deleted and if we are allowed to return a deleted resource
2538        if ((resource != null) && resource.getState().isDeleted() && !includeDeleted) {
2539            throw new CmsVfsResourceNotFoundException(
2540                Messages.get().container(
2541                    Messages.ERR_READ_DELETED_RESOURCE_1,
2542                    dbc.removeSiteRoot(resource.getRootPath())));
2543        }
2544
2545        return resource;
2546    }
2547
2548    /**
2549     * @see org.opencms.db.I_CmsVfsDriver#readResources(org.opencms.db.CmsDbContext, CmsUUID, CmsResourceState, int)
2550     */
2551    public List<CmsResource> readResources(CmsDbContext dbc, CmsUUID projectId, CmsResourceState state, int mode)
2552    throws CmsDataAccessException {
2553
2554        List<CmsResource> result = new ArrayList<CmsResource>();
2555
2556        ResultSet res = null;
2557        PreparedStatement stmt = null;
2558        Connection conn = null;
2559
2560        try {
2561            conn = m_sqlManager.getConnection(dbc);
2562            if (mode == CmsDriverManager.READMODE_MATCHSTATE) {
2563                stmt = m_sqlManager.getPreparedStatement(
2564                    conn,
2565                    projectId,
2566                    "C_RESOURCES_GET_RESOURCE_IN_PROJECT_WITH_STATE");
2567                stmt.setString(1, projectId.toString());
2568                stmt.setInt(2, state.getState());
2569                stmt.setInt(3, state.getState());
2570                stmt.setInt(4, state.getState());
2571                stmt.setInt(5, state.getState());
2572            } else if (mode == CmsDriverManager.READMODE_UNMATCHSTATE) {
2573                stmt = m_sqlManager.getPreparedStatement(
2574                    conn,
2575                    projectId,
2576                    "C_RESOURCES_GET_RESOURCE_IN_PROJECT_WITHOUT_STATE");
2577                stmt.setString(1, projectId.toString());
2578                stmt.setInt(2, state.getState());
2579                stmt.setInt(3, state.getState());
2580            } else {
2581                stmt = m_sqlManager.getPreparedStatement(
2582                    conn,
2583                    projectId,
2584                    "C_RESOURCES_GET_RESOURCE_IN_PROJECT_IGNORE_STATE");
2585                stmt.setString(1, projectId.toString());
2586            }
2587
2588            res = stmt.executeQuery();
2589            while (res.next()) {
2590                CmsResource resource = createResource(res, projectId);
2591                result.add(resource);
2592            }
2593        } catch (SQLException e) {
2594            throw new CmsDbSqlException(
2595                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2596                e);
2597        } finally {
2598            m_sqlManager.closeAll(dbc, conn, stmt, res);
2599        }
2600
2601        return result;
2602    }
2603
2604    /**
2605     * @see org.opencms.db.I_CmsVfsDriver#readResourcesForPrincipalACE(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.util.CmsUUID)
2606     */
2607    public List<CmsResource> readResourcesForPrincipalACE(CmsDbContext dbc, CmsProject project, CmsUUID principalId)
2608    throws CmsDataAccessException {
2609
2610        PreparedStatement stmt = null;
2611        Connection conn = null;
2612        ResultSet res = null;
2613        CmsResource currentResource = null;
2614        List<CmsResource> resources = new ArrayList<CmsResource>();
2615
2616        try {
2617            conn = m_sqlManager.getConnection(dbc);
2618            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_SELECT_RESOURCES_FOR_PRINCIPAL_ACE");
2619
2620            stmt.setString(1, principalId.toString());
2621            res = stmt.executeQuery();
2622
2623            while (res.next()) {
2624                currentResource = createFile(dbc, res, project.getUuid(), false);
2625                resources.add(currentResource);
2626            }
2627        } catch (SQLException e) {
2628            throw new CmsDbSqlException(
2629                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2630                e);
2631        } finally {
2632            m_sqlManager.closeAll(dbc, conn, stmt, res);
2633        }
2634        return resources;
2635    }
2636
2637    /**
2638     * @see org.opencms.db.I_CmsVfsDriver#readResourcesForPrincipalAttr(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.util.CmsUUID)
2639     */
2640    public List<CmsResource> readResourcesForPrincipalAttr(CmsDbContext dbc, CmsProject project, CmsUUID principalId)
2641    throws CmsDataAccessException {
2642
2643        PreparedStatement stmt = null;
2644        Connection conn = null;
2645        ResultSet res = null;
2646        CmsResource currentResource = null;
2647        List<CmsResource> resources = new ArrayList<CmsResource>();
2648
2649        try {
2650            conn = m_sqlManager.getConnection(dbc);
2651            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_SELECT_RESOURCES_FOR_PRINCIPAL_ATTR");
2652
2653            stmt.setString(1, principalId.toString());
2654            stmt.setString(2, principalId.toString());
2655            res = stmt.executeQuery();
2656
2657            while (res.next()) {
2658                currentResource = createFile(dbc, res, project.getUuid(), false);
2659                resources.add(currentResource);
2660            }
2661        } catch (SQLException e) {
2662            throw new CmsDbSqlException(
2663                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2664                e);
2665        } finally {
2666            m_sqlManager.closeAll(dbc, conn, stmt, res);
2667        }
2668        return resources;
2669    }
2670
2671    /**
2672     * @see org.opencms.db.I_CmsVfsDriver#readResourcesWithProperty(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID, String, String)
2673     */
2674    public List<CmsResource> readResourcesWithProperty(
2675        CmsDbContext dbc,
2676        CmsUUID projectId,
2677        CmsUUID propertyDef,
2678        String path,
2679        String value)
2680    throws CmsDataAccessException {
2681
2682        List<CmsResource> resources = new ArrayList<CmsResource>();
2683        ResultSet res = null;
2684        PreparedStatement stmt = null;
2685        Connection conn = null;
2686
2687        try {
2688            conn = m_sqlManager.getConnection(dbc);
2689            if (value == null) {
2690                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_GET_RESOURCE_WITH_PROPERTYDEF");
2691                stmt.setString(1, propertyDef.toString());
2692                stmt.setString(2, path + "%");
2693                stmt.setString(3, propertyDef.toString());
2694                stmt.setString(4, path + "%");
2695            } else {
2696                stmt = m_sqlManager.getPreparedStatement(
2697                    conn,
2698                    projectId,
2699                    "C_RESOURCES_GET_RESOURCE_WITH_PROPERTYDEF_VALUE");
2700                stmt.setString(1, propertyDef.toString());
2701                stmt.setString(2, path + "%");
2702                stmt.setString(3, "%" + value + "%");
2703                stmt.setString(4, propertyDef.toString());
2704                stmt.setString(5, path + "%");
2705                stmt.setString(6, "%" + value + "%");
2706            }
2707            res = stmt.executeQuery();
2708
2709            while (res.next()) {
2710                CmsResource resource = createResource(res, projectId);
2711                resources.add(resource);
2712            }
2713        } catch (SQLException e) {
2714            throw new CmsDbSqlException(
2715                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2716                e);
2717        } finally {
2718            m_sqlManager.closeAll(dbc, conn, stmt, res);
2719        }
2720
2721        return resources;
2722    }
2723
2724    /**
2725     * @see org.opencms.db.I_CmsVfsDriver#readResourceTree(org.opencms.db.CmsDbContext, CmsUUID, java.lang.String, int, CmsResourceState, long, long, long, long, long, long, int)
2726     */
2727    public List<CmsResource> readResourceTree(
2728        CmsDbContext dbc,
2729        CmsUUID projectId,
2730        String parentPath,
2731        int type,
2732        CmsResourceState state,
2733        long lastModifiedAfter,
2734        long lastModifiedBefore,
2735        long releasedAfter,
2736        long releasedBefore,
2737        long expiredAfter,
2738        long expiredBefore,
2739        int mode)
2740    throws CmsDataAccessException {
2741
2742        List<CmsResource> result = new ArrayList<CmsResource>();
2743
2744        StringBuffer conditions = new StringBuffer();
2745        List<Object> params = new ArrayList<Object>(5);
2746
2747        // prepare the selection criteria
2748        prepareProjectCondition(projectId, mode, conditions, params);
2749        prepareResourceCondition(projectId, mode, conditions);
2750        prepareTypeCondition(projectId, type, mode, conditions, params);
2751        prepareTimeRangeCondition(projectId, lastModifiedAfter, lastModifiedBefore, conditions, params);
2752        prepareReleasedTimeRangeCondition(projectId, releasedAfter, releasedBefore, conditions, params);
2753        prepareExpiredTimeRangeCondition(projectId, expiredAfter, expiredBefore, conditions, params);
2754        preparePathCondition(projectId, parentPath, mode, conditions, params);
2755        prepareStateCondition(projectId, state, mode, conditions, params);
2756
2757        // now read matching resources within the subtree
2758        ResultSet res = null;
2759        PreparedStatement stmt = null;
2760        Connection conn = null;
2761
2762        try {
2763            conn = m_sqlManager.getConnection(dbc);
2764            StringBuffer queryBuf = new StringBuffer(256);
2765            queryBuf.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_READ_TREE"));
2766            queryBuf.append(conditions);
2767            queryBuf.append(" ");
2768            queryBuf.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_ORDER_BY_PATH"));
2769            stmt = m_sqlManager.getPreparedStatementForSql(conn, queryBuf.toString());
2770
2771            for (int i = 0; i < params.size(); i++) {
2772                if (params.get(i) instanceof Integer) {
2773                    stmt.setInt(i + 1, ((Integer)params.get(i)).intValue());
2774                } else if (params.get(i) instanceof Long) {
2775                    stmt.setLong(i + 1, ((Long)params.get(i)).longValue());
2776                } else {
2777                    stmt.setString(i + 1, (String)params.get(i));
2778                }
2779            }
2780
2781            res = stmt.executeQuery();
2782            while (res.next()) {
2783                CmsResource resource = createResource(res, projectId);
2784                result.add(resource);
2785            }
2786
2787        } catch (SQLException e) {
2788            throw new CmsDbSqlException(
2789                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2790                e);
2791        } finally {
2792            m_sqlManager.closeAll(dbc, conn, stmt, res);
2793        }
2794
2795        return result;
2796    }
2797
2798    /**
2799     * @see org.opencms.db.I_CmsVfsDriver#readRewriteAliases(org.opencms.db.CmsDbContext, org.opencms.db.CmsRewriteAliasFilter)
2800     */
2801    public List<CmsRewriteAlias> readRewriteAliases(CmsDbContext dbc, CmsRewriteAliasFilter filter)
2802    throws CmsDataAccessException {
2803
2804        Connection conn = null;
2805        PreparedStatement stmt = null;
2806        ResultSet res = null;
2807        List<CmsRewriteAlias> result = new ArrayList<CmsRewriteAlias>();
2808        try {
2809            conn = m_sqlManager.getConnection(dbc);
2810            CmsPair<String, List<Object>> conditionAndParams = prepareRewriteAliasConditions(filter);
2811            String condition = conditionAndParams.getFirst();
2812            List<Object> params = conditionAndParams.getSecond();
2813            String query = m_sqlManager.readQuery("C_REWRITE_ALIAS_READ") + condition;
2814            stmt = m_sqlManager.getPreparedStatementForSql(conn, query);
2815            CmsDbUtil.fillParameters(stmt, params);
2816            res = stmt.executeQuery();
2817            while (res.next()) {
2818                int col = 1;
2819                String id = res.getString(col++);
2820                String siteRoot = res.getString(col++);
2821                String patternString = res.getString(col++);
2822                String replacementString = res.getString(col++);
2823                int mode = res.getInt(col++);
2824                CmsRewriteAlias alias = new CmsRewriteAlias(
2825                    new CmsUUID(id),
2826                    siteRoot,
2827                    patternString,
2828                    replacementString,
2829                    CmsAliasMode.fromInt(mode));
2830                result.add(alias);
2831            }
2832            return result;
2833        } catch (SQLException e) {
2834            throw wrapException(stmt, e);
2835        } finally {
2836            m_sqlManager.closeAll(dbc, conn, stmt, res);
2837        }
2838
2839    }
2840
2841    /**
2842     * @see org.opencms.db.I_CmsVfsDriver#readSiblings(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.file.CmsResource, boolean)
2843     */
2844    public List<CmsResource> readSiblings(
2845        CmsDbContext dbc,
2846        CmsUUID projectId,
2847        CmsResource resource,
2848        boolean includeDeleted)
2849    throws CmsDataAccessException {
2850
2851        PreparedStatement stmt = null;
2852        Connection conn = null;
2853        ResultSet res = null;
2854        CmsResource currentResource = null;
2855        List<CmsResource> vfsLinks = new ArrayList<CmsResource>();
2856
2857        try {
2858            conn = m_sqlManager.getConnection(dbc);
2859
2860            if (includeDeleted) {
2861                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_SELECT_VFS_SIBLINGS");
2862            } else {
2863                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_SELECT_NONDELETED_VFS_SIBLINGS");
2864            }
2865
2866            stmt.setString(1, resource.getResourceId().toString());
2867            res = stmt.executeQuery();
2868
2869            while (res.next()) {
2870                currentResource = createFile(dbc, res, projectId, false);
2871                vfsLinks.add(currentResource);
2872            }
2873        } catch (SQLException e) {
2874            throw new CmsDbSqlException(
2875                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2876                e);
2877        } finally {
2878            m_sqlManager.closeAll(dbc, conn, stmt, res);
2879        }
2880
2881        return vfsLinks;
2882    }
2883
2884    /**
2885     * Reads the URL name mapping entries which match a given filter.<p>
2886     *
2887     * @param dbc the database context
2888     * @param online if true, reads from the online mapping, else from the offline mapping
2889     * @param filter the filter which the entries to be read should match
2890     *
2891     * @return the mapping entries which match the given filter
2892     *
2893     * @throws CmsDataAccessException if something goes wrong
2894     */
2895    public List<CmsUrlNameMappingEntry> readUrlNameMappingEntries(
2896        CmsDbContext dbc,
2897        boolean online,
2898        CmsUrlNameMappingFilter filter)
2899    throws CmsDataAccessException {
2900
2901        Connection conn = null;
2902        ResultSet resultSet = null;
2903        PreparedStatement stmt = null;
2904        List<CmsUrlNameMappingEntry> result = new ArrayList<CmsUrlNameMappingEntry>();
2905        try {
2906            conn = m_sqlManager.getConnection(dbc);
2907            String query = m_sqlManager.readQuery("C_READ_URLNAME_MAPPINGS");
2908            query = replaceProject(query, online);
2909            stmt = getPreparedStatementForFilter(conn, query, filter);
2910            resultSet = stmt.executeQuery();
2911            while (resultSet.next()) {
2912                CmsUrlNameMappingEntry entry = internalCreateUrlNameMappingEntry(resultSet);
2913                result.add(entry);
2914            }
2915            return result;
2916        } catch (SQLException e) {
2917            throw wrapException(stmt, e);
2918        } finally {
2919            m_sqlManager.closeAll(dbc, conn, stmt, resultSet);
2920        }
2921    }
2922
2923    /**
2924     * @see org.opencms.db.I_CmsVfsDriver#readVersions(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, org.opencms.util.CmsUUID, org.opencms.util.CmsUUID)
2925     */
2926    public Map<String, Integer> readVersions(
2927        CmsDbContext dbc,
2928        CmsUUID projectId,
2929        CmsUUID resourceId,
2930        CmsUUID structureId)
2931    throws CmsDataAccessException {
2932
2933        int structureVersion = -1;
2934        int resourceVersion = -1;
2935
2936        Connection conn = null;
2937        PreparedStatement stmt = null;
2938        ResultSet res = null;
2939
2940        try {
2941            conn = m_sqlManager.getConnection(dbc);
2942
2943            // read the offline version numbers, first for the resource entry
2944            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ_VERSION_RES");
2945            stmt.setString(1, resourceId.toString());
2946            res = stmt.executeQuery();
2947            if (res.next()) {
2948                resourceVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_VERSION"));
2949                while (res.next()) {
2950                    // do nothing only move through all rows because of mssql odbc driver
2951                }
2952            }
2953            m_sqlManager.closeAll(dbc, null, stmt, res);
2954            // then for the structure entry
2955            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ_VERSION_STR");
2956            stmt.setString(1, structureId.toString());
2957            res = stmt.executeQuery();
2958            if (res.next()) {
2959                structureVersion = res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_VERSION"));
2960                while (res.next()) {
2961                    // do nothing only move through all rows because of mssql odbc driver
2962                }
2963            }
2964        } catch (SQLException e) {
2965            throw new CmsDbSqlException(
2966                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
2967                e);
2968        } finally {
2969            m_sqlManager.closeAll(dbc, conn, stmt, res);
2970        }
2971        Map<String, Integer> result = new HashMap<String, Integer>();
2972        result.put("structure", Integer.valueOf(structureVersion));
2973        result.put(I_CmsEventListener.KEY_RESOURCE, Integer.valueOf(resourceVersion));
2974        return result;
2975    }
2976
2977    /**
2978     * @see org.opencms.db.I_CmsVfsDriver#removeFile(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.file.CmsResource)
2979     */
2980    public void removeFile(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsDataAccessException {
2981
2982        PreparedStatement stmt = null;
2983        Connection conn = null;
2984        int siblingCount = 0;
2985
2986        try {
2987            conn = m_sqlManager.getConnection(dbc);
2988
2989            // delete the structure record
2990            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_STRUCTURE_DELETE_BY_STRUCTUREID");
2991            stmt.setString(1, resource.getStructureId().toString());
2992            stmt.executeUpdate();
2993
2994            m_sqlManager.closeAll(dbc, conn, stmt, null);
2995
2996            // count the references to the resource
2997            siblingCount = countSiblings(dbc, projectId, resource.getResourceId());
2998
2999            conn = m_sqlManager.getConnection(dbc);
3000            if (siblingCount > 0) {
3001                // update the link Count
3002                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_SIBLING_COUNT");
3003                stmt.setInt(1, siblingCount);
3004                stmt.setString(2, resource.getResourceId().toString());
3005                stmt.executeUpdate();
3006
3007                m_sqlManager.closeAll(dbc, null, stmt, null);
3008
3009                // update the resource flags
3010                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_FLAGS");
3011                stmt.setInt(1, resource.getFlags());
3012                stmt.setString(2, resource.getResourceId().toString());
3013                stmt.executeUpdate();
3014
3015            } else {
3016                // if not referenced any longer, also delete the resource and the content record
3017                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_DELETE_BY_RESOURCEID");
3018                stmt.setString(1, resource.getResourceId().toString());
3019                stmt.executeUpdate();
3020
3021                m_sqlManager.closeAll(dbc, null, stmt, null);
3022
3023                boolean dbcHasProjectId = (dbc.getProjectId() != null) && !dbc.getProjectId().isNullUUID();
3024
3025                // if online we have to keep historical content
3026                if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
3027                    // put the online content in the history
3028                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_HISTORY");
3029                    stmt.setString(1, resource.getResourceId().toString());
3030                    stmt.executeUpdate();
3031                } else if (dbcHasProjectId) {
3032                    // remove current online version
3033                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_DELETE");
3034                    stmt.setString(1, resource.getResourceId().toString());
3035                    stmt.executeUpdate();
3036                } else {
3037                    // delete content records with this resource id
3038                    stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_OFFLINE_FILE_CONTENT_DELETE");
3039                    stmt.setString(1, resource.getResourceId().toString());
3040                    stmt.executeUpdate();
3041                }
3042            }
3043        } catch (SQLException e) {
3044            throw new CmsDbSqlException(
3045                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3046                e);
3047        } finally {
3048            m_sqlManager.closeAll(dbc, conn, stmt, null);
3049        }
3050    }
3051
3052    /**
3053     * @see org.opencms.db.I_CmsVfsDriver#removeFolder(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource)
3054     */
3055    public void removeFolder(CmsDbContext dbc, CmsProject currentProject, CmsResource resource)
3056    throws CmsDataAccessException {
3057
3058        if ((dbc.getRequestContext() != null)
3059            && (dbc.getRequestContext().getAttribute(REQ_ATTR_CHECK_PERMISSIONS) != null)) {
3060            // only check write permissions
3061            checkWritePermissionsInFolder(dbc, resource);
3062            return;
3063        }
3064
3065        // check if the folder has any resources in it
3066        Iterator<CmsResource> childResources = readChildResources(dbc, currentProject, resource, true, true).iterator();
3067
3068        CmsUUID projectId = CmsProject.ONLINE_PROJECT_ID;
3069        if (currentProject.isOnlineProject()) {
3070            projectId = CmsUUID.getOpenCmsUUID(); // HACK: to get an offline project id
3071        }
3072
3073        // collect the names of the resources inside the folder, excluding the moved resources
3074        I_CmsVfsDriver vfsDriver = m_driverManager.getVfsDriver(dbc);
3075        StringBuffer errorResNames = new StringBuffer(128);
3076        while (childResources.hasNext()) {
3077            CmsResource errorRes = childResources.next();
3078            // if deleting offline, or not moved, or just renamed inside the deleted folder
3079            // so, it may remain some orphan online entries for moved resources
3080            // which will be fixed during the publishing of the moved resources
3081            boolean error = !currentProject.isOnlineProject();
3082            if (!error) {
3083                try {
3084                    String originalPath = vfsDriver.readResource(
3085                        dbc,
3086                        projectId,
3087                        errorRes.getRootPath(),
3088                        true).getRootPath();
3089                    error = originalPath.equals(errorRes.getRootPath())
3090                        || originalPath.startsWith(resource.getRootPath());
3091                } catch (CmsVfsResourceNotFoundException e) {
3092                    // ignore
3093                }
3094            }
3095            if (error) {
3096                if (errorResNames.length() != 0) {
3097                    errorResNames.append(", ");
3098                }
3099                errorResNames.append("[" + dbc.removeSiteRoot(errorRes.getRootPath()) + "]");
3100            }
3101        }
3102
3103        // the current implementation only deletes empty folders
3104        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(errorResNames.toString())) {
3105
3106            throw new CmsVfsException(
3107                Messages.get().container(
3108                    Messages.ERR_DELETE_NONEMTY_FOLDER_2,
3109                    dbc.removeSiteRoot(resource.getRootPath()),
3110                    errorResNames.toString()));
3111        }
3112        internalRemoveFolder(dbc, currentProject, resource);
3113
3114        // remove project resources
3115        String deletedResourceRootPath = resource.getRootPath();
3116        if (dbc.getRequestContext() != null) {
3117            dbc.getRequestContext().setAttribute(CmsProjectDriver.DBC_ATTR_READ_PROJECT_FOR_RESOURCE, Boolean.TRUE);
3118            I_CmsProjectDriver projectDriver = m_driverManager.getProjectDriver(dbc);
3119            Iterator<CmsProject> itProjects = projectDriver.readProjects(dbc, deletedResourceRootPath).iterator();
3120            while (itProjects.hasNext()) {
3121                CmsProject project = itProjects.next();
3122                projectDriver.deleteProjectResource(dbc, project.getUuid(), deletedResourceRootPath);
3123            }
3124        }
3125    }
3126
3127    /**
3128     * @see org.opencms.db.I_CmsVfsDriver#replaceResource(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, byte[], int)
3129     */
3130    public void replaceResource(CmsDbContext dbc, CmsResource newResource, byte[] resContent, int newResourceType)
3131    throws CmsDataAccessException {
3132
3133        if (resContent == null) {
3134            // nothing to do
3135            return;
3136        }
3137        Connection conn = null;
3138        PreparedStatement stmt = null;
3139        try {
3140            // write the file content
3141            writeContent(dbc, newResource, resContent);
3142
3143            // update the resource record
3144            conn = m_sqlManager.getConnection(dbc);
3145            stmt = m_sqlManager.getPreparedStatement(conn, dbc.currentProject(), "C_RESOURCE_REPLACE");
3146            stmt.setInt(1, newResourceType);
3147            stmt.setInt(2, resContent.length);
3148            stmt.setLong(3, System.currentTimeMillis());
3149            stmt.setString(4, newResource.getResourceId().toString());
3150            stmt.executeUpdate();
3151
3152        } catch (SQLException e) {
3153            throw new CmsDbSqlException(
3154                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3155                e);
3156        } finally {
3157            m_sqlManager.closeAll(dbc, conn, stmt, null);
3158        }
3159    }
3160
3161    /**
3162     * @see org.opencms.db.I_CmsVfsDriver#setDriverManager(org.opencms.db.CmsDriverManager)
3163     */
3164    public void setDriverManager(CmsDriverManager driverManager) {
3165
3166        m_driverManager = driverManager;
3167    }
3168
3169    /**
3170     * @see org.opencms.db.I_CmsVfsDriver#setSqlManager(org.opencms.db.CmsSqlManager)
3171     */
3172    public void setSqlManager(org.opencms.db.CmsSqlManager sqlManager) {
3173
3174        m_sqlManager = (CmsSqlManager)sqlManager;
3175    }
3176
3177    /**
3178     * @see org.opencms.db.I_CmsVfsDriver#transferResource(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource, org.opencms.util.CmsUUID, org.opencms.util.CmsUUID)
3179     */
3180    public void transferResource(
3181        CmsDbContext dbc,
3182        CmsProject project,
3183        CmsResource resource,
3184        CmsUUID createdUser,
3185        CmsUUID lastModifiedUser)
3186    throws CmsDataAccessException {
3187
3188        if (createdUser == null) {
3189            createdUser = resource.getUserCreated();
3190        }
3191        if (lastModifiedUser == null) {
3192            lastModifiedUser = resource.getUserLastModified();
3193        }
3194
3195        PreparedStatement stmt = null;
3196        Connection conn = null;
3197        try {
3198            conn = m_sqlManager.getConnection(dbc);
3199            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_TRANSFER_RESOURCE");
3200            stmt.setString(1, createdUser.toString());
3201            stmt.setString(2, lastModifiedUser.toString());
3202            stmt.setString(3, resource.getResourceId().toString());
3203            stmt.executeUpdate();
3204        } catch (SQLException e) {
3205            throw new CmsDbSqlException(
3206                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3207                e);
3208        } finally {
3209            m_sqlManager.closeAll(dbc, conn, stmt, null);
3210        }
3211    }
3212
3213    /**
3214     * @see org.opencms.db.I_CmsVfsDriver#updateRelations(CmsDbContext, CmsProject, CmsResource)
3215     */
3216    public void updateRelations(CmsDbContext dbc, CmsProject onlineProject, CmsResource offlineResource)
3217    throws CmsDataAccessException {
3218
3219        // delete online relations
3220        I_CmsVfsDriver vfsDriver = m_driverManager.getVfsDriver(dbc);
3221        vfsDriver.deleteRelations(dbc, onlineProject.getUuid(), offlineResource, CmsRelationFilter.TARGETS);
3222
3223        CmsUUID projectId;
3224        if (!dbc.getProjectId().isNullUUID()) {
3225            projectId = CmsProject.ONLINE_PROJECT_ID;
3226        } else {
3227            projectId = dbc.currentProject().getUuid();
3228        }
3229
3230        // copy offline to online relations
3231        CmsUUID dbcProjectId = dbc.getProjectId();
3232        dbc.setProjectId(CmsUUID.getNullUUID());
3233        Iterator<CmsRelation> itRelations = m_driverManager.getVfsDriver(
3234            dbc).readRelations(dbc, projectId, offlineResource, CmsRelationFilter.TARGETS).iterator();
3235        dbc.setProjectId(dbcProjectId);
3236        while (itRelations.hasNext()) {
3237            vfsDriver.createRelation(dbc, onlineProject.getUuid(), itRelations.next());
3238        }
3239    }
3240
3241    /**
3242     * @see org.opencms.db.I_CmsVfsDriver#validateResourceIdExists(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID)
3243     */
3244    public boolean validateResourceIdExists(CmsDbContext dbc, CmsUUID projectId, CmsUUID resourceId)
3245    throws CmsDataAccessException {
3246
3247        Connection conn = null;
3248        PreparedStatement stmt = null;
3249        ResultSet res = null;
3250        boolean exists = false;
3251
3252        try {
3253            conn = m_sqlManager.getConnection(dbc);
3254            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ_RESOURCE_STATE");
3255            stmt.setString(1, resourceId.toString());
3256
3257            res = stmt.executeQuery();
3258            exists = res.next();
3259        } catch (SQLException e) {
3260            throw new CmsDbSqlException(
3261                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3262                e);
3263        } finally {
3264            m_sqlManager.closeAll(dbc, conn, stmt, res);
3265        }
3266
3267        return exists;
3268    }
3269
3270    /**
3271     * @see org.opencms.db.I_CmsVfsDriver#validateStructureIdExists(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID)
3272     */
3273    public boolean validateStructureIdExists(CmsDbContext dbc, CmsUUID projectId, CmsUUID structureId)
3274    throws CmsDataAccessException {
3275
3276        Connection conn = null;
3277        PreparedStatement stmt = null;
3278        ResultSet res = null;
3279        boolean found = false;
3280        int count = 0;
3281
3282        try {
3283            conn = m_sqlManager.getConnection(dbc);
3284            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_SELECT_STRUCTURE_ID");
3285            stmt.setString(1, structureId.toString());
3286
3287            res = stmt.executeQuery();
3288            if (res.next()) {
3289                count = res.getInt(1);
3290                found = (count == 1);
3291                while (res.next()) {
3292                    // do nothing only move through all rows because of mssql odbc driver
3293                }
3294            } else {
3295                found = false;
3296            }
3297        } catch (SQLException e) {
3298            throw new CmsDbSqlException(
3299                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3300                e);
3301        } finally {
3302            m_sqlManager.closeAll(dbc, conn, stmt, res);
3303        }
3304
3305        return found;
3306    }
3307
3308    /**
3309     * @see org.opencms.db.I_CmsVfsDriver#writeContent(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, byte[])
3310     */
3311    public void writeContent(CmsDbContext dbc, CmsResource resource, byte[] content) throws CmsDataAccessException {
3312
3313        Connection conn = null;
3314        PreparedStatement stmt = null;
3315
3316        try {
3317            conn = m_sqlManager.getConnection(dbc);
3318            stmt = m_sqlManager.getPreparedStatement(conn, dbc.currentProject(), "C_OFFLINE_CONTENTS_UPDATE");
3319            // update the file content in the database.
3320            if (content.length < 2000) {
3321                stmt.setBytes(1, content);
3322            } else {
3323                stmt.setBinaryStream(1, new ByteArrayInputStream(content), content.length);
3324            }
3325            stmt.setString(2, resource.getResourceId().toString());
3326            stmt.executeUpdate();
3327        } catch (SQLException e) {
3328            throw new CmsDbSqlException(
3329                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3330                e);
3331        } finally {
3332            m_sqlManager.closeAll(dbc, conn, stmt, null);
3333        }
3334    }
3335
3336    /**
3337     * @see org.opencms.db.I_CmsVfsDriver#writeLastModifiedProjectId(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, CmsUUID, org.opencms.file.CmsResource)
3338     */
3339    public void writeLastModifiedProjectId(
3340        CmsDbContext dbc,
3341        CmsProject project,
3342        CmsUUID projectId,
3343        CmsResource resource)
3344    throws CmsDataAccessException {
3345
3346        Connection conn = null;
3347        PreparedStatement stmt = null;
3348
3349        try {
3350            conn = m_sqlManager.getConnection(dbc);
3351            stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_PROJECT_LASTMODIFIED");
3352            stmt.setString(1, projectId.toString());
3353            stmt.setString(2, resource.getResourceId().toString());
3354            stmt.executeUpdate();
3355        } catch (SQLException e) {
3356            throw new CmsDbSqlException(
3357                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3358                e);
3359        } finally {
3360            m_sqlManager.closeAll(dbc, conn, stmt, null);
3361        }
3362    }
3363
3364    /**
3365     * @see org.opencms.db.I_CmsVfsDriver#writePropertyObject(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource, org.opencms.file.CmsProperty)
3366     */
3367    public void writePropertyObject(CmsDbContext dbc, CmsProject project, CmsResource resource, CmsProperty property)
3368    throws CmsDataAccessException {
3369
3370        CmsUUID projectId = ((dbc.getProjectId() == null) || dbc.getProjectId().isNullUUID())
3371        ? project.getUuid()
3372        : dbc.getProjectId();
3373
3374        // TODO: check if we need autocreation for link property definition types too
3375        CmsPropertyDefinition propertyDefinition = null;
3376        try {
3377            // read the property definition
3378            propertyDefinition = readPropertyDefinition(dbc, property.getName(), projectId);
3379        } catch (CmsDbEntryNotFoundException e) {
3380            if (property.autoCreatePropertyDefinition()) {
3381                propertyDefinition = createPropertyDefinition(
3382                    dbc,
3383                    projectId,
3384                    property.getName(),
3385                    CmsPropertyDefinition.TYPE_NORMAL);
3386                try {
3387                    readPropertyDefinition(dbc, property.getName(), CmsProject.ONLINE_PROJECT_ID);
3388                } catch (CmsDataAccessException e1) {
3389                    createPropertyDefinition(
3390                        dbc,
3391                        CmsProject.ONLINE_PROJECT_ID,
3392                        property.getName(),
3393                        CmsPropertyDefinition.TYPE_NORMAL);
3394                }
3395                try {
3396                    m_driverManager.getHistoryDriver(dbc).readPropertyDefinition(dbc, property.getName());
3397                } catch (CmsDataAccessException e1) {
3398                    m_driverManager.getHistoryDriver(dbc).createPropertyDefinition(
3399                        dbc,
3400                        property.getName(),
3401                        CmsPropertyDefinition.TYPE_NORMAL);
3402                }
3403                OpenCms.fireCmsEvent(
3404                    new CmsEvent(
3405                        I_CmsEventListener.EVENT_PROPERTY_DEFINITION_CREATED,
3406                        Collections.<String, Object> singletonMap("propertyDefinition", propertyDefinition)));
3407
3408            } else {
3409                throw new CmsDbEntryNotFoundException(
3410                    Messages.get().container(Messages.ERR_NO_PROPERTYDEF_WITH_NAME_1, property.getName()));
3411            }
3412        }
3413
3414        PreparedStatement stmt = null;
3415        Connection conn = null;
3416
3417        try {
3418            // read the existing property to test if we need the
3419            // insert or update query to write a property value
3420            CmsProperty existingProperty = readPropertyObject(dbc, propertyDefinition.getName(), project, resource);
3421
3422            if (existingProperty.isIdentical(property)) {
3423                // property already has the identical values set, no write required
3424                return;
3425            }
3426
3427            conn = m_sqlManager.getConnection(dbc);
3428
3429            for (int i = 0; i < 2; i++) {
3430                int mappingType = -1;
3431                String value = null;
3432                CmsUUID id = null;
3433                boolean existsPropertyValue = false;
3434                boolean deletePropertyValue = false;
3435
3436                // 1) take any required decisions to choose and fill the correct SQL query
3437
3438                if (i == 0) {
3439                    // write/delete the *structure value* on the first cycle
3440                    if ((existingProperty.getStructureValue() != null) && property.isDeleteStructureValue()) {
3441                        // this property value is marked to be deleted
3442                        deletePropertyValue = true;
3443                    } else {
3444                        value = property.getStructureValue();
3445                        if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
3446                            // no structure value set or the structure value is an empty string,
3447                            // continue with the resource value
3448                            continue;
3449                        }
3450                    }
3451
3452                    // set the vars to be written to the database
3453                    mappingType = CmsProperty.STRUCTURE_RECORD_MAPPING;
3454                    id = resource.getStructureId();
3455                    existsPropertyValue = existingProperty.getStructureValue() != null;
3456                } else {
3457                    // write/delete the *resource value* on the second cycle
3458                    if ((existingProperty.getResourceValue() != null) && property.isDeleteResourceValue()) {
3459                        // this property value is marked to be deleted
3460                        deletePropertyValue = true;
3461                    } else {
3462                        value = property.getResourceValue();
3463                        if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
3464                            // no resource value set or the resource value is an empty string,
3465                            // break out of the loop
3466                            break;
3467                        }
3468                    }
3469
3470                    // set the vars to be written to the database
3471                    mappingType = CmsProperty.RESOURCE_RECORD_MAPPING;
3472                    id = resource.getResourceId();
3473                    existsPropertyValue = existingProperty.getResourceValue() != null;
3474                }
3475
3476                // 2) execute the SQL query
3477                try {
3478                    if (!deletePropertyValue) {
3479                        // insert/update the property value
3480                        if (existsPropertyValue) {
3481                            // {structure|resource} property value already exists- use update statement
3482                            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_UPDATE");
3483                            stmt.setString(1, m_sqlManager.validateEmpty(value));
3484                            stmt.setString(2, id.toString());
3485                            stmt.setInt(3, mappingType);
3486                            stmt.setString(4, propertyDefinition.getId().toString());
3487                        } else {
3488                            // {structure|resource} property value doesn't exist- use create statement
3489                            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_CREATE");
3490                            stmt.setString(1, new CmsUUID().toString());
3491                            stmt.setString(2, propertyDefinition.getId().toString());
3492                            stmt.setString(3, id.toString());
3493                            stmt.setInt(4, mappingType);
3494                            stmt.setString(5, m_sqlManager.validateEmpty(value));
3495                        }
3496                    } else {
3497                        // {structure|resource} property value marked as deleted- use delete statement
3498                        stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_DELETE");
3499                        stmt.setString(1, propertyDefinition.getId().toString());
3500                        stmt.setString(2, id.toString());
3501                        stmt.setInt(3, mappingType);
3502                    }
3503                    stmt.executeUpdate();
3504                } finally {
3505                    m_sqlManager.closeAll(dbc, null, stmt, null);
3506                }
3507            }
3508        } catch (SQLException e) {
3509            throw new CmsDbSqlException(
3510                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3511                e);
3512        } finally {
3513            m_sqlManager.closeAll(dbc, conn, stmt, null);
3514        }
3515    }
3516
3517    /**
3518     * @see org.opencms.db.I_CmsVfsDriver#writePropertyObjects(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource, java.util.List)
3519     */
3520    public void writePropertyObjects(
3521        CmsDbContext dbc,
3522        CmsProject project,
3523        CmsResource resource,
3524        List<CmsProperty> properties)
3525    throws CmsDataAccessException {
3526
3527        CmsProperty property = null;
3528
3529        for (int i = 0; i < properties.size(); i++) {
3530            property = properties.get(i);
3531            writePropertyObject(dbc, project, resource, property);
3532        }
3533    }
3534
3535    /**
3536     * @see org.opencms.db.I_CmsVfsDriver#writeResource(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.file.CmsResource, int)
3537     */
3538    public void writeResource(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, int changed)
3539    throws CmsDataAccessException {
3540
3541        // validate the resource length
3542        internalValidateResourceLength(resource);
3543
3544        String resourcePath = CmsFileUtil.removeTrailingSeparator(resource.getRootPath());
3545
3546        // this task is split into two statements because some DBs (e.g. Oracle) doesn't support multi-table updates
3547        PreparedStatement stmt = null;
3548        Connection conn = null;
3549        long resourceDateModified;
3550
3551        if (resource.isTouched()) {
3552            resourceDateModified = resource.getDateLastModified();
3553        } else {
3554            resourceDateModified = System.currentTimeMillis();
3555        }
3556
3557        CmsResourceState structureState = resource.getState();
3558        CmsResourceState resourceState = resource.getState();
3559        CmsResourceState structureStateOld = internalReadStructureState(dbc, projectId, resource);
3560        CmsResourceState resourceStateOld = internalReadResourceState(dbc, projectId, resource);
3561        CmsUUID projectLastModified = projectId;
3562
3563        if (changed == CmsDriverManager.UPDATE_RESOURCE_STATE) {
3564            resourceState = resourceStateOld;
3565            resourceState = (resourceState.isNew() ? CmsResource.STATE_NEW : CmsResource.STATE_CHANGED);
3566            structureState = structureStateOld;
3567        } else if (changed == CmsDriverManager.UPDATE_STRUCTURE_STATE) {
3568            structureState = structureStateOld;
3569            structureState = (structureState.isNew() ? CmsResource.STATE_NEW : CmsResource.STATE_CHANGED);
3570        } else if (changed == CmsDriverManager.NOTHING_CHANGED) {
3571            projectLastModified = resource.getProjectLastModified();
3572        } else {
3573            resourceState = resourceStateOld;
3574            resourceState = (resourceState.isNew() ? CmsResource.STATE_NEW : CmsResource.STATE_CHANGED);
3575            structureState = structureStateOld;
3576            structureState = (structureState.isNew() ? CmsResource.STATE_NEW : CmsResource.STATE_CHANGED);
3577        }
3578
3579        try {
3580
3581            // read the parent id
3582            String parentId = internalReadParentId(dbc, projectId, resourcePath);
3583            int sibCount = countSiblings(dbc, projectId, resource.getResourceId());
3584
3585            conn = m_sqlManager.getConnection(dbc);
3586
3587            if (changed != CmsDriverManager.UPDATE_STRUCTURE_STATE) {
3588                // if the resource was unchanged
3589                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_RESOURCES");
3590                stmt.setInt(1, resource.getTypeId());
3591                stmt.setInt(2, resource.getFlags());
3592                stmt.setLong(3, resourceDateModified);
3593                stmt.setString(4, resource.getUserLastModified().toString());
3594                stmt.setInt(5, resourceState.getState());
3595                stmt.setInt(6, resource.getLength());
3596                stmt.setLong(7, resource.getDateContent());
3597                stmt.setString(8, projectLastModified.toString());
3598                stmt.setInt(9, sibCount);
3599                stmt.setString(10, resource.getResourceId().toString());
3600                stmt.executeUpdate();
3601                m_sqlManager.closeAll(dbc, null, stmt, null);
3602            } else {
3603                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_RESOURCES_WITHOUT_STATE");
3604                stmt.setInt(1, resource.getTypeId());
3605                stmt.setInt(2, resource.getFlags());
3606                stmt.setLong(3, resourceDateModified);
3607                stmt.setString(4, resource.getUserLastModified().toString());
3608                stmt.setInt(5, resource.getLength());
3609                stmt.setLong(6, resource.getDateContent());
3610                stmt.setString(7, projectLastModified.toString());
3611                stmt.setInt(8, sibCount);
3612                stmt.setString(9, resource.getResourceId().toString());
3613                stmt.executeUpdate();
3614                m_sqlManager.closeAll(dbc, null, stmt, null);
3615            }
3616
3617            // update the structure
3618            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_UPDATE_STRUCTURE");
3619            stmt.setString(1, resource.getResourceId().toString());
3620            stmt.setString(2, resourcePath);
3621            stmt.setInt(3, structureState.getState());
3622            stmt.setLong(4, resource.getDateReleased());
3623            stmt.setLong(5, resource.getDateExpired());
3624            stmt.setString(6, parentId);
3625            stmt.setString(7, resource.getStructureId().toString());
3626            stmt.executeUpdate();
3627        } catch (SQLException e) {
3628            throw new CmsDbSqlException(
3629                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3630                e);
3631        } finally {
3632            m_sqlManager.closeAll(dbc, conn, stmt, null);
3633        }
3634    }
3635
3636    /**
3637     * @see org.opencms.db.I_CmsVfsDriver#writeResourceState(org.opencms.db.CmsDbContext, org.opencms.file.CmsProject, org.opencms.file.CmsResource, int, boolean)
3638     */
3639    public void writeResourceState(
3640        CmsDbContext dbc,
3641        CmsProject project,
3642        CmsResource resource,
3643        int changed,
3644        boolean isPublishing)
3645    throws CmsDataAccessException {
3646
3647        PreparedStatement stmt = null;
3648        Connection conn = null;
3649
3650        if (project.getUuid().equals(CmsProject.ONLINE_PROJECT_ID)) {
3651            return;
3652        }
3653
3654        try {
3655            conn = m_sqlManager.getConnection(dbc);
3656
3657            if (changed == CmsDriverManager.UPDATE_RESOURCE_PROJECT) {
3658                stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_RESOURCE_PROJECT");
3659                stmt.setInt(1, resource.getFlags());
3660                stmt.setString(2, project.getUuid().toString());
3661                stmt.setString(3, resource.getResourceId().toString());
3662                stmt.executeUpdate();
3663                m_sqlManager.closeAll(dbc, null, stmt, null);
3664            }
3665
3666            if (changed == CmsDriverManager.UPDATE_RESOURCE) {
3667                stmt = m_sqlManager.getPreparedStatement(
3668                    conn,
3669                    project,
3670                    "C_RESOURCES_UPDATE_RESOURCE_STATELASTMODIFIED");
3671                stmt.setInt(1, resource.getState().getState());
3672                stmt.setLong(2, resource.getDateLastModified());
3673                stmt.setString(3, resource.getUserLastModified().toString());
3674                stmt.setString(4, project.getUuid().toString());
3675                stmt.setString(5, resource.getResourceId().toString());
3676                stmt.executeUpdate();
3677                m_sqlManager.closeAll(dbc, null, stmt, null);
3678            }
3679
3680            if ((changed == CmsDriverManager.UPDATE_RESOURCE_STATE) || (changed == CmsDriverManager.UPDATE_ALL)) {
3681                stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_RESOURCE_STATE");
3682                stmt.setInt(1, resource.getState().getState());
3683                stmt.setString(2, project.getUuid().toString());
3684                stmt.setString(3, resource.getResourceId().toString());
3685                stmt.executeUpdate();
3686                m_sqlManager.closeAll(dbc, null, stmt, null);
3687            }
3688
3689            if ((changed == CmsDriverManager.UPDATE_STRUCTURE)
3690                || (changed == CmsDriverManager.UPDATE_ALL)
3691                || (changed == CmsDriverManager.UPDATE_STRUCTURE_STATE)) {
3692                stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_STRUCTURE_STATE");
3693                stmt.setInt(1, resource.getState().getState());
3694                stmt.setString(2, resource.getStructureId().toString());
3695                stmt.executeUpdate();
3696                m_sqlManager.closeAll(dbc, null, stmt, null);
3697            }
3698
3699            if ((changed == CmsDriverManager.UPDATE_STRUCTURE) || (changed == CmsDriverManager.UPDATE_ALL)) {
3700                stmt = m_sqlManager.getPreparedStatement(conn, project, "C_RESOURCES_UPDATE_RELEASE_EXPIRED");
3701                stmt.setLong(1, resource.getDateReleased());
3702                stmt.setLong(2, resource.getDateExpired());
3703                stmt.setString(3, resource.getStructureId().toString());
3704                stmt.executeUpdate();
3705                m_sqlManager.closeAll(dbc, null, stmt, null);
3706            }
3707        } catch (SQLException e) {
3708            throw new CmsDbSqlException(
3709                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3710                e);
3711        } finally {
3712            m_sqlManager.closeAll(dbc, conn, stmt, null);
3713        }
3714
3715        if (isPublishing) {
3716            internalUpdateVersions(dbc, resource);
3717        }
3718    }
3719
3720    /**
3721     * Checks that the current user has write permissions for all subresources of the given folder.<p>
3722     *
3723     * @param dbc the current database context
3724     * @param folder the folder to check
3725     *
3726     * @throws CmsDataAccessException if something goes wrong
3727     */
3728    protected void checkWritePermissionsInFolder(CmsDbContext dbc, CmsResource folder) throws CmsDataAccessException {
3729
3730        ResultSet res = null;
3731        PreparedStatement stmt = null;
3732        Connection conn = null;
3733
3734        CmsUUID projectId = dbc.getRequestContext().getCurrentProject().getUuid();
3735
3736        // first read all subresources with ACEs
3737        List<CmsResource> resources = new ArrayList<CmsResource>();
3738        try {
3739            conn = m_sqlManager.getConnection(dbc);
3740            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ_WITH_ACE_1");
3741            stmt.setString(1, folder.getRootPath() + "%");
3742            res = stmt.executeQuery();
3743
3744            while (res.next()) {
3745                resources.add(createResource(res, projectId));
3746            }
3747        } catch (SQLException e) {
3748            throw new CmsDbSqlException(
3749                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3750                e);
3751        } finally {
3752            m_sqlManager.closeAll(dbc, conn, stmt, res);
3753        }
3754
3755        // check current user write permission for each of these resources
3756        Iterator<CmsResource> itResources = resources.iterator();
3757        while (itResources.hasNext()) {
3758            CmsResource resource = itResources.next();
3759            try {
3760                m_driverManager.getSecurityManager().checkPermissions(
3761                    dbc.getRequestContext(),
3762                    resource,
3763                    CmsPermissionSet.ACCESS_WRITE,
3764                    false,
3765                    CmsResourceFilter.ALL);
3766            } catch (CmsException e) {
3767                throw new CmsDataAccessException(e.getMessageContainer(), e);
3768            }
3769        }
3770
3771        // then check for possible jsp pages without permissions
3772        CmsResourceFilter filter = CmsResourceFilter.ALL;
3773        itResources = readTypesInResourceTree(
3774            dbc,
3775            projectId,
3776            folder.getRootPath(),
3777            CmsResourceTypeJsp.getJspResourceTypeIds(),
3778            filter.getState(),
3779            filter.getModifiedAfter(),
3780            filter.getModifiedBefore(),
3781            filter.getReleaseAfter(),
3782            filter.getReleaseBefore(),
3783            filter.getExpireAfter(),
3784            filter.getExpireBefore(),
3785            CmsDriverManager.READMODE_INCLUDE_TREE).iterator();
3786        while (itResources.hasNext()) {
3787            CmsResource resource = itResources.next();
3788            try {
3789                m_driverManager.getSecurityManager().checkPermissions(
3790                    dbc.getRequestContext(),
3791                    resource,
3792                    CmsPermissionSet.ACCESS_WRITE,
3793                    false,
3794                    CmsResourceFilter.ALL);
3795            } catch (CmsException e) {
3796                throw new CmsDataAccessException(e.getMessageContainer(), e);
3797            }
3798        }
3799    }
3800
3801    /**
3802     * Returns additional SQL query property files to load between the generic and database-specific queries.<p>
3803     *
3804     * @param configuration the OpenCms parameter configuration
3805     *
3806     * @return the additional SQL query property files
3807     */
3808    protected List<String> getAdditionalSqlQueryProperties(CmsParameterConfiguration configuration) {
3809
3810        return Collections.emptyList();
3811    }
3812
3813    /**
3814     * Returns the count of properties for a property definition.<p>
3815     *
3816     * @param dbc the current database context
3817     * @param propertyDefinition the property definition to test
3818     * @param projectId the ID of the current project
3819     *
3820     * @return the amount of properties for a property definition
3821     * @throws CmsDataAccessException if something goes wrong
3822     */
3823    protected int internalCountProperties(CmsDbContext dbc, CmsPropertyDefinition propertyDefinition, CmsUUID projectId)
3824    throws CmsDataAccessException {
3825
3826        ResultSet res = null;
3827        PreparedStatement stmt = null;
3828        Connection conn = null;
3829        int count = 0;
3830
3831        try {
3832            // create statement
3833            conn = m_sqlManager.getConnection(dbc);
3834            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_PROPERTIES_READALL_COUNT");
3835            stmt.setString(1, propertyDefinition.getId().toString());
3836            res = stmt.executeQuery();
3837
3838            if (res.next()) {
3839                count = res.getInt(1);
3840                while (res.next()) {
3841                    // do nothing only move through all rows because of mssql odbc driver
3842                }
3843            } else {
3844                throw new CmsDbConsistencyException(
3845                    Messages.get().container(Messages.ERR_COUNTING_PROPERTIES_1, propertyDefinition.getName()));
3846            }
3847        } catch (SQLException e) {
3848            throw new CmsDbSqlException(
3849                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
3850                e);
3851        } finally {
3852            m_sqlManager.closeAll(dbc, conn, stmt, res);
3853        }
3854
3855        return count;
3856    }
3857
3858    /**
3859     * Creates a new counter.<p>
3860     *
3861     * @param dbc the database context
3862     * @param name the name of the counter to create
3863     * @param value the inital value of the counter
3864     *
3865     * @throws CmsDbSqlException if something goes wrong
3866     */
3867    protected void internalCreateCounter(CmsDbContext dbc, String name, int value) throws CmsDbSqlException {
3868
3869        PreparedStatement stmt = null;
3870        Connection conn = null;
3871        try {
3872            conn = m_sqlManager.getConnection(dbc);
3873            stmt = m_sqlManager.getPreparedStatement(conn, CmsProject.ONLINE_PROJECT_ID, "C_CREATE_COUNTER");
3874            stmt.setString(1, name);
3875            stmt.setInt(2, value);
3876            stmt.executeUpdate();
3877        } catch (SQLException e) {
3878            throw wrapException(stmt, e);
3879        } finally {
3880            m_sqlManager.closeAll(dbc, conn, stmt, null);
3881        }
3882    }
3883
3884    /**
3885     * Creates an URL name mapping entry from a result set.<p>
3886     *
3887     * @param resultSet a result set
3888     * @return the URL name mapping entry created from the result set
3889     *
3890     * @throws SQLException if something goes wrong
3891     */
3892    protected CmsUrlNameMappingEntry internalCreateUrlNameMappingEntry(ResultSet resultSet) throws SQLException {
3893
3894        String name = resultSet.getString(1);
3895        CmsUUID structureId = new CmsUUID(resultSet.getString(2));
3896        int state = resultSet.getInt(3);
3897        long dateChanged = resultSet.getLong(4);
3898        String locale = resultSet.getString(5);
3899        return new CmsUrlNameMappingEntry(name, structureId, state, dateChanged, locale);
3900    }
3901
3902    /**
3903     * Increments a counter.<p>
3904     *
3905     * @param dbc the current db context
3906     * @param name the name of the counter which should be incremented
3907     *
3908     * @throws CmsDbSqlException if something goes wrong
3909     */
3910    protected void internalIncrementCounter(CmsDbContext dbc, String name) throws CmsDbSqlException {
3911
3912        PreparedStatement stmt = null;
3913        Connection conn = null;
3914        ResultSet resultSet = null;
3915        try {
3916            conn = m_sqlManager.getConnection(dbc);
3917            stmt = m_sqlManager.getPreparedStatement(conn, CmsProject.ONLINE_PROJECT_ID, "C_INCREMENT_COUNTER");
3918            stmt.setString(1, name);
3919            stmt.executeUpdate();
3920
3921        } catch (SQLException e) {
3922            throw wrapException(stmt, e);
3923        } finally {
3924            m_sqlManager.closeAll(dbc, conn, stmt, resultSet);
3925        }
3926    }
3927
3928    /**
3929     * Helper method to create an alias object from a result set.<p>
3930     *
3931     * @param resultset the result set
3932     * @return the alias object created from the result set
3933     *
3934     * @throws SQLException if something goes wrong
3935     */
3936    protected CmsAlias internalReadAlias(ResultSet resultset) throws SQLException {
3937
3938        String siteRoot = resultset.getString(1);
3939        String path = resultset.getString(2);
3940        int mode = resultset.getInt(3);
3941        String structId = resultset.getString(4);
3942        return new CmsAlias(new CmsUUID(structId), siteRoot, path, CmsAliasMode.fromInt(mode));
3943    }
3944
3945    /**
3946     * Reads the current value of a counter.<p>
3947     *
3948     * @param dbc the database context
3949     * @param name the name of the counter
3950     * @return the current value of the  counter, or null if the counter was not found
3951     *
3952     * @throws CmsDbSqlException if something goes wrong
3953     */
3954    protected Integer internalReadCounter(CmsDbContext dbc, String name) throws CmsDbSqlException {
3955
3956        PreparedStatement stmt = null;
3957        Connection conn = null;
3958        ResultSet resultSet = null;
3959        try {
3960            conn = m_sqlManager.getConnection(dbc);
3961            stmt = m_sqlManager.getPreparedStatement(conn, CmsProject.ONLINE_PROJECT_ID, "C_READ_COUNTER");
3962            stmt.setString(1, name);
3963            resultSet = stmt.executeQuery();
3964            Integer result = null;
3965            if (resultSet.next()) {
3966                int counter = resultSet.getInt(1);
3967                result = Integer.valueOf(counter);
3968                while (resultSet.next()) {
3969                    // for MSSQL
3970                }
3971            }
3972            return result;
3973        } catch (SQLException e) {
3974            throw wrapException(stmt, e);
3975        } finally {
3976            m_sqlManager.closeAll(dbc, conn, stmt, resultSet);
3977        }
3978    }
3979
3980    /**
3981     * Returns the parent id of the given resource.<p>
3982     *
3983     * @param dbc the current database context
3984     * @param projectId the current project id
3985     * @param resourcename the resource name to read the parent id for
3986     *
3987     * @return  the parent id of the given resource
3988     *
3989     * @throws CmsDataAccessException if something goes wrong
3990     */
3991    protected String internalReadParentId(CmsDbContext dbc, CmsUUID projectId, String resourcename)
3992    throws CmsDataAccessException {
3993
3994        if ("/".equalsIgnoreCase(resourcename)) {
3995            return CmsUUID.getNullUUID().toString();
3996        }
3997
3998        String parent = CmsResource.getParentFolder(resourcename);
3999        parent = CmsFileUtil.removeTrailingSeparator(parent);
4000
4001        ResultSet res = null;
4002        PreparedStatement stmt = null;
4003        Connection conn = null;
4004        String parentId = null;
4005
4006        try {
4007            conn = m_sqlManager.getConnection(dbc);
4008            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RESOURCES_READ_PARENT_STRUCTURE_ID");
4009            stmt.setString(1, parent);
4010            res = stmt.executeQuery();
4011
4012            if (res.next()) {
4013                parentId = res.getString(1);
4014                while (res.next()) {
4015                    // do nothing only move through all rows because of mssql odbc driver
4016                }
4017            } else {
4018                throw new CmsVfsResourceNotFoundException(
4019                    Messages.get().container(Messages.ERR_READ_PARENT_ID_1, dbc.removeSiteRoot(resourcename)));
4020            }
4021        } catch (SQLException e) {
4022            throw new CmsDbSqlException(
4023                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4024                e);
4025        } finally {
4026            m_sqlManager.closeAll(dbc, conn, stmt, res);
4027        }
4028
4029        return parentId;
4030    }
4031
4032    /**
4033     * Creates a new {@link CmsRelation} object from the given result set entry.<p>
4034     *
4035     * @param res the result set
4036     *
4037     * @return the new {@link CmsRelation} object
4038     *
4039     * @throws SQLException if something goes wrong
4040     */
4041    protected CmsRelation internalReadRelation(ResultSet res) throws SQLException {
4042
4043        CmsUUID sourceId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RELATION_SOURCE_ID")));
4044        String sourcePath = res.getString(m_sqlManager.readQuery("C_RELATION_SOURCE_PATH"));
4045        CmsUUID targetId = new CmsUUID(res.getString(m_sqlManager.readQuery("C_RELATION_TARGET_ID")));
4046        String targetPath = res.getString(m_sqlManager.readQuery("C_RELATION_TARGET_PATH"));
4047        int type = res.getInt(m_sqlManager.readQuery("C_RELATION_TYPE"));
4048        return new CmsRelation(sourceId, sourcePath, targetId, targetPath, CmsRelationType.valueOf(type));
4049    }
4050
4051    /**
4052     * Returns the resource state of the given resource.<p>
4053     *
4054     * @param dbc the database context
4055     * @param projectId the id of the project
4056     * @param resource the resource to read the resource state for
4057     *
4058     * @return the resource state of the given resource
4059     *
4060     * @throws CmsDbSqlException if something goes wrong
4061     */
4062    protected CmsResourceState internalReadResourceState(CmsDbContext dbc, CmsUUID projectId, CmsResource resource)
4063    throws CmsDbSqlException {
4064
4065        CmsResourceState state = CmsResource.STATE_KEEP;
4066
4067        Connection conn = null;
4068        PreparedStatement stmt = null;
4069        ResultSet res = null;
4070        try {
4071            conn = m_sqlManager.getConnection(dbc);
4072            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_READ_RESOURCE_STATE");
4073            stmt.setString(1, resource.getResourceId().toString());
4074            res = stmt.executeQuery();
4075            if (res.next()) {
4076                state = CmsResourceState.valueOf(res.getInt(m_sqlManager.readQuery("C_RESOURCES_STATE")));
4077                while (res.next()) {
4078                    // do nothing only move through all rows because of mssql odbc driver
4079                }
4080            }
4081        } catch (SQLException e) {
4082            throw new CmsDbSqlException(
4083                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4084                e);
4085        } finally {
4086            m_sqlManager.closeAll(dbc, conn, stmt, res);
4087        }
4088        return state;
4089    }
4090
4091    /**
4092     * Returns the structure state of the given resource.<p>
4093     *
4094     * @param dbc the database context
4095     * @param projectId the id of the project
4096     * @param resource the resource to read the structure state for
4097     *
4098     * @return the structure state of the given resource
4099     *
4100     * @throws CmsDbSqlException if something goes wrong
4101     */
4102    protected CmsResourceState internalReadStructureState(CmsDbContext dbc, CmsUUID projectId, CmsResource resource)
4103    throws CmsDbSqlException {
4104
4105        CmsResourceState state = CmsResource.STATE_KEEP;
4106
4107        Connection conn = null;
4108        PreparedStatement stmt = null;
4109        ResultSet res = null;
4110        try {
4111            conn = m_sqlManager.getConnection(dbc);
4112            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_READ_STRUCTURE_STATE");
4113            stmt.setString(1, resource.getStructureId().toString());
4114            res = stmt.executeQuery();
4115            if (res.next()) {
4116                state = CmsResourceState.valueOf(res.getInt(m_sqlManager.readQuery("C_RESOURCES_STRUCTURE_STATE")));
4117                while (res.next()) {
4118                    // do nothing only move through all rows because of mssql odbc driver
4119                }
4120            }
4121        } catch (SQLException e) {
4122            throw new CmsDbSqlException(
4123                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4124                e);
4125        } finally {
4126            m_sqlManager.closeAll(dbc, conn, stmt, res);
4127        }
4128        return state;
4129    }
4130
4131    /**
4132     * Removes a resource physically in the database.<p>
4133     *
4134     * @param dbc the current database context
4135     * @param currentProject the current project
4136     * @param resource the folder to remove
4137     *
4138     * @throws CmsDataAccessException if something goes wrong
4139     */
4140    protected void internalRemoveFolder(CmsDbContext dbc, CmsProject currentProject, CmsResource resource)
4141    throws CmsDataAccessException {
4142
4143        PreparedStatement stmt = null;
4144        Connection conn = null;
4145
4146        try {
4147            conn = m_sqlManager.getConnection(dbc);
4148
4149            // delete the structure record
4150            stmt = m_sqlManager.getPreparedStatement(conn, currentProject, "C_STRUCTURE_DELETE_BY_STRUCTUREID");
4151            stmt.setString(1, resource.getStructureId().toString());
4152            stmt.executeUpdate();
4153
4154            m_sqlManager.closeAll(dbc, null, stmt, null);
4155
4156            // delete the resource record
4157            stmt = m_sqlManager.getPreparedStatement(conn, currentProject, "C_RESOURCES_DELETE_BY_RESOURCEID");
4158            stmt.setString(1, resource.getResourceId().toString());
4159            stmt.executeUpdate();
4160        } catch (SQLException e) {
4161            throw new CmsDbSqlException(
4162                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4163                e);
4164        } finally {
4165            m_sqlManager.closeAll(dbc, conn, stmt, null);
4166        }
4167    }
4168
4169    /**
4170     * Updates the offline version numbers.<p>
4171     *
4172     * @param dbc the current database context
4173     * @param resource the resource to update the version number for
4174     *
4175     * @throws CmsDataAccessException if something goes wrong
4176     */
4177    protected void internalUpdateVersions(CmsDbContext dbc, CmsResource resource) throws CmsDataAccessException {
4178
4179        if (dbc.getRequestContext() == null) {
4180            // no needed during initialization
4181            return;
4182        }
4183        if (dbc.currentProject().isOnlineProject()) {
4184            // this method is supposed to be used only in the offline project
4185            return;
4186        }
4187
4188        // read the online version numbers
4189        Map<String, Integer> onlineVersions = readVersions(
4190            dbc,
4191            CmsProject.ONLINE_PROJECT_ID,
4192            resource.getResourceId(),
4193            resource.getStructureId());
4194        int onlineStructureVersion = onlineVersions.get("structure").intValue();
4195        int onlineResourceVersion = onlineVersions.get("resource").intValue();
4196
4197        Connection conn = null;
4198        PreparedStatement stmt = null;
4199        ResultSet res = null;
4200
4201        try {
4202            conn = m_sqlManager.getConnection(dbc);
4203
4204            // update the resource version
4205            stmt = m_sqlManager.getPreparedStatement(conn, dbc.currentProject(), "C_RESOURCES_UPDATE_RESOURCE_VERSION");
4206            stmt.setInt(1, onlineResourceVersion);
4207            stmt.setString(2, resource.getResourceId().toString());
4208            stmt.executeUpdate();
4209            m_sqlManager.closeAll(dbc, null, stmt, null);
4210
4211            // update the structure version
4212            stmt = m_sqlManager.getPreparedStatement(
4213                conn,
4214                dbc.currentProject(),
4215                "C_RESOURCES_UPDATE_STRUCTURE_VERSION");
4216            stmt.setInt(1, onlineStructureVersion);
4217            stmt.setString(2, resource.getStructureId().toString());
4218            stmt.executeUpdate();
4219            m_sqlManager.closeAll(dbc, null, stmt, null);
4220
4221        } catch (SQLException e) {
4222            throw new CmsDbSqlException(
4223                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4224                e);
4225        } finally {
4226            m_sqlManager.closeAll(dbc, conn, stmt, res);
4227        }
4228    }
4229
4230    /**
4231     * Validates that the length setting of a resource is always correct.<p>
4232     *
4233     * Files need to have a resource length of >= 0, while folders require
4234     * a resource length of -1.<p>
4235     *
4236     * @param resource the resource to check the length for
4237     * @throws CmsDataAccessException if the resource length is not correct
4238     */
4239    protected void internalValidateResourceLength(CmsResource resource) throws CmsDataAccessException {
4240
4241        if (resource.isFolder() && (resource.getLength() == -1)) {
4242            return;
4243        }
4244
4245        if (resource.isFile() && (resource.getLength() >= 0)) {
4246            return;
4247        }
4248
4249        throw new CmsDataAccessException(
4250            Messages.get().container(
4251                Messages.ERR_INVALID_RESOURCE_LENGTH_2,
4252                Integer.valueOf(resource.getLength()),
4253                resource.getRootPath()));
4254    }
4255
4256    /**
4257     * Moves all relations of a resource to the new path.<p>
4258     *
4259     * @param dbc the current database context
4260     * @param projectId the id of the project to apply the changes
4261     * @param structureId the structure id of the resource to apply the changes to
4262     * @param rootPath the new root path
4263     *
4264     * @throws CmsDataAccessException if something goes wrong
4265     */
4266    protected void moveRelations(CmsDbContext dbc, CmsUUID projectId, CmsUUID structureId, String rootPath)
4267    throws CmsDataAccessException {
4268
4269        Connection conn = null;
4270        PreparedStatement stmt = null;
4271
4272        try {
4273            conn = m_sqlManager.getConnection(dbc);
4274            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_MOVE_RELATIONS_SOURCE");
4275            stmt.setString(1, rootPath);
4276            stmt.setString(2, structureId.toString());
4277
4278            stmt.executeUpdate();
4279            m_sqlManager.closeAll(dbc, null, stmt, null);
4280
4281            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_MOVE_RELATIONS_TARGET");
4282            stmt.setString(1, rootPath);
4283            stmt.setString(2, structureId.toString());
4284
4285            stmt.executeUpdate();
4286        } catch (SQLException e) {
4287            throw new CmsDbSqlException(
4288                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4289                e);
4290        } finally {
4291            m_sqlManager.closeAll(dbc, conn, stmt, null);
4292        }
4293    }
4294
4295    /**
4296     * Appends the appropriate selection criteria related with the expiration date.<p>
4297     *
4298     * @param projectId the id of the project of the resources
4299     * @param startTime the start time
4300     * @param endTime the end time
4301     * @param conditions buffer to append the selection criteria
4302     * @param params list to append the selection parameters
4303     */
4304    protected void prepareExpiredTimeRangeCondition(
4305        CmsUUID projectId,
4306        long startTime,
4307        long endTime,
4308        StringBuffer conditions,
4309        List<Object> params) {
4310
4311        if (startTime > 0L) {
4312            // READ_IGNORE_TIME: if NOT set, add condition to match expired date against startTime
4313            conditions.append(BEGIN_INCLUDE_CONDITION);
4314            conditions.append(m_sqlManager.readQuery(projectId, "C_STRUCTURE_SELECT_BY_DATE_EXPIRED_AFTER"));
4315            conditions.append(END_CONDITION);
4316            params.add(Long.valueOf(startTime));
4317        }
4318
4319        if (endTime > 0L) {
4320            // READ_IGNORE_TIME: if NOT set, add condition to match expired date against endTime
4321            conditions.append(BEGIN_INCLUDE_CONDITION);
4322            conditions.append(m_sqlManager.readQuery(projectId, "C_STRUCTURE_SELECT_BY_DATE_EXPIRED_BEFORE"));
4323            conditions.append(END_CONDITION);
4324            params.add(Long.valueOf(endTime));
4325        }
4326    }
4327
4328    /**
4329     * Appends the appropriate selection criteria related with the parentPath.<p>
4330     *
4331     * @param projectId the id of the project of the resources
4332     * @param parent the parent path or UUID (if mode is C_READMODE_EXCLUDE_TREE)
4333     * @param mode the selection mode
4334     * @param conditions buffer to append the selection criteria
4335     * @param params list to append the selection parameters
4336     */
4337    protected void preparePathCondition(
4338        CmsUUID projectId,
4339        String parent,
4340        int mode,
4341        StringBuffer conditions,
4342        List<Object> params) {
4343
4344        if (parent == CmsDriverManager.READ_IGNORE_PARENT) {
4345            // parent can be ignored
4346            return;
4347        }
4348
4349        if ((mode & CmsDriverManager.READMODE_EXCLUDE_TREE) > 0) {
4350            // only return immediate children - use UUID optimization
4351            conditions.append(BEGIN_INCLUDE_CONDITION);
4352            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_PARENT_UUID"));
4353            conditions.append(END_CONDITION);
4354            params.add(parent);
4355            return;
4356        }
4357
4358        if ("/".equalsIgnoreCase(parent)) {
4359            // if root folder is parent, no additional condition is needed since all resources match anyway
4360            return;
4361        }
4362
4363        // add condition to read path subtree
4364        conditions.append(BEGIN_INCLUDE_CONDITION);
4365        conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_PATH_PREFIX"));
4366        conditions.append(END_CONDITION);
4367        params.add(CmsFileUtil.addTrailingSeparator(escapeDbWildcard(parent)) + "%");
4368    }
4369
4370    /**
4371     * Appends the appropriate selection criteria related with the projectId.<p>
4372     *
4373     * @param projectId the id of the project of the resources
4374     * @param mode the selection mode
4375     * @param conditions buffer to append the selection criteria
4376     * @param params list to append the selection parameters
4377     */
4378    protected void prepareProjectCondition(CmsUUID projectId, int mode, StringBuffer conditions, List<Object> params) {
4379
4380        if ((mode & CmsDriverManager.READMODE_INCLUDE_PROJECT) > 0) {
4381            // C_READMODE_INCLUDE_PROJECT: add condition to match the PROJECT_ID
4382            conditions.append(BEGIN_INCLUDE_CONDITION);
4383            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_PROJECT_LASTMODIFIED"));
4384            conditions.append(END_CONDITION);
4385            params.add(projectId.toString());
4386        }
4387    }
4388
4389    /**
4390     * Build the whole WHERE sql statement part for the given relation filter.<p>
4391     *
4392     * @param mode the relation condition mode - indicates what the conditions will be used for
4393     * @param projectId the current project id
4394     * @param filter the filter
4395     * @param resource the resource (may be null, if you want to delete all relations for the resource in the filter)
4396     * @param params the parameter values (return parameter)
4397     * @param checkSource if the query is for the source relations
4398     *
4399     * @return the WHERE sql statement part string
4400     */
4401    protected String prepareRelationConditions(
4402        RelationConditionMode mode,
4403        CmsUUID projectId,
4404        CmsRelationFilter filter,
4405        CmsResource resource,
4406        List<Object> params,
4407        boolean checkSource) {
4408
4409        StringBuffer conditions = new StringBuffer(128);
4410        params.clear(); // be sure the parameters list is clear
4411
4412        // source or target filter
4413        if (filter.isSource() || filter.isTarget()) {
4414            // source or target id filter from resource
4415            if (resource != null) {
4416                conditions.append(BEGIN_CONDITION);
4417                if (filter.isSource() && checkSource) {
4418                    if (!filter.isIncludeSubresources()) {
4419                        conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_TARGET_ID"));
4420                        params.add(resource.getStructureId().toString());
4421                    } else {
4422                        conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_TARGET_PATH"));
4423                        params.add(escapeDbWildcard(resource.getRootPath()) + '%');
4424                    }
4425                } else if (filter.isTarget() && !checkSource) {
4426                    if (!filter.isIncludeSubresources() || (mode == RelationConditionMode.delete)) {
4427                        // the mode check above is there to *not* silently remove categories for everything below the passed in resource!
4428                        conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_SOURCE_ID"));
4429                        params.add(resource.getStructureId().toString());
4430                    } else {
4431                        conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_SOURCE_PATH"));
4432                        params.add(escapeDbWildcard(resource.getRootPath()) + '%');
4433                    }
4434                }
4435                conditions.append(END_CONDITION);
4436            }
4437
4438            // target or source id filter from filter parameter
4439            if (filter.getStructureId() != null) {
4440                if (conditions.length() == 0) {
4441                    conditions.append(BEGIN_CONDITION);
4442                } else {
4443                    conditions.append(BEGIN_INCLUDE_CONDITION);
4444                }
4445
4446                if (filter.isSource() && checkSource) {
4447                    conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_SOURCE_ID"));
4448                    params.add(filter.getStructureId().toString());
4449                } else if (filter.isTarget() && !checkSource) {
4450                    conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_TARGET_ID"));
4451                    params.add(filter.getStructureId().toString());
4452                }
4453                conditions.append(END_CONDITION);
4454            }
4455
4456            // target or source path filter from filter parameter
4457            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(filter.getPath())) {
4458                if (conditions.length() == 0) {
4459                    conditions.append(BEGIN_CONDITION);
4460                } else {
4461                    conditions.append(BEGIN_INCLUDE_CONDITION);
4462                }
4463
4464                String queryPath = filter.getPath();
4465                queryPath = escapeDbWildcard(queryPath);
4466                if (filter.isIncludeSubresources()) {
4467                    queryPath += '%';
4468                }
4469                if (filter.isSource() && checkSource) {
4470                    conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_SOURCE_PATH"));
4471                    params.add(queryPath);
4472                } else if (filter.isTarget() && !checkSource) {
4473                    conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_TARGET_PATH"));
4474                    params.add(queryPath);
4475                }
4476                conditions.append(END_CONDITION);
4477            }
4478        }
4479
4480        // relation type filter
4481        Set<CmsRelationType> types = filter.getTypes();
4482        if (!types.isEmpty()) {
4483            if (conditions.length() == 0) {
4484                conditions.append(BEGIN_CONDITION);
4485            } else {
4486                conditions.append(BEGIN_INCLUDE_CONDITION);
4487            }
4488            conditions.append(m_sqlManager.readQuery(projectId, "C_RELATION_FILTER_TYPE"));
4489            conditions.append(BEGIN_CONDITION);
4490            Iterator<CmsRelationType> it = types.iterator();
4491            while (it.hasNext()) {
4492                CmsRelationType type = it.next();
4493                conditions.append("?");
4494                params.add(Integer.valueOf(type.getId()));
4495                if (it.hasNext()) {
4496                    conditions.append(", ");
4497                }
4498            }
4499            conditions.append(END_CONDITION);
4500            conditions.append(END_CONDITION);
4501        }
4502        return conditions.toString();
4503    }
4504
4505    /**
4506     * Appends the appropriate selection criteria related with the released date.<p>
4507     *
4508     * @param projectId the id of the project
4509     * @param startTime the start time
4510     * @param endTime the stop time
4511     * @param conditions buffer to append the selection criteria
4512     * @param params list to append the selection parameters
4513     */
4514    protected void prepareReleasedTimeRangeCondition(
4515        CmsUUID projectId,
4516        long startTime,
4517        long endTime,
4518        StringBuffer conditions,
4519        List<Object> params) {
4520
4521        if (startTime > 0L) {
4522            // READ_IGNORE_TIME: if NOT set, add condition to match released date against startTime
4523            conditions.append(BEGIN_INCLUDE_CONDITION);
4524            conditions.append(m_sqlManager.readQuery(projectId, "C_STRUCTURE_SELECT_BY_DATE_RELEASED_AFTER"));
4525            conditions.append(END_CONDITION);
4526            params.add(Long.valueOf(startTime));
4527        }
4528
4529        if (endTime > 0L) {
4530            // READ_IGNORE_TIME: if NOT set, add condition to match released date against endTime
4531            conditions.append(BEGIN_INCLUDE_CONDITION);
4532            conditions.append(m_sqlManager.readQuery(projectId, "C_STRUCTURE_SELECT_BY_DATE_RELEASED_BEFORE"));
4533            conditions.append(END_CONDITION);
4534            params.add(Long.valueOf(endTime));
4535        }
4536    }
4537
4538    /**
4539     * Appends the appropriate selection criteria related with the read mode.<p>
4540     *
4541     * @param projectId the id of the project of the resources
4542     * @param mode the selection mode
4543     * @param conditions buffer to append the selection criteria
4544     */
4545    protected void prepareResourceCondition(CmsUUID projectId, int mode, StringBuffer conditions) {
4546
4547        if ((mode & CmsDriverManager.READMODE_ONLY_FOLDERS) > 0) {
4548            // C_READMODE_ONLY_FOLDERS: add condition to match only folders
4549            conditions.append(BEGIN_INCLUDE_CONDITION);
4550            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_ONLY_FOLDERS"));
4551            conditions.append(END_CONDITION);
4552        } else if ((mode & CmsDriverManager.READMODE_ONLY_FILES) > 0) {
4553            // C_READMODE_ONLY_FILES: add condition to match only files
4554            conditions.append(BEGIN_INCLUDE_CONDITION);
4555            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_ONLY_FILES"));
4556            conditions.append(END_CONDITION);
4557        }
4558    }
4559
4560    /**
4561     * Appends the appropriate selection criteria related with the resource state.<p>
4562     *
4563     * @param projectId the id of the project of the resources
4564     * @param state the resource state
4565     * @param mode the selection mode
4566     * @param conditions buffer to append the selection criteria
4567     * @param params list to append the selection parameters
4568     */
4569    protected void prepareStateCondition(
4570        CmsUUID projectId,
4571        CmsResourceState state,
4572        int mode,
4573        StringBuffer conditions,
4574        List<Object> params) {
4575
4576        if (state != null) {
4577            if ((mode & CmsDriverManager.READMODE_EXCLUDE_STATE) > 0) {
4578                // C_READ_MODIFIED_STATES: add condition to match against any state but not given state
4579                conditions.append(BEGIN_EXCLUDE_CONDITION);
4580            } else {
4581                // otherwise add condition to match against given state if necessary
4582                conditions.append(BEGIN_INCLUDE_CONDITION);
4583            }
4584            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_RESOURCE_STATE"));
4585            conditions.append(END_CONDITION);
4586            params.add(Integer.valueOf(state.getState()));
4587            params.add(Integer.valueOf(state.getState()));
4588        }
4589    }
4590
4591    /**
4592     * Appends the appropriate selection criteria related with the date of the last modification.<p>
4593     *
4594     * @param projectId the id of the project of the resources
4595     * @param startTime start of the time range
4596     * @param endTime end of the time range
4597     * @param conditions buffer to append the selection criteria
4598     * @param params list to append the selection parameters
4599     */
4600    protected void prepareTimeRangeCondition(
4601        CmsUUID projectId,
4602        long startTime,
4603        long endTime,
4604        StringBuffer conditions,
4605        List<Object> params) {
4606
4607        if (startTime > 0L) {
4608            // READ_IGNORE_TIME: if NOT set, add condition to match last modified date against startTime
4609            conditions.append(BEGIN_INCLUDE_CONDITION);
4610            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_DATE_LASTMODIFIED_AFTER"));
4611            conditions.append(END_CONDITION);
4612            params.add(Long.valueOf(startTime));
4613        }
4614
4615        if (endTime > 0L) {
4616            // READ_IGNORE_TIME: if NOT set, add condition to match last modified date against endTime
4617            conditions.append(BEGIN_INCLUDE_CONDITION);
4618            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_DATE_LASTMODIFIED_BEFORE"));
4619            conditions.append(END_CONDITION);
4620            params.add(Long.valueOf(endTime));
4621        }
4622    }
4623
4624    /**
4625     * Appends the appropriate selection criteria related with the resource type.<p>
4626     *
4627     * @param projectId the id of the project of the resources
4628     * @param type the resource type
4629     * @param mode the selection mode
4630     * @param conditions buffer to append the selection criteria
4631     * @param params list to append the selection parameters
4632     */
4633    protected void prepareTypeCondition(
4634        CmsUUID projectId,
4635        int type,
4636        int mode,
4637        StringBuffer conditions,
4638        List<Object> params) {
4639
4640        if (type != CmsDriverManager.READ_IGNORE_TYPE) {
4641            if ((mode & CmsDriverManager.READMODE_EXCLUDE_TYPE) > 0) {
4642                // C_READ_FILE_TYPES: add condition to match against any type, but not given type
4643                conditions.append(BEGIN_EXCLUDE_CONDITION);
4644                conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_RESOURCE_TYPE"));
4645                conditions.append(END_CONDITION);
4646                params.add(Integer.valueOf(type));
4647            } else {
4648                //otherwise add condition to match against given type if necessary
4649                conditions.append(BEGIN_INCLUDE_CONDITION);
4650                conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_RESOURCE_TYPE"));
4651                conditions.append(END_CONDITION);
4652                params.add(Integer.valueOf(type));
4653            }
4654        }
4655    }
4656
4657    /**
4658     * Appends the appropriate selection criteria related with the resource type.<p>
4659     *
4660     * @param projectId the id of the project of the resources
4661     * @param types the resource type id's
4662     * @param mode the selection mode
4663     * @param conditions buffer to append the selection criteria
4664     * @param params list to append the selection parameters
4665     */
4666    protected void prepareTypesCondition(
4667        CmsUUID projectId,
4668        List<Integer> types,
4669        int mode,
4670        StringBuffer conditions,
4671        List<Object> params) {
4672
4673        if ((mode & CmsDriverManager.READMODE_EXCLUDE_TYPE) > 0) {
4674            // C_READ_FILE_TYPES: add condition to match against any type, but not given type
4675            conditions.append(BEGIN_EXCLUDE_CONDITION);
4676            conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_RESOURCE_TYPE"));
4677            conditions.append(END_CONDITION);
4678            params.add(Integer.valueOf(CmsDriverManager.READ_IGNORE_TYPE));
4679        } else if (!((types == null) || types.isEmpty())) {
4680            //otherwise add condition to match against given type if necessary
4681            conditions.append(BEGIN_INCLUDE_CONDITION);
4682            Iterator<Integer> typeIt = types.iterator();
4683            while (typeIt.hasNext()) {
4684                conditions.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_SELECT_BY_RESOURCE_TYPE"));
4685                params.add(typeIt.next());
4686                if (typeIt.hasNext()) {
4687                    conditions.append(OR_CONDITION);
4688                }
4689            }
4690            conditions.append(END_CONDITION);
4691        }
4692    }
4693
4694    /**
4695     * Reads all resources inside a given project matching the criteria specified by parameter values.<p>
4696     *
4697     * Important: If {@link CmsDriverManager#READMODE_EXCLUDE_TREE} is true (or {@link CmsDriverManager#READMODE_INCLUDE_TREE} is false),
4698     * the provided parent String must be the UUID of the parent folder, NOT the parent folder path.<p>
4699     *
4700     * @param dbc the current database context
4701     * @param projectId the project id for matching resources
4702     * @param parentPath the path to the resource used as root of the searched subtree or {@link CmsDriverManager#READ_IGNORE_PARENT},
4703     *               {@link CmsDriverManager#READMODE_EXCLUDE_TREE} means to read immediate children only
4704     * @param types the resource types of matching resources or <code>null</code> (meaning inverted by {@link CmsDriverManager#READMODE_EXCLUDE_TYPE}
4705     * @param state the state of matching resources (meaning inverted by {@link CmsDriverManager#READMODE_EXCLUDE_STATE} or <code>null</code> to ignore
4706     * @param lastModifiedAfter the start of the time range for the last modification date of matching resources or READ_IGNORE_TIME
4707     * @param lastModifiedBefore the end of the time range for the last modification date of matching resources or READ_IGNORE_TIME
4708     * @param releasedAfter the start of the time range for the release date of matching resources
4709     * @param releasedBefore the end of the time range for the release date of matching resources
4710     * @param expiredAfter the start of the time range for the expire date of matching resources
4711     * @param expiredBefore the end of the time range for the expire date of matching resources
4712     * @param mode additional mode flags:
4713     * <ul>
4714     *  <li>{@link CmsDriverManager#READMODE_INCLUDE_TREE}
4715     *  <li>{@link CmsDriverManager#READMODE_EXCLUDE_TREE}
4716     *  <li>{@link CmsDriverManager#READMODE_INCLUDE_PROJECT}
4717     *  <li>{@link CmsDriverManager#READMODE_EXCLUDE_TYPE}
4718     *  <li>{@link CmsDriverManager#READMODE_EXCLUDE_STATE}
4719     * </ul>
4720     *
4721     * @return a list of CmsResource objects matching the given criteria
4722     *
4723     * @throws CmsDataAccessException if something goes wrong
4724     */
4725    protected List<CmsResource> readTypesInResourceTree(
4726        CmsDbContext dbc,
4727        CmsUUID projectId,
4728        String parentPath,
4729        List<Integer> types,
4730        CmsResourceState state,
4731        long lastModifiedAfter,
4732        long lastModifiedBefore,
4733        long releasedAfter,
4734        long releasedBefore,
4735        long expiredAfter,
4736        long expiredBefore,
4737        int mode)
4738    throws CmsDataAccessException {
4739
4740        List<CmsResource> result = new ArrayList<CmsResource>();
4741
4742        StringBuffer conditions = new StringBuffer();
4743        List<Object> params = new ArrayList<Object>(5);
4744
4745        // prepare the selection criteria
4746        prepareProjectCondition(projectId, mode, conditions, params);
4747        prepareResourceCondition(projectId, mode, conditions);
4748        prepareTypesCondition(projectId, types, mode, conditions, params);
4749        prepareTimeRangeCondition(projectId, lastModifiedAfter, lastModifiedBefore, conditions, params);
4750        prepareReleasedTimeRangeCondition(projectId, releasedAfter, releasedBefore, conditions, params);
4751        prepareExpiredTimeRangeCondition(projectId, expiredAfter, expiredBefore, conditions, params);
4752        preparePathCondition(projectId, parentPath, mode, conditions, params);
4753        prepareStateCondition(projectId, state, mode, conditions, params);
4754
4755        // now read matching resources within the subtree
4756        ResultSet res = null;
4757        PreparedStatement stmt = null;
4758        Connection conn = null;
4759
4760        try {
4761            conn = m_sqlManager.getConnection(dbc);
4762            StringBuffer queryBuf = new StringBuffer(256);
4763            queryBuf.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_READ_TREE"));
4764            queryBuf.append(conditions);
4765            queryBuf.append(" ");
4766            queryBuf.append(m_sqlManager.readQuery(projectId, "C_RESOURCES_ORDER_BY_PATH"));
4767            stmt = m_sqlManager.getPreparedStatementForSql(conn, queryBuf.toString());
4768
4769            for (int i = 0; i < params.size(); i++) {
4770                if (params.get(i) instanceof Integer) {
4771                    stmt.setInt(i + 1, ((Integer)params.get(i)).intValue());
4772                } else if (params.get(i) instanceof Long) {
4773                    stmt.setLong(i + 1, ((Long)params.get(i)).longValue());
4774                } else {
4775                    stmt.setString(i + 1, (String)params.get(i));
4776                }
4777            }
4778
4779            res = stmt.executeQuery();
4780            while (res.next()) {
4781                CmsResource resource = createResource(res, projectId);
4782                result.add(resource);
4783            }
4784
4785        } catch (SQLException e) {
4786            throw new CmsDbSqlException(
4787                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4788                e);
4789        } finally {
4790            m_sqlManager.closeAll(dbc, conn, stmt, res);
4791        }
4792
4793        return result;
4794    }
4795
4796    /**
4797     * Repairs broken links.<p>
4798     *
4799     * When a resource is created any relation pointing to it is updated to use the right id.<p>
4800     *
4801     * @param dbc the current database context
4802     * @param projectId the project id
4803     * @param structureId the structure id of the resource that may help to repair broken links
4804     * @param rootPath the path of the resource that may help to repair broken links
4805     *
4806     * @throws CmsDataAccessException if something goes wrong
4807     */
4808    protected void repairBrokenRelations(CmsDbContext dbc, CmsUUID projectId, CmsUUID structureId, String rootPath)
4809    throws CmsDataAccessException {
4810
4811        PreparedStatement stmt = null;
4812        Connection conn = null;
4813
4814        try {
4815            conn = m_sqlManager.getConnection(dbc);
4816            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RELATIONS_REPAIR_BROKEN");
4817            stmt.setString(1, structureId.toString());
4818            stmt.setString(2, rootPath);
4819            stmt.executeUpdate();
4820        } catch (SQLException e) {
4821            throw new CmsDbSqlException(
4822                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4823                e);
4824        } finally {
4825            m_sqlManager.closeAll(dbc, conn, stmt, null);
4826        }
4827    }
4828
4829    /**
4830     * Updates broken links.<p>
4831     *
4832     * When a resource is deleted, then the relations pointing to
4833     * the deleted resource are set to the null uuid.<p>
4834     *
4835     * @param dbc the current database context
4836     * @param projectId the project id
4837     * @param rootPath the root path of the resource that has been deleted
4838     *
4839     * @throws CmsDataAccessException if something goes wrong
4840     */
4841    protected void updateBrokenRelations(CmsDbContext dbc, CmsUUID projectId, String rootPath)
4842    throws CmsDataAccessException {
4843
4844        PreparedStatement stmt = null;
4845        Connection conn = null;
4846        try {
4847            try {
4848                conn = m_sqlManager.getConnection(dbc);
4849                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RELATIONS_UPDATE_BROKEN");
4850                stmt.setString(1, rootPath);
4851                stmt.executeUpdate();
4852            } finally {
4853                m_sqlManager.closeAll(dbc, conn, stmt, null);
4854            }
4855
4856            try {
4857                conn = m_sqlManager.getConnection(dbc);
4858                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_RELATIONS_DELETE_BROKEN_LOCALE_RELATIONS");
4859                stmt.setString(1, rootPath);
4860                stmt.executeUpdate();
4861            } finally {
4862                m_sqlManager.closeAll(dbc, conn, stmt, null);
4863            }
4864        } catch (SQLException e) {
4865            throw new CmsDbSqlException(
4866                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4867                e);
4868        }
4869
4870    }
4871
4872    /**
4873     * Wrap a SQL exception into a CmsDbSqlException.<p>
4874     *
4875     * @param stmt the used statement
4876     * @param e the exception
4877     *
4878     * @return the CmsDbSqlException
4879     */
4880    protected CmsDbSqlException wrapException(PreparedStatement stmt, SQLException e) {
4881
4882        return new CmsDbSqlException(
4883            Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
4884            e);
4885    }
4886
4887    /**
4888     * Creates a prepared statement by combining a base query with the generated SQL conditions for a given
4889     * URL name mapping filter.<p>
4890     *
4891     * @param conn the connection to use for creating the prepared statement
4892     * @param baseQuery the base query to which the conditions should be appended
4893     * @param filter the filter from which to generate the conditions
4894     *
4895     * @return the created prepared statement
4896     *
4897     * @throws SQLException if something goes wrong
4898     */
4899    PreparedStatement getPreparedStatementForFilter(Connection conn, String baseQuery, CmsUrlNameMappingFilter filter)
4900    throws SQLException {
4901
4902        CmsPair<String, List<I_CmsPreparedStatementParameter>> conditionData = prepareUrlNameMappingConditions(filter);
4903        String whereClause = "";
4904        if (!conditionData.getFirst().equals("")) {
4905            whereClause = " WHERE " + conditionData.getFirst();
4906        }
4907        String query = baseQuery + whereClause;
4908        PreparedStatement stmt = m_sqlManager.getPreparedStatementForSql(conn, query);
4909        int counter = 1;
4910        for (I_CmsPreparedStatementParameter param : conditionData.getSecond()) {
4911            param.insertIntoStatement(stmt, counter);
4912            counter += 1;
4913        }
4914        return stmt;
4915    }
4916
4917    /**
4918     * Helper method to convert an alias filter to SQL conditions.<p>
4919     *
4920     * @param filter the alias filter
4921     * @return a pair containing a condition string and the parameters which are necessary for the conditions
4922     */
4923    private CmsPair<String, List<String>> buildAliasConditions(CmsAliasFilter filter) {
4924
4925        List<String> conditions = new ArrayList<String>();
4926        conditions.add("1 = 1");
4927        List<String> conditionParams = new ArrayList<String>();
4928        if (filter.getSiteRoot() != null) {
4929            conditions.add("site_root = ?");
4930            conditionParams.add(filter.getSiteRoot());
4931        }
4932        if (filter.getStructureId() != null) {
4933            conditions.add("structure_id = ?");
4934            conditionParams.add(filter.getStructureId().toString());
4935        }
4936        if (filter.getPath() != null) {
4937            conditions.add("path = ?");
4938            conditionParams.add(filter.getPath());
4939        }
4940        String conditionString = CmsStringUtil.listAsString(conditions, " AND ");
4941        return CmsPair.create(conditionString, conditionParams);
4942    }
4943
4944    /**
4945     * Helper method to prepare the SQL conditions for accessing rewrite aliases using a given filter.<p>
4946     *
4947     * @param filter the filter to use for rewrite aliases
4948     *
4949     * @return a pair consisting of an SQL condition string and a list of query parameters
4950     */
4951    private CmsPair<String, List<Object>> prepareRewriteAliasConditions(CmsRewriteAliasFilter filter) {
4952
4953        List<String> conditions = new ArrayList<String>();
4954        conditions.add("1 = 1");
4955        List<Object> parameters = new ArrayList<Object>();
4956        if (filter.getSiteRoot() != null) {
4957            parameters.add(filter.getSiteRoot());
4958            conditions.add("SITE_ROOT = ?");
4959        }
4960        if (filter.getId() != null) {
4961            parameters.add(filter.getId().toString());
4962            conditions.add("ID = ?");
4963        }
4964        return CmsPair.create(CmsStringUtil.listAsString(conditions, " AND "), parameters);
4965    }
4966
4967    /**
4968     * Replaces the %(PROJECT) macro inside a query with either ONLINE or OFFLINE, depending on the value
4969     * of a flag.<p>
4970     *
4971     * We use this instead of the ${PROJECT} replacement mechanism when we need explicit control over the
4972     * project, and don't want to implicitly use the project of the DB context.<p>
4973     *
4974     * @param query the query in which the macro should be replaced
4975     * @param online if true, the macro will be replaced with "ONLINE", else "OFFLINE"
4976     *
4977     * @return the query with the replaced macro
4978     */
4979    private String replaceProject(String query, boolean online) {
4980
4981        return query.replace("%(PROJECT)", online ? ONLINE : OFFLINE);
4982    }
4983
4984}