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 ; 058 059 private static final Logger ourLog = LoggerFactory.getLogger(DriverTypeEnum.class); 060 private String myDriverClassName; 061 private boolean myDerby; 062 063 /** 064 * Constructor 065 */ 066 DriverTypeEnum(String theDriverClassName, boolean theDerby) { 067 myDriverClassName = theDriverClassName; 068 myDerby = theDerby; 069 } 070 071 public static DriverTypeEnum fromDriverClassName(String theDriverClassName) { 072 for (DriverTypeEnum driverTypeEnum : DriverTypeEnum.values()) { 073 if (driverTypeEnum.myDriverClassName.equals(theDriverClassName)) { 074 return driverTypeEnum; 075 } 076 } 077 return null; 078 } 079 080 public String getDriverClassName() { 081 return myDriverClassName; 082 } 083 084 public String getSchemaFilename() { 085 String retval; 086 switch (this) { 087 case H2_EMBEDDED: 088 retval = "h2.sql"; 089 break; 090 case DERBY_EMBEDDED: 091 retval = "derbytenseven.sql"; 092 break; 093 case MYSQL_5_7: 094 case MARIADB_10_1: 095 retval = "mysql57.sql"; 096 break; 097 case POSTGRES_9_4: 098 retval = "hapifhirpostgres94.sql"; 099 break; 100 case ORACLE_12C: 101 retval = "oracle12c.sql"; 102 break; 103 case MSSQL_2012: 104 retval = "sqlserver2012.sql"; 105 break; 106 default: 107 throw new ConfigurationException(Msg.code(45) + "No schema initialization script available for driver " + this); 108 } 109 return retval; 110 } 111 112 public ConnectionProperties newConnectionProperties(String theUrl, String theUsername, String thePassword) { 113 114 BasicDataSource dataSource = new BasicDataSource() { 115 @Override 116 public Connection getConnection() throws SQLException { 117 ourLog.debug("Creating new DB connection"); 118 return super.getConnection(); 119 } 120 }; 121 dataSource.setDriverClassName(myDriverClassName); 122 dataSource.setUrl(theUrl); 123 dataSource.setUsername(theUsername); 124 dataSource.setPassword(thePassword); 125 126 // A check for WS-2020-0287 127 assert dataSource.getJmxName() == null; 128 129 return newConnectionProperties(dataSource); 130 } 131 132 @Nonnull 133 public ConnectionProperties newConnectionProperties(DataSource theDataSource) { 134 try { 135 Class.forName(myDriverClassName).getConstructor().newInstance(); 136 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { 137 throw new InternalErrorException(Msg.code(46) + "Unable to find driver class: " + myDriverClassName, e); 138 } 139 140 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); 141 transactionManager.setDataSource(theDataSource); 142 transactionManager.afterPropertiesSet(); 143 144 TransactionTemplate txTemplate = new TransactionTemplate(); 145 txTemplate.setTransactionManager(transactionManager); 146 txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 147 txTemplate.afterPropertiesSet(); 148 149 return new ConnectionProperties(theDataSource, txTemplate, this); 150 } 151 152 public static class ConnectionProperties implements AutoCloseable { 153 154 private final DriverTypeEnum myDriverType; 155 private final DataSource myDataSource; 156 private final TransactionTemplate myTxTemplate; 157 158 /** 159 * Constructor 160 */ 161 public ConnectionProperties(DataSource theDataSource, TransactionTemplate theTxTemplate, DriverTypeEnum theDriverType) { 162 Validate.notNull(theDataSource); 163 Validate.notNull(theTxTemplate); 164 Validate.notNull(theDriverType); 165 166 myDataSource = theDataSource; 167 myTxTemplate = theTxTemplate; 168 myDriverType = theDriverType; 169 } 170 171 public DriverTypeEnum getDriverType() { 172 return myDriverType; 173 } 174 175 @Nonnull 176 public DataSource getDataSource() { 177 return myDataSource; 178 } 179 180 @Nonnull 181 public JdbcTemplate newJdbcTemplate() { 182 JdbcTemplate jdbcTemplate = new JdbcTemplate(); 183 jdbcTemplate.setDataSource(myDataSource); 184 return jdbcTemplate; 185 } 186 187 @Nonnull 188 public TransactionTemplate getTxTemplate() { 189 return myTxTemplate; 190 } 191 192 @Override 193 public void close() { 194 if (myDataSource instanceof DisposableBean) { 195 try { 196 ((DisposableBean) myDataSource).destroy(); 197 } catch (Exception e) { 198 ourLog.warn("Could not dispose of driver", e); 199 } 200 } 201 } 202 } 203}