You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2011/09/01 08:36:30 UTC

svn commit: r1163919 - in /openejb/trunk/openejb3/container/openejb-core/src: main/java/org/apache/openejb/config/ main/java/org/apache/openejb/resource/jdbc/ main/resources/META-INF/org.apache.openejb.embedded/ main/resources/META-INF/org.apache.opene...

Author: dblevins
Date: Thu Sep  1 06:36:30 2011
New Revision: 1163919

URL: http://svn.apache.org/viewvc?rev=1163919&view=rev
Log:
Slight rework of the DataSourceDefinition code.  Still in progress -- works with Driver's but not so well with DataSource

Modified:
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/DataSourceFactory.java
    openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml
    openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/DataSourceDefinitionTest.java

Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java?rev=1163919&r1=1163918&r2=1163919&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java Thu Sep  1 06:36:30 2011
@@ -1301,6 +1301,7 @@ public class AnnotationDeployer implemen
             def.setTransactional(dsDef.transactional());
             def.setInitialPoolSize(dsDef.initialPoolSize());
             def.setIsolationLevel(dsDef.isolationLevel());
+            def.setLoginTimeout(dsDef.loginTimeout());
             def.setMaxIdleTime(dsDef.maxIdleTime());
             def.setMaxPoolSize(dsDef.maxPoolSize());
             def.setMaxStatements(dsDef.maxStatements());

Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java?rev=1163919&r1=1163918&r2=1163919&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java Thu Sep  1 06:36:30 2011
@@ -16,6 +16,7 @@
  */
 package org.apache.openejb.config;
 
+import javax.sql.DataSource;
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Request;
@@ -765,6 +766,8 @@ public class AutoConfig implements Dynam
             ejbModule.setOpenejbJar(openejbJar);
         }
 
+        processDataSourceDefinitions(ejbModule);
+
         Map<String, EjbDeployment> deployments = openejbJar.getDeploymentsByEjbName();
 
         for (EnterpriseBean bean : ejbModule.getEjbJar().getEnterpriseBeans()) {
@@ -824,6 +827,100 @@ public class AutoConfig implements Dynam
         }
     }
 
+    private void processDataSourceDefinitions(Module module) throws OpenEJBException {
+        Set<DatasourceDefinition> datasources = module.getDatasources();
+
+        for (DatasourceDefinition datasource : datasources) {
+
+            String name = datasource.getName();
+            name = name.replaceFirst("java:comp/env/", "");
+            name = name.replaceFirst("java:", "");
+
+            Resource resource = new Resource(name, DataSource.class.getName());
+            Properties properties = resource.getProperties();
+            for (String s : datasource.getProperties()) {
+                final String key = s.substring(0, s.indexOf('='));
+                final String value = s.substring(s.indexOf('='));
+                properties.put(key, value);
+            }
+
+            properties.put("JtaManaged", datasource.isTransactional());
+            set(properties, "InitialSize", datasource.getInitialPoolSize());
+            set(properties, "DefaultIsolationLevel", datasource.getIsolationLevel());
+            set(properties, "LoginTimeout", datasource.getLoginTimeout(), 1);
+            set(properties, "MinEvictableIdleTimeMillis", datasource.getMaxIdleTime());
+            set(properties, "MaxIdle", datasource.getMaxPoolSize());
+            set(properties, "MinIdle", datasource.getMinPoolSize());
+            set(properties, "MaxStatements", datasource.getMaxStatements());
+            set(properties, "Password", datasource.getPassword());
+            set(properties, "JdbcUrl", datasource.getUrl());
+            set(properties, "UserName", datasource.getUser());
+            set(properties, "JdbcDriver", datasource.getClassName());
+
+            if (properties.get("JdbcUrl") == null) {
+                properties.put("JdbcUrl", getVendorUrl(datasource));
+            }
+
+            ResourceInfo resourceInfo = configFactory.configureService(resource, ResourceInfo.class);
+            installResource(module.getUniqueId(), resourceInfo);
+        }
+
+        datasources.clear();
+    }
+
+    private String getVendorUrl(DatasourceDefinition d) {
+
+        final String driver = d.getClassName();
+        final String serverName = d.getServerName();
+        final int port = d.getPortNumber();
+        final boolean remote = port != -1;
+        final String databaseName = d.getDatabaseName();
+
+        if (driver == null || driver.equals("org.hsqldb.jdbcDriver")) {
+            if (remote) {
+                return String.format("jdbc:hsqldb:hsql://%s:%s/%s", serverName, port, databaseName);
+            } else {
+                return String.format("jdbc:hsqldb:mem:%s", databaseName);
+            }
+        }
+
+        if (driver.equals("org.apache.derby.jdbc.EmbeddedDriver")) {
+            return String.format("jdbc:derby:%s;create=true", databaseName);
+        }
+
+        if (driver.equals("org.apache.derby.jdbc.ClientDriver")) {
+            return String.format("jdbc:derby://%s:%s/%s;create=true", serverName, port, databaseName);
+        }
+
+        if (driver.equals("com.mysql.jdbc.Driver")) {
+            return String.format("jdbc:mysql://%s:%s/%s", serverName, port, databaseName);
+        }
+
+        if (driver.equals("com.postgresql.jdbc.Driver")) {
+            return String.format("jdbc:postgresql://%s:%s/%s", serverName, port, databaseName);
+        }
+
+        if (driver.equals("oracle.jdbc.OracleDriver")) {
+            return String.format("jdbc:oracle:thin:@//%s:%s/%s", serverName, port, databaseName);
+        }
+
+        return null;
+    }
+
+    private void set(Properties properties, String key, String value) {
+        if (value == null || value.length() == 0) return;
+        properties.put(key, value);
+    }
+
+    private void set(Properties properties, String key, int value) {
+        set(properties, key, value, 0);
+    }
+
+    private void set(Properties properties, String key, int value, int min) {
+        if (value < min) return;
+        properties.put(key, value);
+    }
+
     private String createContainer(Class<? extends ContainerInfo> containerInfoType, EjbDeployment ejbDeployment, EnterpriseBean bean) throws OpenEJBException {
         if (!autoCreateContainers) {
             throw new OpenEJBException("A container of type " + getType(bean) + " must be declared in the configuration file for bean: " + bean.getEjbName());
@@ -1754,31 +1851,31 @@ public class AutoConfig implements Dynam
                 }
             }
 
-            for (EjbModule module : appModule.getEjbModules()) {
-                EnterpriseBean[] enterpriseBeans = module.getEjbJar().getEnterpriseBeans();
-                OpenejbJar openejbJar = module.getOpenejbJar();
-                Map<String, EjbDeployment> deployments = openejbJar.getDeploymentsByEjbName();
-
-                for (DatasourceDefinition ds : module.getDatasources()) {
-                    final String id = module.getUniqueId() + '/' + ds.getName().replace("java:", "");
-                    if (resourceIdsByType.get("javax.sql.DataSource") == null) {
-                        resourceIdsByType.put("javax.sql.DataSource", new ArrayList<String>());
-                    }
-                    resourceIdsByType.get("javax.sql.DataSource").add(id);
-
-                    for (EnterpriseBean bean : enterpriseBeans) {
-                        EjbDeployment ejbDeployment = deployments.get(bean.getEjbName());
-                        for (ResourceRef ref : bean.getResourceRef()) {
-                            if (ds.getName().equals(ref.getName())) {
-                                ResourceLink link = new ResourceLink();
-                                link.setResId(id);
-                                link.setResRefName(ds.getName());
-                                ejbDeployment.addResourceLink(link);
-                            }
-                        }
-                    }
-                }
-            }
+//            for (EjbModule module : appModule.getEjbModules()) {
+//                EnterpriseBean[] enterpriseBeans = module.getEjbJar().getEnterpriseBeans();
+//                OpenejbJar openejbJar = module.getOpenejbJar();
+//                Map<String, EjbDeployment> deployments = openejbJar.getDeploymentsByEjbName();
+//
+//                for (DatasourceDefinition ds : module.getDatasources()) {
+//                    final String id = module.getUniqueId() + '/' + ds.getName().replace("java:", "");
+//                    if (resourceIdsByType.get("javax.sql.DataSource") == null) {
+//                        resourceIdsByType.put("javax.sql.DataSource", new ArrayList<String>());
+//                    }
+//                    resourceIdsByType.get("javax.sql.DataSource").add(id);
+//
+//                    for (EnterpriseBean bean : enterpriseBeans) {
+//                        EjbDeployment ejbDeployment = deployments.get(bean.getEjbName());
+//                        for (ResourceRef ref : bean.getResourceRef()) {
+//                            if (ds.getName().equals(ref.getName())) {
+//                                ResourceLink link = new ResourceLink();
+//                                link.setResId(id);
+//                                link.setResRefName(ds.getName());
+//                                ejbDeployment.addResourceLink(link);
+//                            }
+//                        }
+//                    }
+//                }
+//            }
         }
 
         public List<String> getResourceIds(String type) {

Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/DataSourceFactory.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/DataSourceFactory.java?rev=1163919&r1=1163918&r2=1163919&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/DataSourceFactory.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/DataSourceFactory.java Thu Sep  1 06:36:30 2011
@@ -16,17 +16,78 @@
  */
 package org.apache.openejb.resource.jdbc;
 
+import org.apache.commons.dbcp.ConnectionFactory;
+import org.apache.commons.dbcp.DataSourceConnectionFactory;
+import org.apache.commons.dbcp.managed.DataSourceXAConnectionFactory;
+import org.apache.commons.dbcp.managed.LocalXAConnectionFactory;
+import org.apache.commons.dbcp.managed.TransactionRegistry;
+import org.apache.commons.dbcp.managed.XAConnectionFactory;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.resource.XAResourceWrapper;
+import org.apache.xbean.recipe.ObjectRecipe;
+import org.apache.xbean.recipe.Option;
 
 import javax.sql.DataSource;
+import javax.sql.XADataSource;
+import java.lang.reflect.Field;
+import java.sql.SQLException;
+import java.util.Map;
 
 /**
  * @version $Rev$ $Date$
  */
 public class DataSourceFactory {
-    public static DataSource create(boolean managed){
-        if (managed){
+
+    public static DataSource create(boolean managed, Class impl) throws IllegalAccessException, InstantiationException {
+
+        // TODO This part needs to get reworked a bit
+        if (DataSource.class.isAssignableFrom(impl)) {
+
+//            ObjectRecipe objectRecipe = new ObjectRecipe(className);
+//            objectRecipe.allow(Option.FIELD_INJECTION);
+//            objectRecipe.allow(Option.PRIVATE_PROPERTIES);
+//            objectRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
+//            objectRecipe.allow(Option.NAMED_PARAMETERS);
+//
+//            if (port <= 0) {
+//                objectRecipe.setProperty("url", url);
+//            } else {
+//                objectRecipe.setProperty("serverName", server);
+//                objectRecipe.setProperty("portNumber", port);
+//            }
+//
+//            for (Map.Entry<Object, Object> prop : resourceInfo.properties.entrySet()) {
+//                String name = (String) prop.getKey();
+//                if (!MANUALLY_SET_PROPERTIES.contains(name)) {
+//                    Object value = prop.getValue();
+//                    if (value != null
+//                            && ((value instanceof Number && ((Number) value).intValue() > 0)
+//                                || !(value instanceof Number))) {
+//                        objectRecipe.setProperty(name, value);
+//                    }
+//                    if (name.endsWith("Name")) {
+//                        // depending of implementations...
+//                        objectRecipe.setProperty(name.substring(0, name.length() - 4), value);
+//                    }
+//                }
+//            }
+//
+//            ds = (DataSource) objectRecipe.create(classLoader);
+
+            DataSource dataSource = (DataSource) impl.newInstance();
+
+            if (managed) {
+                return new DbcpManagedDataSource(dataSource);
+            } else {
+                return new DbcpDataSource(dataSource);
+            }
+        }
+
+        return create(managed);
+    }
+
+    public static DataSource create(boolean managed) {
+        if (managed) {
             XAResourceWrapper xaResourceWrapper = SystemInstance.get().getComponent(XAResourceWrapper.class);
             if (xaResourceWrapper != null) {
                 return new ManagedDataSourceWithRecovery(xaResourceWrapper);
@@ -37,4 +98,74 @@ public class DataSourceFactory {
             return new BasicDataSource();
         }
     }
+
+    public static class DbcpDataSource extends BasicDataSource {
+
+        private final DataSource dataSource;
+
+        public DbcpDataSource(DataSource dataSource) {
+            this.dataSource = dataSource;
+        }
+
+        @Override
+        protected ConnectionFactory createConnectionFactory() throws SQLException {
+            return new DataSourceConnectionFactory(dataSource, username, password);
+        }
+
+        @Override
+        public void setJdbcUrl(String string) {
+            // TODO This is a big whole and we will need to rework this
+            if (dataSource instanceof org.hsqldb.jdbc.jdbcDataSource) {
+                ((org.hsqldb.jdbc.jdbcDataSource)dataSource).setDatabase(string);
+            }
+        }
+    }
+
+    public static class DbcpManagedDataSource extends BasicManagedDataSource {
+
+        private final DataSource dataSource;
+
+        public DbcpManagedDataSource(DataSource dataSource) {
+            this.dataSource = dataSource;
+        }
+
+        @Override
+        public void setJdbcUrl(String string) {
+            // TODO This is a big whole and we will need to rework this
+            if (dataSource instanceof org.hsqldb.jdbc.jdbcDataSource) {
+                ((org.hsqldb.jdbc.jdbcDataSource)dataSource).setDatabase(string);
+            }
+        }
+
+        @Override
+        protected ConnectionFactory createConnectionFactory() throws SQLException {
+
+            if (dataSource instanceof XADataSource) {
+
+                // Create the XAConectionFactory using the XA data source
+                XADataSource xaDataSourceInstance = (XADataSource) dataSource;
+                XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), xaDataSourceInstance, username, password);
+                setTransactionRegistry(xaConnectionFactory.getTransactionRegistry());
+                return xaConnectionFactory;
+
+            } else {
+
+                // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory
+                ConnectionFactory connectionFactory = new DataSourceConnectionFactory(dataSource, username, password);
+                XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), connectionFactory);
+                setTransactionRegistry(xaConnectionFactory.getTransactionRegistry());
+                return xaConnectionFactory;
+            }
+        }
+
+        public void setTransactionRegistry(TransactionRegistry registry) {
+            try {
+                final Field field = org.apache.commons.dbcp.managed.BasicManagedDataSource.class.getDeclaredField("transactionRegistry");
+                field.setAccessible(true);
+                field.set(this, registry);
+            } catch (Exception e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
 }

Modified: openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml?rev=1163919&r1=1163918&r2=1163919&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb.embedded/service-jar.xml Thu Sep  1 06:36:30 2011
@@ -517,7 +517,7 @@
           service="Resource"
           types="javax.sql.DataSource, DataSource"
           factory-name="create"
-          constructor="JtaManaged"
+          constructor="JtaManaged, JdbcDriver"
           class-name="org.apache.openejb.resource.jdbc.DataSourceFactory">
 
     # Determines wether or not this data source should be JTA managed
@@ -713,7 +713,7 @@
           service="Resource"
           types="javax.sql.DataSource, DataSource"
           factory-name="create"
-          constructor="JtaManaged"
+          constructor="JtaManaged, JdbcDriver"
           class-name="org.apache.openejb.resource.jdbc.DataSourceFactory">
 
     JtaManaged = false

Modified: openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml?rev=1163919&r1=1163918&r2=1163919&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml Thu Sep  1 06:36:30 2011
@@ -526,7 +526,7 @@
           service="Resource"
           types="javax.sql.DataSource, DataSource"
           factory-name="create"
-          constructor="JtaManaged"
+          constructor="JtaManaged, JdbcDriver"
           class-name="org.apache.openejb.resource.jdbc.DataSourceFactory">
 
     # Determines wether or not this data source should be JTA managed
@@ -723,7 +723,7 @@
           service="Resource"
           types="javax.sql.DataSource, DataSource"
           factory-name="create"
-          constructor="JtaManaged"
+          constructor="JtaManaged, JdbcDriver"
           class-name="org.apache.openejb.resource.jdbc.DataSourceFactory">
 
     JtaManaged = false

Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/DataSourceDefinitionTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/DataSourceDefinitionTest.java?rev=1163919&r1=1163918&r2=1163919&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/DataSourceDefinitionTest.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/DataSourceDefinitionTest.java Thu Sep  1 06:36:30 2011
@@ -12,7 +12,14 @@ import javax.ejb.EJB;
 import javax.ejb.Singleton;
 import javax.ejb.Stateless;
 import javax.sql.DataSource;
+import javax.transaction.UserTransaction;
 import java.lang.reflect.Field;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
@@ -43,7 +50,7 @@ public class DataSourceDefinitionTest {
     )
     @Singleton
     public static class DatasourceDefinitionBean {
-        @Resource(lookup = "java:comp/env/superDS") private DataSource ds;
+        @Resource(name = "java:comp/env/superDS") private DataSource ds;
 
         public DataSource getDs() {
             return ds;
@@ -54,22 +61,22 @@ public class DataSourceDefinitionTest {
         @DataSourceDefinition(
             name = "java:comp/env/superMegaDS",
             className = "org.hsqldb.jdbc.jdbcDataSource",
-            user = "foo1",
-            password = "bar1",
+            user = "sa",
+            password = "",
             url = "jdbc:hsqldb:mem:superDS"
         ),
         @DataSourceDefinition(
             name = "java:comp/env/superGigaDS",
             className = "org.hsqldb.jdbc.jdbcDataSource",
-            user = "foo2",
-            password = "bar2",
+            user = "sa",
+            password = "",
             url = "jdbc:hsqldb:mem:superDS"
         )
     })
     @Stateless
     public static class DatasourceDefinitionsBean {
-        @Resource(lookup = "java:comp/env/superMegaDS") private DataSource mega;
-        @Resource(lookup = "java:comp/env/superGigaDS") private DataSource giga;
+        @Resource(name = "java:comp/env/superMegaDS") private DataSource mega;
+        @Resource(name = "java:comp/env/superGigaDS") private DataSource giga;
 
         public DataSource getMega() {
             return mega;
@@ -89,11 +96,29 @@ public class DataSourceDefinitionTest {
         assertDataSourceDefinitionValues(multipleDatasources.getGiga(), "org.hsqldb.jdbc.jdbcDataSource", "foo2", "bar2");
     }
 
-    private void assertDataSourceDefinitionValues(Object value, String clazz, String user, String password) throws Exception {
-        assertNotNull("injection should work", value);
-        assertEquals("configuration should be ok - class", "org.hsqldb.jdbc.jdbcDataSource", value.getClass().getName());
-        assertEqualsByReflection("configuration should be ok - user", value, "user", user);
-        assertEqualsByReflection("configuration should be ok - password", value, "password", password);
+    private void assertDataSourceDefinitionValues(DataSource dataSource, String clazz, String user, String password) throws Exception {
+        assertNotNull("injection should work", dataSource);
+
+        Movies movies = new Movies(dataSource);
+
+        movies.addMovie(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992));
+        movies.addMovie(new Movie("Joel Coen", "Fargo", 1996));
+        movies.addMovie(new Movie("Joel Coen", "The Big Lebowski", 1998));
+
+        List<Movie> list = movies.getMovies();
+        assertEquals("List.size()", 3, list.size());
+
+        for (Movie movie : list) {
+            movies.deleteMovie(movie);
+        }
+
+        assertEquals("Movies.getMovies()", 0, movies.getMovies().size());
+
+        Connection connection = dataSource.getConnection();
+        connection.prepareStatement("DROP TABLE movie").execute();
+//        assertEquals("configuration should be ok - class", "org.hsqldb.jdbc.jdbcDataSource", dataSource.getClass().getName());
+//        assertEqualsByReflection("configuration should be ok - user", dataSource, "user", user);
+//        assertEqualsByReflection("configuration should be ok - password", dataSource, "password", password);
     }
 
     private void assertEqualsByReflection(String message, Object value, String name, Object expected) throws Exception {
@@ -111,4 +136,103 @@ public class DataSourceDefinitionTest {
         }
 
     }
+
+    public static class Movies {
+        private final DataSource movieDatabase;
+
+        public Movies(DataSource movieDatabase) throws SQLException {
+            this.movieDatabase = movieDatabase;
+
+            Connection connection = movieDatabase.getConnection();
+            PreparedStatement stmt = connection.prepareStatement("CREATE TABLE movie ( director VARCHAR(255), title VARCHAR(255), year integer)");
+            stmt.execute();
+
+        }
+
+        public void addMovie(Movie movie) throws Exception {
+            Connection conn = movieDatabase.getConnection();
+            try {
+                PreparedStatement sql = conn.prepareStatement("INSERT into movie (director, title, year) values (?, ?, ?)");
+                sql.setString(1, movie.getDirector());
+                sql.setString(2, movie.getTitle());
+                sql.setInt(3, movie.getYear());
+                sql.execute();
+            } finally {
+                conn.close();
+            }
+        }
+
+
+        public void deleteMovie(Movie movie) throws Exception {
+            Connection conn = movieDatabase.getConnection();
+            try {
+                PreparedStatement sql = conn.prepareStatement("DELETE from movie where director = ? AND title = ? AND year = ?");
+                sql.setString(1, movie.getDirector());
+                sql.setString(2, movie.getTitle());
+                sql.setInt(3, movie.getYear());
+                sql.execute();
+            } finally {
+                conn.close();
+            }
+        }
+
+        public List<Movie> getMovies() throws Exception {
+            ArrayList<Movie> movies = new ArrayList<Movie>();
+            Connection conn = movieDatabase.getConnection();
+            try {
+                PreparedStatement sql = conn.prepareStatement("SELECT director, title, year from movie");
+                ResultSet set = sql.executeQuery();
+                while (set.next()) {
+                    Movie movie = new Movie();
+                    movie.setDirector(set.getString("director"));
+                    movie.setTitle(set.getString("title"));
+                    movie.setYear(set.getInt("year"));
+                    movies.add(movie);
+                }
+
+            } finally {
+                conn.close();
+            }
+            return movies;
+        }
+    }
+
+    public static class Movie {
+        private String director;
+        private String title;
+        private int year;
+
+        public Movie() {
+        }
+
+        public Movie(String director, String title, int year) {
+            this.director = director;
+            this.title = title;
+            this.year = year;
+        }
+
+        public String getDirector() {
+            return director;
+        }
+
+        public void setDirector(String director) {
+            this.director = director;
+        }
+
+        public String getTitle() {
+            return title;
+        }
+
+        public void setTitle(String title) {
+            this.title = title;
+        }
+
+        public int getYear() {
+            return year;
+        }
+
+        public void setYear(int year) {
+            this.year = year;
+        }
+    }
 }