modularize project
This commit is contained in:
27
cli/src/main/java/jef/main/Main.java
Normal file
27
cli/src/main/java/jef/main/Main.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package jef.main;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0) {
|
||||
printHelp();
|
||||
System.exit(1);
|
||||
}
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "help":
|
||||
printHelp();
|
||||
case "migration":
|
||||
MigrationCommandHandler.handleMigration(Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
default:
|
||||
printHelp();
|
||||
}
|
||||
}
|
||||
|
||||
static void printHelp() {
|
||||
System.out.println("Usage: java -jar thisfile <command> [options]");
|
||||
MigrationCommandHandler.printHelp();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
294
cli/src/main/java/jef/main/MigrationCommandHandler.java
Normal file
294
cli/src/main/java/jef/main/MigrationCommandHandler.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package jef.main;
|
||||
|
||||
import jef.Database;
|
||||
import jef.model.DbContext;
|
||||
import jef.model.DbContextOptions;
|
||||
import jef.model.ModelBuilder;
|
||||
import jef.model.migration.creator.MigrationCreator;
|
||||
import jef.mysql.MysqlDatabase;
|
||||
import jef.util.Util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class MigrationCommandHandler {
|
||||
static void handleMigration(String[] args) {
|
||||
if (args.length == 0) {
|
||||
Main.printHelp();
|
||||
}
|
||||
|
||||
try {
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "add":
|
||||
handleMigrationAdd(Arrays.copyOfRange(args, 1, args.length));
|
||||
case "remove":
|
||||
handleMigrationRemove(Arrays.copyOfRange(args, 1, args.length));
|
||||
case "list":
|
||||
handleMigrationList(Arrays.copyOfRange(args, 1, args.length));
|
||||
default:
|
||||
Main.printHelp();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void handleMigrationList(String[] args) {
|
||||
}
|
||||
|
||||
private static void handleMigrationRemove(String[] args) {
|
||||
}
|
||||
|
||||
private static void handleMigrationAdd(String[] args) throws Exception {
|
||||
String contextName = null;
|
||||
String migrationPackage = null;
|
||||
String targetFolder = null;
|
||||
String name = null;
|
||||
var classPath = new ArrayList<String>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
switch (args[i].toLowerCase()) {
|
||||
case "--cp":
|
||||
if (i + 1 >= args.length) {
|
||||
Main.printHelp();
|
||||
}
|
||||
classPath.addAll(List.of(args[++i].split(":")));//TODO does not work well on windows (:)
|
||||
break;
|
||||
case "--context":
|
||||
case "-c":
|
||||
if (i + 1 >= args.length) {
|
||||
Main.printHelp();
|
||||
}
|
||||
contextName = args[++i];
|
||||
break;
|
||||
case "--output":
|
||||
case "-o":
|
||||
if (i + 1 >= args.length) {
|
||||
Main.printHelp();
|
||||
}
|
||||
targetFolder = args[++i];
|
||||
break;
|
||||
default:
|
||||
if (name != null) {
|
||||
Main.printHelp();
|
||||
}
|
||||
name = args[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if (Set.of(contextName, targetFolder, name).contains(null)) {
|
||||
// Main.printUsage();
|
||||
// }
|
||||
|
||||
if (name == null) {
|
||||
System.err.println("The migration requires a name.");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
//find data locations
|
||||
var urls = classPath.stream().map(e -> Util.tryGet(() -> new File(e).toURI().toURL()).orElseThrow()).toArray(URL[]::new);
|
||||
var cl = new URLClassLoader(urls, MigrationCommandHandler.class.getClassLoader());
|
||||
var context = contextName == null ? findAnyContext(cl) : findContextByName(cl, contextName);
|
||||
targetFolder = targetFolder == null ? System.getProperty("user.dir") : targetFolder;
|
||||
var targetFolderFile = new File(targetFolder);
|
||||
targetFolderFile.mkdirs();
|
||||
migrationPackage = findMigrationPackageName(targetFolder);
|
||||
|
||||
//find data
|
||||
var currentSnapshotFile = new File(targetFolderFile, "CurrentSnapshot.java");
|
||||
var from = !currentSnapshotFile.isFile()
|
||||
? new ModelBuilder()
|
||||
: (ModelBuilder) cl.loadClass((migrationPackage != null ? migrationPackage + "." : "") + "CurrentSnapshot").getConstructor().newInstance();
|
||||
var to = ModelBuilder.from(context.orElseThrow().getClass());
|
||||
// context.orElseThrow().onModelCreate(to);
|
||||
|
||||
//begin
|
||||
var date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
|
||||
var className = "M" + date + "_" + name;
|
||||
|
||||
var creator = new MigrationCreator();
|
||||
var result = creator.createMigration(from, to, className, migrationPackage, currentSnapshotFile.isFile() ? Files.readString(currentSnapshotFile.toPath()) : null);
|
||||
|
||||
Files.writeString(new File(targetFolder, className + ".java").toPath(), result.getMigration(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||
Files.writeString(new File(targetFolder, className + "Snapshot.java").toPath(), result.getMigrationSnapshot(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||
Files.writeString(new File(targetFolder, "CurrentSnapshot.java").toPath(), result.getCurrentSnapshot(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static Optional<DbContext> findAnyContext(URLClassLoader cl) {
|
||||
return findContextByName(cl, "");
|
||||
}
|
||||
|
||||
private static Optional<DbContext> findContextByName(URLClassLoader cl, String contextName) {
|
||||
Util.ThrowableFunction<String, DbContext> finder = (String entryName) -> {
|
||||
var cls = cl.loadClass(entryName.substring(0, entryName.length() - ".class".length()).replace("/", "."));
|
||||
var parent = cls;
|
||||
// var interfaces = cls.getInterfaces();
|
||||
// while (parent != Object.class && !Set.of(interfaces).contains(DbContext.class)) {
|
||||
while (parent != Object.class && parent != DbContext.class) {
|
||||
parent = parent.getSuperclass();
|
||||
// interfaces = cls.getInterfaces();
|
||||
}
|
||||
// if (Set.of(interfaces).contains(DbContext.class)) {
|
||||
if (parent == DbContext.class) {
|
||||
var paramInitializer = new HashMap<Class<?>, Supplier<?>>();
|
||||
paramInitializer.put(Database.class, () -> new MysqlDatabase(null, null));
|
||||
paramInitializer.put(DbContextOptions.class, () -> new DbContextOptions(null));
|
||||
|
||||
var ctors = cls.getDeclaredConstructors();//TODO create a function for initializing db contexts
|
||||
if (ctors.length == 0) {
|
||||
System.err.println("No constructor found in " + cls.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
var ctor = Arrays.stream(ctors)
|
||||
.filter(e -> Arrays.stream(e.getParameterTypes())
|
||||
.allMatch(p -> Database.class.isAssignableFrom(p) || DbContextOptions.class.isAssignableFrom(p)))
|
||||
.findFirst().orElse(null);
|
||||
if (ctor == null) {
|
||||
return null;
|
||||
}
|
||||
var args = Arrays.stream(ctor.getParameterTypes()).map(p -> paramInitializer.get(p).get()).toArray();
|
||||
return Util.tryGet(() -> (DbContext) ctor.newInstance(args)).orElse(null);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return Arrays.stream(cl.getURLs()).map(url -> {
|
||||
URLConnection conn;
|
||||
try {
|
||||
conn = url.openConnection();
|
||||
} catch (IOException ignored) {
|
||||
return null;
|
||||
}
|
||||
if (conn instanceof JarURLConnection) {
|
||||
try (var zip = new ZipInputStream(conn.getInputStream())) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zip.getNextEntry()) != null) {
|
||||
if (entry.isDirectory() || !entry.getName().endsWith(contextName + ".class")) {
|
||||
continue;
|
||||
}
|
||||
return finder.apply(entry.getName());
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} else {
|
||||
var search = new ArrayList<URL>();
|
||||
search.add(url);
|
||||
while (search.size() > 0) {
|
||||
try {
|
||||
var currentUrl = search.remove(0);
|
||||
conn = currentUrl.openConnection();
|
||||
try (var is = conn.getInputStream();
|
||||
var isr = new InputStreamReader(is);
|
||||
var br = new BufferedReader(isr)) {
|
||||
String entry;
|
||||
while ((entry = br.readLine()) != null) {
|
||||
// System.out.println(entry);
|
||||
var newUri = currentUrl.toURI().toString();
|
||||
newUri = newUri.endsWith("/") ? newUri : newUri + "/";
|
||||
newUri += entry;
|
||||
search.add(new URI(newUri).toURL());
|
||||
if (!entry.endsWith(contextName + ".class")) {//entry.isDirectory() ||
|
||||
continue;
|
||||
}
|
||||
return finder.apply(new URI(newUri).getPath().substring(url.toURI().getPath().length()));
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private static String findMigrationPackageName(String dir) {
|
||||
//search for java file in dir and take package name from there
|
||||
var javaFiles = new File(dir).listFiles(file -> file.isFile() && file.getName().endsWith(".java"));
|
||||
if (javaFiles.length > 0) {
|
||||
try {
|
||||
var contextFileString = Files.readAllLines(javaFiles[0].toPath()).stream().collect(Collectors.joining(" "));
|
||||
var pattern = Pattern.compile("\\s*package\\s+(.*?);");
|
||||
var matcher = pattern.matcher(contextFileString);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// //try finding out from dir path
|
||||
var path = new File(dir).getAbsolutePath();
|
||||
var i = path.indexOf("/src/");
|
||||
if (i >= 0) {
|
||||
path = path.substring(i + "/src/".length());
|
||||
if (path.startsWith("main/")) {
|
||||
path = path.substring("main/".length());
|
||||
if (path.startsWith("java/")) {
|
||||
path = path.substring("java/".length());
|
||||
}
|
||||
}
|
||||
}
|
||||
i = path.indexOf("/target/");
|
||||
if (i >= 0) {
|
||||
path = path.substring(i + "/target/".length());
|
||||
if (path.startsWith("generated-migrations/")) {
|
||||
path = path.substring("generated-migrations/".length());
|
||||
if (path.startsWith("src/")) {
|
||||
path = path.substring("src/".length());
|
||||
}
|
||||
}
|
||||
}
|
||||
return path.replace("/", ".");
|
||||
// return null;
|
||||
}
|
||||
|
||||
public static void printHelp() {
|
||||
System.out.println("migration add --cp <classpath> [--context/-c <context class name>] [--output/-o <folder for migrations>] <name>");
|
||||
System.out.println("migration remove --cp <classpath> [--context/-c <context class name>] [--output/-o <folder for migrations>] <name>");
|
||||
System.out.println("migration list");
|
||||
}
|
||||
|
||||
// private static String findMigrationSnapshot(String dir) {
|
||||
// var javaFiles = new File(dir).listFiles(file -> file.isFile() && file.getName().endsWith(".java"));
|
||||
// if (javaFiles.length > 0) {
|
||||
// try {
|
||||
// var contextFileString = Files.readAllLines(javaFiles[0].toPath()).stream().collect(Collectors.joining(" "));
|
||||
// var pattern = Pattern.compile("\\s*package\\s+(.*?);");
|
||||
// var matcher = pattern.matcher(contextFileString);
|
||||
// if (matcher.find()) {
|
||||
// return matcher.group(1);
|
||||
// }
|
||||
// } catch (IOException ignored) {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
23
cli/src/main/java/jef/util/Util.java
Normal file
23
cli/src/main/java/jef/util/Util.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package jef.util;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class Util {
|
||||
public static <T> Optional<T> tryGet(ThrowableSupplier<T> s) {
|
||||
try {
|
||||
return Optional.ofNullable(s.get());
|
||||
} catch (Throwable t) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowableSupplier<T> {
|
||||
T get() throws Throwable;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowableFunction<T, R> {
|
||||
R apply(T t) throws Throwable;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user