JavaMigrationLoader.java

  1. /*
  2.  *    Copyright 2010-2023 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.migration;

  17. import java.io.Reader;
  18. import java.io.StringReader;
  19. import java.lang.reflect.Modifier;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.Set;

  23. import org.apache.ibatis.migration.io.ResolverUtil;

  24. public class JavaMigrationLoader implements MigrationLoader {

  25.   private String[] packageNames;

  26.   private ClassLoader classLoader;

  27.   public JavaMigrationLoader(String... packageNames) {
  28.     this(null, packageNames);
  29.   }

  30.   public JavaMigrationLoader(ClassLoader classLoader, String... packageNames) {
  31.     this.classLoader = classLoader;
  32.     this.packageNames = packageNames;
  33.   }

  34.   @Override
  35.   public List<Change> getMigrations() {
  36.     List<Change> migrations = new ArrayList<>();
  37.     ResolverUtil<MigrationScript> resolver = getResolver();
  38.     resolver.findImplementations(MigrationScript.class, packageNames);
  39.     Set<Class<? extends MigrationScript>> classes = resolver.getClasses();
  40.     for (Class<? extends MigrationScript> clazz : classes) {
  41.       try {
  42.         if (!Modifier.isAbstract(clazz.getModifiers())) {
  43.           MigrationScript script = clazz.getDeclaredConstructor().newInstance();
  44.           Change change = parseChangeFromMigrationScript(script);
  45.           migrations.add(change);
  46.         }
  47.       } catch (Exception e) {
  48.         throw new MigrationException("Could not instanciate MigrationScript: " + clazz.getName(), e);
  49.       }
  50.     }
  51.     return migrations;
  52.   }

  53.   private Change parseChangeFromMigrationScript(MigrationScript script) {
  54.     Change change = new Change();
  55.     change.setId(script.getId());
  56.     change.setDescription(script.getDescription());
  57.     change.setFilename(script.getClass().getName());
  58.     return change;
  59.   }

  60.   @Override
  61.   public Reader getScriptReader(Change change, boolean undo) {
  62.     ResolverUtil<MigrationScript> resolver = getResolver();
  63.     final String className = change.getFilename();
  64.     for (String pkg : packageNames) {
  65.       resolver.find(
  66.           type -> type != null && MigrationScript.class.isAssignableFrom(type) && type.getName().equals(className),
  67.           pkg);
  68.     }
  69.     Set<Class<? extends MigrationScript>> classes = resolver.getClasses();
  70.     // There should be only one script.
  71.     for (Class<? extends MigrationScript> clazz : classes) {
  72.       try {
  73.         MigrationScript script = clazz.getDeclaredConstructor().newInstance();
  74.         return new StringReader(undo ? script.getDownScript() : script.getUpScript());
  75.       } catch (Exception e) {
  76.         throw new MigrationException("Could not instanciate MigrationScript: " + clazz.getName(), e);
  77.       }
  78.     }
  79.     return null;
  80.   }

  81.   @Override
  82.   public Reader getBootstrapReader() {
  83.     return getSoleScriptReader(BootstrapScript.class);
  84.   }

  85.   @Override
  86.   public Reader getOnAbortReader() {
  87.     return getSoleScriptReader(OnAbortScript.class);
  88.   }

  89.   public <T extends SimpleScript> Reader getSoleScriptReader(Class<T> scriptClass) {
  90.     ResolverUtil<T> resolver = getResolver();
  91.     resolver.findImplementations(scriptClass, packageNames);
  92.     Set<Class<? extends T>> classes = resolver.getClasses();
  93.     if (classes == null || classes.isEmpty()) {
  94.       return null;
  95.     }
  96.     if (classes.size() > 1) {
  97.       throw new MigrationException("There can be only one implementation of " + scriptClass.getName());
  98.     }
  99.     Class<? extends T> clazz = classes.iterator().next();
  100.     try {
  101.       T script = clazz.getDeclaredConstructor().newInstance();
  102.       return new StringReader(script.getScript());
  103.     } catch (Exception e) {
  104.       throw new MigrationException("Could not instanciate script class: " + clazz.getName(), e);
  105.     }
  106.   }

  107.   private <T> ResolverUtil<T> getResolver() {
  108.     ResolverUtil<T> resolver = new ResolverUtil<>();
  109.     if (classLoader != null) {
  110.       resolver.setClassLoader(classLoader);
  111.     }
  112.     return resolver;
  113.   }
  114. }