001package io.ebean.migration; 002 003import java.sql.Connection; 004import java.sql.DriverManager; 005import java.sql.SQLException; 006import java.util.HashSet; 007import java.util.Map; 008import java.util.Properties; 009import java.util.Set; 010 011/** 012 * Configuration used to run the migration. 013 */ 014public class MigrationConfig { 015 016 private String migrationPath = "dbmigration"; 017 018 private String metaTable = "db_migration"; 019 020 private String applySuffix = ".sql"; 021 022 private String runPlaceholders; 023 024 private boolean skipChecksum; 025 026 private Map<String, String> runPlaceholderMap; 027 028 private ClassLoader classLoader; 029 030 private String dbUsername; 031 private String dbPassword; 032 private String dbDriver; 033 private String dbUrl; 034 035 private String dbSchema; 036 private boolean createSchemaIfNotExists; 037 private String platformName; 038 039 /** 040 * Versions that we want to insert into migration history without actually running. 041 */ 042 private Set<String> patchInsertOn; 043 044 /** 045 * Versions that we want to update the checksum on without actually running. 046 */ 047 private Set<String> patchResetChecksumOn; 048 049 /** 050 * Return the name of the migration table. 051 */ 052 public String getMetaTable() { 053 return metaTable; 054 } 055 056 /** 057 * Set the name of the migration table. 058 */ 059 public void setMetaTable(String metaTable) { 060 this.metaTable = metaTable; 061 } 062 063 /** 064 * Parse as comma delimited versions. 065 */ 066 private Set<String> parseCommaDelimited(String versionsCommaDelimited) { 067 if (versionsCommaDelimited != null) { 068 Set<String> versions = new HashSet<>(); 069 String[] split = versionsCommaDelimited.split(","); 070 for (String version : split) { 071 if (version.startsWith("R__")) { 072 version = version.substring(3); 073 } 074 versions.add(version); 075 } 076 return versions; 077 } 078 return null; 079 } 080 081 /** 082 * Set the migrations that should have their checksum reset as a comma delimited list. 083 */ 084 public void setPatchResetChecksumOn(String versionsCommaDelimited) { 085 patchResetChecksumOn = parseCommaDelimited(versionsCommaDelimited); 086 } 087 088 /** 089 * Set the migrations that should have their checksum reset. 090 */ 091 public void setPatchResetChecksumOn(Set<String> patchResetChecksumOn) { 092 this.patchResetChecksumOn = patchResetChecksumOn; 093 } 094 095 /** 096 * Return the migrations that should have their checksum reset. 097 */ 098 public Set<String> getPatchResetChecksumOn() { 099 return patchResetChecksumOn; 100 } 101 102 /** 103 * Set the migrations that should not be run but inserted into history as if they have run. 104 */ 105 public void setPatchInsertOn(String versionsCommaDelimited) { 106 patchInsertOn = parseCommaDelimited(versionsCommaDelimited); 107 } 108 109 /** 110 * Set the migrations that should not be run but inserted into history as if they have run. 111 * <p> 112 * This can be useful when we need to pull out DDL from a repeatable migration that should really 113 * only run once. We can pull out that DDL as a new migration and add it to history as if it had been 114 * run (we can only do this when we know it exists in all environments including production). 115 * </p> 116 */ 117 public void setPatchInsertOn(Set<String> patchInsertOn) { 118 this.patchInsertOn = patchInsertOn; 119 } 120 121 /** 122 * Return the migrations that should not be run but inserted into history as if they have run. 123 */ 124 public Set<String> getPatchInsertOn() { 125 return patchInsertOn; 126 } 127 128 /** 129 * Return true if checksum check should be skipped (during development). 130 */ 131 public boolean isSkipChecksum() { 132 return skipChecksum; 133 } 134 135 /** 136 * Set to true to skip the checksum check. 137 * <p> 138 * This is intended for use during development only. 139 * </p> 140 */ 141 public void setSkipChecksum(boolean skipChecksum) { 142 this.skipChecksum = skipChecksum; 143 } 144 145 /** 146 * Return a Comma and equals delimited key/value placeholders to replace in DDL scripts. 147 */ 148 public String getRunPlaceholders() { 149 return runPlaceholders; 150 } 151 152 /** 153 * Set a Comma and equals delimited key/value placeholders to replace in DDL scripts. 154 */ 155 public void setRunPlaceholders(String runPlaceholders) { 156 this.runPlaceholders = runPlaceholders; 157 } 158 159 /** 160 * Return a map of name/value pairs that can be expressions replaced in migration scripts. 161 */ 162 public Map<String, String> getRunPlaceholderMap() { 163 return runPlaceholderMap; 164 } 165 166 /** 167 * Set a map of name/value pairs that can be expressions replaced in migration scripts. 168 */ 169 public void setRunPlaceholderMap(Map<String, String> runPlaceholderMap) { 170 this.runPlaceholderMap = runPlaceholderMap; 171 } 172 173 /** 174 * Return the root path used to find migrations. 175 */ 176 public String getMigrationPath() { 177 return migrationPath; 178 } 179 180 /** 181 * Set the root path used to find migrations. 182 */ 183 public void setMigrationPath(String migrationPath) { 184 this.migrationPath = migrationPath; 185 } 186 187 /** 188 * Return the suffix for migration resources (defaults to .sql). 189 */ 190 public String getApplySuffix() { 191 return applySuffix; 192 } 193 194 /** 195 * Set the suffix for migration resources. 196 */ 197 public void setApplySuffix(String applySuffix) { 198 this.applySuffix = applySuffix; 199 } 200 201 /** 202 * Return the DB username. 203 * <p> 204 * Used when a Connection to run the migration is not supplied. 205 * </p> 206 */ 207 public String getDbUsername() { 208 return dbUsername; 209 } 210 211 /** 212 * Set the DB username. 213 * <p> 214 * Used when a Connection to run the migration is not supplied. 215 * </p> 216 */ 217 public void setDbUsername(String dbUsername) { 218 this.dbUsername = dbUsername; 219 } 220 221 /** 222 * Return the DB password. 223 * <p> 224 * Used when creating a Connection to run the migration. 225 * </p> 226 */ 227 public String getDbPassword() { 228 return dbPassword; 229 } 230 231 /** 232 * Set the DB password. 233 * <p> 234 * Used when creating a Connection to run the migration. 235 * </p> 236 */ 237 public void setDbPassword(String dbPassword) { 238 this.dbPassword = dbPassword; 239 } 240 241 /** 242 * Return the DB Driver. 243 * <p> 244 * Used when creating a Connection to run the migration. 245 * </p> 246 */ 247 public String getDbDriver() { 248 return dbDriver; 249 } 250 251 /** 252 * Set the DB Driver. 253 * <p> 254 * Used when creating a Connection to run the migration. 255 * </p> 256 */ 257 public void setDbDriver(String dbDriver) { 258 this.dbDriver = dbDriver; 259 } 260 261 /** 262 * Return the DB connection URL. 263 * <p> 264 * Used when creating a Connection to run the migration. 265 * </p> 266 */ 267 public String getDbUrl() { 268 return dbUrl; 269 } 270 271 /** 272 * Set the DB connection URL. 273 * <p> 274 * Used when creating a Connection to run the migration. 275 * </p> 276 */ 277 public void setDbUrl(String dbUrl) { 278 this.dbUrl = dbUrl; 279 } 280 281 /** 282 * Return the DB connection Schema. 283 * <p> 284 * Used when creating a Connection to run the migration. 285 * </p> 286 */ 287 public String getDbSchema() { 288 return dbSchema; 289 } 290 291 /** 292 * Set the DB connection Schema. 293 * <p> 294 * Used when creating a Connection to run the migration. 295 * </p> 296 */ 297 public void setDbSchema(String dbSchema) { 298 this.dbSchema = dbSchema; 299 } 300 301 /** 302 * Return true if migration should create the schema if it does not exist. 303 */ 304 public boolean isCreateSchemaIfNotExists() { 305 return createSchemaIfNotExists; 306 } 307 308 /** 309 * Set to create Schema if it does not exist. 310 */ 311 public void setCreateSchemaIfNotExists(boolean createSchemaIfNotExists) { 312 this.createSchemaIfNotExists = createSchemaIfNotExists; 313 } 314 315 /** 316 * Return the DB platform name (used for platform create table and select for update syntax). 317 */ 318 public String getPlatformName() { 319 return platformName; 320 } 321 322 /** 323 * Set a DB platform name (to load specific create table and select for update syntax). 324 */ 325 public void setPlatformName(String platformName) { 326 this.platformName = platformName; 327 } 328 329 /** 330 * Return the ClassLoader to use to load resources. 331 */ 332 public ClassLoader getClassLoader() { 333 if (classLoader == null) { 334 classLoader = Thread.currentThread().getContextClassLoader(); 335 if (classLoader == null) { 336 classLoader = this.getClass().getClassLoader(); 337 } 338 } 339 return classLoader; 340 } 341 342 /** 343 * Set the ClassLoader to use when loading resources. 344 */ 345 public void setClassLoader(ClassLoader classLoader) { 346 this.classLoader = classLoader; 347 } 348 349 /** 350 * Load configuration from standard properties. 351 */ 352 public void load(Properties props) { 353 354 dbUsername = props.getProperty("dbmigration.username", dbUsername); 355 dbPassword = props.getProperty("dbmigration.password", dbPassword); 356 dbDriver = props.getProperty("dbmigration.driver", dbDriver); 357 dbUrl = props.getProperty("dbmigration.url", dbUrl); 358 359 String skip = props.getProperty("dbmigration.skipchecksum"); 360 if (skip != null) { 361 skipChecksum = Boolean.parseBoolean(skip); 362 } 363 platformName = props.getProperty("dbmigration.platformName", platformName); 364 applySuffix = props.getProperty("dbmigration.applySuffix", applySuffix); 365 metaTable = props.getProperty("dbmigration.metaTable", metaTable); 366 migrationPath = props.getProperty("dbmigration.migrationPath", migrationPath); 367 runPlaceholders = props.getProperty("dbmigration.placeholders", runPlaceholders); 368 } 369 370 /** 371 * Create a Connection to the database using the configured driver, url, username etc. 372 * <p> 373 * Used when an existing DataSource or Connection is not supplied. 374 * </p> 375 */ 376 public Connection createConnection() { 377 378 if (dbUsername == null) throw new MigrationException("Database username is null?"); 379 if (dbPassword == null) throw new MigrationException("Database password is null?"); 380 if (dbDriver == null) throw new MigrationException("Database Driver is null?"); 381 if (dbUrl == null) throw new MigrationException("Database connection URL is null?"); 382 383 loadDriver(); 384 385 try { 386 Properties props = new Properties(); 387 props.setProperty("user", dbUsername); 388 props.setProperty("password", dbPassword); 389 return DriverManager.getConnection(dbUrl, props); 390 391 } catch (SQLException e) { 392 throw new MigrationException("Error trying to create Connection", e); 393 } 394 } 395 396 private void loadDriver() { 397 try { 398 ClassLoader contextLoader = getClassLoader(); 399 Class.forName(dbDriver, true, contextLoader); 400 } catch (Throwable e) { 401 throw new MigrationException("Problem loading Database Driver [" + dbDriver + "]: " + e.getMessage(), e); 402 } 403 } 404 405}