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