001package com.avaje.ebean.config; 002 003import com.avaje.ebean.EbeanServer; 004import com.avaje.ebean.config.dbplatform.DbPlatformName; 005import com.avaje.ebean.dbmigration.DbMigration; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Configuration for the DB migration processing. 011 */ 012public class DbMigrationConfig { 013 014 protected static final Logger logger = LoggerFactory.getLogger(DbMigrationConfig.class); 015 016 /** 017 * The database platform to generate migration DDL for. 018 */ 019 protected DbPlatformName platform; 020 021 /** 022 * Set to true if the DB migration should be generated on server start. 023 */ 024 protected boolean generate; 025 026 /** 027 * The migration version name (typically FlywayDb compatible). 028 * <p> 029 * Example: 1.1.1_2 030 * <p> 031 * The version is expected to be the combination of the current pom version plus 032 * a 'feature' id. The combined version must be unique and ordered to work with 033 * FlywayDb so each developer sets a unique version so that the migration script 034 * generated is unique (typically just prior to being submitted as a merge request). 035 */ 036 protected String version; 037 038 /** 039 * Description text that can be appended to the version to become the ddl script file name. 040 * <p> 041 * So if the name is "a foo table" then the ddl script file could be: 042 * "1.1.1_2__a-foo-table.sql" 043 * <p> 044 * When the DB migration relates to a git feature (merge request) then this description text 045 * is a short description of the feature. 046 */ 047 protected String name; 048 049 /** 050 * Resource path for the migration xml and sql. 051 */ 052 protected String migrationPath = "dbmigration"; 053 054 /** 055 * Subdirectory the model xml files go into. 056 */ 057 protected String modelPath = "model"; 058 059 protected String applySuffix = ".sql"; 060 061 protected String modelSuffix = ".model.xml"; 062 063 protected boolean includeGeneratedFileComment; 064 065 /** 066 * The version of a pending drop that should be generated as the next migration. 067 */ 068 protected String generatePendingDrop; 069 070 /** 071 * Return the DB platform to generate migration DDL for. 072 * 073 * We typically need to explicitly specify this as migration can often be generated 074 * when running against H2. 075 */ 076 public DbPlatformName getPlatform() { 077 return platform; 078 } 079 080 /** 081 * Set the DB platform to generate migration DDL for. 082 */ 083 public void setPlatform(DbPlatformName platform) { 084 this.platform = platform; 085 } 086 087 /** 088 * Return the resource path for db migrations. 089 */ 090 public String getMigrationPath() { 091 return migrationPath; 092 } 093 094 /** 095 * Set the resource path for db migrations. 096 * <p> 097 * The default of "dbmigration" is reasonable in most cases. You may look to set this 098 * to be something like "dbmigration/myapp" where myapp gives it a unique resource path 099 * in the case there are multiple EbeanServer applications in the single classpath. 100 * </p> 101 */ 102 public void setMigrationPath(String migrationPath) { 103 this.migrationPath = migrationPath; 104 } 105 106 /** 107 * Return the relative path for the model files (defaults to model). 108 */ 109 public String getModelPath() { 110 return modelPath; 111 } 112 113 /** 114 * Set the relative path for the model files. 115 */ 116 public void setModelPath(String modelPath) { 117 this.modelPath = modelPath; 118 } 119 120 /** 121 * Return the model suffix (defaults to model.xml) 122 */ 123 public String getModelSuffix() { 124 return modelSuffix; 125 } 126 127 /** 128 * Set the model suffix. 129 */ 130 public void setModelSuffix(String modelSuffix) { 131 this.modelSuffix = modelSuffix; 132 } 133 134 /** 135 * Return the apply script suffix (defaults to sql). 136 */ 137 public String getApplySuffix() { 138 return applySuffix; 139 } 140 141 /** 142 * Set the apply script suffix (defaults to sql). 143 */ 144 public void setApplySuffix(String applySuffix) { 145 this.applySuffix = applySuffix; 146 } 147 148 /** 149 * Return true if the generated file comment should be included. 150 */ 151 public boolean isIncludeGeneratedFileComment() { 152 return includeGeneratedFileComment; 153 } 154 155 /** 156 * Set to true if the generated file comment should be included. 157 */ 158 public void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment) { 159 this.includeGeneratedFileComment = includeGeneratedFileComment; 160 } 161 162 /** 163 * Return the migration version (or "next") to generate pending drops for. 164 */ 165 public String getGeneratePendingDrop() { 166 return generatePendingDrop; 167 } 168 169 /** 170 * Set the migration version (or "next") to generate pending drops for. 171 */ 172 public void setGeneratePendingDrop(String generatePendingDrop) { 173 this.generatePendingDrop = generatePendingDrop; 174 } 175 176 /** 177 * Set the migration version. 178 * <p> 179 * Note that version set via System property or environment variable <code>ddl.migration.version</code> takes precedence. 180 */ 181 public void setVersion(String version) { 182 this.version = version; 183 } 184 185 /** 186 * Set the migration name. 187 * <p> 188 * Note that name set via System property or environment variable <code>ddl.migration.name</code> takes precedence. 189 */ 190 public void setName(String name) { 191 this.name = name; 192 } 193 194 /** 195 * Set the model, rollback and drop paths to be empty such that all the migration files are generated 196 * into a single directory. 197 */ 198 public void singleDirectory() { 199 this.modelPath = ""; 200 } 201 202 /** 203 * Load the settings from the PropertiesWrapper. 204 */ 205 public void loadSettings(PropertiesWrapper properties) { 206 207 migrationPath = properties.get("migration.migrationPath", migrationPath); 208 if (properties.getBoolean("migration.singleDirectory", false)) { 209 singleDirectory(); 210 } else { 211 modelPath = properties.get("migration.modelPath", modelPath); 212 } 213 applySuffix = properties.get("migration.applySuffix", applySuffix); 214 modelSuffix = properties.get("migration.modelSuffix", modelSuffix); 215 includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment); 216 generatePendingDrop = properties.get("migration.generatePendingDrop", generatePendingDrop); 217 218 platform = properties.getEnum(DbPlatformName.class, "migration.platform", platform); 219 220 generate = properties.getBoolean("migration.generate", generate); 221 version = properties.get("migration.version", version); 222 name = properties.get("migration.name", name); 223 } 224 225 /** 226 * Return true if the migration should be generated. 227 * <p> 228 * It is expected that when an environment variable <code>ddl.migration.enabled</code> 229 * is set to <code>true</code> then the DB migration will generate the migration DDL. 230 * </p> 231 */ 232 public boolean isGenerateOnStart() { 233 234 // environment properties take precedence 235 String envGenerate = readEnvironment("ddl.migration.generate"); 236 if (envGenerate != null) { 237 return "true".equalsIgnoreCase(envGenerate.trim()); 238 } 239 return generate; 240 } 241 242 /** 243 * Called by EbeanServer on start. 244 * 245 * <p> 246 * If enabled this generates the migration xml and DDL scripts. 247 * </p> 248 */ 249 public void generateOnStart(EbeanServer server) { 250 251 if (isGenerateOnStart()) { 252 if (platform == null) { 253 logger.warn("No platform set for migration DDL generation"); 254 } else { 255 // generate the migration xml and platform specific DDL 256 DbMigration migration = new DbMigration(server); 257 migration.setPlatform(platform); 258 try { 259 migration.generateMigration(); 260 } catch (Exception e) { 261 throw new RuntimeException("Error generating DB migration", e); 262 } 263 } 264 } 265 } 266 267 /** 268 * Return the migration version (typically FlywayDb compatible). 269 * <p> 270 * Example: 1.1.1_2 271 * <p> 272 * The version is expected to be the combination of the current pom version plus 273 * a 'feature' id. The combined version must be unique and ordered to work with 274 * FlywayDb so each developer sets a unique version so that the migration script 275 * generated is unique (typically just prior to being submitted as a merge request). 276 */ 277 public String getVersion() { 278 String envVersion = readEnvironment("ddl.migration.version"); 279 if (!isEmpty(envVersion)) { 280 return envVersion.trim(); 281 } 282 return version; 283 } 284 285 /** 286 * Return the migration name which is short description text that can be appended to 287 * the migration version to become the ddl script file name. 288 * <p> 289 * So if the name is "a foo table" then the ddl script file could be: 290 * "1.1.1_2__a-foo-table.sql" 291 * </p> 292 * <p> 293 * When the DB migration relates to a git feature (merge request) then this description text 294 * is a short description of the feature. 295 * </p> 296 */ 297 public String getName() { 298 String envName = readEnvironment("ddl.migration.name"); 299 if (!isEmpty(envName)) { 300 return envName.trim(); 301 } 302 return name; 303 } 304 305 /** 306 * Return the system or environment property. 307 */ 308 protected String readEnvironment(String key) { 309 310 String val = System.getProperty(key); 311 if (val == null) { 312 val = System.getenv(key); 313 } 314 return val; 315 } 316 317 /** 318 * Return true if the string is null or empty. 319 */ 320 protected boolean isEmpty(String val) { 321 return val == null || val.trim().isEmpty(); 322 } 323 324}