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