diff --git a/bindings/java/ivy.xml b/bindings/java/ivy.xml index 346f0b45258ee95a5f2437eb8345f211bd622bfa..22edc80b426c8d5c60f8855659fe5781f04787ea 100644 --- a/bindings/java/ivy.xml +++ b/bindings/java/ivy.xml @@ -9,6 +9,7 @@ <dependency org="org.postgresql" name="postgresql" rev="9.4-1201-jdbc41" > <artifact name="postgresql" type="jar" /> </dependency> + <dependency org="com.mchange" name="c3p0" rev="0.9.5" /> </dependencies> </ivy-module> diff --git a/bindings/java/nbproject/project.xml b/bindings/java/nbproject/project.xml index a117606b0a0de183f07c908b3f1ad455b77cd7e8..5e23bd944009d23a25e8f4cc92de7b4cb50fe449 100755 --- a/bindings/java/nbproject/project.xml +++ b/bindings/java/nbproject/project.xml @@ -114,7 +114,7 @@ <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3"> <compilation-unit> <package-root>src</package-root> - <classpath mode="compile">lib;lib/diffutils-1.2.1.jar;lib/junit-4.8.2.jar;lib/sqlite-jdbc-3.7.15-M1.jar;lib/postgresql-9.4-1201.jdbc41.jar</classpath> + <classpath mode="compile">lib;lib/diffutils-1.2.1.jar;lib/junit-4.8.2.jar;lib/sqlite-jdbc-3.7.15-M1.jar;lib/postgresql-9.4-1201.jdbc41.jar;lib/c3p0-0.9.5.jar;lib/mchange-commons-java-0.2.9.jar;lib/c3p0-0.9.5-sources.jar;lib/c3p0-0.9.5-javadoc.jar</classpath> <built-to>build</built-to> <source-level>1.6</source-level> </compilation-unit> diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDbConnectionInfo.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDbConnectionInfo.java index 1a1c7ee387dcdfed67dba65f6b07650e8cd48054..7eecc1707ada0d9ad173a04331d1500729b1d902 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDbConnectionInfo.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDbConnectionInfo.java @@ -102,12 +102,14 @@ public boolean settingsValid() { try { switch (dbType) { case POSTGRESQL: + /// TODO this should be done through the connection pool if we can. Connection conn = DriverManager.getConnection( "jdbc:postgresql://" + this.hostNameOrIP + ":" + this.portNumber + "/postgres", this.userName, this.password); // NON-NLS if (conn != null) { commsEstablished = true; + conn.close(); } break; diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 971c082f6d57e383821e87af4033ffbe00f85721..b2e6fa4e89fb217be99dba1233ab9bc728f3dd84 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -1,7 +1,7 @@ /* * Sleuth Kit Data Model * - * Copyright 2012-2014 Basis Technology Corp. + * Copyright 2012-2015 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,7 +27,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -39,7 +38,6 @@ import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -62,6 +60,9 @@ import org.sleuthkit.datamodel.TskData.DbType; import org.postgresql.util.PSQLException; import org.postgresql.util.PSQLState; +import com.mchange.v2.c3p0.*; +import java.beans.PropertyVetoException; +import org.sqlite.SQLiteDataSource; /** * Represents the case database with methods that provide abstractions for @@ -73,7 +74,7 @@ public class SleuthkitCase { private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle"); - private final ConnectionPerThreadDispenser connections; + private final ConnectionDispenser connections; private final ResultSetHelper rsHelper = new ResultSetHelper(this); private final Map<Long, Long> carvedFileContainersCache = new HashMap<Long, Long>(); // Caches the IDs of the root $CarvedFiles for each volume. private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<Long, FileSystem>(); // Cache for file system results. @@ -170,6 +171,7 @@ private void initBlackboardArtifactTypes() throws SQLException, TskCoreException } finally { closeResultSet(resultSet); closeStatement(statement); + connection.close(); } } @@ -201,6 +203,7 @@ private void initBlackboardAttributeTypes() throws SQLException, TskCoreExceptio } finally { closeResultSet(resultSet); closeStatement(statement); + connection.close(); } } @@ -228,6 +231,7 @@ private void initNextArtifactId() throws TskCoreException, SQLException { } finally { closeResultSet(resultSet); closeStatement(statement); + connection.close(); } } @@ -280,6 +284,7 @@ private void updateDatabaseSchema(String dbPath) throws Exception { } finally { closeResultSet(resultSet); closeStatement(statement); + connection.close(); } } @@ -319,6 +324,7 @@ private void updateSchemaVersion() throws Exception { } finally { closeResultSet(resultSet); closeStatement(statement); + connection.close(); } } @@ -508,6 +514,7 @@ private int updateFromSchema2toSchema3(int schemaVersionNumber) throws SQLExcept closeStatement(updateStatement); closeResultSet(resultSet); closeStatement(statement); + connection.close(); } } @@ -536,6 +543,9 @@ public String getBackupDatabasePath() { * The caller is responsible for calling either commit() or rollback() on * the transaction object. * + * Note that any CaseDbTransaction must call close() to release the database + * connection back to the pool. + * * @return A CaseDbTransaction object. * @throws TskCoreException */ @@ -726,6 +736,7 @@ public List<Content> getRootObjects() throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -757,6 +768,7 @@ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throw new TskCoreException("Error getting or creating a blackboard artifact", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -786,6 +798,7 @@ public long getBlackboardArtifactsCount(long objId) throws TskCoreException { throw new TskCoreException("Error getting number of blackboard artifacts by content", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -815,6 +828,7 @@ public long getBlackboardArtifactsTypeCount(int artifactTypeID) throws TskCoreEx throw new TskCoreException("Error getting number of blackboard artifacts by type", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -869,6 +883,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -910,6 +925,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -944,6 +960,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -978,6 +995,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1012,6 +1030,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1046,6 +1065,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1084,6 +1104,7 @@ public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypes() } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1135,6 +1156,7 @@ public ArrayList<BlackboardAttribute.ATTRIBUTE_TYPE> getBlackboardAttributeTypes } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1167,6 +1189,7 @@ public int getBlackboardAttributeTypesCount() throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1201,6 +1224,7 @@ private ArrayList<BlackboardArtifact> getArtifactsHelper(int artifactTypeID, Str throw new TskCoreException("Error getting or creating a blackboard artifact", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -1234,6 +1258,7 @@ private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws Tsk throw new TskCoreException("Error getting blackboard artifact count", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -1265,6 +1290,7 @@ private ArrayList<BlackboardArtifact> getArtifactsHelper(int artifactTypeID, Str throw new TskCoreException("Error getting or creating a blackboard artifact", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -1418,6 +1444,7 @@ public List<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactTyp } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1448,6 +1475,7 @@ public BlackboardArtifact getBlackboardArtifact(long artifactID) throws TskCoreE throw new TskCoreException("Error getting a blackboard artifact. " + ex.getMessage(), ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -1467,6 +1495,7 @@ public void addBlackboardAttribute(BlackboardAttribute attr, int artifactTypeId) } catch (SQLException ex) { throw new TskCoreException("Error adding blackboard attribute " + attr.toString(), ex); } finally { + connection.close(); releaseExclusiveLock(); } } @@ -1492,6 +1521,7 @@ public void addBlackboardAttributes(Collection<BlackboardAttribute> attributes, connection.rollbackTransaction(); throw new TskCoreException("Error adding blackboard attributes", ex); } finally { + connection.close(); releaseExclusiveLock(); } } @@ -1569,6 +1599,7 @@ public int addAttrType(String attrTypeString, String displayName) throws TskCore } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseExclusiveLock(); } } @@ -1599,6 +1630,7 @@ public int getAttrTypeID(String attrTypeName) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1630,6 +1662,7 @@ public String getAttrTypeString(int attrTypeID) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1661,6 +1694,7 @@ public String getAttrTypeDisplayName(int attrTypeID) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1691,6 +1725,7 @@ public int getArtifactTypeID(String artifactTypeName) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1723,6 +1758,7 @@ String getArtifactTypeString(int artifactTypeID) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1756,6 +1792,7 @@ String getArtifactTypeDisplayName(int artifactTypeID) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1794,6 +1831,7 @@ public int addArtifactType(String artifactTypeName, String displayName) throws T } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseExclusiveLock(); } } @@ -1827,6 +1865,7 @@ public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardAr throw new TskCoreException("Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -1864,6 +1903,7 @@ public ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause) } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1897,6 +1937,7 @@ public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) th } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -1956,6 +1997,7 @@ private BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_ throw new TskCoreException("Error creating a blackboard artifact", ex); } finally { closeResultSet(rs); + connection.close(); releaseExclusiveLock(); } } @@ -1988,6 +2030,7 @@ boolean getContentHasChildren(Content content) throws TskCoreException { throw new TskCoreException("Error checking for children of parent " + content, e); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2020,6 +2063,7 @@ int getContentChildrenCount(Content content) throws TskCoreException { throw new TskCoreException("Error checking for children of parent " + content, e); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2049,6 +2093,7 @@ List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM typ throw new TskCoreException("Error getting AbstractFile children for Content", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2077,6 +2122,7 @@ List<Content> getAbstractFileChildren(Content parent) throws TskCoreException { throw new TskCoreException("Error getting AbstractFile children for Content", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2109,6 +2155,7 @@ List<Long> getAbstractFileChildrenIds(Content parent, TSK_DB_FILES_TYPE_ENUM typ throw new TskCoreException("Error getting AbstractFile children for Content", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2138,6 +2185,7 @@ List<Long> getAbstractFileChildrenIds(Content parent) throws TskCoreException { throw new TskCoreException("Error getting AbstractFile children for Content", ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2185,6 +2233,7 @@ Collection<ObjectInfo> getChildrenInfo(Content c) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -2220,6 +2269,7 @@ ObjectInfo getParentInfo(Content c) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -2255,6 +2305,7 @@ ObjectInfo getParentInfo(long contentId) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -2333,6 +2384,7 @@ public Content getContentById(long id) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -2366,6 +2418,7 @@ String getFilePath(long id) { logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } return filePath; @@ -2400,6 +2453,7 @@ String getFileParentPath(long id) { logger.log(Level.SEVERE, "Error getting file parent_path for file " + id, ex); //NON-NLS } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } return parentPath; @@ -2434,6 +2488,7 @@ String getFileName(long id) { logger.log(Level.SEVERE, "Error getting file parent_path for file " + id, ex); //NON-NLS } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } return fileName; @@ -2477,6 +2532,7 @@ DerivedFile.DerivedMethod getDerivedMethod(long id) throws TskCoreException { } finally { closeResultSet(rs2); closeResultSet(rs1); + connection.close(); releaseSharedLock(); } return method; @@ -2509,6 +2565,7 @@ public AbstractFile getAbstractFileById(long id) throws TskCoreException { throw new TskCoreException("Error getting file by id, id = " + id, ex); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } } @@ -2549,6 +2606,7 @@ private long getFileSystemId(long fileId) { logger.log(Level.SEVERE, "Error checking file system id of a file, id = " + fileId, e); //NON-NLS } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } return ret; @@ -2645,6 +2703,7 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName) throws throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles.exception.msg3.text"), e); } finally { closeResultSet(rs); + connection.close(); releaseSharedLock(); } return files; @@ -2699,13 +2758,8 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName, String } catch (SQLException e) { throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles3.exception.msg3.text"), e); } finally { - if (rs != null) { - try { - rs.close(); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Error closing result set after finding files", ex); //NON-NLS - } - } + closeResultSet(rs); + connection.close(); releaseSharedLock(); } return files; @@ -2731,6 +2785,7 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) localTrans.rollback(); throw ex; } finally { + localTrans.close(); releaseExclusiveLock(); } } @@ -2876,6 +2931,7 @@ public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3102,6 +3158,9 @@ public List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) thr } finally { closeResultSet(rs); closeStatement(s); + if(localTrans!=null) { + localTrans.close(); + } releaseExclusiveLock(); } } else { @@ -3222,6 +3281,7 @@ public DerivedFile addDerivedFile(String fileName, String localPath, throw new TskCoreException("Failed to add derived file to case database", ex); } finally { closeResultSet(rs); + connection.close(); releaseExclusiveLock(); } } @@ -3256,6 +3316,7 @@ public LocalFile addLocalFile(String fileName, String localPath, localTrans.rollback(); throw ex; } finally { + localTrans.close(); releaseExclusiveLock(); } } @@ -3291,7 +3352,7 @@ public LocalFile addLocalFile(String fileName, String localPath, if (trans == null) { throw new TskCoreException("Passed null CaseDbTransaction"); } - + CaseDbConnection connection = connections.getConnection(); acquireExclusiveLock(); ResultSet resultSet = null; try { @@ -3306,7 +3367,6 @@ public LocalFile addLocalFile(String fileName, String localPath, // Insert a row for the local/logical file into the tsk_objects table. // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) - CaseDbConnection connection = connections.getConnection(); PreparedStatement statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS); statement.clearParameters(); statement.setLong(1, parentId); @@ -3371,6 +3431,7 @@ public LocalFile addLocalFile(String fileName, String localPath, throw new TskCoreException("Error adding local file directory " + fileName + " with local path " + localPath, e); } finally { closeResultSet(resultSet); + connection.close(); releaseExclusiveLock(); } } @@ -3431,6 +3492,7 @@ public long countFilesWhere(String sqlWhereClause) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3460,6 +3522,7 @@ public List<AbstractFile> findAllFilesWhere(String sqlWhereClause) throws TskCor } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3492,6 +3555,7 @@ public List<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreExcep } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3524,6 +3588,7 @@ public List<FsContent> findFilesWhere(String sqlWhereClause) throws TskCoreExcep } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3584,6 +3649,7 @@ public List<TskFileRange> getFileRanges(long id) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3624,6 +3690,7 @@ public Image getImageById(long id) throws TskCoreException { closeStatement(s2); closeResultSet(rs1); closeStatement(s1); + connection.close(); releaseSharedLock(); } } @@ -3656,6 +3723,7 @@ VolumeSystem getVolumeSystemById(long id, Image parent) throws TskCoreException } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3752,6 +3820,7 @@ private FileSystem getFileSystemByIdHelper(long id, Content parent) throws TskCo } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3784,6 +3853,7 @@ Volume getVolumeById(long id, VolumeSystem parent) throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3837,6 +3907,7 @@ Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -3908,6 +3979,7 @@ public Collection<FileSystem> getFileSystems(Image image) { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } return fileSystems; @@ -4097,6 +4169,7 @@ public Map<Long, List<String>> getImagePaths() throws TskCoreException { closeStatement(s2); closeResultSet(rs1); closeStatement(s1); + connection.close(); releaseSharedLock(); } } @@ -4128,6 +4201,7 @@ public List<Image> getImages() throws TskCoreException { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -4155,6 +4229,7 @@ public long getLastObjectId() throws TskCoreException { throw new TskCoreException("Error getting last object id", e); } finally { closeResultSet(rs); + connection.close(); releaseExclusiveLock(); } } @@ -4184,6 +4259,7 @@ public void setImagePaths(long obj_id, List<String> paths) throws TskCoreExcepti throw new TskCoreException("Error updating image paths.", ex); } finally { closeStatement(statement); + connection.close(); releaseExclusiveLock(); } } @@ -4295,6 +4371,7 @@ public ResultSet runQuery(String query) throws SQLException { } finally { //TODO unlock should be done in closeRunQuery() //but currently not all code calls closeRunQuery - need to fix this + connection.close(); releaseSharedLock(); } } @@ -4346,10 +4423,10 @@ public void close() { System.err.println(this.hashCode() + " closed"); //NON-NLS System.err.flush(); acquireExclusiveLock(); - connections.close(); fileSystemIdMap.clear(); try { + connections.close(); if (this.caseHandle != null) { this.caseHandle.free(); this.caseHandle = null; @@ -4392,6 +4469,7 @@ public boolean setKnown(AbstractFile file, FileKnown fileKnown) throws TskCoreEx throw new TskCoreException("Error setting Known status.", ex); } finally { closeStatement(statement); + connection.close(); releaseExclusiveLock(); } return true; @@ -4422,6 +4500,7 @@ void setMd5Hash(AbstractFile file, String md5Hash) throws TskCoreException { } catch (SQLException ex) { throw new TskCoreException("Error setting MD5 hash", ex); } finally { + connection.close(); releaseExclusiveLock(); } } @@ -4453,6 +4532,7 @@ public int countFsContentType(TskData.TSK_FS_META_TYPE_ENUM contentType) throws } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -4503,6 +4583,7 @@ public List<AbstractFile> findFilesByMd5(String md5Hash) { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } } @@ -4539,6 +4620,7 @@ public boolean allFilesMd5Hashed() { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } return allFilesAreHashed; @@ -4574,6 +4656,7 @@ public int countFilesMd5Hashed() { } finally { closeResultSet(rs); closeStatement(s); + connection.close(); releaseSharedLock(); } return count; @@ -4652,6 +4735,7 @@ public List<TagName> getAllTagNames() throws TskCoreException { throw new TskCoreException("Error selecting rows from tag_names table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -4682,6 +4766,7 @@ public List<TagName> getTagNamesInUse() throws TskCoreException { throw new TskCoreException("Error selecting rows from tag_names table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -4714,6 +4799,7 @@ public TagName addTagName(String displayName, String description, TagName.HTML_C throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseExclusiveLock(); } } @@ -4750,6 +4836,7 @@ public ContentTag addContentTag(Content content, TagName tagName, String comment throw new TskCoreException("Error adding row to content_tags table (obj_id = " + content.getId() + ", tag_name_id = " + tagName.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseExclusiveLock(); } } @@ -4771,6 +4858,7 @@ public void deleteContentTag(ContentTag tag) throws TskCoreException { } catch (SQLException ex) { throw new TskCoreException("Error deleting row from content_tags table (id = " + tag.getId() + ")", ex); } finally { + connection.close(); releaseExclusiveLock(); } } @@ -4801,6 +4889,7 @@ public List<ContentTag> getAllContentTags() throws TskCoreException { throw new TskCoreException("Error selecting rows from content_tags table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -4835,6 +4924,7 @@ public long getContentTagsCountByTagName(TagName tagName) throws TskCoreExceptio throw new TskCoreException("Error getting content_tags row count for tag name (tag_name_id = " + tagName.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -4872,6 +4962,7 @@ public List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreE throw new TskCoreException("Error getting content_tags rows (tag_name_id = " + tagName.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -4906,6 +4997,7 @@ public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreE throw new TskCoreException("Error getting content tags data for content (obj_id = " + content.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -4940,6 +5032,7 @@ public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifac throw new TskCoreException("Error adding row to blackboard_artifact_tags table (obj_id = " + artifact.getArtifactID() + ", tag_name_id = " + tagName.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseExclusiveLock(); } } @@ -4961,6 +5054,7 @@ public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCor } catch (SQLException ex) { throw new TskCoreException("Error deleting row from blackboard_artifact_tags table (id = " + tag.getId() + ")", ex); } finally { + connection.close(); releaseExclusiveLock(); } } @@ -4994,6 +5088,7 @@ public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCore throw new TskCoreException("Error selecting rows from blackboard_artifact_tags table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -5028,6 +5123,7 @@ public long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskC throw new TskCoreException("Error getting blackboard artifact_content_tags row count for tag name (tag_name_id = " + tagName.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -5066,6 +5162,7 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName ta throw new TskCoreException("Error getting blackboard artifact tags data (tag_name_id = " + tagName.getId() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -5102,6 +5199,7 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(Blackboar throw new TskCoreException("Error getting blackboard artifact tags data (artifact_id = " + artifact.getArtifactID() + ")", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -5157,6 +5255,7 @@ public Report addReport(String localPath, String sourceModuleName, String report throw new TskCoreException("Error adding report " + localPath + " to reports table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseExclusiveLock(); } } @@ -5188,6 +5287,7 @@ public List<Report> getAllReports() throws TskCoreException { throw new TskCoreException("Error querying reports table", ex); } finally { closeResultSet(resultSet); + connection.close(); releaseSharedLock(); } } @@ -5213,83 +5313,109 @@ private static void closeStatement(Statement statement) { } /** - * An abstract base class for objects that store a case database connection - * for each thread that requests one. + * A class for the connection pool. This dispenser will hand out connections + * of the appropriate type based on the subclass that is calling + * getPooledConnection(); */ - private abstract static class ConnectionPerThreadDispenser extends ThreadLocal<CaseDbConnection> { + abstract private static class ConnectionDispenser { - private final HashSet<CaseDbConnection> databaseConnections = new HashSet<CaseDbConnection>(); + protected PooledDataSource pooledDataSource; private boolean isClosed = false; + public ConnectionDispenser() { + pooledDataSource = null; + } + synchronized CaseDbConnection getConnection() throws TskCoreException { if (isClosed) { throw new TskCoreException("Error getting case database connection - case is closed"); } - - CaseDbConnection connection = get(); - if (!connection.isOpen()) { - throw new TskCoreException("Case database connection for current thread is not open"); + try { + return getPooledConnection(); + } catch (SQLException exp) { + throw new TskCoreException(exp.getMessage()); } - databaseConnections.add(connection); - return connection; } - public synchronized void close() { - for (CaseDbConnection entry : databaseConnections) { - entry.close(); - } - databaseConnections.clear(); + public synchronized void close() throws TskCoreException { isClosed = true; + + if (pooledDataSource != null) { + try { + pooledDataSource.close(); + } catch (SQLException exp) { + throw new TskCoreException(exp.getMessage()); + } + } } - @Override - abstract public CaseDbConnection initialValue(); + abstract CaseDbConnection getPooledConnection() throws SQLException; } /** - * Stores a SQLite case database connection for each thread that requests - * one. + * Handles the initial setup of SQLite database connections, as well as + * overriding getPooledConnection() */ - private final static class SQLiteConnections extends ConnectionPerThreadDispenser { + private final static class SQLiteConnections extends ConnectionDispenser { - private final String dbPath; + private static final Map<String, String> configurationOverrides = new HashMap<String, String>(); SQLiteConnections(String dbPath) { - this.dbPath = dbPath; + configurationOverrides.put("acquireIncrement", "10"); + configurationOverrides.put("initialPoolSize", "20"); + configurationOverrides.put("maxPoolSize", "100"); + configurationOverrides.put("minPoolSize", "5"); + configurationOverrides.put("maxStatements", "100"); + configurationOverrides.put("maxStatementsPerConnection", "20"); + + try { + SQLiteConfig config = new SQLiteConfig(); + config.setSynchronous(SQLiteConfig.SynchronousMode.OFF); // Reduce I/O operations, we have no OS crash recovery anyway. + config.setReadUncommited(true); + config.enforceForeignKeys(true); // Enforce foreign key constraints. + SQLiteDataSource unpooled = new SQLiteDataSource(config); + unpooled.setUrl("jdbc:sqlite:" + dbPath); + pooledDataSource = (PooledDataSource) DataSources.pooledDataSource(unpooled, configurationOverrides); + } catch (SQLException ex) { + SleuthkitCase.logger.log(Level.SEVERE, "Error setting up case database connection for thread", ex); //NON-NLS + } } @Override - public CaseDbConnection initialValue() { - return new SQLiteConnection(dbPath); + public CaseDbConnection getPooledConnection() throws SQLException { + return new SQLiteConnection(pooledDataSource.getConnection()); } - } /** - * Stores a PostgreSQL case database connection for each thread that - * requests one. + * Handles the initial setup of PostgreSQL database connections, as well as + * overriding getPooledConnection() */ - private final static class PostgreSQLConnections extends ConnectionPerThreadDispenser { - - private final String host; - private final int port; - private final String dbName; - private final String userName; - private final String password; + private final static class PostgreSQLConnections extends ConnectionDispenser { PostgreSQLConnections(String host, int port, String dbName, String userName, String password) { - this.host = host; - this.port = port; - this.dbName = dbName; - this.userName = userName; - this.password = password; + ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); + try { + comboPooledDataSource.setDriverClass("org.postgresql.Driver"); //loads the jdbc driver + } catch (PropertyVetoException ex) { + Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE, null, ex); + } + comboPooledDataSource.setJdbcUrl("jdbc:postgresql://" + host + ":" + port + "/" + dbName); + comboPooledDataSource.setUser(userName); + comboPooledDataSource.setPassword(password); + comboPooledDataSource.setAcquireIncrement(10); + comboPooledDataSource.setInitialPoolSize(20); + comboPooledDataSource.setMaxPoolSize(100); + comboPooledDataSource.setMinPoolSize(5); + comboPooledDataSource.setMaxStatements(100); + comboPooledDataSource.setMaxStatementsPerConnection(20); + pooledDataSource = comboPooledDataSource; } @Override - public CaseDbConnection initialValue() { - return new PostgreSQLConnection(host, port, dbName, userName, password); + public CaseDbConnection getPooledConnection() throws SQLException { + return new PostgreSQLConnection(pooledDataSource.getConnection()); } - } /** @@ -5303,7 +5429,7 @@ protected interface DbCommand { } static final int SLEEP_LENGTH_IN_MILLISECONDS = 50; - + final static class CreateStatement implements DbCommand { private final Connection connection; @@ -5611,7 +5737,7 @@ PreparedStatement prepareStatement(String sqlStatement, int generateKeys) throws executeCommand(prepareStatement); return prepareStatement.getPreparedStatement(); } - + Statement createStatement() throws SQLException { CreateStatement createStatement = new CreateStatement(this.connection); executeCommand(createStatement); @@ -5691,7 +5817,7 @@ void executeUpdate(Statement statement, String update, int generateKeys) throws ExecuteStatementUpdate executeStatementUpdate = new ExecuteStatementUpdate(statement, update); executeCommand(executeStatementUpdate); } - + void executeUpdate(PreparedStatement statement) throws SQLException { ExecutePreparedStatementUpdate executePreparedStatementUpdate = new ExecutePreparedStatementUpdate(statement); executeCommand(executePreparedStatementUpdate); @@ -5704,7 +5830,7 @@ void close() { try { connection.close(); } catch (SQLException ex) { - logger.log(Level.SEVERE, "Unable to close connection to case database", ex); + logger.log(Level.WARNING, "Unable to close connection to case database", ex); } } @@ -5721,47 +5847,8 @@ private static final class SQLiteConnection extends CaseDbConnection { private static final int DATABASE_LOCKED_ERROR = 0; // This should be 6 according to documentation, but it has been observed to be 0. private static final int SQLITE_BUSY_ERROR = 5; - SQLiteConnection(String dbPath) { - super(createConnection(dbPath)); - } - - static Connection createConnection(String dbPath) { - Connection connection = null; - try { - SQLiteConfig config = new SQLiteConfig(); - - // Reduce I/O operations, we have no OS crash recovery anyway. - config.setSynchronous(SQLiteConfig.SynchronousMode.OFF); - - // The original comment for "read_uncommited" indicating that it - // was being set to "allow query while in transaction". I don't fully - // understand why this is needed since all it does it expose dirty writes - // within one transaction to other queries. There was also the suggestion - // that it may have helped to increase performance. - config.setReadUncommited(true); - - // Enforce foreign key constraints. - config.enforceForeignKeys(true); - - connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath, config.toProperties()); //NON-NLS - } catch (SQLException ex) { - // The exception is caught and logged here because this - // constructor will be called by an override of - // ThreadLocal<T>.initialValue() which cannot throw. Calls to - // ConnectionPerThreadDispenser.getConnection() will detect - // the error state via isOpen() and throw an appropriate - // exception. - SleuthkitCase.logger.log(Level.SEVERE, "Error setting up case database connection for thread", ex); //NON-NLS - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - SleuthkitCase.logger.log(Level.SEVERE, "Failed to close connection", e); - } - connection = null; - } - } - return connection; + SQLiteConnection(Connection conn) { + super(conn); } @Override @@ -5798,9 +5885,9 @@ private static final class PostgreSQLConnection extends CaseDbConnection { private static final String SYSTEM_ERROR = PSQLState.SYSTEM_ERROR.getState(); private static final String UNKNOWN_STATE = PSQLState.UNKNOWN_STATE.getState(); private static final int MAX_RETRIES = 5; - - PostgreSQLConnection(String host, int port, String dbName, String userName, String password) { - super(createConnection(host, port, dbName, userName, password)); + + PostgreSQLConnection(Connection conn) { + super(conn); } @Override @@ -5816,15 +5903,6 @@ PreparedStatement prepareStatement(String sqlStatement, int generateKeys) throws return prepareStatementGenerateKeys.getPreparedStatement(); } - static Connection createConnection(String host, int port, String dbName, String userName, String password) { - try { - return DriverManager.getConnection("jdbc:postgresql://" + host + ":" + port + "/" + dbName, userName, password); // NON-NLS - } catch (SQLException ex) { - Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - @Override void executeCommand(DbCommand command) throws SQLException { for (int retries = 0; retries < MAX_RETRIES; retries++) { @@ -5908,6 +5986,13 @@ public void rollback() throws TskCoreException { } } + /** + * Close the database connection + * + */ + public void close() { + this.connection.close(); + } } /** @@ -5923,14 +6008,12 @@ public void rollback() throws TskCoreException { */ public final class CaseDbQuery implements AutoCloseable { private ResultSet resultSet; + private CaseDbConnection connection; private CaseDbQuery(String query) throws TskCoreException { if (!query.regionMatches(true, 0, "SELECT", 0, "SELECT".length())) { throw new TskCoreException("Unsupported query: Only SELECT queries are supported."); } - - CaseDbConnection connection; - try { connection = connections.getConnection(); } catch (TskCoreException ex) { @@ -5943,7 +6026,7 @@ private CaseDbQuery(String query) throws TskCoreException { } catch (SQLException ex) { - SleuthkitCase.this.releaseSharedLock() ; + SleuthkitCase.this.releaseSharedLock(); throw new TskCoreException("Error executing query: ", ex); } } @@ -5966,7 +6049,7 @@ public void close() throws TskCoreException { } resultSet.close(); } - + connection.close(); SleuthkitCase.this.releaseSharedLock(); } catch (SQLException ex) {