package org.reldb.rel.v0.interpreter; import java.io.*; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.HashMap; import java.util.Set; import org.reldb.rel.exceptions.*; import org.reldb.rel.shared.Defaults; import org.reldb.rel.v0.server.Server; import org.reldb.rel.v0.version.Version; import org.reldb.rel.v0.interpreter.ClassPathHack; import org.reldb.rel.v0.interpreter.Interpreter; import org.reldb.rel.v0.storage.RelDatabase; import org.reldb.rel.v0.interpreter.Instance; /** A self-contained instance of the Rel system. */ public class Instance { // Static cache of open databases ensures each database is open once, // even if there are multiple InstancesS. private static HashMap<File, RelDatabase> openDatabases = null; private static String localHostName; private static Server server = null; private RelDatabase database; private boolean evaluate = false; private boolean debugOnRun = false; private boolean debugAST = false; public void announceActive(PrintStream output) { output.println(Version.getCopyright()); output.println("Rel is running on " + localHostName); output.println("using " + System.getProperty("java.vendor") + "'s Java version " + System.getProperty("java.version") + " from " + System.getProperty("java.vendor.url")); output.println("at " + System.getProperty("java.home")); output.println("on " + System.getProperty("os.name") + " version " + System.getProperty("os.version") + " for " + System.getProperty("os.arch")); output.println("with database format v" + Version.getDatabaseVersion() + "."); output.println("Persistence is provided by " + database.getNativeDBVersion()); output.println(Version.getLicense()); output.println("Ok."); } /** Get host name. */ public String getHost() { return localHostName; } /** Get the database. */ public RelDatabase getDatabase() { return database; } private static boolean deleteRecursive(File path) throws FileNotFoundException { if (!path.exists()) return true; boolean ret = true; if (path.isDirectory()) { for (File f: path.listFiles()) { ret = ret && deleteRecursive(f); } } return ret && path.delete(); } private boolean shutdownHookSetup = false; private void setupShutdownHook() { if (shutdownHookSetup) return; Thread serverShutdownHook = new Thread() { public void run() { if (server != null) server.shutdown(); } }; Runtime.getRuntime().addShutdownHook(serverShutdownHook); if (openDatabases == null) { openDatabases = new HashMap<File, RelDatabase>(); Thread dbShutdownHook = new Thread() { public void run() { for (RelDatabase database: openDatabases.values()) if (database.isOpen()) database.close(); } }; Runtime.getRuntime().addShutdownHook(dbShutdownHook); } try { localHostName = InetAddress.getLocalHost().getCanonicalHostName(); } catch (UnknownHostException uhe) { localHostName = "<unknown>"; } shutdownHookSetup = true; } private void initDb(String databasePath, boolean createDbAllowed, PrintStream output, String[] additionalJarsForClasspath) throws DatabaseFormatVersionException { setupShutdownHook(); File dbPath = new File(databasePath); database = openDatabases.get(dbPath); if (database == null) { database = new RelDatabase(); database.setAdditionalJarsForJavaCompilerClasspath(additionalJarsForClasspath); database.open(dbPath, createDbAllowed, output); openDatabases.put(dbPath, database); } } public static void convertToLatestFormat(String dbPath, PrintStream conversionOutput, String[] additionalJarsForClasspath) throws DatabaseFormatVersionException { RelDatabase database = new RelDatabase(); int oldVersion = -1; File databasePath = new File(dbPath); try { database.open(databasePath, false, conversionOutput); database.close(); } catch (DatabaseFormatVersionException dce) { oldVersion = dce.getOldVersion(); } if (oldVersion == -1) throw new DatabaseFormatVersionException("RS0415: Database is already the latest format."); try { String launchMsg = "Database conversion from format v" + oldVersion + " to v" + Version.getDatabaseVersion() + " launched..."; conversionOutput.println(launchMsg); // Load detected version's .jar file (should already be done externally if run as Eclipse RCP app.) ClassPathHack.addFile("lib/" + Version.getCoreJarFilename(oldVersion)); // Instantiate old version as oldRel Class<?> oldRelEngine = Class.forName("org.reldb.rel.v" + oldVersion + ".engine.Rel"); Method oldRelEngineBackup = oldRelEngine.getMethod("backup", new Class[] {String.class, String.class}); // Backup oldRel's database conversionOutput.println("Running backup..."); Path backupScriptPath = Paths.get(databasePath.getAbsolutePath(), "relbackup_v" + oldVersion + ".rel"); oldRelEngineBackup.invoke(null, databasePath.toString(), backupScriptPath.toString()); // Create new database conversionOutput.println("Creating new database..."); Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwx------"); FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); Path newDbDirectory = Files.createDirectory(Paths.get(databasePath.toString(), "_new_Reldb" + Double.toString(Math.random())), attr); database.open(newDbDirectory.toFile(), true, conversionOutput); database.setAdditionalJarsForJavaCompilerClasspath(additionalJarsForClasspath); // Import oldRel's database script into new database conversionOutput.println("Importing backup from old database..."); Interpreter interpreter = new Interpreter(database, conversionOutput); interpreter.interpret(new FileInputStream(backupScriptPath.toFile())); database.close(); conversionOutput.println("Import done"); Path oldReldbPath = Paths.get(databasePath.toString(), "Reldb"); Path newReldbPath = Paths.get(newDbDirectory.toString(), "Reldb"); Path backupReldbPath = Paths.get(databasePath.toString(), "Backup_Reldb_v" + oldVersion); // Move Reldb to Backup_Reldb_v<n>/Reldb where <n> is its detected version conversionOutput.println("Move " + oldReldbPath + " to " + backupReldbPath); deleteRecursive(backupReldbPath.toFile()); Files.move(oldReldbPath, backupReldbPath, StandardCopyOption.REPLACE_EXISTING); // Move new database directory to Reldb conversionOutput.println("Move " + newReldbPath + " to " + oldReldbPath); deleteRecursive(oldReldbPath.toFile()); Files.move(newReldbPath, oldReldbPath, StandardCopyOption.REPLACE_EXISTING); deleteRecursive(newDbDirectory.toFile()); conversionOutput.println("Database conversion complete."); conversionOutput.close(); // Re-open, if possible database = new RelDatabase(); database.open(databasePath, false, conversionOutput); database.close(); } catch (Throwable e1) { e1.printStackTrace(); String msg = "Unable to complete database conversion due to " + e1; conversionOutput.close(); throw new ExceptionSemantic(msg); } } public Instance(String databasePath, boolean createDbAllowed, PrintStream output, String[] additionalJarsForJavaClasspath) throws DatabaseFormatVersionException { initDb(databasePath, createDbAllowed, output, additionalJarsForJavaClasspath); } public Instance(String databasePath, boolean createDbAllowed, PrintStream output) throws DatabaseFormatVersionException { initDb(databasePath, createDbAllowed, output, null); } private void usage(String databasePath) { System.out.println("Usage: RelDBMS [-f<database>] [-D[port] | [-e] [-v0 | -v1]] < <source>"); System.out.println(" -f<database> -- database - default is " + databasePath); System.out.println(" -D[port] -- run as server (port optional - default is " + Defaults.getDefaultPort() + ")"); System.out.println(" -e -- evaluate expression"); System.out.println(" -v0 -- run-time debugging"); System.out.println(" -v1 -- output AST"); } private Instance(String args[]) { String databasePath = "./DefaultRelDb"; if (args.length >= 1) { for (int i=0; i<args.length; i++) { if (args[i].startsWith("-D")) { int portnum = Defaults.getDefaultPort(); if (args[i].length() > 2) { String portnumStr = args[i].substring(2); try { portnum = Integer.parseInt(portnumStr); } catch (NumberFormatException nfe) { System.out.println("Error: Invalid port number: " + portnumStr); return; } } try { initDb(databasePath, true, System.out, null); } catch (DatabaseFormatVersionException e) { try { convertToLatestFormat(databasePath, System.out, null); initDb(databasePath, true, System.out, null); } catch (DatabaseFormatVersionException e1) { System.out.println("Error: unable to convert database to latest version."); return; } } server = new Server(this, portnum); return; } else if (args[i].equals("-v0")) debugOnRun = true; else if (args[i].equals("-v1")) debugAST = true; else if (args[i].equals("-e")) evaluate = true; else if (args[i].startsWith("-f")) { if (args[i].length() <= 2) { usage(databasePath); return; } databasePath = args[i].substring(2); } else { usage(databasePath); return; } } } try { initDb(databasePath, true, System.out, null); } catch (DatabaseFormatVersionException e) { try { convertToLatestFormat(databasePath, System.out, null); initDb(databasePath, true, System.out, null); } catch (DatabaseFormatVersionException e1) { System.out.println("Error: unable to convert database to latest version."); return; } } Interpreter interpreter = new Interpreter(database, System.out); interpreter.setDebugOnRun(debugOnRun); interpreter.setDebugAST(debugAST); announceActive(System.out); try { if (evaluate) interpreter.evaluate(System.in).toStream(System.out); else interpreter.interpret(System.in); } catch (Throwable e) { if (debugOnRun) e.printStackTrace(); else System.out.println(e.getMessage()); } } public void dbclose() { if (database != null) database.close(); } public static void main(String args[]) { new Instance(args); } }