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