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.oracle;
029
030import org.opencms.db.CmsDbContext;
031import org.opencms.db.CmsDbEntryNotFoundException;
032import org.opencms.db.CmsDbIoException;
033import org.opencms.db.CmsDbSqlException;
034import org.opencms.db.generic.CmsSqlManager;
035import org.opencms.db.generic.Messages;
036import org.opencms.db.storage.CmsStorageManager.StorageResult;
037import org.opencms.db.storage.policy.CmsStoragePolicyContext;
038import org.opencms.file.CmsDataAccessException;
039import org.opencms.file.CmsProject;
040import org.opencms.file.CmsResource;
041import org.opencms.main.OpenCms;
042import org.opencms.util.CmsStringUtil;
043import org.opencms.util.CmsUUID;
044
045import java.io.IOException;
046import java.io.OutputStream;
047import java.sql.Connection;
048import java.sql.PreparedStatement;
049import java.sql.ResultSet;
050import java.sql.SQLException;
051
052/**
053 * Oracle VFS driver for the OpenCms deduplicating media storage engine (DMSE).<p>
054 *
055 * @see org.opencms.db.generic.CmsStorageVfsDriver
056 * @see org.opencms.db.storage.CmsStorageManager
057 */
058public class CmsStorageVfsDriver extends org.opencms.db.generic.CmsStorageVfsDriver {
059
060    /**
061     * @see org.opencms.db.I_CmsVfsDriver#createContent(CmsDbContext, CmsUUID, CmsResource, byte[])
062     */
063    @Override
064    public void createContent(CmsDbContext dbc, CmsUUID projectId, CmsResource resource, byte[] content)
065    throws CmsDataAccessException {
066
067        Connection conn = null;
068        PreparedStatement stmt = null;
069        StorageResult storageResult = m_storageManager.prepareContent(
070            dbc,
071            new CmsStoragePolicyContext(content, resource));
072        try {
073            conn = m_sqlManager.getConnection(dbc);
074            stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ORACLE_OFFLINE_CONTENTS_WRITE_STORAGE");
075            stmt.setString(1, resource.getResourceId().toString());
076            stmt.setString(2, storageResult.getStorage());
077            stmt.setString(3, storageResult.getHash());
078            stmt.executeUpdate();
079        } catch (SQLException e) {
080            throw new CmsDbSqlException(
081                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
082                e);
083        } finally {
084            m_sqlManager.closeAll(dbc, conn, stmt, null);
085        }
086
087        internalWriteContent(dbc, projectId, resource.getResourceId(), storageResult.getFileContent(), -1);
088    }
089
090    /**
091     * @see org.opencms.db.I_CmsVfsDriver#createOnlineContent(CmsDbContext, CmsUUID, byte[], int, boolean, boolean)
092     */
093    @Override
094    public void createOnlineContent(
095        CmsDbContext dbc,
096        CmsUUID resourceId,
097        byte[] contents,
098        int publishTag,
099        boolean keepOnline,
100        boolean needToUpdateContent)
101    throws CmsDataAccessException {
102
103        Connection conn = null;
104        PreparedStatement stmt = null;
105        ResultSet res = null;
106        String oldOnlineStorage = null;
107        String oldOnlineHash = null;
108        try {
109            conn = m_sqlManager.getConnection(dbc);
110            boolean dbcHasProjectId = (dbc.getProjectId() != null) && !dbc.getProjectId().isNullUUID();
111            if (needToUpdateContent || dbcHasProjectId) {
112                stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_FILES_CONTENT");
113                stmt.setString(1, resourceId.toString());
114                res = stmt.executeQuery();
115                if (res.next()) {
116                    oldOnlineStorage = res.getString("STORAGE");
117                    oldOnlineHash = res.getString("HASH");
118                }
119                m_sqlManager.closeAll(dbc, null, stmt, res);
120                stmt = null;
121                res = null;
122
123                String storage = null;
124                String hash = null;
125                stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_FILES_CONTENT");
126                stmt.setString(1, resourceId.toString());
127                res = stmt.executeQuery();
128                if (res.next()) {
129                    storage = res.getString("STORAGE");
130                    hash = res.getString("HASH");
131                }
132                m_sqlManager.closeAll(dbc, null, stmt, res);
133                stmt = null;
134                res = null;
135
136                if (dbcHasProjectId || !OpenCms.getSystemInfo().isHistoryEnabled()) {
137                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_DELETE");
138                    stmt.setString(1, resourceId.toString());
139                    stmt.executeUpdate();
140                    m_sqlManager.closeAll(dbc, null, stmt, null);
141                    stmt = null;
142                } else {
143                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_HISTORY");
144                    stmt.setString(1, resourceId.toString());
145                    stmt.executeUpdate();
146                    m_sqlManager.closeAll(dbc, null, stmt, null);
147                    stmt = null;
148                }
149
150                stmt = m_sqlManager.getPreparedStatement(conn, "C_ORACLE_ONLINE_CONTENTS_WRITE_STORAGE");
151                stmt.setString(1, resourceId.toString());
152                stmt.setString(2, storage);
153                stmt.setString(3, hash);
154                stmt.setInt(4, publishTag);
155                stmt.setInt(5, publishTag);
156                stmt.setInt(6, keepOnline ? 1 : 0);
157                stmt.executeUpdate();
158                m_sqlManager.closeAll(dbc, conn, stmt, null);
159                conn = null;
160                stmt = null;
161
162                if ((contents != null) && (contents.length > 0) && CmsStringUtil.isEmpty(hash)) {
163                    internalWriteContent(dbc, CmsProject.ONLINE_PROJECT_ID, resourceId, contents, publishTag);
164                }
165                if (CmsStringUtil.isNotEmpty(oldOnlineHash)) {
166                    m_storageManager.deleteContent(dbc, oldOnlineStorage, oldOnlineHash);
167                }
168            } else {
169                stmt = m_sqlManager.getPreparedStatement(conn, "C_HISTORY_CONTENTS_UPDATE");
170                stmt.setInt(1, publishTag);
171                stmt.setString(2, resourceId.toString());
172                stmt.executeUpdate();
173                m_sqlManager.closeAll(dbc, null, stmt, null);
174                stmt = null;
175
176                if (!keepOnline) {
177                    stmt = m_sqlManager.getPreparedStatement(conn, "C_ONLINE_CONTENTS_HISTORY");
178                    stmt.setString(1, resourceId.toString());
179                    stmt.executeUpdate();
180                    m_sqlManager.closeAll(dbc, null, stmt, null);
181                    stmt = null;
182                }
183            }
184        } catch (SQLException e) {
185            throw new CmsDbSqlException(
186                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
187                e);
188        } finally {
189            m_sqlManager.closeAll(dbc, conn, stmt, res);
190        }
191    }
192
193    /**
194     * @see org.opencms.db.I_CmsVfsDriver#initSqlManager(String)
195     */
196    @Override
197    public org.opencms.db.generic.CmsSqlManager initSqlManager(String classname) {
198
199        return CmsSqlManager.getInstance(classname);
200    }
201
202    /**
203     * @see org.opencms.db.generic.CmsVfsDriver#initSqlManager(String, java.util.List)
204     */
205    @Override
206    public org.opencms.db.generic.CmsSqlManager initSqlManager(
207        String classname,
208        java.util.List<String> additionalQueryProperties) {
209
210        return CmsSqlManager.getInstance(classname, additionalQueryProperties);
211    }
212
213    /**
214     * @see org.opencms.db.I_CmsVfsDriver#writeContent(CmsDbContext, CmsResource, byte[])
215     */
216    @Override
217    public void writeContent(CmsDbContext dbc, CmsResource resource, byte[] content) throws CmsDataAccessException {
218
219        Connection conn = null;
220        PreparedStatement stmt = null;
221        ResultSet res = null;
222        String oldStorage = null;
223        String oldHash = null;
224        StorageResult storageResult = m_storageManager.prepareContent(
225            dbc,
226            new CmsStoragePolicyContext(content, resource));
227        try {
228            conn = m_sqlManager.getConnection(dbc);
229            stmt = m_sqlManager.getPreparedStatement(conn, "C_OFFLINE_FILES_CONTENT");
230            stmt.setString(1, resource.getResourceId().toString());
231            res = stmt.executeQuery();
232            if (res.next()) {
233                oldStorage = res.getString("STORAGE");
234                oldHash = res.getString("HASH");
235            }
236            m_sqlManager.closeAll(dbc, null, stmt, res);
237            stmt = null;
238            res = null;
239
240            stmt = m_sqlManager.getPreparedStatement(conn, "C_ORACLE_OFFLINE_CONTENTS_UPDATE_STORAGE");
241            stmt.setString(1, storageResult.getStorage());
242            stmt.setString(2, storageResult.getHash());
243            stmt.setString(3, resource.getResourceId().toString());
244            stmt.executeUpdate();
245            m_sqlManager.closeAll(dbc, conn, stmt, null);
246            conn = null;
247            stmt = null;
248        } catch (SQLException e) {
249            throw new CmsDbSqlException(
250                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
251                e);
252        } finally {
253            m_sqlManager.closeAll(dbc, conn, stmt, res);
254        }
255
256        internalWriteContent(
257            dbc,
258            dbc.currentProject().getUuid(),
259            resource.getResourceId(),
260            storageResult.getFileContent(),
261            -1);
262        if (CmsStringUtil.isNotEmpty(oldHash)) {
263            m_storageManager.deleteContent(dbc, oldStorage, oldHash);
264        }
265    }
266
267    /**
268     * Writes the resource content with the specified resource id.<p>
269     *
270     * @param dbc the current database context
271     * @param projectId the id of the current project
272     * @param resourceId the id of the resource used to identify the content to update
273     * @param contents the new content of the file
274     * @param publishTag the publish tag if to be written to the online content
275     *
276     * @throws CmsDataAccessException if something goes wrong
277     */
278    protected void internalWriteContent(
279        CmsDbContext dbc,
280        CmsUUID projectId,
281        CmsUUID resourceId,
282        byte[] contents,
283        int publishTag)
284    throws CmsDataAccessException {
285
286        PreparedStatement stmt = null;
287        PreparedStatement commit = null;
288        Connection conn = null;
289        ResultSet res = null;
290
291        boolean wasInTransaction = false;
292        try {
293            conn = m_sqlManager.getConnection(dbc);
294            if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
295                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ORACLE_ONLINE_CONTENTS_UPDATECONTENT");
296            } else {
297                stmt = m_sqlManager.getPreparedStatement(conn, projectId, "C_ORACLE_OFFLINE_CONTENTS_UPDATECONTENT");
298            }
299
300            wasInTransaction = !conn.getAutoCommit();
301            if (!wasInTransaction) {
302                conn.setAutoCommit(false);
303            }
304
305            stmt.setString(1, resourceId.toString());
306            if (projectId.equals(CmsProject.ONLINE_PROJECT_ID)) {
307                stmt.setInt(2, publishTag);
308                stmt.setInt(3, publishTag);
309            }
310            res = stmt.executeQuery();
311            if (!res.next()) {
312                throw new CmsDbEntryNotFoundException(
313                    Messages.get().container(Messages.LOG_READING_RESOURCE_1, resourceId));
314            }
315            OutputStream output = CmsUserDriver.getOutputStreamFromBlob(res, "FILE_CONTENT");
316            output.write(contents, 0, contents.length);
317            output.close();
318
319            if (!wasInTransaction) {
320                commit = m_sqlManager.getPreparedStatement(conn, "C_COMMIT");
321                commit.execute();
322                m_sqlManager.closeAll(dbc, null, commit, null);
323            }
324
325            m_sqlManager.closeAll(dbc, null, stmt, res);
326
327            commit = null;
328            stmt = null;
329            res = null;
330
331            if (!wasInTransaction) {
332                conn.setAutoCommit(true);
333            }
334        } catch (IOException e) {
335            throw new CmsDbIoException(
336                Messages.get().container(Messages.ERR_WRITING_TO_OUTPUT_STREAM_1, resourceId),
337                e);
338        } catch (SQLException e) {
339            throw new CmsDbSqlException(
340                Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)),
341                e);
342        } finally {
343            org.opencms.db.oracle.CmsSqlManager.closeAllInTransaction(
344                m_sqlManager,
345                dbc,
346                conn,
347                stmt,
348                res,
349                commit,
350                wasInTransaction);
351        }
352    }
353}