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();
 
  202                 return connectionPool.getConnection();
 
  203             } 
catch (SQLException ex) {
 
  204                 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)