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