001package ca.uhn.fhir.jpa.migrate; 002 003import ca.uhn.fhir.context.ConfigurationException; 004import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 005import org.apache.commons.dbcp2.BasicDataSource; 006import org.apache.commons.lang3.Validate; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009import org.springframework.beans.factory.DisposableBean; 010import org.springframework.jdbc.core.JdbcTemplate; 011import org.springframework.jdbc.datasource.DataSourceTransactionManager; 012import org.springframework.transaction.TransactionDefinition; 013import org.springframework.transaction.support.TransactionTemplate; 014 015import javax.annotation.Nonnull; 016import javax.sql.DataSource; 017import java.lang.reflect.InvocationTargetException; 018import java.sql.Connection; 019import java.sql.SQLException; 020 021/*- 022 * #%L 023 * HAPI FHIR Server - SQL Migration 024 * %% 025 * Copyright (C) 2014 - 2021 Smile CDR, Inc. 026 * %% 027 * Licensed under the Apache License, Version 2.0 (the "License"); 028 * you may not use this file except in compliance with the License. 029 * You may obtain a copy of the License at 030 * 031 * http://www.apache.org/licenses/LICENSE-2.0 032 * 033 * Unless required by applicable law or agreed to in writing, software 034 * distributed under the License is distributed on an "AS IS" BASIS, 035 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 036 * See the License for the specific language governing permissions and 037 * limitations under the License. 038 * #L% 039 */ 040 041public enum DriverTypeEnum { 042 043 H2_EMBEDDED("org.h2.Driver", false), 044 DERBY_EMBEDDED("org.apache.derby.jdbc.EmbeddedDriver", true), 045 MARIADB_10_1("org.mariadb.jdbc.Driver", false), 046 047 // Formerly com.mysql.jdbc.Driver 048 MYSQL_5_7("com.mysql.cj.jdbc.Driver", false), 049 050 POSTGRES_9_4("org.postgresql.Driver", false), 051 052 ORACLE_12C("oracle.jdbc.OracleDriver", false), 053 054 MSSQL_2012("com.microsoft.sqlserver.jdbc.SQLServerDriver", false), 055 056 ; 057 058 private static final Logger ourLog = LoggerFactory.getLogger(DriverTypeEnum.class); 059 private String myDriverClassName; 060 private boolean myDerby; 061 062 /** 063 * Constructor 064 */ 065 DriverTypeEnum(String theDriverClassName, boolean theDerby) { 066 myDriverClassName = theDriverClassName; 067 myDerby = theDerby; 068 } 069 070 public static DriverTypeEnum fromDriverClassName(String theDriverClassName) { 071 for (DriverTypeEnum driverTypeEnum : DriverTypeEnum.values()) { 072 if (driverTypeEnum.myDriverClassName.equals(theDriverClassName)) { 073 return driverTypeEnum; 074 } 075 } 076 return null; 077 } 078 079 public String getDriverClassName() { 080 return myDriverClassName; 081 } 082 083 public String getSchemaFilename() { 084 String retval; 085 switch (this) { 086 case H2_EMBEDDED: 087 retval = "h2.sql"; 088 break; 089 case DERBY_EMBEDDED: 090 retval = "derbytenseven.sql"; 091 break; 092 case MYSQL_5_7: 093 case MARIADB_10_1: 094 retval = "mysql57.sql"; 095 break; 096 case POSTGRES_9_4: 097 retval = "hapifhirpostgres94.sql"; 098 break; 099 case ORACLE_12C: 100 retval = "oracle12c.sql"; 101 break; 102 case MSSQL_2012: 103 retval = "sqlserver2012.sql"; 104 break; 105 default: 106 throw new ConfigurationException("No schema initialization script available for driver " + this); 107 } 108 return retval; 109 } 110 111 public ConnectionProperties newConnectionProperties(String theUrl, String theUsername, String thePassword) { 112 113 BasicDataSource dataSource = new BasicDataSource() { 114 @Override 115 public Connection getConnection() throws SQLException { 116 ourLog.debug("Creating new DB connection"); 117 return super.getConnection(); 118 } 119 }; 120 dataSource.setDriverClassName(myDriverClassName); 121 dataSource.setUrl(theUrl); 122 dataSource.setUsername(theUsername); 123 dataSource.setPassword(thePassword); 124 125 // A check for WS-2020-0287 126 assert dataSource.getJmxName() == null; 127 128 return newConnectionProperties(dataSource); 129 } 130 131 @Nonnull 132 public ConnectionProperties newConnectionProperties(DataSource theDataSource) { 133 try { 134 Class.forName(myDriverClassName).getConstructor().newInstance(); 135 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { 136 throw new InternalErrorException("Unable to find driver class: " + myDriverClassName, e); 137 } 138 139 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); 140 transactionManager.setDataSource(theDataSource); 141 transactionManager.afterPropertiesSet(); 142 143 TransactionTemplate txTemplate = new TransactionTemplate(); 144 txTemplate.setTransactionManager(transactionManager); 145 txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 146 txTemplate.afterPropertiesSet(); 147 148 return new ConnectionProperties(theDataSource, txTemplate, this); 149 } 150 151 public static class ConnectionProperties implements AutoCloseable { 152 153 private final DriverTypeEnum myDriverType; 154 private final DataSource myDataSource; 155 private final TransactionTemplate myTxTemplate; 156 157 /** 158 * Constructor 159 */ 160 public ConnectionProperties(DataSource theDataSource, TransactionTemplate theTxTemplate, DriverTypeEnum theDriverType) { 161 Validate.notNull(theDataSource); 162 Validate.notNull(theTxTemplate); 163 Validate.notNull(theDriverType); 164 165 myDataSource = theDataSource; 166 myTxTemplate = theTxTemplate; 167 myDriverType = theDriverType; 168 } 169 170 public DriverTypeEnum getDriverType() { 171 return myDriverType; 172 } 173 174 @Nonnull 175 public DataSource getDataSource() { 176 return myDataSource; 177 } 178 179 @Nonnull 180 public JdbcTemplate newJdbcTemplate() { 181 JdbcTemplate jdbcTemplate = new JdbcTemplate(); 182 jdbcTemplate.setDataSource(myDataSource); 183 return jdbcTemplate; 184 } 185 186 @Nonnull 187 public TransactionTemplate getTxTemplate() { 188 return myTxTemplate; 189 } 190 191 @Override 192 public void close() { 193 if (myDataSource instanceof DisposableBean) { 194 try { 195 ((DisposableBean) myDataSource).destroy(); 196 } catch (Exception e) { 197 ourLog.warn("Could not dispose of driver", e); 198 } 199 } 200 } 201 } 202}