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.db.CmsDbContext; 031import org.opencms.db.CmsDbSqlException; 032import org.opencms.db.storage.I_CmsDbStorage; 033import org.opencms.file.I_CmsFileContentStreamHandler; 034import org.opencms.util.CmsFileUtil; 035 036import java.io.ByteArrayInputStream; 037import java.io.InputStream; 038import java.io.OutputStream; 039import java.sql.Connection; 040import java.sql.PreparedStatement; 041import java.sql.ResultSet; 042import java.sql.SQLException; 043 044/** 045 * Database backend for the OpenCms deduplicating media storage engine (DMSE).<p> 046 * 047 * This implementation stores binary content in a specialized table (CMS_STORAGE), 048 * using a unique hash for deduplication. It ensures that identical files are only 049 * stored once physically.<p> 050 * 051 * @see org.opencms.db.storage.CmsStorageManager 052 */ 053public class CmsDbStorage implements I_CmsDbStorage { 054 055 /** The SQL manager to execute database queries. */ 056 protected final CmsSqlManager m_sqlManager; 057 058 /** 059 * Public constructor.<p> 060 * 061 * @param sqlManager the SQL manager to use 062 */ 063 public CmsDbStorage(CmsSqlManager sqlManager) { 064 065 m_sqlManager = sqlManager; 066 } 067 068 /** 069 * @see org.opencms.db.storage.I_CmsStorage#deleteContent(org.opencms.db.CmsDbContext, java.lang.String) 070 */ 071 @Override 072 public void deleteContent(CmsDbContext dbc, String hash) throws Exception { 073 074 if (hash == null) { 075 return; 076 } 077 Connection conn = null; 078 PreparedStatement stmt = null; 079 try { 080 conn = m_sqlManager.getConnection(dbc); 081 stmt = m_sqlManager.getPreparedStatement(conn, "C_STORAGE_DELETE"); 082 stmt.setString(1, hash); 083 stmt.executeUpdate(); 084 } catch (SQLException e) { 085 throw new CmsDbSqlException( 086 Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), 087 e); 088 } finally { 089 m_sqlManager.closeAll(dbc, conn, stmt, null); 090 } 091 } 092 093 /** 094 * @see org.opencms.db.storage.I_CmsStorage#getStorageIdentifier() 095 */ 096 @Override 097 public String getStorageIdentifier() { 098 099 return STORAGE_TYPE; 100 } 101 102 /** 103 * @see org.opencms.db.storage.I_CmsStorage#loadContent(org.opencms.db.CmsDbContext, java.lang.String) 104 */ 105 @Override 106 public byte[] loadContent(CmsDbContext dbc, String hash) throws Exception { 107 108 if (hash == null) { 109 return null; 110 } 111 Connection conn = null; 112 PreparedStatement stmt = null; 113 ResultSet res = null; 114 byte[] content = null; 115 try { 116 conn = m_sqlManager.getConnection(dbc); 117 stmt = m_sqlManager.getPreparedStatement(conn, "C_STORAGE_FILE_CONTENT"); 118 stmt.setString(1, hash); 119 res = stmt.executeQuery(); 120 if (res.next()) { 121 content = m_sqlManager.getBytes(res, "FILE_CONTENT"); 122 } 123 } catch (SQLException e) { 124 throw new CmsDbSqlException( 125 Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), 126 e); 127 } finally { 128 m_sqlManager.closeAll(dbc, conn, stmt, res); 129 } 130 return content; 131 } 132 133 /** 134 * @see org.opencms.db.storage.I_CmsStorage#loadContentFrom(CmsDbContext, String, I_CmsFileContentStreamHandler) 135 */ 136 @Override 137 public void loadContentFrom(CmsDbContext dbc, String hash, I_CmsFileContentStreamHandler handler) throws Exception { 138 139 if (hash == null) { 140 return; 141 } 142 Connection conn = null; 143 PreparedStatement stmt = null; 144 ResultSet res = null; 145 try { 146 conn = m_sqlManager.getConnection(dbc); 147 stmt = m_sqlManager.getPreparedStatement(conn, "C_STORAGE_FILE_CONTENT"); 148 stmt.setString(1, hash); 149 res = stmt.executeQuery(); 150 if (res.next()) { 151 try (InputStream in = res.getBinaryStream("FILE_CONTENT")) { 152 if (in != null) { 153 handler.read(in); 154 } 155 } 156 } 157 } catch (SQLException e) { 158 throw new CmsDbSqlException( 159 Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), 160 e); 161 } finally { 162 m_sqlManager.closeAll(dbc, conn, stmt, res); 163 } 164 } 165 166 /** 167 * @see org.opencms.db.storage.I_CmsStorage#loadContentTo(CmsDbContext, String, OutputStream) 168 */ 169 @Override 170 public void loadContentTo(CmsDbContext dbc, String hash, OutputStream out) throws Exception { 171 172 if (hash == null) { 173 return; 174 } 175 Connection conn = null; 176 PreparedStatement stmt = null; 177 ResultSet res = null; 178 try { 179 conn = m_sqlManager.getConnection(dbc); 180 stmt = m_sqlManager.getPreparedStatement(conn, "C_STORAGE_FILE_CONTENT"); 181 stmt.setString(1, hash); 182 res = stmt.executeQuery(); 183 if (res.next()) { 184 try (InputStream in = res.getBinaryStream("FILE_CONTENT")) { 185 if (in != null) { 186 CmsFileUtil.copy(in, out); 187 } 188 } 189 } 190 } catch (SQLException e) { 191 throw new CmsDbSqlException( 192 Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), 193 e); 194 } finally { 195 m_sqlManager.closeAll(dbc, conn, stmt, res); 196 } 197 } 198 199 /** 200 * @see org.opencms.db.storage.I_CmsStorage#storeContent(org.opencms.db.CmsDbContext, java.lang.String, byte[]) 201 */ 202 @Override 203 public void storeContent(CmsDbContext dbc, String hash, byte[] content) throws Exception { 204 205 Connection conn = null; 206 PreparedStatement stmt = null; 207 ResultSet res = null; 208 try { 209 conn = m_sqlManager.getConnection(dbc); 210 // check for deduplication 211 stmt = m_sqlManager.getPreparedStatement(conn, "C_STORAGE_EXISTS"); 212 stmt.setString(1, hash); 213 res = stmt.executeQuery(); 214 if (res.next()) { 215 // content already in storage, skip insert 216 return; 217 } 218 m_sqlManager.closeAll(dbc, null, stmt, res); 219 220 // insert new storage entry 221 stmt = m_sqlManager.getPreparedStatement(conn, "C_STORAGE_WRITE"); 222 stmt.setString(1, hash); 223 if (content.length < 2000) { 224 stmt.setBytes(2, content); 225 } else { 226 stmt.setBinaryStream(2, new ByteArrayInputStream(content), content.length); 227 } 228 stmt.executeUpdate(); 229 } catch (SQLException e) { 230 throw new CmsDbSqlException( 231 Messages.get().container(Messages.ERR_GENERIC_SQL_1, CmsDbSqlException.getErrorQuery(stmt)), 232 e); 233 } finally { 234 m_sqlManager.closeAll(dbc, conn, stmt, res); 235 } 236 } 237 238 /** 239 * @see org.opencms.db.storage.I_CmsStorage#validateAvailable(org.opencms.db.CmsDbContext) 240 */ 241 @Override 242 public void validateAvailable(CmsDbContext dbc) throws Exception { 243 244 // The database connection is already validated during OpenCms startup. 245 } 246 247}