19 package org.sleuthkit.autopsy.centralrepository.datamodel;
21 import java.sql.Connection;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.sql.Statement;
25 import java.util.concurrent.TimeUnit;
26 import java.util.logging.Level;
27 import org.apache.commons.dbcp2.BasicDataSource;
28 import org.openide.util.NbBundle.Messages;
36 final class PostgresCentralRepo
extends RdbmsCentralRepo {
38 private final static Logger LOGGER = Logger.
getLogger(PostgresCentralRepo.class.getName());
40 private final static String CONFLICT_CLAUSE =
"ON CONFLICT DO NOTHING";
42 private static PostgresCentralRepo instance;
44 private static final int CONN_POOL_SIZE = 10;
45 private BasicDataSource connectionPool = null;
47 private final PostgresCentralRepoSettings dbSettings;
57 public synchronized static PostgresCentralRepo getInstance() throws CentralRepoException {
58 if (instance == null) {
59 instance =
new PostgresCentralRepo();
71 private PostgresCentralRepo() throws CentralRepoException {
72 dbSettings =
new PostgresCentralRepoSettings();
73 bulkArtifactsThreshold = dbSettings.getBulkThreshold();
77 public void shutdownConnections() throws CentralRepoException {
80 if (connectionPool != null) {
81 connectionPool.close();
82 connectionPool = null;
86 }
catch (SQLException ex) {
87 throw new CentralRepoException(
"Failed to close existing database connections.", ex);
92 public void updateSettings() {
94 dbSettings.loadSettings();
95 bulkArtifactsThreshold = dbSettings.getBulkThreshold();
100 public void saveSettings() {
101 synchronized (
this) {
102 dbSettings.saveSettings();
107 public void reset() throws CentralRepoException {
108 Connection conn = connect();
111 Statement dropContent = conn.createStatement();
112 dropContent.executeUpdate(
"TRUNCATE TABLE organizations RESTART IDENTITY CASCADE");
113 dropContent.executeUpdate(
"TRUNCATE TABLE cases RESTART IDENTITY CASCADE");
114 dropContent.executeUpdate(
"TRUNCATE TABLE data_sources RESTART IDENTITY CASCADE");
115 dropContent.executeUpdate(
"TRUNCATE TABLE reference_sets RESTART IDENTITY CASCADE");
116 dropContent.executeUpdate(
"TRUNCATE TABLE correlation_types RESTART IDENTITY CASCADE");
117 dropContent.executeUpdate(
"TRUNCATE TABLE db_info RESTART IDENTITY CASCADE");
119 String instancesTemplate =
"TRUNCATE TABLE %s_instances RESTART IDENTITY CASCADE";
120 String referencesTemplate =
"TRUNCATE TABLE reference_%s RESTART IDENTITY CASCADE";
121 for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) {
122 dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName()));
124 if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
125 dropContent.executeUpdate(String.format(referencesTemplate, type.getDbTableName()));
128 }
catch (SQLException ex) {
129 LOGGER.log(Level.WARNING,
"Failed to reset database.", ex);
131 CentralRepoDbUtil.closeConnection(conn);
135 RdbmsCentralRepoFactory centralRepoSchemaFactory =
new RdbmsCentralRepoFactory(CentralRepoPlatforms.POSTGRESQL, dbSettings);
136 centralRepoSchemaFactory.insertDefaultDatabaseContent();
143 private void setupConnectionPool() throws CentralRepoException {
144 connectionPool =
new BasicDataSource();
145 connectionPool.setUsername(dbSettings.getUserName());
146 connectionPool.setPassword(dbSettings.getPassword());
147 connectionPool.setDriverClassName(dbSettings.getDriver());
149 StringBuilder connectionURL =
new StringBuilder();
150 connectionURL.append(dbSettings.getJDBCBaseURI());
151 connectionURL.append(dbSettings.getHost());
152 connectionURL.append(
":");
153 connectionURL.append(dbSettings.getPort());
154 connectionURL.append(
"/");
155 connectionURL.append(dbSettings.getDbName());
157 connectionPool.setUrl(connectionURL.toString());
158 connectionPool.setUsername(dbSettings.getUserName());
159 connectionPool.setPassword(dbSettings.getPassword());
162 connectionPool.setInitialSize(5);
163 connectionPool.setMaxIdle(CONN_POOL_SIZE);
164 connectionPool.setValidationQuery(dbSettings.getValidationQuery());
177 protected Connection connect(
boolean foreignKeys)
throws CentralRepoException {
189 @Messages({
"PostgresEamDb.centralRepoDisabled.message=Central Repository module is not enabled.",
190 "PostgresEamDb.connectionFailed.message=Error getting connection to database."})
192 protected Connection connect() throws CentralRepoException {
193 synchronized (
this) {
194 if (!CentralRepository.isEnabled()) {
195 throw new CentralRepoException(
"Central Repository module is not enabled", Bundle.PostgresEamDb_centralRepoDisabled_message());
198 if (connectionPool == null) {
199 setupConnectionPool();
203 return connectionPool.getConnection();
204 }
catch (SQLException ex) {
205 throw new CentralRepoException(
"Error getting connection from connection pool.", Bundle.PostgresEamDb_connectionFailed_message(), ex);
210 protected String getConflictClause() {
211 return CONFLICT_CLAUSE;
215 protected Connection getEphemeralConnection() {
216 return this.dbSettings.getEphemeralConnection(
false);
230 @Messages({
"PostgresEamDb.multiUserLockError.message=Error acquiring database lock"})
231 public CoordinationService.Lock getExclusiveMultiUserDbLock()
throws CentralRepoException {
234 if (!UserPreferences.getIsMultiUserModeEnabled()) {
238 String databaseNodeName = dbSettings.getHost() +
"_" + dbSettings.getDbName();
239 CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CENTRAL_REPO, databaseNodeName, 5, TimeUnit.MINUTES);
244 throw new CentralRepoException(
"Error acquiring database lock", Bundle.PostgresEamDb_multiUserLockError_message());
245 }
catch (InterruptedException ex) {
246 throw new CentralRepoException(
"Error acquiring database lock", Bundle.PostgresEamDb_multiUserLockError_message(), ex);
247 }
catch (CoordinationService.CoordinationServiceException ex) {
254 boolean doesColumnExist(Connection conn, String tableName, String columnName)
throws SQLException {
255 final String objectIdColumnExistsTemplate =
"SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='%s' AND column_name='%s')";
256 ResultSet resultSet = null;
257 Statement statement = null;
258 boolean columnExists =
false;
260 statement = conn.createStatement();
261 resultSet = statement.executeQuery(String.format(objectIdColumnExistsTemplate, tableName, columnName));
262 if (resultSet.next()) {
263 columnExists = resultSet.getBoolean(1);
266 CentralRepoDbUtil.closeResultSet(resultSet);
267 CentralRepoDbUtil.closeStatement(statement);
synchronized static Logger getLogger(String name)