diff --git a/datamodel/table.csv b/datamodel/table.csv index a3baec88c..b053246e7 100644 --- a/datamodel/table.csv +++ b/datamodel/table.csv @@ -1 +1,2 @@ # Name; Upsert; Primary key; ; Author +sample; N; A INTEGER; ; Data Model Editor; ; diff --git a/extractionmodel/scott-without-subordinates.csv b/extractionmodel/scott-without-subordinates.csv index 333295d7d..2f780f791 100644 --- a/extractionmodel/scott-without-subordinates.csv +++ b/extractionmodel/scott-without-subordinates.csv @@ -1,8 +1,4 @@ # subject; condition; limit; restrictions -EMPLOYEE; NAME='SCOTT'; ; .embedded +null; ; ; .embedded # from A (or association name); to B; restriction-condition -X; ; dsdsdsd -DEPARTMENT; EMPLOYEE; ignore -SUBORDINATE; ; neu -SALARYGRADE; EMPLOYEE; ignore diff --git a/src/net/sf/jailer/database/StatementExecutor.java b/src/net/sf/jailer/database/StatementExecutor.java index 8d47ea048..7c9a822f8 100755 --- a/src/net/sf/jailer/database/StatementExecutor.java +++ b/src/net/sf/jailer/database/StatementExecutor.java @@ -23,21 +23,12 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.DriverPropertyInfo; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Properties; +import java.sql.*; +import java.util.*; +import java.text.MessageFormat; import org.apache.log4j.Logger; +import net.sf.jailer.drivermanager.JDBCDriverManager; /** * Executes SQL-Statements. @@ -90,14 +81,14 @@ public class StatementExecutor { /** * The logger. */ - private static final Logger _log = Logger.getLogger("sql"); + private static final Logger myLog = Logger.getLogger("sql"); /** * Connection factory. */ private interface ConnectionFactory { Connection getConnection() throws SQLException; - }; + } /** * Connection factory. @@ -132,7 +123,7 @@ public class StatementExecutor { /** * Classloader to load Jdbc-Driver with. */ - public static ClassLoader classLoaderForJdbcDriver = null; + //public static ClassLoader myJDBCDriverLoader = null; /** * Wraps a Jdbc-Driver. @@ -163,23 +154,23 @@ public class StatementExecutor { } /** - * Constructor. + * Constructs {@code StatementExecutor} for executing statements at + * specified DB-server with specified DB-driver and authorizes using + * specified DB-username and DB-password. * - * @param driverClassName name of JDBC-driver class - * @param dbUrl the database URL - * @param user the DB-user - * @param password the DB-password + * @param driverClassName Name of JDBC-driver class + * @param host The database host URL + * @param user The DB-user + * @param password The DB-password */ - public StatementExecutor(String driverClassName, final String dbUrl, final String user, final String password) throws Exception { - _log.info("connect to user " + user + " at "+ dbUrl); - if (classLoaderForJdbcDriver != null) { - Driver d = (Driver)Class.forName(driverClassName, true, classLoaderForJdbcDriver).newInstance(); - DriverManager.registerDriver(new DriverShim(d)); - } else { - Class.forName(driverClassName); - } + public StatementExecutor(String driverClassName, final String host, final String user, final String password) + throws Exception { + // todo: replace with locale string + myLog.info("connecting to db-server at " + host + " with user " + user + ";"); + // Loading and registering a driver. + DriverManager.registerDriver(JDBCDriverManager.getDriver(driverClassName)); this.schemaName = user; - this.dbUrl = dbUrl; + this.dbUrl = host; this.dbUser = user; this.dbPassword = password; connectionFactory = new ConnectionFactory() { @@ -187,12 +178,12 @@ public class StatementExecutor { Connection con = connection.get(); if (con == null) { - con = DriverManager.getConnection(dbUrl, user, password); + con = DriverManager.getConnection(host, user, password); con.setAutoCommit(true); try { con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); } catch (SQLException e) { - _log.info("can't set isolation level to UR. Reason: " + e.getMessage()); + myLog.info("can't set isolation level to UR. Reason: " + e.getMessage()); } connection.set(con); connections.add(con); @@ -220,7 +211,7 @@ public class StatementExecutor { * @param reader the reader for the result */ public void executeQuery(String sqlQuery, ResultSetReader reader) throws SQLException { - _log.debug(sqlQuery); + myLog.debug(sqlQuery); Statement statement = connectionFactory.getConnection().createStatement(); ResultSet resultSet = statement.executeQuery(sqlQuery); while (resultSet.next()) { @@ -264,11 +255,11 @@ public class StatementExecutor { * Executes a SQL-Update (INSERT, DELETE or UPDATE). * * @param sqlUpdate the update in SQL - * * @return update-count + * @throws SQLException */ public int executeUpdate(String sqlUpdate) throws SQLException { - _log.debug(sqlUpdate); + myLog.debug(sqlUpdate); int rowCount = 0; int failures = 0; boolean ok = false; @@ -285,14 +276,15 @@ public class StatementExecutor { rowCount = statement.executeUpdate(sqlUpdate); } ok = true; - _log.debug("" + rowCount + " row(s)"); + myLog.debug(MessageFormat.format("{0} row(s) affected", rowCount)); } catch (SQLException e) { - if (++failures > 10 || e.getErrorCode() != -911) { + // todo: replace '-911' sql-error with an appropriate constant. + if (++failures > myMaxFailures || e.getErrorCode() != -911) { throw e; } // deadlock serializeAccess = true; - _log.info("Deadlock! Try again."); + myLog.info("Deadlock! Try again."); } finally { if (statement != null) { statement.close(); @@ -307,7 +299,7 @@ public class StatementExecutor { */ public void insertClob(String table, String column, String where, File lobFile) throws SQLException, FileNotFoundException { String sqlUpdate = "Update " + table + " set " + column + "=? where " + where; - _log.debug(sqlUpdate); + myLog.debug(sqlUpdate); PreparedStatement statement = null; statement = connectionFactory.getConnection().prepareStatement(sqlUpdate); statement.setCharacterStream(1, new InputStreamReader(new FileInputStream(lobFile)), (int) lobFile.length()); @@ -320,7 +312,7 @@ public class StatementExecutor { */ public void insertBlob(String table, String column, String where, File lobFile) throws SQLException, FileNotFoundException { String sqlUpdate = "Update " + table + " set " + column + "=? where " + where; - _log.debug(sqlUpdate); + myLog.debug(sqlUpdate); PreparedStatement statement = null; statement = connectionFactory.getConnection().prepareStatement(sqlUpdate); statement.setBinaryStream(1, new FileInputStream(lobFile), (int) lobFile.length()); @@ -334,7 +326,7 @@ public class StatementExecutor { * @param sql the SQL-Statement */ public void execute(String sql) throws SQLException { - _log.debug(sql); + myLog.debug(sql); Statement statement = connectionFactory.getConnection().createStatement(); statement.execute(sql); statement.close(); @@ -350,16 +342,6 @@ public class StatementExecutor { return connection.getMetaData(); } - /** - * Sets Classloader to load Jdbc-Driver with. - * - * @param classLoader Classloader to load Jdbc-Driver with - */ - public static void setClassLoaderForJdbcDriver(ClassLoader classLoader) { - classLoaderForJdbcDriver = classLoader; - } - - /** * Closes all connections. */ @@ -386,5 +368,48 @@ public class StatementExecutor { public void setIntrospectionSchema(String introspectionSchema) { this.introspectionSchema = introspectionSchema; } + + ////////////////////////// + // Max Filures settings // + ////////////////////////// + + /** + * A maximum number of failures that could occurs during exeution of one + * query before an exception will be thrown out. + * + * The default value is 10. + * + * @see #getMaxFailures() + * @see #setMaxFailures(int) + */ + protected int myMaxFailures = 10; + + /** + * Returns a maximum number of failures during query execution. + * + * @return A maximum number of failures during query execution. + * + * @see #setMaxFailures(int) + */ + public int getMaxFailures() { + return myMaxFailures; + } + + /** + * Sets a maximum number of failures during query execution. + * + * @param maxFailures A new maximum number of failures during query + * execution. + * @throws IllegalArgumentException If a received value is negative. + * + * @see #setMaxFailures(int) + */ + public void setMaxFailures(int maxFailures) + throws IllegalArgumentException { + if (maxFailures < 0) { + throw new IllegalArgumentException("The maximum number of failures should be positive value"); + } + myMaxFailures = maxFailures; + } } diff --git a/src/net/sf/jailer/domainmodel/DomainModel.java b/src/net/sf/jailer/domainmodel/DomainModel.java index 43f6af6d2..9967445c3 100755 --- a/src/net/sf/jailer/domainmodel/DomainModel.java +++ b/src/net/sf/jailer/domainmodel/DomainModel.java @@ -26,9 +26,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import net.sf.jailer.datamodel.Association; -import net.sf.jailer.datamodel.DataModel; -import net.sf.jailer.datamodel.Table; +import net.sf.jailer.datamodel.*; import net.sf.jailer.util.CsvFile; import net.sf.jailer.util.PrintUtil; import net.sf.jailer.util.CsvFile.Line; diff --git a/src/net/sf/jailer/drivermanager/DriverNotFoundException.java b/src/net/sf/jailer/drivermanager/DriverNotFoundException.java new file mode 100644 index 000000000..37537fc1a --- /dev/null +++ b/src/net/sf/jailer/drivermanager/DriverNotFoundException.java @@ -0,0 +1,26 @@ +package net.sf.jailer.drivermanager; + +/** + * @author Vladimir "Dair T'arg" Berkutov + * @date: 09.02.2008 + * @time: 22:19:38 + */ +public class DriverNotFoundException extends Exception { + + protected String myRequestedServer; + + public DriverNotFoundException(String requestedServer) { + super("Driver for " + requestedServer + " has not found"); + myRequestedServer = requestedServer; + } + + /** + * Returns a server type for which driver has been requested. + * + * @return a server type for which driver has been requested. + */ + public String getRequestedServer() { + return myRequestedServer; + } + +} diff --git a/src/net/sf/jailer/drivermanager/JDBCDriverManager.java b/src/net/sf/jailer/drivermanager/JDBCDriverManager.java new file mode 100644 index 000000000..bd124ffbf --- /dev/null +++ b/src/net/sf/jailer/drivermanager/JDBCDriverManager.java @@ -0,0 +1,272 @@ +package net.sf.jailer.drivermanager; + +import java.io.File; +import java.io.IOException; +import java.io.FileReader; +import java.io.FileWriter; +import java.sql.Driver; +import java.sql.SQLException; +import java.sql.Connection; +import java.sql.DriverPropertyInfo; +import java.util.Properties; +import java.net.URL; +import java.net.URLClassLoader; +import java.lang.reflect.InvocationTargetException; + +/** + * This object manages the list of databases drivers. It performs loading of + * appropriate drivers during connection to a database server. + * + * Also, this object registers and unregisters a drivers. + * + * @author Vladimir "Dair T'arg" Berkutov + * @date: 08.02.2008 + * @time: 14:49:29 + */ +public final class JDBCDriverManager { + + /** + * A default configuration file path. + */ + public final static String DEFAULT_DRIVERS_LIST_FILE = "drivers/drivers.list"; + + /** + * A default drivers directory. + */ + public final static String DEFAULT_DRIVERS_DIRECTORY = "drivers/"; + + /** + * Initializes the {@code JDBCDriverManager} engine. + * + * @param configurationFile A configuration xml file with a list of + * installed drivers. + * @param driversDirectory A directory with drivers. + * + * @throws RuntimeException If configuration file could not be loaded. + */ + public static void initialize(String configurationFile, String driversDirectory) + throws RuntimeException { + myConfigurationFile = configurationFile; + myDriversDirectory = driversDirectory; + initialized = true; + try { + loadDriverList(configurationFile); + } catch (IOException exception) { + throw new RuntimeException("Driver list could not be loaded", exception); + } + } + + ///////////////////////// + // Initialization flag // + ///////////////////////// + + /** + * The flag of the {@code JDBCDriverManager} initialization. If the + * {@code JDBCDriverManager} is initialized then this flag is true, otherways + * this flag is false. + * + * @see #isInitialized() + */ + private static boolean initialized = false; + + /** + * Checks whether {@code JDBCDriverManager} engine is initialized or not. + * + * @return {@code true} if {@code JDBCDriverManager} is initialized and + * {@code false} if {@code JDBCDriverManager} is not initialized. + */ + public static boolean isInitialized() { + return initialized; + } + + //////////////////////// + // Configuration File // + //////////////////////// + + private static String myConfigurationFile; + + /** + * Returns a path to the configuration file with a drivers list. + * + * @return A path to the configuration file with a drivers list. + */ + public static String getConfigurationFile() { + return myConfigurationFile; + } + + /////////////////////// + // Drivers Directory // + /////////////////////// + + /** + * A path to a directory with drivers libraries. + * + * @see #getDriversDirectory() + */ + private static String myDriversDirectory = null; + + /** + * Returns a path to a directory with drivers libraries. + * + * @return A path to a directory with drivers libraries. + */ + public static String getDriversDirectory() { + if (!initialized) { + return ""; + } + if (myDriversDirectory == null) { + return ""; + } + return myDriversDirectory; + } + + ////////////////// + // Drivers List // + ////////////////// + + private final static Properties myDriversList = new Properties(); + + private static void loadDriverList(String configurationFile) + throws IOException { + myDriversList.load(new FileReader(configurationFile)); + } + + ///////// + // API // + ///////// + + /** + * Returns a {@link java.sql.Driver} instance for requested server type. + * + * @param serverName A database server type. + * @return A {@link java.sql.Driver} instance for requested server type. + * + * @throws RuntimeException If the {@code JDBCDriverManager} engine has not been + * initialized or if a driver instance could not be created. + * @throws DriverNotFoundException If no {@link java.sql.Driver}s has been + * found for the specified serverName. + * @throws IOException If a driver library could not be loaded. + */ + public static Driver getDriver(String serverName) + throws RuntimeException, DriverNotFoundException, IOException { + if (!initialized) { + initialize(DEFAULT_DRIVERS_LIST_FILE, DEFAULT_DRIVERS_DIRECTORY); + } + if (!myDriversList.containsKey(serverName)) { + throw new DriverNotFoundException(serverName); + } + String fullName = myDriversList.getProperty(serverName); + String libraryName = myDriversDirectory + getLibraryName(fullName); + String className = getClassName(fullName); + try { + return createDriverInstance(libraryName, className); + } catch (Exception exception) { + System.err.println(exception); + throw new RuntimeException("Could not load database server driver", exception); + } + } + + private static String getLibraryName(String fullName) { + return fullName.substring(0, fullName.indexOf('#')); + } + + private static String getClassName(String fullName) { + return fullName.substring(fullName.indexOf("#") + 1); + } + + private static Driver createDriverInstance(String libraryFileName, String className) + throws IOException, ClassNotFoundException, InstantiationException, + IllegalAccessException, NoSuchMethodException, InvocationTargetException { + URL libraryURL = new File(libraryFileName).toURI().toURL(); + // Adding libraryFileName to a system ClassLoader. + ClassLoader loader = new URLClassLoader(new URL[] {libraryURL}, + ClassLoader.getSystemClassLoader()); + try { + return new DriverProxy((Driver)loader.loadClass(className).newInstance()); + } catch (Exception exception) { + System.err.println(exception); + throw new ClassNotFoundException("Specified class is not an extension of java.sql.Driver", exception); + } + } + + /** + * Sets a driver for the server type. This method just adds a record to a + * drivers list and flushes it to a file. + * + * @param serverType A type of server - just a key for a driver. + * @param libraryName A name of .jar file with a library. + * @param className A name of class from a library which implements + * {@link java.sql.Driver}. + * + * @throws RuntimeException If there were some problems with writing drivers + * list to a file. + */ + public static void setDriver(String serverType, String libraryName, String className) + throws RuntimeException { + if (!initialized) { + initialize(DEFAULT_DRIVERS_LIST_FILE, DEFAULT_DRIVERS_DIRECTORY); + } + if (!isDriverExists(serverType)) { + return; + } + myDriversList.setProperty(serverType, libraryName + "#" + className); + try { + myDriversList.store(new FileWriter(myConfigurationFile), ""); + } catch (IOException exception) { + throw new RuntimeException("Driver list could not be written", exception); + } + } + + /** + * Checks whether driver is set for the specified server type or not. + * No library existing or file check is made - only a check for a list + * record + * + * @param serverType A server type. + * @return {@code true} If a default driver for the specified server type + * is set and {@code false} otherways. + */ + public static boolean isDriverExists(String serverType) { + if (!initialized) { + initialize(DEFAULT_DRIVERS_LIST_FILE, DEFAULT_DRIVERS_DIRECTORY); + } + return myDriversList.containsKey(serverType) + && myDriversList.getProperty(serverType).length() != 0; + } + + ////////////////// + // Driver Proxy // + ////////////////// + + /** + * A proxy for dynamicly loaded JDBC Driver. + * + * @author Wisser + */ + public static class DriverProxy implements Driver { + private final Driver driver; + private DriverProxy(Driver driver) { + this.driver = driver; + } + public boolean acceptsURL(String u) throws SQLException { + return driver.acceptsURL(u); + } + public Connection connect(String u, Properties p) throws SQLException { + return driver.connect(u, p); + } + public int getMajorVersion() { + return driver.getMajorVersion(); + } + public int getMinorVersion() { + return driver.getMinorVersion(); + } + public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException { + return driver.getPropertyInfo(u, p); + } + public boolean jdbcCompliant() { + return driver.jdbcCompliant(); + } + } + + +} diff --git a/src/net/sf/jailer/ui/DbConnectionDialog.java b/src/net/sf/jailer/ui/DbConnectionDialog.java index 35e6a1d4b..67220c3a9 100755 --- a/src/net/sf/jailer/ui/DbConnectionDialog.java +++ b/src/net/sf/jailer/ui/DbConnectionDialog.java @@ -19,13 +19,9 @@ import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; import java.sql.Connection; -import java.sql.Driver; import java.sql.DriverManager; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -41,6 +37,7 @@ import net.sf.jailer.database.StatementExecutor; import net.sf.jailer.modelbuilder.JDBCMetaDataBasedModelElementFinder; import net.sf.jailer.util.CsvFile; import net.sf.jailer.util.CsvFile.Line; +import net.sf.jailer.drivermanager.JDBCDriverManager; /** * "Connect with DB" dialog. @@ -375,20 +372,9 @@ public class DbConnectionDialog extends javax.swing.JDialog { if (d2.length() == 0) { d2 = null; } + try { - StatementExecutor.setClassLoaderForJdbcDriver(addJarToClasspath(d1, d2)); - } catch (Exception e) { - UIUtil.showException(this, "Error loading driver jars", e); - return; - } - - try { - if (StatementExecutor.classLoaderForJdbcDriver != null) { - Driver d = (Driver)Class.forName(driverClass.getText(), true, StatementExecutor.classLoaderForJdbcDriver).newInstance(); - DriverManager.registerDriver(new StatementExecutor.DriverShim(d)); - } else { - Class.forName(driverClass.getText()); - } + DriverManager.registerDriver(JDBCDriverManager.getDriver(driverClass.getText())); Connection con = DriverManager.getConnection(dbUrl.getText(), user.getText(), password.getText()); con.close(); isConnected = true; @@ -400,32 +386,6 @@ public class DbConnectionDialog extends javax.swing.JDialog { }//GEN-LAST:event_jButton1ActionPerformed - /** - * Adds one or two jars to classpath. - * - * @param jarName1 filename of jar 1 - * @param jarName2 filename of jar 2 - */ - private URLClassLoader addJarToClasspath(String jarName1, String jarName2) throws Exception { - URL[] urls; - if (jarName1 == null) { - if (jarName2 == null) { - return null; - } - jarName1 = jarName2; - jarName2 = null; - } - System.out.println("add '" + jarName1 + "' to classpath"); - if (jarName2 == null) { - urls = new URL[] {new URL("file", null, jarName1)}; - } else { - System.out.println("add '" + jarName2 + "' to classpath"); - urls = new URL[] {new URL("file", null, jarName1), new URL("file", null, jarName2)}; - } - URLClassLoader urlLoader = new URLClassLoader(urls); - return urlLoader; - } - /** * Selects the DB-schema to for introspection. *