001package io.ebean.migration.runner; 002 003import io.ebean.migration.JdbcMigration; 004import io.ebean.migration.MigrationConfig; 005import io.ebean.migration.MigrationVersion; 006import io.avaje.classpath.scanner.Resource; 007import io.avaje.classpath.scanner.ResourceFilter; 008import io.avaje.classpath.scanner.core.Scanner; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import java.util.ArrayList; 013import java.util.Collections; 014import java.util.List; 015 016/** 017 * Loads the DB migration resources and sorts them into execution order. 018 */ 019public class LocalMigrationResources { 020 021 private static final Logger logger = LoggerFactory.getLogger(LocalMigrationResources.class); 022 023 private final MigrationConfig migrationConfig; 024 025 private final List<LocalMigrationResource> versions = new ArrayList<>(); 026 027 /** 028 * Construct with configuration options. 029 */ 030 public LocalMigrationResources(MigrationConfig migrationConfig) { 031 this.migrationConfig = migrationConfig; 032 } 033 034 /** 035 * Read the init migration resources (usually only 1) returning true if there are versions. 036 */ 037 public boolean readInitResources() { 038 return readResourcesForPath(migrationConfig.getMigrationInitPath()); 039 } 040 041 /** 042 * Read all the migration resources (SQL scripts) returning true if there are versions. 043 */ 044 public boolean readResources() { 045 return readResourcesForPath(migrationConfig.getMigrationPath()); 046 } 047 048 private boolean readResourcesForPath(String path) { 049 050 ClassLoader classLoader = migrationConfig.getClassLoader(); 051 052 Scanner scanner = new Scanner(classLoader); 053 List<Resource> resourceList = scanner.scanForResources(path, new Match(migrationConfig)); 054 055 logger.debug("resources: {}", resourceList); 056 057 for (Resource resource : resourceList) { 058 String filename = resource.getFilename(); 059 if (filename.endsWith(migrationConfig.getApplySuffix())) { 060 versions.add(createScriptMigration(resource, filename)); 061 } else if (migrationConfig.getJdbcMigrationFactory() != null && filename.endsWith(".class")) { 062 versions.add(createJdbcMigration(resource, filename)); 063 } 064 } 065 066 Collections.sort(versions); 067 return !versions.isEmpty(); 068 } 069 070 /** 071 * Return a programmatic JDBC migration. 072 */ 073 private LocalMigrationResource createJdbcMigration(Resource resource, String filename) { 074 int pos = filename.lastIndexOf(".class"); 075 String mainName = filename.substring(0, pos); 076 MigrationVersion migrationVersion = MigrationVersion.parse(mainName); 077 String className = resource.getLocation().replace('/', '.'); 078 className = className.substring(0, className.length()-6); 079 JdbcMigration instance = migrationConfig.getJdbcMigrationFactory().createInstance(className); 080 return new LocalJdbcMigrationResource(migrationVersion, resource.getLocation(), instance); 081 } 082 083 /** 084 * Create a script based migration. 085 */ 086 private LocalMigrationResource createScriptMigration(Resource resource, String filename) { 087 int pos = filename.lastIndexOf(migrationConfig.getApplySuffix()); 088 String mainName = filename.substring(0, pos); 089 MigrationVersion migrationVersion = MigrationVersion.parse(mainName); 090 return new LocalDdlMigrationResource(migrationVersion, resource.getLocation(), resource); 091 } 092 093 /** 094 * Return the list of migration resources in version order. 095 */ 096 public List<LocalMigrationResource> getVersions() { 097 return versions; 098 } 099 100 101 /** 102 * Filter used to find the migration scripts. 103 */ 104 private static class Match implements ResourceFilter { 105 106 private final MigrationConfig migrationConfig; 107 108 Match(MigrationConfig migrationConfig) { 109 this.migrationConfig = migrationConfig; 110 } 111 112 @Override 113 public boolean isMatch(String name) { 114 return name.endsWith(migrationConfig.getApplySuffix()) 115 || migrationConfig.getJdbcMigrationFactory() != null && name.endsWith(".class") && !name.contains("$"); 116 } 117 } 118}