001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.db.generic;
029
030import org.opencms.configuration.CmsConfigurationManager;
031import org.opencms.configuration.CmsParameterConfiguration;
032import org.opencms.configuration.CmsStoredContentInfoCacheConfiguration;
033import org.opencms.configuration.CmsSystemConfiguration;
034import org.opencms.configuration.CmsVfsConfiguration;
035import org.opencms.db.CmsDbContext;
036import org.opencms.db.CmsDbSqlException;
037import org.opencms.db.CmsDriverManager;
038import org.opencms.db.storage.CmsNoopStoredContentInfoCache;
039import org.opencms.db.storage.CmsStorageException;
040import org.opencms.db.storage.CmsStorageManager;
041import org.opencms.db.storage.CmsStorageManager.StorageResult;
042import org.opencms.db.storage.CmsStoredContentInfoCacheKey;
043import org.opencms.db.storage.I_CmsStorageDelivery;
044import org.opencms.db.storage.I_CmsStoredContentInfoCache;
045import org.opencms.db.storage.I_CmsStoredContentInfoCacheFactory;
046import org.opencms.db.storage.policy.CmsStoragePolicyContext;
047import org.opencms.file.CmsDataAccessException;
048import org.opencms.file.CmsFile;
049import org.opencms.file.CmsProject;
050import org.opencms.file.CmsResource;
051import org.opencms.file.CmsStoredContentInfo;
052import org.opencms.file.CmsVfsResourceNotFoundException;
053import org.opencms.file.I_CmsFileContentStreamHandler;
054import org.opencms.main.CmsInitException;
055import org.opencms.main.OpenCms;
056import org.opencms.util.CmsStringUtil;
057import org.opencms.util.CmsUUID;
058
059import java.io.ByteArrayInputStream;
060import java.io.OutputStream;
061import java.sql.Connection;
062import java.sql.PreparedStatement;
063import java.sql.ResultSet;
064import java.sql.SQLException;
065import java.util.ArrayList;
066import java.util.List;
067
068/**
069 * Integrates the OpenCms deduplicating media storage engine (DMSE) with VFS content operations.<p>
070 *
071 * This driver reads and writes storage identifiers and content hashes alongside
072 * VFS content rows. It delegates backend access to the storage manager and
073 * supports content in both the regular VFS content columns and storage backends.<p>
074 *
075 * @see org.opencms.db.storage.CmsStorageManager
076 */
077public class CmsStorageVfsDriver extends CmsVfsDriver {
078
079    /**
080     * Performs one variant of reading content from a VFS content row.<p>
081     *
082     * @param <T> the handler result type
083     */
084    private interface I_CmsContentReadOperation<T> {
085
086        /**
087         * Reads content described by a content row.<p>
088         *
089         * @param localContent the local file content bytes
090         * @param storage the external storage identifier
091         * @param hash the external storage hash
092         * @return the handler result
093         * @throws CmsStorageException if external storage content can not be loaded
094         */
095        T read(byte[] localContent, String storage, String hash) throws CmsStorageException;
096    }
097
098    /** The filename/path of the storage SQL query properties. */
099    private static final String STORAGE_QUERY_PROPERTIES = "org/opencms/db/generic/storage.properties";
100
101    protected CmsStorageManager m_storageManager;
102
103    /** The stored content info cache. */
104    protected I_CmsStoredContentInfoCache m_storedContentInfoCache = new CmsNoopStoredContentInfoCache();
105
106    /**
107     * @see org.opencms.db.I_CmsVfsDriver#createContent(CmsDbContext, CmsUUID, CmsResource, byte[])
108     */
109    public void createContent(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, byte[] content)
110    throws CmsDataAccessException {
111
112        Connection conn = null;
113        PreparedStatement stmt = null;
114        try {
115            StorageResult storageResult = m_storageManager.prepareContent(
116                dbc,
117                new CmsStoragePolicyContext(content, resource));
118            byte[] fileContent = storageResult.getFileContent();
119            String storage = storageResult.getStorage();
120            String hash = storageResult.getHash();
121            conn = m_sqlManager.getConnection(dbc);
122            // create new offline content
123            stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_CONTENTS_WRITE");
124            stmt.setString(1, resource.getResourceId().toString());
125            if (fileContent.length < 2000) {
126                stmt.setBytes(2, fileContent);
127            } else {
128                stmt.setBinaryStream(2, new ByteArrayInputStream(fileContent), fileContent.length);
129            }
130            stmt.setString(3, storage);
131            stmt.setString(4, hash);
132            stmt.executeUpdate();
133            m_storedContentInfoCache.clear();
134        } catch (SQLException e) {
135            throw new CmsDbSqlException(
136                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
137                e);
138        } finally {
139            m_sqlManager.closeAll(dbc, conn, stmt, null);
140        }
141    }
142
143    /**
144     * @see org.opencms.db.I_CmsVfsDriver#createFile(CmsDbContext, java.sql.ResultSet, CmsUUID)
145     */
146    public CmsFile createFile(CmsDbContext dbc, ResultSet res, CmsUUID projectId)
147    throws CmsDataAccessException, SQLException {
148
149        // use the super class to read all standard properties
150        CmsFile file = super.createFile(dbc, res, projectId);
151        // read the storage information
152        String storage = res.getString("STORAGE");
153        // file content is stored locally -> return the file
154        if (CmsStringUtil.isEmpty(storage)) {
155            return file;
156        }
157        // load the file content from the storage
158        String hash = res.getString("HASH");
159        byte[] finalContent;
160        try {
161            finalContent = m_storageManager.loadContent(dbc, file.getContents(), storage, hash);
162        } catch (CmsStorageException e) {
163            throw createStorageReadException(storage, hash, e);
164        }
165        // copy the file but use the file content from the storage
166        if (finalContent != file.getContents()) {
167            return new CmsFile(
168                file.getStructureId(),
169                file.getResourceId(),
170                file.getRootPath(),
171                file.getTypeId(),
172                file.getFlags(),
173                projectId,
174                file.getState(),
175                file.getDateCreated(),
176                file.getUserCreated(),
177                file.getDateLastModified(),
178                file.getUserLastModified(),
179                file.getDateReleased(),
180                file.getDateExpired(),
181                file.getSiblingCount(),
182                file.getLength(),
183                file.getDateContent(),
184                file.getVersion(),
185                finalContent);
186        }
187        return file;
188    }
189
190    /**
191     * @see org.opencms.db.I_CmsVfsDriver#createFile(CmsDbContext, java.sql.ResultSet, CmsUUID, boolean)
192     */
193    public CmsFile createFile(CmsDbContext dbc, ResultSet res, CmsUUID projectId, boolean hasFileContentInResultSet)
194    throws CmsDataAccessException, SQLException {
195
196        CmsFile file = super.createFile(dbc, res, projectId, hasFileContentInResultSet);
197        if (!hasFileContentInResultSet) { // content is not relevant
198            return file;
199        }
200        String storage = res.getString("STORAGE");
201        if (CmsStringUtil.isEmpty(storage)) { // content is stored locally, already read by standard VFS driver
202            return file;
203        }
204        // load the content from the storage
205        String hash = res.getString("HASH");
206        byte[] finalContent;
207        try {
208            finalContent = m_storageManager.loadContent(dbc, file.getContents(), storage, hash);
209        } catch (CmsStorageException e) {
210            throw createStorageReadException(storage, hash, e);
211        }
212        if (finalContent != file.getContents()) {
213            return new CmsFile(
214                file.getStructureId(),
215                file.getResourceId(),
216                file.getRootPath(),
217                file.getTypeId(),
218                file.getFlags(),
219                file.getProjectLastModified(),
220                file.getState(),
221                file.getDateCreated(),
222                file.getUserCreated(),
223                file.getDateLastModified(),
224                file.getUserLastModified(),
225                file.getDateReleased(),
226                file.getDateExpired(),
227                file.getSiblingCount(),
228                file.getLength(),
229                file.getDateContent(),
230                file.getVersion(),
231                finalContent);
232        }
233        return file;
234    }
235
236    /**
237     * @see org.opencms.db.I_CmsVfsDriver#createOnlineContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, byte[], int, boolean, boolean)
238     */
239    public void createOnlineContent(
240        CmsDbContext dbc,
241        CmsUUID resourceId,
242        byte[] contents,
243        int publishTag,
244        boolean keepOnline,
245        boolean needToUpdateContent)
246    throws CmsDataAccessException {
247
248        Connection conn = null;
249        PreparedStatement stmt = null;
250        ResultSet res = null;
251        String oldOnlineStorage = null;
252        String oldOnlineHash = null;
253        try {
254            conn = m_sqlManager.getConnection(dbc);
255            boolean dbcHasProjectId = (dbc.getProjectId() != null) && !dbc.getProjectId().isNullUUID();
256            if (needToUpdateContent || dbcHasProjectId) {
257                // read the hash of the actual online content
258                stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_FILES_CONTENT");
259                stmt.setString(1, resourceId.toString());
260                res = stmt.executeQuery();
261                if (res.next()) {
262                    oldOnlineStorage = res.getString("STORAGE");
263                    oldOnlineHash = res.getString("HASH");
264                }
265                m_sqlManager.closeAll(dbc, null, stmt, res);
266                // read the actual offline content to take over the actual storage policy if needed
267                String storage = null;
268                String hash = null;
269                stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_FILES_CONTENT");
270                stmt.setString(1, resourceId.toString());
271                res = stmt.executeQuery();
272                if (res.next()) {
273                    storage = res.getString("STORAGE");
274                    hash = res.getString("HASH");
275                }
276                m_sqlManager.closeAll(dbc, null, stmt, res);
277                // move the content to the history or delete it as does the standard VFS driver
278                if (dbcHasProjectId || !OpenCms.getSystemInfo().isHistoryEnabled()) {
279                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_DELETE");
280                    stmt.setString(1, resourceId.toString());
281                    stmt.executeUpdate();
282                    m_sqlManager.closeAll(dbc, null, stmt, null);
283                } else {
284                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_HISTORY");
285                    stmt.setString(1, resourceId.toString());
286                    stmt.executeUpdate();
287                    m_sqlManager.closeAll(dbc, null, stmt, null);
288                }
289                // write the new online content taking over the offline storage policy
290                stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_WRITE");
291                stmt.setString(1, resourceId.toString());
292                // handle the local binary blob (the external blob was already created in the offline table)
293                if ((contents != null) && (contents.length > 0)) {
294                    if (CmsStringUtil.isEmpty(hash)) {
295                        if (contents.length < 2000) {
296                            stmt.setBytes(2, contents);
297                        } else {
298                            stmt.setBinaryStream(2, new ByteArrayInputStream(contents), contents.length);
299                        }
300                    } else {
301                        stmt.setBytes(2, new byte[0]);
302                    }
303                } else {
304                    stmt.setBytes(2, new byte[0]);
305                }
306                stmt.setString(3, storage);
307                stmt.setString(4, hash);
308                stmt.setInt(5, publishTag);
309                stmt.setInt(6, publishTag);
310                stmt.setInt(7, keepOnline ? 1 : 0);
311                stmt.executeUpdate();
312                m_storedContentInfoCache.clear();
313                m_sqlManager.closeAll(dbc, null, stmt, null);
314                // delete the old storage entry if necessary
315                if (CmsStringUtil.isNotEmpty(oldOnlineHash)) {
316                    m_storageManager.deleteContent(dbc, oldOnlineStorage, oldOnlineHash);
317                }
318            } else {
319                // standard update in case the content blob is not relevant
320                super.createOnlineContent(dbc, resourceId, contents, publishTag, keepOnline, needToUpdateContent);
321            }
322        } catch (SQLException e) {
323            throw new CmsDbSqlException(
324                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
325                e);
326        } finally {
327            m_sqlManager.closeAll(dbc, conn, stmt, res);
328        }
329    }
330
331    /**
332     * @see org.opencms.db.I_CmsVfsDriver#deleteHistoryContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
333     */
334    @Override
335    public void deleteHistoryContent(CmsDbContext dbc, CmsUUID resourceId, int publishTagToKeep)
336    throws CmsDataAccessException {
337
338        Connection conn = null;
339        PreparedStatement stmt = null;
340        ResultSet res = null;
341        List<String[]> storageReferences = new ArrayList<String[]>();
342        try {
343            conn = m_sqlManager.getConnection(dbc);
344            stmt = m_sqlManager.getPreparedStatement(conn, "C_CONTENT_HISTORY_STORAGE_READ");
345            stmt.setString(1, resourceId.toString());
346            stmt.setInt(2, publishTagToKeep);
347            res = stmt.executeQuery();
348            while (res.next()) {
349                String storage = res.getString("STORAGE");
350                String hash = res.getString("HASH");
351                if (CmsStringUtil.isNotEmpty(storage) && CmsStringUtil.isNotEmpty(hash)) {
352                    storageReferences.add(new String[] {storage, hash});
353                }
354            }
355        } catch (SQLException e) {
356            throw new CmsDbSqlException(
357                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
358                e);
359        } finally {
360            m_sqlManager.closeAll(dbc, conn, stmt, res);
361        }
362
363        super.deleteHistoryContent(dbc, resourceId, publishTagToKeep);
364
365        for (String[] storageReference : storageReferences) {
366            m_storageManager.deleteContent(dbc, storageReference[0], storageReference[1]);
367        }
368    }
369
370    /**
371     * @see org.opencms.db.I_CmsVfsDriver#destroy()
372     */
373    public void destroy() throws Throwable {
374
375        Throwable failure = null;
376        try {
377            if (m_storageManager != null) {
378                m_storageManager.close();
379            }
380        } catch (Throwable e) {
381            failure = e;
382        } finally {
383            m_storageManager = null;
384            try {
385                super.destroy();
386            } catch (Throwable e) {
387                if (failure == null) {
388                    failure = e;
389                } else {
390                    failure.addSuppressed(e);
391                }
392            }
393        }
394        if (failure != null) {
395            throw failure;
396        }
397    }
398
399    /**
400     * @see org.opencms.db.I_CmsVfsDriver#getStoredContentDelivery(org.opencms.db.CmsDbContext, java.lang.String)
401     */
402    @Override
403    public I_CmsStorageDelivery getStoredContentDelivery(CmsDbContext dbc, String storage)
404    throws CmsDataAccessException {
405
406        try {
407            return m_storageManager.getDeliveryStorage(storage);
408        } catch (CmsStorageException e) {
409            throw createStorageReadException(storage, null, e);
410        }
411    }
412
413    /**
414     * @see org.opencms.db.I_CmsDriver#init(org.opencms.db.CmsDbContext, org.opencms.configuration.CmsConfigurationManager, java.util.List, org.opencms.db.CmsDriverManager)
415     */
416    public void init(
417        CmsDbContext dbc,
418        CmsConfigurationManager configurationManager,
419        List<String> successiveDrivers,
420        CmsDriverManager driverManager) {
421
422        super.init(dbc, configurationManager, successiveDrivers, driverManager);
423        try {
424            CmsParameterConfiguration configuration = configurationManager.getConfiguration();
425            CmsVfsConfiguration vfsConfiguration = (CmsVfsConfiguration)configurationManager.getConfiguration(
426                CmsVfsConfiguration.class);
427            CmsSystemConfiguration systemConfiguration = (CmsSystemConfiguration)configurationManager.getConfiguration(
428                CmsSystemConfiguration.class);
429            m_storedContentInfoCache = createStoredContentInfoCache(
430                systemConfiguration.getStoredContentInfoCacheConfiguration());
431            m_storageManager = new CmsStorageManager(
432                m_sqlManager,
433                configuration,
434                vfsConfiguration.getStoragePolicyConfiguration());
435            m_storageManager.validateStorages(dbc);
436        } catch (Exception e) {
437            throw new CmsInitException(Messages.get().container(Messages.ERR_INITIALIZING_VFS_DRIVER_0), e);
438        }
439    }
440
441    /**
442     * @see org.opencms.db.I_CmsVfsDriver#initSqlManager(String)
443     */
444    @Override
445    public org.opencms.db.generic.CmsSqlManager initSqlManager(String classname) {
446
447        return CmsSqlManager.getInstance(classname);
448    }
449
450    /**
451     * @see org.opencms.db.generic.CmsVfsDriver#initSqlManager(String, List)
452     */
453    @Override
454    public org.opencms.db.generic.CmsSqlManager initSqlManager(
455        String classname,
456        List<String> additionalQueryProperties) {
457
458        return CmsSqlManager.getInstance(classname, additionalQueryProperties);
459    }
460
461    /**
462     * @see org.opencms.db.I_CmsVfsDriver#readContent(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.util.CmsUUID)
463     */
464    public byte[] readContent(CmsDbContext dbc, CmsUUID projectId, CmsUUID resourceId) throws CmsDataAccessException {
465
466        return readContentInternal(dbc, projectId, resourceId, new I_CmsContentReadOperation<byte[]>() {
467
468            public byte[] read(byte[] localContent, String storage, String hash) throws CmsStorageException {
469
470                return m_storageManager.loadContent(dbc, localContent, storage, hash);
471            }
472        });
473    }
474
475    /**
476     * @see org.opencms.db.I_CmsVfsDriver#readContentFrom(CmsDbContext, CmsUUID, CmsUUID, I_CmsFileContentStreamHandler)
477     */
478    @Override
479    public void readContentFrom(
480        CmsDbContext dbc,
481        CmsUUID projectId,
482        CmsUUID resourceId,
483        I_CmsFileContentStreamHandler handler)
484    throws CmsDataAccessException {
485
486        readContentInternal(dbc, projectId, resourceId, new I_CmsContentReadOperation<Void>() {
487
488            public Void read(byte[] localContent, String storage, String hash) throws CmsStorageException {
489
490                m_storageManager.loadContentFrom(dbc, localContent, storage, hash, handler);
491                return null;
492            }
493        });
494    }
495
496    /**
497     * @see org.opencms.db.I_CmsVfsDriver#readContentTo(CmsDbContext, CmsUUID, CmsUUID, OutputStream)
498     */
499    @Override
500    public void readContentTo(CmsDbContext dbc, CmsUUID projectId, CmsUUID resourceId, OutputStream out)
501    throws CmsDataAccessException {
502
503        readContentInternal(dbc, projectId, resourceId, new I_CmsContentReadOperation<Void>() {
504
505            public Void read(byte[] localContent, String storage, String hash) throws CmsStorageException {
506
507                m_storageManager.loadContentTo(dbc, localContent, storage, hash, out);
508                return null;
509            }
510        });
511    }
512
513    /**
514     * @see org.opencms.db.I_CmsVfsDriver#readHistoryContent(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, int)
515     */
516    public byte[] readHistoryContent(CmsDbContext dbc, CmsUUID resourceId, int publishTag)
517    throws CmsDataAccessException {
518
519        Connection conn = null;
520        PreparedStatement stmt = null;
521        ResultSet res = null;
522        byte[] content = null;
523
524        try {
525            conn = m_sqlManager.getConnection(dbc);
526            stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_READ_CONTENT");
527            stmt.setString(1, resourceId.toString());
528            stmt.setInt(2, publishTag);
529            stmt.setInt(3, publishTag);
530            res = stmt.executeQuery();
531
532            if (res.next()) {
533                byte[] localContent = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
534                String storage = res.getString("STORAGE");
535                String hash = res.getString("HASH");
536                try {
537                    content = m_storageManager.loadContent(dbc, localContent, storage, hash);
538                } catch (CmsStorageException e) {
539                    throw createStorageReadException(storage, hash, e);
540                }
541                while (res.next()) {
542                    // do nothing only move through all rows because of mssql odbc driver
543                }
544            }
545        } catch (SQLException e) {
546            throw new CmsDbSqlException(
547                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
548                e);
549        } finally {
550            m_sqlManager.closeAll(dbc, conn, stmt, res);
551        }
552        return content;
553    }
554
555    /**
556     * @see org.opencms.db.I_CmsVfsDriver#readStoredContentInfo(org.opencms.db.CmsDbContext, org.opencms.util.CmsUUID, org.opencms.file.CmsResource)
557     */
558    @Override
559    public CmsStoredContentInfo readStoredContentInfo(CmsDbContext dbc, CmsUUID projectId, CmsResource resource)
560    throws CmsDataAccessException {
561
562        CmsStoredContentInfoCacheKey cacheKey = new CmsStoredContentInfoCacheKey(projectId, resource);
563        CmsStoredContentInfo cachedInfo = m_storedContentInfoCache.get(cacheKey);
564        if (cachedInfo != null) {
565            return cachedInfo;
566        }
567        Connection conn = null;
568        PreparedStatement stmt = null;
569        ResultSet res = null;
570        try {
571            conn = m_sqlManager.getConnection(dbc);
572            if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
573                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ONLINE_STORED_CONTENT_INFO");
574            } else {
575                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_OFFLINE_STORED_CONTENT_INFO");
576            }
577            stmt.setString(1, resource.getResourceId().toString());
578            res = stmt.executeQuery();
579            if (res.next()) {
580                String storage = res.getString("STORAGE");
581                String hash = res.getString("HASH");
582                while (res.next()) {
583                    // do nothing only move through all rows because of mssql odbc driver
584                }
585                CmsStoredContentInfo info = new CmsStoredContentInfo(resource, storage, hash, resource.getLength());
586                m_storedContentInfoCache.put(cacheKey, info);
587                return info;
588            }
589            throw new CmsVfsResourceNotFoundException(
590                Messages.get().container(
591                    Messages.ERR_READ_CONTENT_WITH_RESOURCE_ID_2,
592                    resource.getResourceId(),
593                    Boolean.valueOf(projectId.equals(CmsProject.ONLINE_PROJECT_ID))));
594        } catch (SQLException e) {
595            throw new CmsDbSqlException(
596                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
597                e);
598        } finally {
599            m_sqlManager.closeAll(dbc, conn, stmt, res);
600        }
601    }
602
603    /**
604     * @see org.opencms.db.I_CmsVfsDriver#removeFile(org.opencms.db.CmsDbContext, CmsUUID, org.opencms.file.CmsResource)
605     */
606    public void removeFile(CmsDbContext dbc, CmsUUID projectId, CmsResource resource) throws CmsDataAccessException {
607
608        PreparedStatement stmt = null;
609        ResultSet res = null;
610        Connection conn = null;
611        CmsUUID resourceId = resource.getResourceId();
612        try {
613            conn = m_sqlManager.getConnection(dbc);
614            if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
615                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ONLINE_FILES_CONTENT");
616            } else {
617                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_OFFLINE_FILES_CONTENT");
618            }
619            stmt.setString(1, resourceId.toString());
620            res = stmt.executeQuery();
621            if (res.next()) {
622                String storage = res.getString("STORAGE");
623                String hash = res.getString("HASH");
624                while (res.next()) {
625                    // do nothing only move through all rows because of mssql odbc driver
626                }
627                m_sqlManager.closeAll(dbc, null, stmt, res);
628                super.removeFile(dbc, projectId, resource);
629                // remove the file if in storage
630                if (CmsStringUtil.isNotEmpty(storage)) {
631                    m_storageManager.deleteContent(dbc, storage, hash);
632                }
633
634            }
635            super.removeFile(dbc, projectId, resource);
636            m_storedContentInfoCache.clear();
637        } catch (SQLException e) {
638            throw new CmsDbSqlException(
639                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
640                e);
641        } finally {
642            m_sqlManager.closeAll(dbc, conn, stmt, res);
643        }
644    }
645
646    /**
647     * @see org.opencms.db.I_CmsVfsDriver#writeContent(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, byte[])
648     */
649    public void writeContent(CmsDbContext dbc, CmsResource resource, byte[] content) throws CmsDataAccessException {
650
651        Connection conn = null;
652        PreparedStatement stmt = null;
653        ResultSet res = null;
654        String oldStorage = null;
655        String oldHash = null;
656        try {
657            // read the actual offline content
658            conn = m_sqlManager.getConnection(dbc);
659            stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_FILES_CONTENT");
660            stmt.setString(1, resource.getResourceId().toString());
661            res = stmt.executeQuery();
662            if (res.next()) {
663                oldStorage = res.getString("STORAGE");
664                oldHash = res.getString("HASH");
665            }
666            m_sqlManager.closeAll(dbc, null, stmt, res);
667            // prepare to store the content blob
668            StorageResult storageResult = m_storageManager.prepareContent(
669                dbc,
670                new CmsStoragePolicyContext(content, resource));
671            stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_CONTENTS_UPDATE");
672            // update the contents table
673            byte[] fileContent = storageResult.getFileContent();
674            if (fileContent.length < 2000) {
675                stmt.setBytes(1, fileContent);
676            } else {
677                stmt.setBinaryStream(1, new ByteArrayInputStream(fileContent), fileContent.length);
678            }
679            stmt.setString(2, storageResult.getStorage());
680            stmt.setString(3, storageResult.getHash());
681            stmt.setString(4, resource.getResourceId().toString());
682            stmt.executeUpdate();
683            m_storedContentInfoCache.clear();
684            // delete the old content blob if not used any more
685            if (CmsStringUtil.isNotEmpty(oldHash)) {
686                m_storageManager.deleteContent(dbc, oldStorage, oldHash);
687            }
688        } catch (SQLException e) {
689            throw new CmsDbSqlException(
690                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
691                e);
692        } finally {
693            m_sqlManager.closeAll(dbc, conn, stmt, res);
694        }
695    }
696
697    /**
698     * Creates the stored content info cache.<p>
699     *
700     * @param configuration the cache configuration
701     * @return the cache
702     * @throws Exception if the configured factory can not be created
703     */
704    protected I_CmsStoredContentInfoCache createStoredContentInfoCache(
705        CmsStoredContentInfoCacheConfiguration configuration)
706    throws Exception {
707
708        if ((configuration == null) || !configuration.isEnabled()) {
709            return new CmsNoopStoredContentInfoCache();
710        }
711        Class<?> factoryClass = Class.forName(configuration.getFactoryClass());
712        if (!I_CmsStoredContentInfoCacheFactory.class.isAssignableFrom(factoryClass)) {
713            throw new CmsInitException(Messages.get().container(Messages.ERR_INITIALIZING_VFS_DRIVER_0));
714        }
715        I_CmsStoredContentInfoCacheFactory factory = (I_CmsStoredContentInfoCacheFactory)factoryClass.getDeclaredConstructor().newInstance();
716        I_CmsStoredContentInfoCache cache = factory.create(configuration);
717        return cache != null ? cache : new CmsNoopStoredContentInfoCache();
718    }
719
720    /**
721     * @see org.opencms.db.generic.CmsVfsDriver#getAdditionalSqlQueryProperties(CmsParameterConfiguration)
722     */
723    @Override
724    protected List<String> getAdditionalSqlQueryProperties(CmsParameterConfiguration configuration) {
725
726        List<String> result = new ArrayList<String>();
727        result.add(STORAGE_QUERY_PROPERTIES);
728        return result;
729    }
730
731    /**
732     * Creates the data access exception for storage read failures.<p>
733     *
734     * @param storage the storage identifier
735     * @param hash the content hash
736     * @param cause the storage exception
737     * @return the data access exception
738     */
739    private CmsDataAccessException createStorageReadException(String storage, String hash, CmsStorageException cause) {
740
741        return new CmsDataAccessException(
742            Messages.get().container(Messages.ERR_STORAGE_CONTENT_READ_2, storage, hash),
743            cause);
744    }
745
746    /**
747     * Reads a content row and delegates content loading to the given read operation.<p>
748     *
749     * @param dbc the database context
750     * @param projectId the project id
751     * @param resourceId the resource id
752     * @param readOperation the content read operation
753     * @return the read operation result
754     * @throws CmsDataAccessException if the content can not be read
755     */
756    private <T> T readContentInternal(
757        CmsDbContext dbc,
758        CmsUUID projectId,
759        CmsUUID resourceId,
760        I_CmsContentReadOperation<T> readOperation)
761    throws CmsDataAccessException {
762
763        PreparedStatement stmt = null;
764        ResultSet res = null;
765        Connection conn = null;
766        T result = null;
767        try {
768            conn = m_sqlManager.getConnection(dbc);
769            if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
770                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ONLINE_FILES_CONTENT");
771            } else {
772                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_OFFLINE_FILES_CONTENT");
773            }
774            stmt.setString(1, resourceId.toString());
775            res = stmt.executeQuery();
776            if (res.next()) {
777                byte[] localContent = m_sqlManager.getBytes(res, m_sqlManager.readQuery("C_RESOURCES_FILE_CONTENT"));
778                String storage = res.getString("STORAGE");
779                String hash = res.getString("HASH");
780                try {
781                    result = readOperation.read(localContent, storage, hash);
782                } catch (CmsStorageException e) {
783                    throw createStorageReadException(storage, hash, e);
784                }
785                while (res.next()) {
786                    // do nothing only move through all rows because of mssql odbc driver
787                }
788            } else {
789                throw new CmsVfsResourceNotFoundException(
790                    Messages.get().container(
791                        Messages.ERR_READ_CONTENT_WITH_RESOURCE_ID_2,
792                        resourceId,
793                        Boolean.valueOf(projectId.equals(CmsProject.ONLINE_PROJECT_ID))));
794            }
795        } catch (SQLException e) {
796            throw new CmsDbSqlException(
797                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
798                e);
799        } finally {
800            m_sqlManager.closeAll(dbc, conn, stmt, res);
801        }
802        return result;
803    }
804}