package net.sourceforge.mayfly; import net.sourceforge.mayfly.datastore.DataStore; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.util.HashMap; import java.util.Properties; /** * JDBC Driver for Mayfly. * * <p>In many cases, it will be more convenient to instantiate a * {@link net.sourceforge.mayfly.Database} object, and then * call {@link net.sourceforge.mayfly.Database#openConnection()} * on it (from there on out, you don't need to do anything * mayfly-specific).</p> * * <p>However, if you want to create a JDBC connection via * non-mayfly-specific means (for example, via a database * mapping layer like Hibernate or SqlMaps), you may need * to access mayfly via a JDBC URL. This creates a bit of * work in terms of getting your tests to run independently * of each other (one line summary: call {@link #shutdown} * from your tearDown method).</p> * * <p>One connects via standard JDBC mechanisms, using * a JDBC URL of one of the following two forms:</p> * * <ol> * <li>The URL <tt>jdbc:mayfly:</tt> means to open * the default database. The default database will be * created if it doesn't exist (without tables or data). * The default database is destroyed by calling {@link #shutdown}. * </li> * * <li>If you want to start a database with some tables or * data, you can call {@link #create(DataStore)}, which will * return a new URL to you. This database lives until the next * call to {@link #shutdown}. * </li> * </ol> * * <p>If you specify a username or a password via JDBC, * Mayfly ignores them.</p> * * <p>Example:</p> * <pre> * Class.forName("net.sourceforge.mayfly.JdbcDriver"); * Connection connection = DriverManager.getConnection("jdbc:mayfly:"); * </pre> */ public class JdbcDriver implements Driver { /** * Create a database which you want to access via a JDBC URL. * * For many purposes, it will be more convenient to * instantiate a {@link Database} object, but if you need a * JDBC URL (for example, to pass to a database mapping layer * like Hibernate or SqlMaps), call this method instead. * * <p>Example:</p> <pre> static final DataStore standardSetup = makeData(); private static DataStore makeData() {     try {         Database original = new Database();         original.execute("create table foo (a integer)");         original.execute("insert into foo(a) values(6)");         return original.dataStore();     } catch (SQLException e) {         throw new RuntimeException(e);     } } String jdbcUrl; public void setUp() {     jdbcUrl = JdbcDriver.create(standardSetup); } public void tearDown() {     JdbcDriver.shutdown(); } </pre> * @param dataStore The initial contents of the database. * @return A JDBC URL which you can use to access the database. */ public static String create(DataStore dataStore) { return getMayflyDriver().createInDriver(dataStore); } /** * Get a snapshot of the database which is accessed by the given JDBC * URL. The JDBC URL must point to a database managed by * {@link JdbcDriver} (including the default database). */ public static DataStore snapshot(String url) { try { return getMayflyDriver().findDatabase(url).dataStore(); } catch (MayflySqlException e) { throw e.asRuntimeException(); } } /** * Destroy databases. * * Destroy databases managed by {@link JdbcDriver}. That is, destroy * all databases which have been created with {@link #create(DataStore)}, * plus the default database (the one with url <tt>jdbc:mayfly:</tt>).</p> * * <p>Databases created by calling constructors of {@link Database} directly * are instead garbage collected like any other object.</p> */ public static void shutdown() { getMayflyDriver().shutdownInDriver(); } private static JdbcDriver getMayflyDriver() { try { return (JdbcDriver) DriverManager.getDriver(JDBC_URL_PREFIX); } catch (SQLException e) { throw new RuntimeException(e); } } private static final String JDBC_URL_PREFIX = "jdbc:mayfly:"; private static final String DEFAULT_DATABASE = JDBC_URL_PREFIX; static { try { DriverManager.registerDriver(new JdbcDriver()); } catch (SQLException e) { // This prevents the class from being loaded and propagates // some kind of exception up to whoever tried to load it, right? // Is that right? Is there another way to handle it? throw new RuntimeException(e); } } private HashMap databases = new HashMap(); private int nextId; public Connection connect(String url, Properties info) throws SQLException { return findDatabase(url).openConnection(); } private Database findDatabase(String url) throws MayflySqlException { if (DEFAULT_DATABASE.equals(url)) { if (!databases.containsKey(DEFAULT_DATABASE)) { databases.put(DEFAULT_DATABASE, new Database()); } } if (databases.containsKey(url)) { return (Database) databases.get(url); } else { throw new MayflyException( "Mayfly JDBC URL " + url + " not recognized").asSqlException(); } } private String createInDriver(DataStore dataStore) { String url = JDBC_URL_PREFIX + nextId++; databases.put(url, new Database(dataStore)); return url; } private void shutdownInDriver() { databases = new HashMap(); } public boolean acceptsURL(String url) throws SQLException { return url != null && url.startsWith(JDBC_URL_PREFIX); } public DriverPropertyInfo[] getPropertyInfo(String url, Properties properties) throws SQLException { throw new UnimplementedException(); } public int getMajorVersion() { throw new UnimplementedException(); } public int getMinorVersion() { throw new UnimplementedException(); } public boolean jdbcCompliant() { return false; } }