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