diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
index e16f899d4..248c3d720 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
@@ -35,6 +35,7 @@ import java.util.Set;
import javax.sql.DataSource;
+import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.event.OrphanedKeyAction;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
@@ -43,6 +44,7 @@ import org.apache.openjpa.jdbc.meta.Discriminator;
import org.apache.openjpa.jdbc.meta.FieldMapping;
import org.apache.openjpa.jdbc.meta.ValueMapping;
import org.apache.openjpa.jdbc.meta.strats.SuperclassDiscriminatorStrategy;
+import org.apache.openjpa.jdbc.schema.DataSourceFactory;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
import org.apache.openjpa.jdbc.sql.Joins;
@@ -146,14 +148,47 @@ public class JDBCStoreManager
if (lm instanceof JDBCLockManager)
_lm = (JDBCLockManager) lm;
- if (!ctx.isManaged() && _conf.isConnectionFactoryModeManaged())
- _ds = _conf.getDataSource2(ctx);
- else
- _ds = _conf.getDataSource(ctx);
+ _ds = getDataSource(ctx);
if (_conf.getUpdateManagerInstance().orderDirty())
ctx.setOrderDirtyObjects(true);
}
+
+ private final boolean useConnectionFactory2(StoreContext ctx) {
+ return (!ctx.isManaged() && _conf.isConnectionFactoryModeManaged());
+ }
+
+ private final DataSource getDataSource(StoreContext ctx) {
+ DataSource ds;
+
+ if (useConnectionFactory2(ctx)) {
+ ds = (DataSource) ctx.getConnectionFactory2();
+ if (ds != null) {
+ ds = DataSourceFactory.decorateDataSource(ds, _conf, false);
+ }
+ else {
+ ds = _conf.getDataSource2(ctx);
+ }
+ } else {
+ ds = (DataSource) ctx.getConnectionFactory();
+ if (ds != null) {
+ ds = DataSourceFactory.decorateDataSource(ds, _conf, false);
+ }
+ else {
+ ds = _conf.getDataSource(ctx);
+ }
+ }
+ return ds;
+ }
+
+ private boolean useContextToGetDataSource(StoreContext ctx) {
+ // configuration check to enable goes here.
+ if (StringUtils.isBlank(ctx.getConnectionFactoryName())
+ && StringUtils.isBlank(ctx.getConnectionFactory2Name())) {
+ return false;
+ }
+ return true;
+ }
public JDBCConfiguration getConfiguration() {
return _conf;
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java
index bf696e944..bfc2ddf86 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java
@@ -183,6 +183,11 @@ public abstract class AbstractBrokerFactory
}
public Broker newBroker(String user, String pass, boolean managed, int connRetainMode, boolean findExisting) {
+ return newBroker(user, pass, managed, connRetainMode, findExisting, "", "");
+ }
+
+ public Broker newBroker(String user, String pass, boolean managed, int connRetainMode, boolean findExisting,
+ String cf1Name, String cf2Name) {
try {
assertOpen();
makeReadOnly();
@@ -192,6 +197,8 @@ public abstract class AbstractBrokerFactory
broker = findBroker(user, pass, managed);
if (broker == null) {
broker = newBrokerImpl(user, pass);
+ broker.setConnectionFactoryName(cf1Name);
+ broker.setConnectionFactory2Name(cf2Name);
initializeBroker(managed, connRetainMode, broker, false);
}
return broker;
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerFactory.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerFactory.java
index 9c135280e..34a87005a 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerFactory.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerFactory.java
@@ -74,6 +74,29 @@ public interface BrokerFactory
*/
public Broker newBroker(String user, String pass, boolean managed,
int connRetainMode, boolean findExisting);
+
+ /**
+ * Return a new broker using the supplied
+ *
+ * - credentials
+ * - transaction management mode
+ * - connectionRetainMode
+ * - connectionFactories
+ *
+ *
+ * @param user Username to use when obtaining a connection. Will be ignored if a connection factory is
+ * obtained from JNDI.
+ * @param pass Password to use when obtaining a connection. Will be ignored if a connection factory is
+ * obtained from JNDI.
+ * @param managed Whether managed transactions will be used by this Broker
+ * @param connRetainMode {@link ConnectionRetainMode}
+ * @param findExisting Whether the internal pool of brokers should be used.
+ * @param cfName JTA ConnectionFactory to use
+ * @param cf2Name Non-JTA ConnectionFactory to use.
+ * @return A Broker which matches the provided criteria.
+ */
+ public Broker newBroker(String user, String pass, boolean managed,
+ int connRetainMode, boolean findExisting, String cfName, String cf2Name);
/**
* Register a listener for lifecycle-related events on the specified
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
index b670f93a4..65c83f306 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
@@ -47,6 +47,7 @@ import org.apache.commons.collections.iterators.IteratorChain;
import org.apache.commons.collections.map.IdentityMap;
import org.apache.commons.collections.map.LinkedMap;
import org.apache.commons.collections.set.MapBackedSet;
+import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.Compatibility;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.datacache.DataCache;
@@ -60,6 +61,7 @@ import org.apache.openjpa.event.RemoteCommitEventManager;
import org.apache.openjpa.event.TransactionEvent;
import org.apache.openjpa.event.TransactionEventManager;
import org.apache.openjpa.kernel.exps.ExpressionParser;
+import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
@@ -144,6 +146,9 @@ public class BrokerImpl
private static final int FLAG_TRANS_ENDING = 2 << 11;
private static final Object[] EMPTY_OBJECTS = new Object[0];
+
+ private String _connectionFactoryName = "";
+ private String _connectionFactory2Name = "";
private static final Localizer _loc =
Localizer.forPackage(BrokerImpl.class);
@@ -4970,4 +4975,60 @@ public class BrokerImpl
public boolean isFromWriteBehindCallback() {
return _fromWriteBehindCallback;
}
+
+ /**
+ * Return the 'JTA' connectionFactoryName
+ */
+ public String getConnectionFactoryName() {
+ return _connectionFactoryName;
+ }
+
+ /**
+ * Set the 'JTA' ConnectionFactoryName. Input will be trimmed to null before being stored.
+ */
+ public void setConnectionFactoryName(String connectionFactoryName) {
+ this._connectionFactoryName = StringUtils.trimToNull(connectionFactoryName);
+ }
+
+ /**
+ * Return the 'NonJTA' ConnectionFactoryName.
+ */
+ public String getConnectionFactory2Name() {
+ return _connectionFactory2Name;
+ }
+
+ /**
+ * Set the 'NonJTA' ConnectionFactoryName. Input will be trimmed to null before being stored.
+ */
+ public void setConnectionFactory2Name(String connectionFactory2Name) {
+ this._connectionFactory2Name = StringUtils.trimToNull(connectionFactory2Name);
+ }
+
+ /**
+ * Return the 'JTA' ConnectionFactory, looking it up from JNDI if needed.
+ *
+ * @return the JTA connection factory or null if connectionFactoryName is blank.
+ */
+ public Object getConnectionFactory() {
+ if(StringUtils.isNotBlank(_connectionFactoryName)) {
+ return Configurations.lookup(_connectionFactoryName, "openjpa.ConnectionFactory", _log );
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the 'NonJTA' ConnectionFactory, looking it up from JNDI if needed.
+ *
+ * @return the NonJTA connection factory or null if connectionFactoryName is blank.
+ */
+ public Object getConnectionFactory2() {
+ if(StringUtils.isNotBlank(_connectionFactory2Name)) {
+ return Configurations.lookup(_connectionFactory2Name, "openjpa.ConnectionFactory2", _log);
+ }
+ else {
+ return null;
+ }
+ }
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
index d035eb114..d237e53ec 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
@@ -1418,4 +1418,28 @@ public class DelegatingBroker
public void setCachePreparedQuery(boolean flag) {
_broker.setCachePreparedQuery(flag);
}
+
+ public String getConnectionFactoryName() {
+ return _broker.getConnectionFactoryName();
+ }
+
+ public void setConnectionFactoryName(String connectionFactoryName) {
+ _broker.setConnectionFactoryName(connectionFactoryName);
+ }
+
+ public String getConnectionFactory2Name() {
+ return _broker.getConnectionFactory2Name();
+ }
+
+ public void setConnectionFactory2Name(String connectionFactory2Name) {
+ _broker.setConnectionFactory2Name(connectionFactory2Name);
+ }
+
+ public Object getConnectionFactory() {
+ return _broker.getConnectionFactory();
+ }
+
+ public Object getConnectionFactory2() {
+ return _broker.getConnectionFactory2();
+ }
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBrokerFactory.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBrokerFactory.java
index 08672d4c1..533e1f8bd 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBrokerFactory.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBrokerFactory.java
@@ -148,9 +148,13 @@ public class DelegatingBrokerFactory
public Broker newBroker(String user, String pass, boolean managed,
int connRetainMode, boolean findExisting) {
+ return newBroker(user, pass, managed, connRetainMode, findExisting, "", "");
+ }
+ public Broker newBroker(String user, String pass, boolean managed,
+ int connRetainMode, boolean findExisting, String cfName, String cf2Name) {
try {
return _factory.newBroker(user, pass, managed, connRetainMode,
- findExisting);
+ findExisting, cfName, cf2Name);
} catch (RuntimeException re) {
throw translate(re);
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java
index ccdd2574a..0fd516710 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java
@@ -456,4 +456,38 @@ public interface StoreContext {
* Releases the internal lock.
*/
public void unlock ();
+
+ /**
+ * Return the 'JTA' connectionFactoryName
+ */
+ public String getConnectionFactoryName();
+
+ /**
+ * Set the 'JTA' ConnectionFactoryName.
+ */
+ public void setConnectionFactoryName(String connectionFactoryName);
+
+ /**
+ * Return the 'NonJTA' ConnectionFactoryName.
+ */
+ public String getConnectionFactory2Name();
+
+ /**
+ * Set the 'NonJTA' ConnectionFactoryName.
+ */
+ public void setConnectionFactory2Name(String connectionFactory2Name);
+
+ /**
+ * Return the 'JTA' ConnectionFactory, looking it up from JNDI if needed.
+ *
+ * @return the JTA connection factory or null if connectionFactoryName is blank.
+ */
+ public Object getConnectionFactory();
+
+ /**
+ * Return the 'NonJTA' ConnectionFactory, looking it up from JNDI if needed.
+ *
+ * @return the NonJTA connection factory or null if connectionFactoryName is blank.
+ */
+ public Object getConnectionFactory2();
}
diff --git a/openjpa-persistence-jdbc/pom.xml b/openjpa-persistence-jdbc/pom.xml
index 718fb2505..349330a9b 100644
--- a/openjpa-persistence-jdbc/pom.xml
+++ b/openjpa-persistence-jdbc/pom.xml
@@ -712,6 +712,12 @@
jaxb-impl
test
+
+ simple-jndi
+ simple-jndi
+ 0.11.4
+ test
+
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/Person.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/Person.java
new file mode 100644
index 000000000..ab16c2f81
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/Person.java
@@ -0,0 +1,55 @@
+package org.apache.openjpa.persistence.conf;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Version;
+
+// override defaults to attempt to prevent collisions.
+@Entity(name="confPerson")
+@Table(name="CONF_PERSON")
+public class Person {
+
+ @Id
+ private int id;
+
+ @Version
+ private int version;
+
+ @Column(length=16)
+ private String name;
+
+ public Person() {
+ super();
+ }
+
+ public Person(int id) {
+ super();
+ setId(id);
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestSwitchConnection.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestSwitchConnection.java
new file mode 100644
index 000000000..c2d5ef286
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/conf/TestSwitchConnection.java
@@ -0,0 +1,107 @@
+package org.apache.openjpa.persistence.conf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.RollbackException;
+
+import org.apache.openjpa.persistence.test.AbstractPersistenceTestCase;
+
+public class TestSwitchConnection extends AbstractPersistenceTestCase {
+ private String defaultJndiName = "jdbc/mocked";
+ private String[] jndiNames = { "jdbc/mocked1" };
+
+ protected void initEMF(String cfName) {
+ EntityManagerFactory emf = getEmf("openjpa.ConnectionFactoryName", cfName);
+
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ em.createQuery("Delete from confPerson").executeUpdate();
+ em.getTransaction().commit();
+ em.close();
+
+ emf.close();
+ }
+
+ protected EntityManagerFactory getEmf(String cfPropertyName, String cfPropertyValue) {
+ // null out the driver to prevent system properties from taking effect.
+ // do not set connectionFactoryModeManaged - or connectionFactory2 will be used.
+ return createEMF(
+ "openjpa.ConnectionDriverName", "",
+ cfPropertyName, cfPropertyValue,
+ Person.class);
+ }
+
+ protected EntityManager getEm(EntityManagerFactory emf, String name, String value) {
+ Map props = new HashMap();
+ props.put(name, value);
+ return emf.createEntityManager(props);
+ }
+
+ protected void createTables() {
+ // create an EMF for each database;
+ initEMF(defaultJndiName);
+ initEMF(jndiNames[0]);
+ }
+
+ public void testConnectionFactoryName() {
+ // split out so that we can try javax.persistence.jtaDataSource in the future.
+ overridePropertyOnEM("openjpa.ConnectionFactoryName", jndiNames[0]);
+ }
+
+ public void overridePropertyOnEM(String name, String value) {
+ // TODO Disable for non derby.
+ createTables();
+
+ // use the default JndiName for the base EntityManagerFactory
+ EntityManagerFactory emf = getEmf(name, defaultJndiName);
+ assertNotNull(emf);
+
+ EntityManager em = emf.createEntityManager();
+ assertNotNull(em);
+
+ EntityManager em1 = getEm(emf, name, value);
+ assertNotNull(em1);
+
+ // 'prove' that we're using a different database by inserting the same row
+ em.getTransaction().begin();
+ em.persist(new Person(1));
+ em.getTransaction().commit();
+
+ em1.getTransaction().begin();
+ em1.persist(new Person(1));
+ em1.getTransaction().commit();
+
+ em.clear();
+ em1.clear();
+
+ // sanity test, make sure inserting the same row again fails.
+
+ em.getTransaction().begin();
+ em.persist(new Person(1));
+ try {
+ em.getTransaction().commit();
+ fail("Should not be able to commit the same row a second time");
+ } catch (RollbackException rbe) {
+ assertTrue(rbe.getCause() instanceof EntityExistsException);
+ // expected
+ }
+
+ em1.getTransaction().begin();
+ em1.persist(new Person(1));
+ try {
+ em1.getTransaction().commit();
+ fail("Should not be able to commit the same row a second time");
+ } catch (RollbackException rbe) {
+ assertTrue(rbe.getCause() instanceof EntityExistsException);
+ // expected
+ }
+
+ em.close();
+ em1.close();
+ emf.close();
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/resources/jndi.properties b/openjpa-persistence-jdbc/src/test/resources/jndi.properties
new file mode 100644
index 000000000..72b75310d
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/resources/jndi.properties
@@ -0,0 +1,4 @@
+ java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
+ org.osjava.sj.root=src/test/resources/simple-jndi
+ org.osjava.sj.colon.replace=--
+ org.osjava.sj.delimiter=/
diff --git a/openjpa-persistence-jdbc/src/test/resources/simple-jndi/jdbc/default.properties b/openjpa-persistence-jdbc/src/test/resources/simple-jndi/jdbc/default.properties
new file mode 100644
index 000000000..5800ae0d2
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/resources/simple-jndi/jdbc/default.properties
@@ -0,0 +1,11 @@
+mocked/type=javax.sql.DataSource
+mocked/driver=org.apache.derby.jdbc.EmbeddedDriver
+mocked/url=jdbc:derby:target/database/jpa-jndi-database;create=true
+mocked/user=app
+mocked/password=app
+
+mocked1/type=javax.sql.DataSource
+mocked1/driver=org.apache.derby.jdbc.EmbeddedDriver
+mocked1/url=jdbc:derby:target/database/jpa-jndi-database1;create=true
+mocked1/user=app
+mocked1/password=app
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
index f9a140537..ffefcfe68 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
@@ -197,7 +197,20 @@ public class EntityManagerFactoryImpl
}
}
- Broker broker = _factory.newBroker(user, pass, managed, retainMode, false);
+ // javax.persistence.jtaDataSource and openjpa.ConnectionFactory name are equivalent.
+ // prefer javax.persistence for now.
+ String cfName = (String) Configurations.removeProperty("jtaDataSource", props);
+ if(cfName == null) {
+ cfName = (String) Configurations.removeProperty("ConnectionFactoryName", props);
+ }
+
+ String cf2Name = (String) Configurations.removeProperty("nonJtaDataSource", props);
+
+ if(cf2Name == null) {
+ cf2Name = (String) Configurations.removeProperty("ConnectionFactory2Name", props);
+ }
+
+ Broker broker = _factory.newBroker(user, pass, managed, retainMode, false, cfName, cf2Name);
// add autodetach for close and rollback conditions to the configuration
broker.setAutoDetach(AutoDetach.DETACH_CLOSE, true);