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