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}