/* * Copyright (C) 2006-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.jlan.server.filesys.db.derby; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import org.alfresco.jlan.debug.Debug; import org.alfresco.jlan.server.config.InvalidConfigurationException; import org.alfresco.jlan.server.filesys.FileAttribute; import org.alfresco.jlan.server.filesys.FileExistsException; import org.alfresco.jlan.server.filesys.FileInfo; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.jlan.server.filesys.FileOpenParams; import org.alfresco.jlan.server.filesys.FileStatus; import org.alfresco.jlan.server.filesys.db.DBDataDetails; import org.alfresco.jlan.server.filesys.db.DBDataDetailsList; import org.alfresco.jlan.server.filesys.db.DBDataInterface; import org.alfresco.jlan.server.filesys.db.DBDeviceContext; import org.alfresco.jlan.server.filesys.db.DBException; import org.alfresco.jlan.server.filesys.db.DBFileInfo; import org.alfresco.jlan.server.filesys.db.DBInterface; import org.alfresco.jlan.server.filesys.db.DBObjectIdInterface; import org.alfresco.jlan.server.filesys.db.DBQueueInterface; import org.alfresco.jlan.server.filesys.db.DBSearchContext; import org.alfresco.jlan.server.filesys.db.JdbcDBInterface; import org.alfresco.jlan.server.filesys.db.RetentionDetails; import org.alfresco.jlan.server.filesys.loader.CachedFileInfo; import org.alfresco.jlan.server.filesys.loader.FileRequest; import org.alfresco.jlan.server.filesys.loader.FileRequestQueue; import org.alfresco.jlan.server.filesys.loader.FileSegment; import org.alfresco.jlan.server.filesys.loader.FileSegmentInfo; import org.alfresco.jlan.server.filesys.loader.MultipleFileRequest; import org.alfresco.jlan.server.filesys.loader.SingleFileRequest; import org.alfresco.jlan.smb.server.ntfs.StreamInfo; import org.alfresco.jlan.smb.server.ntfs.StreamInfoList; import org.alfresco.jlan.util.MemorySize; import org.alfresco.jlan.util.WildCard; import org.alfresco.jlan.util.db.DBConnectionPool; import org.springframework.extensions.config.ConfigElement; /** * Derby Base Database Interface Class * * <p>Derby (formerly Cloudscape) specific implementation of the database interface used by the database filesystem driver (DBDiskDriver). * * @author gkspencer */ public class DerbyDBInterface extends JdbcDBInterface implements DBQueueInterface, DBDataInterface, DBObjectIdInterface { // Memory buffer maximum size public final static long MaxMemoryBuffer = MemorySize.MEGABYTE / 2; // 512Kb // Blob reading buffer size public static final int BlobReadBuffer = 32768; // Lock file name, used to check if server shutdown was clean or not public final static String LockFileName = "DerbyLoader.lock"; // Database connection and prepared statement used to write file requests to the queue tables private Connection m_dbConn; private PreparedStatement m_reqStmt; private PreparedStatement m_tranStmt; /** * Default constructor */ public DerbyDBInterface() { super(); } /** * Return the database interface name * * @return String */ public String getDBInterfaceName() { return "Derby"; } /** * Get the supported database features mask * * @return int */ protected int getSupportedFeatures() { // Determine the available database interface features return FeatureNTFS + FeatureRetention + FeatureSymLinks + FeatureQueue + FeatureData + FeatureJarData + FeatureObjectId; } /** * Initialize the database interface * * @param dbCtx DBDeviceContext * @param params ConfigElement * @exception InvalidConfigurationException */ public void initializeDatabase(DBDeviceContext dbCtx, ConfigElement params) throws InvalidConfigurationException { // Set the JDBC driver class, must be set before the connection pool is created setDriverName("org.apache.derby.jdbc.EmbeddedDriver"); // Call the base class to do the main initialization super.initializeDatabase(dbCtx, params); // Create the database connection pool try { createConnectionPool(); } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Error creating connection pool, " + ex.toString()); // Rethrow the exception throw new InvalidConfigurationException("Failed to create connection pool, " + ex.getMessage()); } // Check if the file system table exists Connection conn = null; try { // Open a connection to the database conn = getConnection(); DatabaseMetaData dbMeta = conn.getMetaData(); ResultSet rs = dbMeta.getTables(null, null, null, null); boolean foundStruct = false; boolean foundStream = false; boolean foundRetain = false; boolean foundQueue = false; boolean foundTrans = false; boolean foundData = false; boolean foundJarData= false; boolean foundObjId = false; while (rs.next()) { // Get the table name String tblName = rs.getString("TABLE_NAME"); // Check if we found the filesystem structure or streams table if (tblName.equalsIgnoreCase(getFileSysTableName())) foundStruct = true; else if ( hasStreamsTableName() && tblName.equalsIgnoreCase(getStreamsTableName())) foundStream = true; else if ( hasRetentionTableName() && tblName.equalsIgnoreCase(getRetentionTableName())) foundRetain = true; else if ( hasDataTableName() && tblName.equalsIgnoreCase(getDataTableName())) foundData = true; else if ( hasJarDataTableName() && tblName.equalsIgnoreCase(getJarDataTableName())) foundJarData = true; else if ( hasQueueTableName() && tblName.equalsIgnoreCase(getQueueTableName())) foundQueue = true; else if ( hasTransactionTableName() && tblName.equalsIgnoreCase(getTransactionTableName())) foundTrans = true; else if ( hasObjectIdTableName() && tblName.equalsIgnoreCase(getObjectIdTableName())) foundObjId = true; } // Check if the file system structure table should be created if (foundStruct == false) { // Create the file system structure table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getFileSysTableName() + "(FileId INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, DirId INTEGER, FileName VARCHAR(255)," + "FileSize INTEGER, CreateDate TIMESTAMP, ModifyDate TIMESTAMP, AccessDate TIMESTAMP, ChangeDate TIMESTAMP, ReadOnlyFile CHAR," + "ArchivedFile CHAR, DirectoryFile CHAR, SystemFile CHAR, HiddenFile CHAR, OwnerUid INTEGER, OwnerGid INTEGER," + "FileMode INTEGER, IsDeleted CHAR DEFAULT 'N')"); // Create various indexes stmt.execute("CREATE INDEX IFileDirId ON " + getFileSysTableName() + "(FileName,DirId)"); stmt.execute("CREATE INDEX IDirId ON " + getFileSysTableName() + "(DirId)"); stmt.execute("CREATE INDEX IDir ON " + getFileSysTableName() + "(DirId,DirectoryFile)"); stmt.execute("CREATE INDEX IFileDirIdDir ON " + getFileSysTableName() + "(FileName,DirId,DirectoryFile)"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getFileSysTableName()); } // Check if the file streams table should be created if ( isNTFSEnabled() && foundStream == false && getStreamsTableName() != null) { // Create the file streams table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getStreamsTableName() + " (StreamId INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, FileId INTEGER NOT NULL, StreamName VARCHAR(255) NOT NULL, StreamSize INTEGER," + "CreateDate TIMESTAMP, ModifyDate TIMESTAMP, AccessDate TIMESTAMP)"); // Create various indexes stmt.execute("CREATE INDEX IFileId ON " + getStreamsTableName() + "(FileId)"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getStreamsTableName()); } // Check if the retention table should be created if ( isRetentionEnabled() && foundRetain == false && getRetentionTableName() != null) { // Create the retention period data table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getRetentionTableName() + " (FileId INTEGER NOT NULL PRIMARY KEY," + "StartDate TIMESTAMP, EndDate TIMESTAMP, PurgeFlag CHAR DEFAULT '0')"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getRetentionTableName()); } // Check if the file loader queue table should be created if ( isQueueEnabled() && foundQueue == false && getQueueTableName() != null) { // Create the request queue data table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getQueueTableName() + " (FileId INTEGER NOT NULL, StreamId INTEGER NOT NULL, ReqType SMALLINT," + "SeqNo INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, TempFile VARCHAR(512), VirtualPath VARCHAR(512), QueuedAt TIMESTAMP)"); stmt.execute("CREATE INDEX IQFileId ON " + getQueueTableName() + "(FileId)"); stmt.execute("CREATE INDEX IQFileIdType ON " + getQueueTableName() + "(FileId, ReqType)"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getQueueTableName()); } // Check if the file loader transaction queue table should be created if ( isQueueEnabled() && foundTrans == false && getTransactionTableName() != null) { // Create the transaction request queue data table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getTransactionTableName() + " (FileId INTEGER NOT NULL, StreamId INTEGER NOT NULL," + "TranId INTEGER NOT NULL, ReqType SMALLINT, TempFile VARCHAR(512), VirtualPath VARCHAR(512), QueuedAt TIMESTAMP," + "PRIMARY KEY (FileId,TranId))"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getTransactionTableName()); } // Check if the file data table should be created if ( isDataEnabled() && foundData == false && hasDataTableName()) { // Create the file data table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getDataTableName() + " (FileId INTEGER NOT NULL, StreamId INTEGER NOT NULL, FragNo INTEGER, FragLen INTEGER, Data BLOB (512K), JarFile CHAR DEFAULT '0', JarId INTEGER)"); stmt.execute("CREATE INDEX IDataFileStreamId ON " + getDataTableName() + "(FileId,StreamId)"); stmt.execute("CREATE INDEX IDataFileId ON " + getDataTableName() + "(FileId)"); stmt.execute("CREATE INDEX IDataFileIdFrag ON " + getDataTableName() + "(FileId,FragNo)"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getDataTableName()); } // Check if the Jar file data table should be created if ( isJarDataEnabled() && foundJarData == false && hasJarDataTableName()) { // Create the Jar file data table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getJarDataTableName() + " (JarId INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, Data BLOB (512K))"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getJarDataTableName()); } // Check if the file id/object id mapping table should be created if ( isObjectIdEnabled() && foundObjId == false && hasObjectIdTableName()) { // Create the file id/object id mapping table Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE " + getObjectIdTableName() + " (FileId INTEGER NOT NULL PRIMARY KEY, StreamId INTEGER NOT NULL, ObjectId VARCHAR(128))"); stmt.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created table " + getObjectIdTableName()); } } catch (Exception ex) { Debug.println("[Derby] Error: " + ex.toString()); } finally { // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Check if a file/folder exists * * @param dirId int * @param fname String * @return FileStatus.NotExist, FileStatus.FileExists or FileStatus.DirectoryExists * @throws DBException */ public int fileExists(int dirId, String fname) throws DBException { // Check if the file exists, and whether it is a file or folder int sts = FileStatus.NotExist; Connection conn = null; Statement stmt = null; try { // Get a connection to the database, create a statement for the database lookup conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT FileName,DirectoryFile FROM " + getFileSysTableName() + " WHERE DirId = " + dirId + " AND FileName = '" + checkNameForSpecialChars(fname) + "'"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] File exists SQL: " + sql); // Search for the file/folder ResultSet rs = stmt.executeQuery(sql); // Check if a file record exists if (rs.next()) { // Check if the record is for a file or folder if ( rs.getBoolean("DirectoryFile") == true) sts = FileStatus.DirectoryExists; else sts = FileStatus.FileExists; } // Close the result set rs.close(); } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] File exists error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the status return sts; } /** * Create a file record for a new file or folder * * @param fname String * @param dirId int * @param params FileOpenParams * @param retain boolean * @return int * @exception DBException * @exception FileExistsException */ public int createFileRecord(String fname, int dirId, FileOpenParams params, boolean retain) throws DBException, FileExistsException { // Create a new file record for a file/folder and return a unique file id Connection conn = null; PreparedStatement pstmt = null; Statement stmt = null; int fileId = -1; try { // Get a database connection conn = getConnection(); // Check if the file already exists in the database stmt = conn.createStatement(); String qsql = "SELECT FileName FROM " + getFileSysTableName() + " WHERE FileName = '" + checkNameForSpecialChars(fname) + "' AND DirId = " + dirId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Create file SQL: " + qsql); // Check if the file/folder already exists ResultSet rs = stmt.executeQuery(qsql); if (rs.next()) throw new FileExistsException(); // Check if a file or folder record should be created boolean dirRec = params.isDirectory(); // Get a statement Timestamp timeNow = new Timestamp(System.currentTimeMillis()); pstmt = conn.prepareStatement("INSERT INTO " + getFileSysTableName() + "(FileName,CreateDate,ModifyDate,AccessDate,DirId,DirectoryFile,ReadOnlyFile," + "ArchivedFile,SystemFile,HiddenFile,FileSize,OwnerGid,OwnerUid,FileMode)" + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); pstmt.setString(1,fname); pstmt.setTimestamp(2, timeNow); pstmt.setTimestamp(3, timeNow); pstmt.setTimestamp(4, timeNow); pstmt.setInt(5,dirId); pstmt.setBoolean(6,dirRec); pstmt.setBoolean(7,FileAttribute.hasAttribute(params.getAttributes(),FileAttribute.ReadOnly)); pstmt.setBoolean(8,FileAttribute.hasAttribute(params.getAttributes(),FileAttribute.Archive)); pstmt.setBoolean(9,FileAttribute.hasAttribute(params.getAttributes(),FileAttribute.System)); pstmt.setBoolean(10,FileAttribute.hasAttribute(params.getAttributes(),FileAttribute.Hidden)); pstmt.setInt(11, 0); pstmt.setInt(12, params.hasGid() ? params.getGid() : 0); pstmt.setInt(13, params.hasUid() ? params.getUid() : 0); pstmt.setInt(14, params.hasMode() ? params.getMode() : 0); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Create file SQL: " + pstmt.toString()); // Create an entry for the new file if (pstmt.executeUpdate() > 0) { // Get the last insert id ResultSet rs2 = stmt.executeQuery("SELECT FileId FROM " + getFileSysTableName() + " WHERE FileName = '" + fname + "' AND DirId = " + dirId); if ( rs2.next()) fileId = rs2.getInt(1); // Check if the returned file id is valid if ( fileId == -1) throw new DBException("Failed to get file id for " + fname); // If retention is enabled then create a retention record for the new file/folder if ( retain == true && isRetentionEnabled()) { // Create a retention record for the new file/directory Timestamp startDate = new Timestamp(System.currentTimeMillis()); Timestamp endDate = new Timestamp(startDate.getTime() + getRetentionPeriod()); String rSql = "INSERT INTO " + getRetentionTableName() + " (FileId,StartDate,EndDate) VALUES (" + fileId + ",'" + startDate.toString() + "','" + endDate.toString() + "')"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Add retention record SQL: " + rSql); // Add the retention record for the file/folder stmt.executeUpdate(rSql); } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Created file name=" + fname + ", dirId=" + dirId + ", fileId=" + fileId); } } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Create file record error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the prepared statement if (pstmt != null) { try { pstmt.close(); } catch (Exception ex) { } } // Close the query statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the allocated file id return fileId; } /** * Create a stream record for a new file stream * * @param sname String * @param fid int * @return int * @exception DBException */ public int createStreamRecord(String sname, int fid) throws DBException { // Create a new file stream attached to the specified file Connection conn = null; PreparedStatement stmt = null; Statement stmt2 = null; int streamId = -1; try { // Get a database connection conn = getConnection(); // Get a statement Timestamp timeNow = new Timestamp(System.currentTimeMillis()); stmt = conn.prepareStatement("INSERT INTO " + getFileSysTableName() + "(FileId,StreamName,CreateDate,ModifyDate,AccessDate,StreamSize) VALUES (?,?,?,?,?,?)"); stmt.setInt(1, fid); stmt.setString(2, sname); stmt.setTimestamp(3, timeNow); stmt.setTimestamp(4, timeNow); stmt.setTimestamp(5, timeNow); stmt.setInt(6, 0); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Create stream SQL: " + stmt.toString()); // Create an entry for the new stream if (stmt.executeUpdate() > 0) { // Get the stream id for the newly created stream stmt2 = conn.createStatement(); ResultSet rs2 = stmt2.executeQuery("SELECT StreamId FROM " + getStreamsTableName() + " WHERE FileId = " + fid + " AND StreamName = '" + sname); if ( rs2.next()) streamId = rs2.getInt(1); rs2.close(); } } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Create file stream error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statements if (stmt != null) { try { stmt.close(); } catch (Exception ex) { } } if ( stmt2 != null) { try { stmt2.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the allocated stream id return streamId; } /** * Delete a file or folder record * * @param dirId int * @param fid int * @param markOnly boolean * @exception DBException */ public void deleteFileRecord(int dirId, int fid, boolean markOnly) throws DBException { // Delete a file record from the database, or mark the file record as deleted Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); // Delete the file entry from the database stmt = conn.createStatement(); String sql = null; if ( markOnly == true) sql = "UPDATE " + getFileSysTableName() + " SET IsDeleted = 'Y' WHERE FileId = " + fid; else sql = "DELETE FROM " + getFileSysTableName() + " WHERE FileId = " + fid; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Delete file SQL: " + sql); // Delete the file/folder, or mark as deleted stmt.executeUpdate(sql); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Delete file error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if (conn != null) releaseConnection(conn); } } /** * Delete a file stream record * * @param fid int * @param stid int * @param markOnly boolean * @exception DBException */ public void deleteStreamRecord(int fid, int stid, boolean markOnly) throws DBException { // Delete a file stream from the database, or mark the stream as deleted Connection conn = null; Statement stmt = null; try { // Get a database connection conn = getConnection(); // Get a statement stmt = conn.createStatement(); String sql = "DELETE FROM " + getStreamsTableName() + " WHERE FileId = " + fid + " AND StreamId = " + stid; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Delete stream SQL: " + sql); // Delete the stream record stmt.executeUpdate(sql); } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Delete stream error: " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if (stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Set file information for a file or folder * * @param dirId int * @param fid int * @param finfo FileInfo * @exception DBException */ public void setFileInformation(int dirId, int fid, FileInfo finfo) throws DBException { // Set file information fields Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); // Build the SQL statement to update the file information settings StringBuffer sql = new StringBuffer(256); sql.append("UPDATE "); sql.append(getFileSysTableName()); sql.append(" SET "); // Check if the file attributes have been updated if ( finfo.hasSetFlag(FileInfo.SetAttributes)) { // Update the basic file attributes sql.append("ReadOnlyFile = "); sql.append(finfo.isReadOnly() ? "'1'" : "'0'"); sql.append(", ArchivedFile ="); sql.append(finfo.isArchived() ? "'1'" : "'0'"); sql.append(", SystemFile = "); sql.append(finfo.isSystem() ? "'1'" : "'0'"); sql.append(", HiddenFile = "); sql.append(finfo.isHidden() ? "'1'" : "'0'"); sql.append(","); } // Check if the file size should be set if ( finfo.hasSetFlag(FileInfo.SetFileSize)) { // Update the file size sql.append("FileSize = "); sql.append(finfo.getSize()); sql.append(","); } // Merge the group id, user id and mode into the in-memory file information if ( finfo.hasSetFlag(FileInfo.SetGid)) { // Update the group id sql.append("OwnerGid = "); sql.append(finfo.getGid()); sql.append(","); } if ( finfo.hasSetFlag(FileInfo.SetUid)) { // Update the user id sql.append("OwnerUid = "); sql.append(finfo.getUid()); sql.append(","); } if ( finfo.hasSetFlag(FileInfo.SetMode)) { // Update the mode sql.append("FileMode = "); sql.append(finfo.getMode()); sql.append(","); } // Check if the access date/time has been set if ( finfo.hasSetFlag(FileInfo.SetAccessDate)) { // Add the SQL to update the access date/time sql.append(" AccessDate = TIMESTAMP('"); sql.append(new Timestamp(finfo.getAccessDateTime())); sql.append("'),"); } // Check if the modify date/time has been set if ( finfo.hasSetFlag(FileInfo.SetModifyDate)) { // Add the SQL to update the modify date/time sql.append(" ModifyDate = TIMESTAMP('"); sql.append(new Timestamp(finfo.getModifyDateTime())); sql.append("'),"); } // Check if the inode change date/time has been set if ( finfo.hasSetFlag(FileInfo.SetChangeDate)) { // Add the SQL to update the change date/time sql.append(" ChangeDate = TIMESTAMP('"); sql.append(new Timestamp(finfo.getChangeDateTime())); sql.append("')"); } // Trim any trailing comma if ( sql.charAt(sql.length() - 1) == ',') sql.setLength(sql.length() - 1); // Complete the SQL request string sql.append(" WHERE FileId = "); sql.append( fid); sql.append(""); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Set file info SQL: " + sql.toString()); // Create the SQL statement stmt = conn.createStatement(); stmt.executeUpdate(sql.toString()); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Set file information error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Set information for a file stream * * @param dirId int * @param fid int * @param stid int * @param sinfo StreamInfo * @exception DBException */ public void setStreamInformation(int dirId, int fid, int stid, StreamInfo sinfo) throws DBException { // Set file stream information fields Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); // Build the SQL statement to update the file information settings StringBuffer sql = new StringBuffer(256); sql.append("UPDATE "); sql.append(getStreamsTableName()); sql.append(" SET "); // Check if the access date/time has been set if ( sinfo.hasSetFlag(StreamInfo.SetAccessDate)) { // Add the SQL to update the access date/time sql.append(" AccessDate = TIMESTAMP('"); sql.append(new Timestamp(sinfo.getAccessDateTime())); sql.append("'),"); } // Check if the modify date/time has been set if ( sinfo.hasSetFlag(StreamInfo.SetModifyDate)) { // Add the SQL to update the modify date/time sql.append(" ModifyDate = TIMESTAMP('"); sql.append(new Timestamp(sinfo.getModifyDateTime())); sql.append("'),"); } // Check if the stream size should be updated if ( sinfo.hasSetFlag(StreamInfo.SetStreamSize)) { // Update the stream size sql.append(" StreamSize = "); sql.append(sinfo.getSize()); } // Trim any trailing comma if ( sql.charAt(sql.length() - 1) == ',') sql.setLength(sql.length() - 1); // Complete the SQL request string sql.append(" WHERE FileId = "); sql.append( fid); sql.append(" AND StreamId = "); sql.append( stid); sql.append(""); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Set stream info SQL: " + sql.toString()); // Create the SQL statement stmt = conn.createStatement(); stmt.executeUpdate(sql.toString()); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Set stream information error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Get the id for a file/folder, or -1 if the file/folder does not exist. * * @param dirId int * @param fname String * @param dirOnly boolean * @param caseLess boolean * @return int * @throws DBException */ public int getFileId(int dirId, String fname, boolean dirOnly, boolean caseLess) throws DBException { // Get the file id for a file/folder int fileId = -1; Connection conn = null; Statement stmt = null; try { // Get a connection to the database, create a statement for the database lookup conn = getConnection(); stmt = conn.createStatement(); // Build the SQL for the file lookup StringBuffer sql = new StringBuffer(128); sql.append("SELECT FileId FROM "); sql.append(getFileSysTableName()); sql.append(" WHERE DirId = "); sql.append(dirId); sql.append(" AND "); // Check if the search is for a directory only if ( dirOnly == true) { // Search for a directory record sql.append(" DirectoryFile = '1' AND "); } // Check if the file name search should be caseless if ( caseLess == true) { // Perform a caseless search sql.append(" UPPER(FileName) = '"); sql.append(fname.toUpperCase()); sql.append("'"); } else { // Perform a case sensitive search sql.append(" FileName = '"); sql.append(fname); sql.append("'"); } // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get file id SQL: " + sql.toString()); // Run the database search ResultSet rs = stmt.executeQuery(sql.toString()); // Check if a file record exists if (rs.next()) { // Get the unique file id for the file or folder fileId = rs.getInt("FileId"); } // Close the result set rs.close(); } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Get file id error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the file id, or -1 if not found return fileId; } /** * Get information for a file or folder * * @param dirId int * @param fid int * @param infoLevel int * @return FileInfo * @exception DBException */ public DBFileInfo getFileInformation(int dirId, int fid, int infoLevel) throws DBException { // Create a SQL select for the required file information StringBuffer sql = new StringBuffer(128); sql.append("SELECT "); // Select fields according to the required information level switch ( infoLevel) { // File name only case DBInterface.FileNameOnly: sql.append("FileName"); break; // File ids and name case DBInterface.FileIds: sql.append("FileName,FileId,DirId"); break; // All file information case DBInterface.FileAll: sql.append("*"); break; // Unknown information level default: throw new DBException("Invalid information level, " + infoLevel); } sql.append(" FROM "); sql.append(getFileSysTableName()); sql.append(" WHERE FileId = "); sql.append(fid); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get file info SQL: " + sql.toString()); // Load the file record Connection conn = null; Statement stmt = null; DBFileInfo finfo = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Load the file record ResultSet rs = stmt.executeQuery(sql.toString()); if (rs != null && rs.next()) { // Create the file informaiton object finfo = new DBFileInfo(); finfo.setFileId(fid); // Load the file information switch ( infoLevel) { // File name only case DBInterface.FileNameOnly: finfo.setFileName(rs.getString("FileName")); break; // File ids and name case DBInterface.FileIds: finfo.setFileName(rs.getString("FileName")); finfo.setDirectoryId(rs.getInt("DirId")); break; // All file information case DBInterface.FileAll: finfo.setFileName(rs.getString("FileName")); finfo.setSize(rs.getLong("FileSize")); finfo.setAllocationSize(finfo.getSize()); finfo.setDirectoryId(rs.getInt("DirId")); // Load the various file date/times Timestamp ts = rs.getTimestamp("CreateDate"); finfo.setCreationDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("ModifyDate"); finfo.setModifyDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("AccessDate"); finfo.setAccessDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("ChangeDate"); finfo.setChangeDateTime( ts != null ? ts.getTime() : 0L); // Build the file attributes flags int attr = 0; if ( rs.getBoolean("ReadOnlyFile") == true) attr += FileAttribute.ReadOnly; if ( rs.getBoolean("SystemFile") == true) attr += FileAttribute.System; if ( rs.getBoolean("HiddenFile") == true) attr += FileAttribute.Hidden; if ( rs.getBoolean("DirectoryFile") == true) attr += FileAttribute.Directory; if ( rs.getBoolean("ArchivedFile") == true) attr += FileAttribute.Archive; finfo.setFileAttributes(attr); // Get the group/owner id finfo.setGid(rs.getInt("OwnerGid")); finfo.setUid(rs.getInt("OwnerUid")); finfo.setMode(rs.getInt("FileMode")); break; } } } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Get file information error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the file information return finfo; } /** * Get information for a file stream * * @param fid int * @param stid int * @param infoLevel int * @return StreamInfo * @exception DBException */ public StreamInfo getStreamInformation(int fid, int stid, int infoLevel) throws DBException { // Create a SQL select for the required stream information StringBuffer sql = new StringBuffer(128); sql.append("SELECT "); // Select fields according to the required information level switch ( infoLevel) { // Stream name only. // // Also used if ids are requested as we already have the ids case DBInterface.StreamNameOnly: case DBInterface.StreamIds: sql.append("StreamName"); break; // All file information case DBInterface.StreamAll: sql.append("*"); break; // Unknown information level default: throw new DBException("Invalid information level, " + infoLevel); } sql.append(" FROM "); sql.append(getStreamsTableName()); sql.append(" WHERE FileId = "); sql.append(fid); sql.append(" AND StreamId = "); sql.append(stid); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get stream info SQL: " + sql.toString()); // Load the stream record Connection conn = null; Statement stmt = null; StreamInfo sinfo = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Load the stream record ResultSet rs = stmt.executeQuery(sql.toString()); if (rs != null && rs.next()) { // Create the stream informaiton object sinfo = new StreamInfo("", fid, stid); // Load the file information switch ( infoLevel) { // Stream name only (or name and ids) case DBInterface.StreamNameOnly: case DBInterface.StreamIds: sinfo.setName(rs.getString("StreamName")); break; // All stream information case DBInterface.FileAll: sinfo.setName(rs.getString("StreamName")); sinfo.setSize(rs.getLong("StreamSize")); // Load the various file date/times Timestamp ts = rs.getTimestamp("CreateDate"); sinfo.setCreationDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("ModifyDate"); sinfo.setModifyDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("AccessDate"); sinfo.setAccessDateTime( ts != null ? ts.getTime() : 0L); break; } } } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Get stream information error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the stream information return sinfo; } /** * Return the list of streams for the specified file * * @param fid int * @param infoLevel int * @return StreamInfoList * @exception DBException */ public StreamInfoList getStreamsList(int fid, int infoLevel) throws DBException { // Create a SQL select for the required stream information StringBuffer sql = new StringBuffer(128); sql.append("SELECT "); // Select fields according to the required information level switch ( infoLevel) { // Stream name only. case DBInterface.StreamNameOnly: sql.append("StreamName"); break; // Stream name and ids case DBInterface.StreamIds: sql.append("StreamName,FileId,StreamId"); break; // All file information case DBInterface.StreamAll: sql.append("*"); break; // Unknown information level default: throw new DBException("Invalid information level, " + infoLevel); } sql.append(" FROM "); sql.append(getStreamsTableName()); sql.append(" WHERE FileId = "); sql.append(fid); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get stream list SQL: " + sql.toString()); // Load the stream record Connection conn = null; Statement stmt = null; StreamInfoList sList = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Load the stream records ResultSet rs = stmt.executeQuery(sql.toString()); sList = new StreamInfoList(); while ( rs.next()) { // Create the stream informaiton object StreamInfo sinfo = new StreamInfo("", fid, -1); // Load the file information switch ( infoLevel) { // Stream name only case DBInterface.StreamNameOnly: sinfo.setName(rs.getString("StreamName")); break; // Stream name and id case DBInterface.StreamIds: sinfo.setName(rs.getString("StreamName")); sinfo.setStreamId(rs.getInt("StreamId")); break; // All stream information case DBInterface.FileAll: sinfo.setName(rs.getString("StreamName")); sinfo.setStreamId(rs.getInt("StreamId")); sinfo.setSize(rs.getLong("StreamSize")); // Load the various file date/times Timestamp ts = rs.getTimestamp("CreateDate"); sinfo.setCreationDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("ModifyDate"); sinfo.setModifyDateTime( ts != null ? ts.getTime() : 0L); ts = rs.getTimestamp("AccessDate"); sinfo.setAccessDateTime( ts != null ? ts.getTime() : 0L); break; } // Add the stream information to the list sList.addStream(sinfo); } } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Get stream list error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the streams list return sList; } /** * Rename a file or folder, may also change the parent directory. * * @param dirId int * @param fid int * @param newName String * @param newDir int * @exception DBException * @exception FileNotFoundException */ public void renameFileRecord(int dirId, int fid, String newName, int newDir) throws DBException, FileNotFoundException { // Rename a file/folder Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Update the file record stmt = conn.createStatement(); String sql = "UPDATE " + getFileSysTableName() + " SET FileName = '" + newName + "', DirId = " + newDir + ", ChangeDate = TIMESTAMP('" + new Timestamp(System.currentTimeMillis()) + "') WHERE FileId = " + fid; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Rename SQL: " + sql.toString()); // Rename the file/folder if ( stmt.executeUpdate(sql) == 0) { // Original file not found throw new FileNotFoundException("" + fid); } } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Rename file error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Rename a file stream * * @param dirId int * @param fid int * @param stid int * @param newName * @exception DBException */ public void renameStreamRecord(int dirId, int fid, int stid, String newName) throws DBException { // TODO Auto-generated method stub } /** * Return the retention period expiry date/time for the specified file, or zero if the file/folder * is not under retention. * * @param dirId int * @param fid int * @return RetentionDetails * @exception DBException */ public RetentionDetails getFileRetentionDetails(int dirId, int fid) throws DBException { // Check if retention is enabled if ( isRetentionEnabled() == false) return null; // Get the retention record for the file/folder Connection conn = null; Statement stmt = null; RetentionDetails retDetails = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Get the retention record, if any retDetails = getRetentionExpiryDateTime(conn, stmt, fid); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Get retention error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the retention details return retDetails; } /** * Start a directory search * * @param dirId int * @param searchPath String * @param attrib int * @param infoLevel int * @param maxRecords int * @return DBSearchContext * @exception DBException */ public DBSearchContext startSearch(int dirId, String searchPath, int attrib, int infoLevel, int maxRecords) throws DBException { // Search for files/folders in the specified folder StringBuffer sql = new StringBuffer(128); sql.append("SELECT * FROM "); sql.append(getFileSysTableName()); sql.append(" WHERE DirId = "); sql.append(dirId); sql.append(" AND IsDeleted = 'N'"); // Split the search path String[] paths = FileName.splitPath(searchPath); // Check if the file name contains wildcard characters WildCard wildCard = null; if ( WildCard.containsWildcards(searchPath)) { // For the '*.*' and '*' wildcards the SELECT will already return all files/directories that are attached to the // parent directory. For 'name.*' and '*.ext' type wildcards we can use the LIKE clause to filter the required // records, for more complex wildcards we will post-process the search using the WildCard class to match the // file names. if (searchPath.endsWith("\\*.*") == false && searchPath.endsWith("\\*") == false) { // Create a wildcard search pattern wildCard = new WildCard(paths[1], true); // Check for a 'name.*' type wildcard if ( wildCard.isType() == WildCard.WILDCARD_EXT) { // Add the wildcard file extension selection clause to the SELECT sql.append(" AND FileName LIKE('"); sql.append(checkNameForSpecialChars(wildCard.getMatchPart())); sql.append("%')"); // Clear the wildcard object, we do not want it to filter the search results wildCard = null; } else if ( wildCard.isType() == WildCard.WILDCARD_NAME) { // Add the wildcard file name selection clause to the SELECT sql.append(" AND FileName LIKE('%"); sql.append(checkNameForSpecialChars(wildCard.getMatchPart())); sql.append("')"); // Clear the wildcard object, we do not want it to filter the search results wildCard = null; } } } else { // Search for a specific file/directory sql.append(" AND FileName = '"); sql.append(checkNameForSpecialChars(paths[1])); sql.append("'"); } // Return directories first sql.append(" ORDER BY DirectoryFile DESC"); // Start the search ResultSet rs = null; Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Start search SQL: " + sql.toString()); // Start the folder search rs = stmt.executeQuery(sql.toString()); } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Start search error " + ex.getMessage()); // Rethrow the exception throw new DBException(ex.toString()); } finally { // Release the database connection if ( conn != null) releaseConnection(conn); } // Create the search context, and return return new DerbySearchContext(rs, wildCard); } /** * Return the used file space, or -1 if not supported. * * @return long */ public long getUsedFileSpace() { // Calculate the total used file space Connection conn = null; Statement stmt = null; long usedSpace = -1L; try { // Get a database connection and statement conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT SUM(FileSize) FROM " + getFileSysTableName(); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get filespace SQL: " + sql); // Calculate the currently used disk space ResultSet rs = stmt.executeQuery(sql); if ( rs.next()) usedSpace = rs.getLong(1); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("[Derby] Get used file space error " + ex.getMessage()); } finally { // Close the prepared statement if (stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the used file space return usedSpace; } /** * Shutdown the database interface, release resources. * * @param context DBDeviceContext */ public void shutdownDatabase(DBDeviceContext context) { // Call the base class super.shutdownDatabase(context); } /** * Get the retention expiry date/time for a file/folder * * @param conn Connection * @param stmt Statement * @param fid int * @return RetentionDetails * @exception SQLException */ private final RetentionDetails getRetentionExpiryDateTime(Connection conn, Statement stmt, int fid) throws SQLException { // Get the retention expiry date/time for the specified file/folder RetentionDetails retDetails = null; String sql = "SELECT StartDate,EndDate FROM " + getRetentionTableName() + " WHERE FileId = " + fid; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get retention expiry SQL: " + sql); // Get the retention record, if any ResultSet rs = stmt.executeQuery(sql); if ( rs.next()) { // Get the retention expiry date Timestamp startDate = rs.getTimestamp("StartDate"); Timestamp endDate = rs.getTimestamp("EndDate"); retDetails = new RetentionDetails(fid, startDate != null ? startDate.getTime() : -1L, endDate.getTime()); } // Return the retention details return retDetails; } /** * Determine if the specified file/folder is still within an active retention period * * @param conn Connection * @param stmt Statement * @param fid int * @return boolean * @exception SQLException */ private final boolean fileHasActiveRetention(Connection conn, Statement stmt, int fid) throws SQLException { // Check if retention is enabled if ( isRetentionEnabled() == false) return false; // Check if the file/folder is within the retention period RetentionDetails retDetails = getRetentionExpiryDateTime(conn, stmt, fid); if ( retDetails == null) return false; // Check if the file/folder is within the retention period return retDetails.isWithinRetentionPeriod(System.currentTimeMillis()); } //***** DBQueueInteface Methods ***** /** * Queue a file request. * * @param req FileRequest * @exception DBException */ public void queueFileRequest(FileRequest req) throws DBException { // Make sure the associated file state stays in memory for a short time, if the queue is small // the request may get processed soon. if ( req instanceof SingleFileRequest) { // Get the request details SingleFileRequest fileReq = (SingleFileRequest) req; try { // Check if the file request queue database connection is valid if ( m_dbConn == null || m_dbConn.isClosed() || m_reqStmt == null || m_reqStmt.getConnection().isClosed()) createQueueStatements(); // Check if the request is part of a transaction, or a standalone request if ( fileReq.isTransaction() == false) { // Write the file request record int recCnt = 0; synchronized ( m_reqStmt) { // Write the file request to the queue database m_reqStmt.clearParameters(); m_reqStmt.setInt(1, fileReq.getFileId()); m_reqStmt.setInt(2, fileReq.getStreamId()); m_reqStmt.setInt(3, fileReq.isType()); m_reqStmt.setString(4, fileReq.getTemporaryFile()); m_reqStmt.setString(5, fileReq.getVirtualPath()); recCnt = m_reqStmt.executeUpdate(); // Retrieve the allocated sequence number if ( recCnt > 0) { // Get the last insert id Statement stmt = m_dbConn.createStatement(); ResultSet rs2 = stmt.executeQuery("VALUES IDENTITY_VAL_LOCAL()"); if ( rs2.next()) fileReq.setSequenceNumber(rs2.getInt(1)); } } } else { // Check if the transaction prepared statement is valid, we may have lost the connection to the // database. if ( m_tranStmt == null || m_tranStmt.getConnection().isClosed()) createQueueStatements(); // Write the transaction file request to the database synchronized ( m_tranStmt) { // Write the request record to the database m_tranStmt.clearParameters(); m_tranStmt.setInt(1, fileReq.getFileId()); m_tranStmt.setInt(2, fileReq.getStreamId()); m_tranStmt.setInt(3, fileReq.isType()); m_tranStmt.setInt(4, fileReq.getTransactionId()); m_tranStmt.setString(5, fileReq.getTemporaryFile()); m_tranStmt.setString(6, fileReq.getVirtualPath()); m_tranStmt.executeUpdate(); } } } catch (SQLException ex) { // If the request is a save then add to a pending queue to retry when the database is back online if ( fileReq.isType() == FileRequest.SAVE || fileReq.isType() == FileRequest.TRANSSAVE) queueOfflineSaveRequest( fileReq); // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException ( ex.getMessage()); } } } /** * Perform a queue cleanup deleting temporary cache files that do not have an associated save or transaction * request. * * @param tempDir File * @param tempDirPrefix String * @param tempFilePrefix String * @param jarFilePrefix String * @return FileRequestQueue * @throws DBException */ public FileRequestQueue performQueueCleanup(File tempDir, String tempDirPrefix, String tempFilePrefix, String jarFilePrefix) throws DBException { // Get a connection to the database Connection conn = null; PreparedStatement stmt = null; Statement delStmt = null; int reqCnt = 0; try { // Get a connection to the database conn = getConnection(DBConnectionPool.PermanentLease); // Delete all load requests from the queue delStmt = conn.createStatement(); delStmt.executeUpdate("DELETE FROM " + getQueueTableName() + " WHERE ReqType = " + FileRequest.LOAD); // Create a statement to check if a temporary file is part of a save request stmt = conn.prepareStatement("SELECT FileId,SeqNo FROM " + getQueueTableName() + " WHERE TempFile = ?"); // Scan all files/sub-directories within the temporary area looking for files that have been saved but not // deleted due to a server shutdown or crash. File[] tempFiles = tempDir.listFiles(); if ( tempFiles != null && tempFiles.length > 0) { // Scan the file loader sub-directories for temporary files for ( int i = 0; i < tempFiles.length; i++) { // Get the current file/sub-directory File curFile = tempFiles[i]; if ( curFile.isDirectory() && curFile.getName().startsWith(tempDirPrefix)) { // Check if the sub-directory has any loader temporary files File[] subFiles = curFile.listFiles(); if ( subFiles != null && subFiles.length > 0) { // Check each file to see if it has a pending save request in the file request database for ( int j = 0; j < subFiles.length; j++) { // Get the current file from the list File ldrFile = subFiles[j]; if ( ldrFile.isFile() && ldrFile.getName().startsWith(tempFilePrefix)) { try { // Get the file details from the file system table stmt.clearParameters(); stmt.setString(1, ldrFile.getAbsolutePath()); ResultSet rs = stmt.executeQuery(); if ( rs.next()) { // Update the recovery file count reqCnt++; } else { // There is no pending save for the temporary file, delete it ldrFile.delete(); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Deleted unqueued file " + ldrFile.getName()); } } catch (SQLException ex) { Debug.println(ex); } } else { // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Deleted temporary file " + ldrFile.getName()); // Delete the temporary file ldrFile.delete(); } } } } } } } catch (SQLException ex) { // DEBUG if (Debug.EnableError && hasDebug()) Debug.println(ex); } finally { // Close the delete statment if (delStmt != null) { try { delStmt.close(); } catch (Exception ex) { } } // Close the prepared statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Cleanup recovered " + reqCnt + " file saves from previous run"); // Return the recovered file save requests // // TODO: Implement recovery logic return null; } /** * Check if the specified temporary file has a queued request. * * @param tempFile String * @param lastFile boolean * @return boolean * @exception DBException */ public boolean hasQueuedRequest(String tempFile, boolean lastFile) throws DBException { Connection conn = null; Statement stmt = null; boolean queued = false; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT FileId FROM " + getQueueTableName() + " WHERE TempFile = '" + tempFile + "'"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Has queued req SQL: " + sql); // Check if there is a queued request using the temporary file ResultSet rs = stmt.executeQuery(sql); if ( rs.next()) queued = true; else { // Check if there is a transaction using the temporary file sql = "SELECT FileId FROM " + getTransactionTableName() + " WHERE TempFile = '" + tempFile + "'"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Has queued req SQL: " + sql); // Check the transaction table rs = stmt.executeQuery(sql); if ( rs.next()) queued = true; } } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the queued status return queued; } /** * Delete a file request from the pending queue. * * @param fileReq FileRequest * @exception DBException */ public void deleteFileRequest(FileRequest fileReq) throws DBException { Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Delete the file request queue entry from the request table or multiple records from the // transaction table if ( fileReq instanceof SingleFileRequest) { // Get the single file request details SingleFileRequest singleReq = (SingleFileRequest) fileReq; // Delete the request record stmt.executeUpdate("DELETE FROM " + getQueueTableName() + " WHERE SeqNo = " + singleReq.getSequenceNumber()); } else { // Delete the transaction records stmt.executeUpdate("DELETE FROM " + getTransactionTableName() + " WHERE TranId = " + fileReq.getTransactionId()); } } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Load a block of file request from the database into the specified queue. * * @param fromSeqNo int * @param reqType int * @param reqQueue FileRequestQueue * @param recLimit int * @return int * @exception DBException */ public int loadFileRequests(int fromSeqNo, int reqType, FileRequestQueue reqQueue, int recLimit) throws DBException { // Load a block of file requests from the loader queue Connection conn = null; Statement stmt = null; int recCnt = 0; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Build the SQL to load the queue records String sql = "SELECT * FROM " + getQueueTableName() + " WHERE SeqNo > " + fromSeqNo + " AND ReqType = " + reqType + " ORDER BY SeqNo"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Load file requests - " + sql); // Get a block of file request records ResultSet rs = stmt.executeQuery(sql); while ( rs.next()) { // Get the file request details int fid = rs.getInt("FileId"); int stid = rs.getInt("StreamId"); int reqTyp = rs.getInt("ReqType"); int seqNo = rs.getInt("SeqNo"); String tempPath = rs.getString("TempFile"); String virtPath = rs.getString("VirtualPath"); // Recreate the file request for the in-memory queue SingleFileRequest fileReq = new SingleFileRequest(reqTyp, fid, stid, tempPath, virtPath, seqNo, null); // Add the request to the callers queue reqQueue.addRequest(fileReq); // Update the count of loaded requests recCnt++; } } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the count of file requests loaded return recCnt; } /** * Load a transaction request from the queue. * * @param tranReq MultiplFileRequest * @return MultipleFileRequest * @exception DBException */ public MultipleFileRequest loadTransactionRequest(MultipleFileRequest tranReq) throws DBException { // Load a transaction request from the transaction loader queue Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT * FROM " + getTransactionTableName() + " WHERE TranId = " + tranReq.getTransactionId(); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Load trans request - " + sql); // Get the block of file request records for the current transaction ResultSet rs = stmt.executeQuery(sql); while ( rs.next()) { // Get the file request details int fid = rs.getInt("FileId"); int stid = rs.getInt("StreamId"); String tempPath = rs.getString("TempFile"); String virtPath = rs.getString("VirtualPath"); // Create the cached file information and add to the request CachedFileInfo finfo = new CachedFileInfo(fid,stid,tempPath,virtPath); tranReq.addFileInfo(finfo); } } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the updated file request return tranReq; } /** * Create the prepared statements used by the file request queueing database * * @exception SQLException */ protected final void createQueueStatements() throws SQLException { // Check if the database connection is valid if ( m_dbConn != null) { // Close the existing statements if ( m_reqStmt != null) m_reqStmt.close(); if ( m_tranStmt != null) m_tranStmt.close(); // Release the current database connection releaseConnection(m_dbConn); m_dbConn = null; } if ( m_dbConn == null) m_dbConn = getConnection(DBConnectionPool.PermanentLease); // Create the prepared statements for accessing the file request queue database m_reqStmt = m_dbConn.prepareStatement("INSERT INTO " + getQueueTableName() + "(FileId,StreamId,ReqType,TempFile,VirtualPath) VALUES (?,?,?,?,?)"); // Create the prepared statements for accessing the transaction request queue database m_tranStmt = m_dbConn.prepareStatement("INSERT INTO " + getTransactionTableName() + "(FileId,StreamId,ReqType,TranId,TempFile,VirtualPath) VALUES (?,?,?,?,?,?)"); } //***** DBDataInterface Methods ***** /** * Return the file data details for the specified file or stream. * * @param fileId * @param streamId * @return DBDataDetails * @throws DBException */ public DBDataDetails getFileDataDetails(int fileId, int streamId) throws DBException { // Load the file details from the data table Connection conn = null; Statement stmt = null; DBDataDetails dbDetails = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT * FROM " + getDataTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId + " AND FragNo = 1"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Get file data details SQL: " + sql); // Load the file details ResultSet rs = stmt.executeQuery(sql); if ( rs.next()) { // Create the file details dbDetails = new DBDataDetails(fileId, streamId); if ( rs.getBoolean("JarFile") == true) dbDetails.setJarId(rs.getInt("JarId")); } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("[Derby] Get file data details " + dbDetails); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // If the file details are not valid throw an exception if ( dbDetails == null) throw new DBException("Failed to load file details for " + fileId + ":" + streamId); // Return the file data details return dbDetails; } /** * Return the maximum data fragment size supported * * @return long */ public long getMaximumFragmentSize() { return 20 * MemorySize.MEGABYTE; } /** * Load file data from the database into a temporary/local file * * @param fileId int * @param streamId int * @param fileSeg FileSegment * @throws DBException * @throws IOException */ public void loadFileData(int fileId, int streamId, FileSegment fileSeg) throws DBException, IOException { // Open the temporary file FileOutputStream fileOut = new FileOutputStream(fileSeg.getTemporaryFile()); // Update the segment status fileSeg.setStatus(FileSegmentInfo.Loading); // DEBUG long startTime = 0L; if ( Debug.EnableInfo && hasDebug()) startTime = System.currentTimeMillis(); // Load the file data fragments Connection conn = null; Statement stmt = null; try { // Get a connection to the database, create a statement conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT * FROM " + getDataTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId + " ORDER BY FragNo"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Load file data SQL: " + sql); // Find the data fragments for the file, check if the file is stored in a Jar ResultSet rs = stmt.executeQuery(sql); // Load the file data from the main file record(s) byte[] inbuf = null; int fragNo = -1; int fragSize = -1; long totLen = 0L; while ( rs.next()) { // Access the file data fragNo = rs.getInt("FragNo"); fragSize = rs.getInt("FragLen"); // We must access the BLOB input stream directly, do not get the Blob and call getBinaryStream() as it // does not work in Cloudscape/Derby InputStream dataFrag = rs.getBinaryStream("Data"); // Allocate the read buffer, if not already allocated if ( inbuf == null) inbuf = new byte[BlobReadBuffer]; // Read the data from the database record and write to the output file int rdLen = dataFrag.read(inbuf); while ( rdLen > 0) { // Write a block of data to the temporary file segment fileOut.write(inbuf, 0, rdLen); totLen += rdLen; // Read another block of data rdLen = dataFrag.read(inbuf); } // Signal to waiting threads that data is available fileSeg.setReadableLength(totLen); fileSeg.signalDataAvailable(); // Renew the lease on the database connection getConnectionPool().renewLease(conn); } // Close the resultset rs.close(); // DEBUG if ( Debug.EnableInfo && hasDebug()) { long endTime = System.currentTimeMillis(); Debug.println("[Derby] Loaded fid=" + fileId + ", stream=" + streamId + ", frags=" + fragNo + ", time=" + (endTime-startTime) + "ms"); } } catch ( SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Check if a statement was allocated if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); // Close the output file if ( fileOut != null) { try { fileOut.close(); } catch (Exception ex) { Debug.println(ex); } } } // Signal that the file data is available fileSeg.signalDataAvailable(); } /** * Load Jar file data from the database into a temporary file * * @param jarId int * @param jarSeg FileSegment * @throws DBException * @throws IOException */ public void loadJarData(int jarId, FileSegment jarSeg) throws DBException, IOException { // Load the Jar file data Connection conn = null; Statement stmt = null; FileOutputStream outJar = null; try { // Get a connection to the database, create a statement conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT * FROM " + getJarDataTableName() + " WHERE JarId = " + jarId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Load Jar data SQL: " + sql); // Create the temporary Jar file outJar = new FileOutputStream(jarSeg.getTemporaryFile()); // Get the Jar data record ResultSet rs = stmt.executeQuery(sql); if ( rs.next()) { // Access the Jar file data InputStream dataFrag = rs.getBinaryStream("Data"); // Allocate the read buffer byte[] inbuf = new byte[BlobReadBuffer]; // Read the Jar data from the database record and write to the output file int rdLen = dataFrag.read(inbuf); long totLen = 0L; while ( rdLen > 0) { // Write a block of data to the temporary file segment outJar.write(inbuf, 0, rdLen); totLen += rdLen; // Read another block of data rdLen = dataFrag.read(inbuf); } } // Close the output Jar file outJar.close(); // Set the Jar file segment status to indicate that the data has been loaded jarSeg.setStatus(FileSegmentInfo.Available, false); } catch ( SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Check if a statement was allocated if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); // Close the output file if ( outJar != null) { try { outJar.close(); } catch (Exception ex) { Debug.println(ex); } } } } /** * Save the file data from the temporary/local file to the database * * @param fileId int * @param streamId int * @param fileSeg FileSegment * @return int * @throws DBException * @throws IOException */ public int saveFileData(int fileId, int streamId, FileSegment fileSeg) throws DBException, IOException { // Determine if we can use an in memory buffer to copy the file fragments boolean useMem = false; byte[] memBuf = null; if ( getDataFragmentSize() <= MaxMemoryBuffer) { // Use a memory buffer to copy the file data fragments useMem = true; memBuf = new byte[(int) getDataFragmentSize()]; } // Get the temporary file size File tempFile = new File( fileSeg.getTemporaryFile()); // Save the file data Connection conn = null; Statement delStmt = null; PreparedStatement stmt = null; int fragNo = 1; FileInputStream inFile = null; try { // Open the temporary file inFile = new FileInputStream(tempFile); // Get a connection to the database conn = getConnection(); delStmt = conn.createStatement(); String sql = "DELETE FROM " + getDataTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Save file data SQL: " + sql); // Delete any existing file data records for this file int recCnt = delStmt.executeUpdate(sql); delStmt.close(); delStmt = null; // Turn off auto-commit whilst we save the data conn.setAutoCommit(false); // Add the file data to the database stmt = conn.prepareStatement("INSERT INTO " + getDataTableName() + " (FileId,StreamId,FragNo,FragLen,Data,JarFile,JarId) VALUES (?,?,?,?,?,'0',0)"); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Save file data SQL: " + stmt.toString()); long saveSize = tempFile.length(); while ( saveSize > 0) { // Determine the current fragment size to store long fragSize = Math.min(saveSize, getDataFragmentSize()); // Determine if the data fragment should be copied to a memory buffer or a seperate temporary file InputStream fragStream = null; if ( saveSize == fragSize) { // Just copy the data from the temporary file, only one fragment fragStream = inFile; } else if ( useMem == true) { // Copy a block of data to the memory buffer fragSize = inFile.read(memBuf); fragStream = new ByteArrayInputStream(memBuf); } else { // Need to create a temporary file and copy the fragment of data to it throw new DBException("File data copy not implemented yet"); } // Store the current fragment stmt.clearParameters(); stmt.setInt(1, fileId); stmt.setInt(2, streamId); stmt.setInt(3, fragNo++); stmt.setInt(4, (int) fragSize); stmt.setBinaryStream(5,fragStream,(int) fragSize); if ( stmt.executeUpdate() < 1 && hasDebug()) Debug.println("## Derby Failed to update file data, fid=" + fileId + ", stream=" + streamId + ", fragNo=" + (fragNo - 1)); conn.commit(); // Update the remaining data size to be saved saveSize -= fragSize; // Renew the lease on the database connection so that it does not expire getConnectionPool().renewLease(conn); } } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the delete statement if (delStmt != null) { try { delStmt.close(); } catch (Exception ex) { } } // Close the insert statement if ( stmt != null) { try { stmt.close(); } catch (Exception ex) { } } // Turn auto-commit back on try { conn.setAutoCommit(true); } catch ( Exception ex) { } // Release the database connection if ( conn != null) releaseConnection(conn); // Close the input file if ( inFile != null) { try { inFile.close(); } catch (Exception ex) { } } } // Return the number of data fragments used to save the file data return fragNo; } /** * Save the file data from a Jar file to the database * * @param jarPath String * @param fileList DBDataDetailsList * @return int * @throws DBException * @throws IOException */ public int saveJarData( String jarPath, DBDataDetailsList fileList) throws DBException, IOException { // Write the Jar file to the blob field in the Jar data table Connection conn = null; PreparedStatement istmt = null; Statement stmt = null; int jarId = -1; try { // Get a connection to the database conn = getConnection(); // Open the Jar file File jarFile = new File(jarPath); FileInputStream inJar = new FileInputStream(jarFile); // Add the Jar file data to the database istmt = conn.prepareStatement("INSERT INTO " + getJarDataTableName() + " (Data) VALUES (?)"); // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Save Jar data SQL: " + istmt.toString()); // Set the Jar data field istmt.setBinaryStream(1, inJar,(int) jarFile.length()); if ( istmt.executeUpdate() < 1 && hasDebug()) Debug.println("## Derby Failed to store Jar data"); // Get the unique jar id allocated to the new Jar record stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("VALUES IDENTITY_VAL_LOCAL()"); if ( rs.next()) jarId = rs.getInt(1); // Update the jar id record for each file in the Jar for ( int i = 0; i < fileList.numberOfFiles(); i++) { // Get the current file details DBDataDetails dbDetails = fileList.getFileAt(i); // Add the file data record(s) to the database stmt.executeUpdate("DELETE FROM " + getDataTableName() + " WHERE FileId = " + dbDetails.getFileId() + " AND StreamId = " + dbDetails.getStreamId()); stmt.executeUpdate("INSERT INTO " + getDataTableName() + " (FileId,StreamId,FragNo,JarId,JarFile) VALUES (" + dbDetails.getFileId() + "," + dbDetails.getStreamId() + ", 1," + jarId + ",'1')"); } } catch ( SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Close the insert statement if ( istmt != null) { try { istmt.close(); } catch ( Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the allocated Jar id return jarId; } /** * Delete the file data for the specified file/stream * * @param fileId int * @param streamId int * @throws DBException * @throws IOException */ public void deleteFileData(int fileId, int streamId) throws DBException, IOException { // Delete the file data records for the file or stream Connection conn = null; Statement delStmt = null; try { // Get a connection to the database conn = getConnection(); // Need to delete the existing data delStmt = conn.createStatement(); String sql = null; // Check if the main file stream is being deleted, if so then delete all stream data too if ( streamId == 0) sql = "DELETE FROM " + getDataTableName() + " WHERE FileId = " + fileId; else sql = "DELETE FROM " + getDataTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Delete file data SQL: " + sql); // Delete the file data records int recCnt = delStmt.executeUpdate(sql); // Debug if ( Debug.EnableInfo && hasDebug() && recCnt > 0) Debug.println("[Derby] Deleted file data fid=" + fileId + ", stream=" + streamId + ", records=" + recCnt); } catch (SQLException ex) { // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println(ex); } finally { // Close the delete statement if (delStmt != null) { try { delStmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Delete the file data for the specified Jar file * * @param jarId int * @throws DBException * @throws IOException */ public void deleteJarData(int jarId) throws DBException, IOException { // Delete the data records for the Jar file data Connection conn = null; Statement delStmt = null; try { // Get a connection to the database conn = getConnection(); // Need to delete the existing data delStmt = conn.createStatement(); String sql = "DELETE FROM " + getJarDataTableName() + " WHERE JarId = " + jarId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Delete Jar data SQL: " + sql); // Delete the Jar data records int recCnt = delStmt.executeUpdate(sql); // Debug if ( Debug.EnableInfo && hasDebug() && recCnt > 0) Debug.println("[Derby] Deleted Jar data jarId=" + jarId + ", records=" + recCnt); } catch (SQLException ex) { // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println(ex); } finally { // Close the delete statement if (delStmt != null) { try { delStmt.close(); } catch (Exception ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } //***** DBObjectIdInterface Methods ***** /** * Create a file id to object id mapping * * @param fileId int * @param streamId int * @param objectId String * @exception DBException */ public void saveObjectId(int fileId, int streamId, String objectId) throws DBException { // Create a new file id/object id mapping record Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); // Delete any current mapping record for the object String sql = "DELETE FROM " + getObjectIdTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Save object id SQL: " + sql); // Delete any current mapping record stmt.executeUpdate( sql); // Insert the new mapping record sql = "INSERT INTO " + getObjectIdTableName() + " (FileId,StreamId,ObjectID) VALUES(" + fileId + "," + streamId + ",'" + objectId + "')"; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Save object id SQL: " + sql); // Create the mapping record if ( stmt.executeUpdate(sql) == 0) throw new DBException("Failed to add object id record, fid=" + fileId + ", objId=" + objectId); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Load the object id for the specified file id * * @param fileId int * @param streamId int * @return String * @exception DBException */ public String loadObjectId(int fileId, int streamId) throws DBException { // Load the object id for the specified file id Connection conn = null; Statement stmt = null; String objectId = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT ObjectId FROM " + getObjectIdTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Load object id SQL: " + sql); // Load the mapping record ResultSet rs = stmt.executeQuery(sql); if ( rs.next()) objectId = rs.getString("ObjectId"); else throw new DBException("Failed to load object id for " + fileId); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } // Return the object id return objectId; } /** * Delete a file id/object id mapping * * @param fileId int * @param streamId int * @param objectId String * @exception DBException */ public void deleteObjectId(int fileId, int streamId, String objectId) throws DBException { // Delete a file id/object id mapping record Connection conn = null; Statement stmt = null; try { // Get a connection to the database conn = getConnection(); stmt = conn.createStatement(); String sql = "DELETE FROM " + getObjectIdTableName() + " WHERE FileId = " + fileId + " AND StreamId = " + streamId; // DEBUG if ( Debug.EnableInfo && hasSQLDebug()) Debug.println("[Derby] Delete object id SQL: " + sql); // Delete the mapping record stmt.executeUpdate(sql); } catch (SQLException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); // Rethrow the exception throw new DBException(ex.getMessage()); } finally { // Close the statement if ( stmt != null) { try { stmt.close(); } catch (SQLException ex) { } } // Release the database connection if ( conn != null) releaseConnection(conn); } } /** * Return the data for a symbolic link * * @param dirId int * @param fid int * @return String * @exception DBException */ public String readSymbolicLink( int dirId, int fid) throws DBException { throw new DBException( "Symbolic links not supported"); } /** * Delete a symbolic link record * * @param dirId int * @param fid int * @exception DBException */ public void deleteSymbolicLinkRecord( int dirId, int fid) throws DBException { throw new DBException( "Symbolic links not supported"); } }