1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.migration;
17
18 import java.io.Reader;
19 import java.io.StringReader;
20 import java.lang.reflect.Modifier;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Set;
24
25 import org.apache.ibatis.migration.io.ResolverUtil;
26
27 public class JavaMigrationLoader implements MigrationLoader {
28
29 private String[] packageNames;
30
31 private ClassLoader classLoader;
32
33 public JavaMigrationLoader(String... packageNames) {
34 this(null, packageNames);
35 }
36
37 public JavaMigrationLoader(ClassLoader classLoader, String... packageNames) {
38 this.classLoader = classLoader;
39 this.packageNames = packageNames;
40 }
41
42 @Override
43 public List<Change> getMigrations() {
44 List<Change> migrations = new ArrayList<>();
45 ResolverUtil<MigrationScript> resolver = getResolver();
46 resolver.findImplementations(MigrationScript.class, packageNames);
47 Set<Class<? extends MigrationScript>> classes = resolver.getClasses();
48 for (Class<? extends MigrationScript> clazz : classes) {
49 try {
50 if (!Modifier.isAbstract(clazz.getModifiers())) {
51 MigrationScript script = clazz.getDeclaredConstructor().newInstance();
52 Change change = parseChangeFromMigrationScript(script);
53 migrations.add(change);
54 }
55 } catch (Exception e) {
56 throw new MigrationException("Could not instanciate MigrationScript: " + clazz.getName(), e);
57 }
58 }
59 return migrations;
60 }
61
62 private Change parseChangeFromMigrationScript(MigrationScript script) {
63 Change change = new Change();
64 change.setId(script.getId());
65 change.setDescription(script.getDescription());
66 change.setFilename(script.getClass().getName());
67 return change;
68 }
69
70 @Override
71 public Reader getScriptReader(Change change, boolean undo) {
72 ResolverUtil<MigrationScript> resolver = getResolver();
73 final String className = change.getFilename();
74 for (String pkg : packageNames) {
75 resolver.find(
76 type -> type != null && MigrationScript.class.isAssignableFrom(type) && type.getName().equals(className),
77 pkg);
78 }
79 Set<Class<? extends MigrationScript>> classes = resolver.getClasses();
80
81 for (Class<? extends MigrationScript> clazz : classes) {
82 try {
83 MigrationScript script = clazz.getDeclaredConstructor().newInstance();
84 return new StringReader(undo ? script.getDownScript() : script.getUpScript());
85 } catch (Exception e) {
86 throw new MigrationException("Could not instanciate MigrationScript: " + clazz.getName(), e);
87 }
88 }
89 return null;
90 }
91
92 @Override
93 public Reader getBootstrapReader() {
94 return getSoleScriptReader(BootstrapScript.class);
95 }
96
97 @Override
98 public Reader getOnAbortReader() {
99 return getSoleScriptReader(OnAbortScript.class);
100 }
101
102 public <T extends SimpleScript> Reader getSoleScriptReader(Class<T> scriptClass) {
103 ResolverUtil<T> resolver = getResolver();
104 resolver.findImplementations(scriptClass, packageNames);
105 Set<Class<? extends T>> classes = resolver.getClasses();
106 if (classes == null || classes.isEmpty()) {
107 return null;
108 }
109 if (classes.size() > 1) {
110 throw new MigrationException("There can be only one implementation of " + scriptClass.getName());
111 }
112 Class<? extends T> clazz = classes.iterator().next();
113 try {
114 T script = clazz.getDeclaredConstructor().newInstance();
115 return new StringReader(script.getScript());
116 } catch (Exception e) {
117 throw new MigrationException("Could not instanciate script class: " + clazz.getName(), e);
118 }
119 }
120
121 private <T> ResolverUtil<T> getResolver() {
122 ResolverUtil<T> resolver = new ResolverUtil<>();
123 if (classLoader != null) {
124 resolver.setClassLoader(classLoader);
125 }
126 return resolver;
127 }
128 }