MySoft Only Works in IE
Note to self. MySoft only works in Internet Explorer.
« December 2007 | Main | February 2008 »
Note to self. MySoft only works in Internet Explorer.
Foiled again by EJB3 and JPA. Just when I thought that switching to EXTENDED transaction mode had solved my problems, I began seeing optimistic lock exceptions.circumvent the problem entirely.
To make a very long and trying series of events very short, fault loading does not exist. With great respect to Dain Sundstrom, just because something should work doesn't mean it will. Nowhere in the EJB 3 spec is it mentioned, I can find no reference to it online aside from an e-mail between Dain and Patrick Linksey, and that e-mail sounded inconclusive. If it does exist, it's called something else or is a feature specific to OpenEJB.
Even so, I got a world class education in transaction mechanics today from David Blevins and Dain Sundstrom. So, Kudos to them both for slogging through my code with me. I'm going to turn my attention once more to JPA fetch groups and see if I can nail down the problem I was having with those earlier. It had something to do with concurrent modification exceptions, but with this new explicit control over transaction duration, I might be able to
Foiled again by EJB3 and JPA. Just when I thought that switching to EXTENDED transaction mode had solved my problems, I began seeing optimistic lock exceptions.
To make a very long and trying series of events very short, I have to move forward under the assumption that fault loading does not exist. Life would be great if it did. With respect to the OpenEJB developers, just because something should work doesn't mean it will. Nowhere in the EJB 3 spec is it mentioned, I can find no reference to it online aside from an e-mail between the OpenEJB and OpenJPA devs, and that e-mail sounded inconclusive. If it does exist, it's called something else or is a feature specific to OpenEJB.
Even so, I got a world class education in transaction mechanics today from David Blevins and Dain Sundstrom. So, Kudos to them both for slogging through my code with me. I'm going to turn my attention once more to JPA fetch groups and see if I can nail down the problem I was having with those earlier. It had something to do with concurrent modification exceptions, but with this new explicit control over transaction duration, I might be able to circumvent the problem entirely.
Moral of the story: if it's not in the spec, it's not a feature.
I finally got the fault loading example to work. I'll post the code here in the list so that other users can look at it in the future.
First, all of this code uses entity beans from the alpha-05 release of CORM (my Open Source commercial O/R model project for Java), which can be downloaded from http://java.eremite.org/bin/view/CORM/CORM_Project_Downloads?rev=25
The example below shows how to use fault loading in an extended session context in order to simplify testing the cascade relationships for multiple collection-valued references inside of a single persistent entity bean. My entity bean is an instance of org.eremite.corm.party.Party (sorry, no public SVN yet, but I'll paste some of the code below).
The major pieces I needed to create for this example were #1: The Context factory I use to make sure all of my test cases don't create a new database (this saves time), #2: The test case, #3: A Stateful Session bean "bean manager" that contained an entity manager inside of an EXTENDED persistence context, and its local interface (not shown), #4: the peristence.xml file, which I place in my src/test/resources directory for mvn, #5: nachos, to keep things calm down below while I worked. I'll show the code for Party and first four pieces below. You will have to find your own nachos. Also, all of the code listed below is AFL 3.0 licensed, so have at it.
package org.eremite.corm.party;
import org.eremite.corm.BaseArchetype;
import org.eremite.corm.party.address.AssociatedAddress;
import org.eremite.corm.party.relationship.Capability;
import org.eremite.corm.party.relationship.PartyRole;
import javax.persistence.*;
import java.util.*;
@Entity
public class Party extends BaseArchetype {
@OneToMany(cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE}, mappedBy="party")
private Set <AssociatedAddress> addresses =
new HashSet <AssociatedAddress> ();
@OneToMany(cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE})
private Set <Binding> bindings = new HashSet <Binding> ();
@OneToMany(cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE})
private Set <RegisteredIdentifier> registeredIds =
new HashSet <RegisteredIdentifier> ();
@OneToMany(cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE})
private Set <Capability> capabilities = new HashSet<Capability>();
@OneToMany(cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE}, mappedBy="party")
private Set <PartyRole> roles = new HashSet <PartyRole> ();
/* Default Zero-arg Constructor */
public Party() {}
public Set<AssociatedAddress> getAddresses() {
return addresses;
}
public void setAddresses(Set<AssociatedAddress> addresses) {
this.addresses = addresses;
}
public Set<Binding> getPreferences() {
return bindings;
}
public void setPreferences(Set<Binding> preferences) {
this.bindings = bindings;
}
public Set<RegisteredIdentifier> getRegisteredIdentifiers() {
return registeredIds;
}
public void setRegisteredIdentifiers(
Set<RegisteredIdentifier> registeredIds) {
this.registeredIds = registeredIds;
}
public Set<PartyRole> getPartyRoles() {
return roles;
}
public void setPartyRoles(Set<PartyRole> roles) {
this.roles = roles;
}
public Set<Capability> getCapabilities() {
return this.capabilities;
}
public void setCapabilities(Set<Capability> capabilities) {
this.capabilities = capabilities;
}
public void addPartyRole(PartyRole ... roles) {
getPartyRoles().addAll(Arrays.asList(roles));
}
public void addPartyRole(PartyRole role) {
getPartyRoles().add(role);
}
public void addRegisteredIdentifier(RegisteredIdentifier rid) {
getRegisteredIdentifiers().add(rid);
}
public void addRegisteredIdentifier(
RegisteredIdentifier ... rids) {
getRegisteredIdentifiers().addAll(
java.util.Arrays.asList(rids));
}
public void addPreference(Binding ... bindings) {
getPreferences().addAll(Arrays.asList(bindings));
}
public void addCapability(Capability ... capabilities) {
getCapabilities().addAll(Arrays.asList(capabilities));
}
public void addCapability(Capability capability) {
getCapabilities().add(capability);
}
public void addAddress(AssociatedAddress ... addresses) {
getAddresses().addAll(Arrays.asList(addresses));
}
public void addAddress(AssociatedAddress address) {
getAddresses().add(address);
}
}
Note that the Party class has 5 collection-valued references to other entities. For brevity I'll not show the definitions of those entities here. Also note that Party and all other entities in my project descend from the same MappedSuperclass, "Archetype". This helps me considerably during testing and other areas. Archetype has a name, description and ID field, and is not shown here.
package org.eremite.corm.party.testutil;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;
public class ContextFactory {
private Context context;
private static ContextFactory instance;
public static Context reference() {
if(instance == null) instance = new ContextFactory();
return instance.getContext();
}
private ContextFactory() {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
p.put("partyDB", "new://Resource?type=DataSource");
p.put("partyDB.JdbcDriver", "org.hsqldb.jdbcDriver");
p.put("partyDB.JdbcUrl", "jdbc:hsqldb:mem:partydb");
p.put("partyDBUnmanaged", "new://Resource?type=DataSource");
p.put("partyDBUnmanaged.JdbcDriver", "org.hsqldb.jdbcDriver");
p.put("partyDBUnmanaged.JdbcUrl", "jdbc:hsqldb:mem:partydb");
p.put("partyDBUnmanaged.JtaManaged", "false");
try {
context = new InitialContext(p);
} catch (NamingException e) {
e.printStackTrace();
}
}
private Context getContext() {
return this.context;
}
}
The ContextFactory is something I whipped up to make sure that my different TestCases didn't all recreate the InitialContext. The addition of this class helped me shave considerable time off of my maven build, and therefore has been useful for speeding up development.
package org.eremite.corm.party;
import junit.framework.TestCase;
import org.eremite.corm.Archetype;
import org.eremite.corm.party.address.Address;
import org.eremite.corm.party.address.AssociatedAddress;
import org.eremite.corm.party.relationship.Capability;
import org.eremite.corm.party.relationship.PartyRole;
import org.eremite.corm.party.relationship.PartyRoleType;
import org.eremite.corm.party.testutil.BeanManager;
import org.eremite.corm.party.testutil.ComponentFactory;
import org.eremite.corm.party.testutil.ExtendedBeanManager;
import javax.naming.Context;
import java.util.Iterator;
import java.util.Set;
public class PartyCascade02Test extends TestCase {
protected ExtendedBeanManager mgr;
protected BeanManager db;
protected ComponentFactory one;
protected Context context;
public void setUp() throws Exception {
super.setUp();
context = org.eremite.corm.party.testutil.
ContextFactory.reference();
mgr = (ExtendedBeanManager) context.lookup(
"ExtendedBeanManagerLocal");
db = (BeanManager) context.lookup("BeanManagerLocal");
one = new ComponentFactory();
}
public void tearDown() throws Exception {
super.tearDown();
}
public void testFaultLoading() {
Party p1 = new Party();
PartyRole<Party> initialRole =
new PartyRole<Party>(new PartyRoleType(), p1);
Capability initialCapability = new Capability();
Binding initialPreference = new Binding();
RegisteredIdentifier initialRegID = new RegisteredIdentifier();
AssociatedAddress initialAA =
new AssociatedAddress(p1, new Address());
p1.addCapability(initialCapability);
p1.addPreference(initialPreference);
p1.addRegisteredIdentifier(initialRegID);
assertNotNull(p1.getAddresses());
assertNotNull(p1.getCapabilities());
assertNotNull(p1.getPartyRoles());
assertNotNull(p1.getPreferences());
assertNotNull(p1.getRegisteredIdentifiers());
assertTrue(p1.getAddresses().contains(initialAA));
assertTrue(p1.getCapabilities().contains(initialCapability));
assertTrue(p1.getPartyRoles().contains(initialRole));
assertTrue(p1.getPreferences().contains(initialPreference));
assertTrue(p1.getRegisteredIdentifiers().contains(initialRegID));
// First unit of work -- cascading persist
mgr.persist(p1);
long id = p1.getID();
assertTrue(id != 0);
// Second unit of work -- get party by ID
Party p2 = mgr.getPartyByID(id);
assertNotNull(p2);
assertNotNull(p2.getAddresses());
assertNotNull(p2.getCapabilities());
assertNotNull(p2.getPreferences());
assertNotNull(p2.getPartyRoles());
assertNotNull(p2.getRegisteredIdentifiers());
assertTrue(p2.getAddresses().size() > 0);
assertTrue(p2.getCapabilities().size() > 0);
assertTrue(p2.getPreferences().size() > 0);
assertTrue(p2.getPartyRoles().size() > 0);
assertTrue(p2.getRegisteredIdentifiers().size() > 0);
Set<AssociatedAddress> addresses = p2.getAddresses();
Set<Capability> capabilities = p2.getCapabilities();
Set<Binding> preferences = p2.getPreferences();
Set<PartyRole> partyRoles = p2.getPartyRoles();
Set<RegisteredIdentifier> regIDs = p2.getRegisteredIdentifiers();
assertNotNull(addresses);
assertNotNull(capabilities);
assertNotNull(preferences);
assertNotNull(partyRoles);
assertNotNull(regIDs);
assertTrue(hasArchetypeWithID(addresses, initialAA.getID()));
assertTrue(hasArchetypeWithID(capabilities, initialCapability.getID()));
assertTrue(hasArchetypeWithID(preferences, initialPreference.getID()));
assertTrue(hasArchetypeWithID(partyRoles, initialRole.getID()));
assertTrue(hasArchetypeWithID(regIDs, initialRegID.getID()));
p2.setName("preMerge");
Archetype address2, cap2, pref2, role2, regID2,
address3, cap3, pref3, role3, regID3;
address2 = getByID(addresses, initialAA.getID());
cap2 = getByID(capabilities, initialCapability.getID());
pref2 = getByID(preferences, initialPreference.getID());
role2 = getByID(partyRoles, initialRole.getID());
regID2 = getByID(regIDs, initialRegID.getID());
address2.setName("preMerge address");
cap2.setName("preMerge capability");
pref2.setName("preMerge preference");
role2.setName("preMerge partyRole");
regID2.setName("preMerge regID");
// Third unit of work -- cascading merges
mgr.merge(p2);
// Fourth unit of work -- get party by ID
Party p3 = mgr.getPartyByID(id);
assertNotNull(p3);
assertNotNull(p3.getAddresses());
assertNotNull(p3.getCapabilities());
assertNotNull(p3.getPreferences());
assertNotNull(p3.getPartyRoles());
assertNotNull(p3.getRegisteredIdentifiers());
assertTrue(p3.getAddresses().size() > 0);
assertTrue(p3.getCapabilities().size() > 0);
assertTrue(p3.getPreferences().size() > 0);
assertTrue(p3.getPartyRoles().size() > 0);
assertTrue(p3.getRegisteredIdentifiers().size() > 0);
Set<AssociatedAddress> addresses2 = p3.getAddresses();
Set<Capability> capabilities2 = p3.getCapabilities();
Set<Binding> preferences2 = p3.getPreferences();
Set<PartyRole> partyRoles2 = p3.getPartyRoles();
Set<RegisteredIdentifier> regIDs2 = p3.getRegisteredIdentifiers();
assertNotNull(addresses2);
assertNotNull(capabilities2);
assertNotNull(preferences2);
assertNotNull(partyRoles2);
assertNotNull(regIDs2);
assertTrue(hasArchetypeWithID(addresses2, initialAA.getID()));
assertTrue(hasArchetypeWithID(capabilities2, initialCapability.getID()));
assertTrue(hasArchetypeWithID(preferences2, initialPreference.getID()));
assertTrue(hasArchetypeWithID(partyRoles2, initialRole.getID()));
assertTrue(hasArchetypeWithID(regIDs2, initialRegID.getID()));
address3 = getByID(addresses2, initialAA.getID());
cap3 = getByID(capabilities2, initialCapability.getID());
pref3 = getByID(preferences2, initialPreference.getID());
role3 = getByID(partyRoles2, initialRole.getID());
regID3 = getByID(regIDs2, initialRegID.getID());
assertEquals(p2.getName(), p3.getName());
assertEquals(address2.getName(), address3.getName());
assertEquals(cap2.getName(), cap3.getName());
assertEquals(pref2.getName(), pref3.getName());
assertEquals(role2.getName(), role3.getName());
assertEquals(regID2.getName(), regID3.getName());
// 5th unit of work -- cascading remove
mgr.remove(p3);
// 6th - 13th units of work -- verification
assertEquals(0, mgr.sizeOf("Party"));
assertEquals(0, mgr.sizeOf("AssociatedAddress"));
assertEquals(0, mgr.sizeOf("Capability"));
assertEquals(0, mgr.sizeOf("Binding"));
assertEquals(0, mgr.sizeOf("PartyRole"));
assertEquals(0, mgr.sizeOf("RegisteredIdentifier"));
assertEquals(1, mgr.sizeOf("PartyRoleType"));
assertEquals(1, mgr.sizeOf("Address"));
}
private static void traceln(Object ... o) {
for(Object item : o) System.out.println(item);
}
private boolean hasArchetypeWithID(Set s, long ID) {
Iterator<Archetype> iter = (Iterator<Archetype>)s.iterator();
while(iter.hasNext()) {
Archetype a = iter.next();
if(a.getID() == ID) return true;
}
return false;
}
private Archetype getByID(Set s, long ID) {
Iterator<Archetype> iter = (Iterator<Archetype>)s.iterator();
while(iter.hasNext()) {
Archetype a = iter.next();
if(a.getID() == ID) return a;
}
fail("There was no archetype with ID==" + ID + " in the set.");
return null;
}
}
Note that this was the test case for a class with 5 collection valued references. My goal was to reduce the number of times I needed to go to the database while performing tests, so as to speed up the testing phase of my maven build and thereby speed up development. Major gains were between the 2nd and 6th units of work, as in previous versions I was unable to use fault loading (for reasons which will be discussed below).
package org.eremite.corm.party.testutil;
import org.eremite.corm.Archetype;
import org.eremite.corm.party.Party;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;
import java.util.List;
@Stateful(name="ExtendedBeanManager")
public class ExtendedBeanManagerImpl implements ExtendedBeanManager {
@PersistenceContext(
unitName = "party-test-unit",
type = PersistenceContextType.EXTENDED)
private EntityManager em;
public Party getPartyByID(long ID) {
Query q = em.createQuery("SELECT a FROM Party as a WHERE a.ID=" + ID);
return (Party) q.getSingleResult();
}
public long persist(Archetype a) {
em.persist(a);
return a.getID();
}
public void merge(Archetype a) {
em.merge(a);
}
public void remove(Archetype a) {
em.remove(a);
em.flush();
}
public int sizeOf(String table) {
Query q = em.createQuery("SELECT a FROM " + table + " as a");
return q.getResultList().size();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="party-test-unit">
<jta-data-source>java:openejb/Resource/partyDB</jta-data-source>
<non-jta-data-source>java:openejb/Resource/partyDBUnmanaged</non-jta-data-source>
<class>org.eremite.corm.BaseArchetype</class>
<class>org.eremite.corm.party.address.Address</class>
<class>org.eremite.corm.party.address.AssociatedAddress</class>
<class>org.eremite.corm.party.address.EmailAddress</class>
<class>org.eremite.corm.party.address.GeographicAddress</class>
<class>org.eremite.corm.party.address.ISOCountryCode</class>
<class>org.eremite.corm.party.address.Locale</class>
<class>org.eremite.corm.party.address.TelecomAddress</class>
<class>org.eremite.corm.party.address.WebPageAddress</class>
<class>org.eremite.corm.party.relationship.AssignedResponsibility</class>
<class>org.eremite.corm.party.relationship.Capability</class>
<class>org.eremite.corm.party.relationship.PartyRelationship</class>
<class>org.eremite.corm.party.relationship.PartyRelationshipType</class>
<class>org.eremite.corm.party.relationship.PartyRole</class>
<class>org.eremite.corm.party.relationship.PartyRoleType</class>
<class>org.eremite.corm.party.relationship.Responsibility</class>
<class>org.eremite.corm.party.Organization</class>
<class>org.eremite.corm.party.OrganizationName</class>
<class>org.eremite.corm.party.Party</class>
<class>org.eremite.corm.party.PartySignature</class>
<class>org.eremite.corm.party.Person</class>
<class>org.eremite.corm.party.PersonName</class>
<class>org.eremite.corm.party.Binding</class>
<class>org.eremite.corm.party.BindingValue</class>
<class>org.eremite.corm.party.BindingType</class>
<class>org.eremite.corm.party.RegisteredIdentifier</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true, SchemaAction='add,deleteTableContents')"/>
<!--<property name="openjpa.Log" value="SQL=TRACE, Query=TRACE"/>-->
</properties>
</persistence-unit>
</persistence>
I did not change my persistence.xml file at all from the one I normally use for testing.
My bean manager for this example was a complete rewrite of my former one. The former bean manager was a Stateless Session Bean with an EntityManager that operated in a TRANSACTION typed session context. I do not know whether it is possible to use fault loading from a Stateless Session Bean and from an EntityManager that uses a TRANSACTION typed session context. However, I can confirm that the technique DOES work if one uses a Stateful Session Bean with an EM that operates in an EXTENDED typed session context. I will now go through my cascade tests and update them to use this new mechanism, and then will rinse and repeat for the rest of the modules in my project.
The overall result includes fewer trips to the database, simpler test cases, forward progress on my project, and a much happier developer.
After much pain and trouble, and ten days of effort, I have made no additional progress on the "fault loading" technique. I was never able to find any reference to it online, no documentation and no examples for how to do this using EJB 3 technology.
I have to conclude that it doesn't exist or that it incredible amounts of expertise are required to use it.
Despite working with the OpenJPA crew, I cannot get fetch groups to work in a transactional context. This is becoming expensive in man-hours and I find myself reconsidering how to do these tests. If I press forward, the test code takes aeons to run. If I try to correct the problem, I may never finish.
Neither of these project communities are very active during the weekend, which makes matters worse. Although I haven't fallen into complete dispair over this recent lack of progress, I can definitely see it from here. Dispair, that is.
(19:35:08) saintx: [INFO] -------------------------------------------------------
(19:35:08) saintx: [INFO] BUILD SUCCESSFUL
(19:35:08) saintx: [INFO] -------------------------------------------------------
(19:35:08) saintx: [INFO] Total time: 19 seconds
(19:35:14) saintx: w00t.
(19:37:19) saintx: I never did figure out how to load an object with more than one collection-valued reference except by using EAGER loading. Fetch plans led to these very ugly ConcurrentModificationExceptions.
(19:54:15) dain: saintx: are you executing the query and inspecting the objects in the same transaction?
(19:54:29) dain: if not, you execute the query and detach the objects
(19:54:43) saintx: dain: no, not inspecting the objects.
(19:54:53) dain: then when you try to inspect them, you can't "fault load" the collections
(19:54:56) saintx: There's something going on with cyclic cascades, I think.
(19:54:58) dain: so you get null
(19:55:17) dain: ya the cascade stuff is tricky to get right
(19:55:19) saintx: "fault load" <-- define, please :)
(19:55:23) saintx: dain: sure is.
(19:55:45) dain: say you execute a query to load all orders
(19:55:52) saintx: I could fix the problem by flagging both ends of the M-M as Eager, but that's not the solution for the long term.
(19:55:57) dain: then you iterate over them and look at the line items
(19:56:01) saintx: yeah
(19:56:10) dain: in the first query you only load the order table
(19:56:19) saintx: correct.
(19:56:34) dain: when you start looking at order.getLineItems().get
(19:56:42) saintx: You get null pointer.
(19:56:42) dain: (0).getName
(19:56:54) dain: the system will fault load the line items
(19:57:08) dain: you only get null if you have a detached object
(19:57:11) saintx: Is this in the transaction--in the query method?
(19:57:35) dain: I don't understand the question
(19:57:48) saintx: In the first query you get the order table.
(19:57:54) dain: as long as the objects are attached you get fault loading
(19:58:01) saintx: Right.
(19:58:20) dain: if you commit the tx all of the orders are detached and you can't fault load the lineitems
(19:58:35) saintx: So, if I return the List results from the query, the transaction ends and the items are detached, because I'm using a TRANSACTIONAL context.
(19:58:56) dain: it is because your transactions are too small
(19:59:14) dain: typically, you want one tx per unit of work (typically a web request)
(19:59:20) saintx: I'm not "commiting" anything explicitly. Just fetching a list of objects and returning them to my test case.
(19:59:37) saintx: okay, that's good to know.
(19:59:56) dain: sure, but you are still committing because your access is wrapped by a stateless bean
(20:00:26) saintx: Yes, it's implicit. The "commit" happens when the method ends and returns the list, I assume.
(20:00:30) dain: you have two options to extends the transaction.. move some of your test logic to a stateless bean, or use the UserTransaction in your test
(20:01:00) saintx: Interesting--what's a UserTransaction?
(20:01:21) dain: my point is that regardless of an implicit or explicit commit, you are controlling the commit of the transaction
(20:01:27) saintx: Sure, I agree.
(20:01:54) dain: javax.transaction.UserTransaction
(20:02:10) dain: it is a dumbed down interface to the TransactionManager
(20:02:29) saintx: What I've been doing is calling a query with a LEFT JOIN FETCH on one of the collection valued references, then checking to see if the merge cascading works, then starting over for a different collection valued reference until I've covered all of them for a class.
(20:02:46) saintx: dain: got it. I'll look at that.
(20:03:34) saintx: My most complex entity has five collection valued references to other entities.
(20:03:45) dain: here is some tests that use user transaction https://svn.apache.org/repos/asf/openejb/trunk/openejb3/itests/openejb-itests-client/src/main/java/org/apache/openejb/test/entity/cmr/OneToOneTests.java
(20:03:55) saintx: dain: awesome
(20:04:38) dain: it is the cmp test that verifies that collection references work
(20:05:04) dain: I had to control the transactions so my objects don't detach
(20:05:43) saintx: this is how I was doing it: http://rifers.org/paste/show/6404
(20:05:55) saintx: mgr is my stateless session bean
(20:08:29) dain: in a normal web application, you typically read in the request and convert the strings to real data types in the servlet, then you hand off the data to a stateless session bean that updates your database
(20:08:45) saintx: sure
(20:09:13) saintx: I'm looking for where you define "ds". Must be in AbstractCMRTest?
(20:09:32) dain: then either that same bean or a new one, query the JPA system for the reponse and assures that all fields are loaded
(20:09:48) dain: that data is detached and stuffed in the request map
(20:10:08) dain: then a jsp, velocity, or template system generates html
(20:10:12) saintx: yep
(20:10:29) dain: so I try use the same model in my tests
(20:11:11) dain: basically, the tests should look like your main code will
(20:12:51) dain: saintx: I don't recommend going directly to the database like that test does
(20:13:05) dain: that test is verifying the correctness of the CMP engine
(20:13:27) saintx: This isn't that much different. If you ignore the servlet part, you make a new bean, populate its entity references, persist it, check to see if those entities made it into the database, then get a copy of that bean from the database, change some values in it and its references, merge the new copy, check out another instance of the bean, check it and its references to ensure they have been changed, then remove and make sure the appropriate obj
(20:13:38) saintx: dain: clear.
(20:14:08) dain: the one difference, is I control the transaction scope
(20:14:18) saintx: Yes, which is nice.
(20:14:20) dain: so I can verify references that are fault loaded
(20:14:29) saintx: Which simplifies things quite a bit.
(20:14:52) saintx: Fault loading isn't mentioned in the book : )
(20:14:55) dain: ya, your code looks like you are struggling against JPA (because of the transaction scoping)
(20:14:58) dblevins hat den Raum verlassen (quit: Read error: 110 (Connection timed out)).
(20:15:17) saintx: I am.
(20:15:20) dain: I think you will be much happier with slightly bigger transactions
(20:15:25) saintx: Which is frustrating.
(20:15:29) saintx: Cool.
(20:16:05) dain: all this stuff works better when you start thinking of "units of work"
(20:16:23) dain: those become your natural transaction boundaries
(20:16:52) saintx: Yeah, this entire effort screams for another abstraction layer--my test class is >1450 lines of code.
(20:17:36) dain: BTW you should take a look at the Oreilly "RESTful Web Services" book
(20:17:54) dain: it fits nicely with the data modeling work you are doing
(20:17:56) saintx: I thought about breaking it out but then would have to reload the database--so I'll probably create a class that grabs one db in memory and then holds on to it for all test classes to reference so it doesn't take so long creating and tearing down the db for each test case.
(20:18:12) saintx: excellent--I'll take a look at that right away.
House Document 103, 86th Congress, 1st Session - Passed by the Congress of the United States on July 11, 1958.
ANY PERSON IN GOVERNMENT SERVICE SHOULD:
I. Put loyalty to the highest moral principles above loyalty to persons, party, or Government department.
II. Uphold the Constitution, laws, and legal regulations of the United States and of all governments therein and never be a party to their evasion.
III. Give a full day's labor for a full day's pay; giving to the performance of his duties his earnest effort and best thought.
IV. Seek to find and employ more efficient and economical ways of getting tasks accomplished.
V. Never discriminate unfairly by the dispensing of special favors or privileges to anyone, whether for remuneration or not; and never accept, for himself or his family, favors or benefits under circumstances which might be construed by reasonable persons as influencing the performance of his governmental duties.
VI. Make no private promises of any kind binding upon the duties of office, since the Government employee has no private word which can be binding on public duty.
VII. Engage in no business with the Government, either directly or indirectly, which is inconsistent with the conscientious performance of his governmental duties.
VIII. Never use any information coming to him confidentially in the performance of governmental duties as a means for making private profit.
IX. Expose corruption wherever discovered.
X. Uphold these principles, ever conscious that public office is a public trust.
To all OpenEJB devs and users,
Today I was able (barely) to tag and release version alpha-05 of my open source project CORM. This release includes the first real step in the project toward supporting persistent entity model. I won't get into details here, because this isn't about my project. I'm writing to thank everyone in the OpenJPA and especially the OpenEJB communities who have worked so hard to make these great products. There is absolutely no way I'd have been able to get this done without you guys (and gals).
In less than two months of nights and weekends from a cold-dead start, including two weeks of vacation, I was able to get up and over the learning curve and implement persistence for the most difficult part of my application library. Every time I thought my project was going to have to bend in order to accomplish persistence, I was proven wrong. I never had to compromise my software design in order to get my entities to into the database and back out, which you should all consider a major accomplishment.
By embedding OpenEJB directly into my unit tests and using an in-memory database, I was able to test my entire API, including every method, CRUD tests for each entity, and every cascade in under 16 seconds. And with a little time on IRC, I think I'll be able to tune my queries even more!
So, thanks to all who helped, from both the OpenEJB and OpenJPA projects. I hope to keep pushing the envelope and writing great software with the ASF.
Warm regards (which matters here in Minneapolis tonight: -17 degrees Fahrenheit!)
--
Alexander R. Saint Croix
I'm really jazzed right now. With surprising ease, I have configured JUnit to test the persistence behaviors of my entity beans from the CORM project by running OpenEJB embedded directly in JUnit. It was so simple, I couldn't believe it. You can definitely expect an article with a clear tutorial about how to accomplish this in the near future. Here's the convo I had with David over the matter:
[11:20pm] dblevins: saintx: here[11:20pm] saintx: dblevins: howdy
[11:21pm] dblevins: hiya!
[11:21pm] saintx: dblevins: I figured out the problem from earlier, I think.
[11:21pm] saintx: It was dependencies of openejb-core. They are legion.
[11:21pm] saintx:
[11:21pm] saintx: So, I had to switch my corm project structure to m2
[11:21pm] saintx: Which I've been putting off ... for a long time now.
[11:21pm] saintx: =-)
[11:22pm] dblevins: what were you using?
[11:22pm] saintx: maven 1.x
[11:22pm] saintx: yeah, yeah I know.
[11:22pm] saintx: don't say it.
[11:22pm] dblevins: ahh
[11:22pm] • saintx grins
[11:22pm] dblevins: well we have Ant and Maven2 scripts in there
[11:22pm] dblevins: but yea, no maven1
[11:22pm] saintx: Yeah, but then I'd have to install ant. Meh.
[11:23pm] dblevins: aren't you on osx?
[11:23pm] dblevins: should be in there already (i think)
[11:23pm] saintx: Running the example is one thing--getting the same thing in my project and getting out of servlet hell is the real goal, though
[11:23pm] dblevins: well the examples are designed as starter projects
[11:23pm] saintx: dblevins: I am in OS X. I hadn't considered that ant'd be built in, but now that you mention it, I wouldn't be surprised.
[11:23pm] saintx: Yep.
[11:23pm] dblevins: i.e. you find one thats close and go from there
[11:24pm] saintx: I just finished the upgrade to m2, so that's all behind me now.
[11:24pm] saintx: Haven't eaten in about 12 hours, though.
[11:25pm] dblevins: anyway you've inspired me to add error messages that list the required jars
[11:25pm] saintx: Cool--hopefully it isn't too much work.
[11:25pm] dblevins: mostly finished with a maven plugin that spits a text file into the META-INF dir of the jar
[11:26pm] saintx: I am totally amped to get the in-container tests running now. Once that's going smooth, I think I'll have come full cycle.
[11:26pm] dblevins: once you have working in-container setup, it's all good
[11:26pm] saintx: I'm trying something a tad tricky--I'm going to make the test directory the client with the persistence.xml file defined in a META-INF of the /target/test-classes directory. Do you think it'll work?
[11:27pm] dblevins: no
[11:27pm] dblevins: well
[11:27pm] dblevins: yes and no
[11:27pm] saintx: *whew* glad I asked.
[11:27pm] dblevins: *generally* you must keep your assets together ...
[11:27pm] saintx: If not, then I have a second idea to run past you.
[11:28pm] dblevins: i.e. if they're typically packaged in the same jar, they must go in the same directory (no spreading them around)
[11:28pm] dblevins: but... the persistence.xml is one of those things that can be anywhere in the classpath
[11:29pm] saintx: The jars in my component library don't have persistence.xml files at present, and in this case I only need the persistence.xml file for testing.
[11:29pm] saintx: My client application that I presently use in Tomcat provides its own persistence.xml file, as you might recall.
[11:29pm] dblevins: i.e., if you tried to do the same with an ejb-jar.xml, openejb-jar.xml, application-client.xml, you'd make a mess
[11:29pm] saintx: I see.
[11:31pm] saintx: Does the persistence.xml in my client WAR override the persistence.xml files in my WEB-INF/lib component jars?
[11:31pm] saintx: (just trying to understand the rules here)
[11:31pm] dblevins: no overriding. you'd just have more persistence units available
[11:31pm] saintx: Nope, can't do that.
[11:32pm] dblevins: and if they had the same name then you'd have to specify teh path to the jar or directory containing the persistence.xml of the unit you wanted
[11:32pm] saintx: For container-driven testing (@EntityManager injection) do I need to supply the ejb-jar.xml?
[11:32pm] saintx: I saw the one in the example was completely empty.
[11:33pm] dblevins: there's a comment in the pom.xml that explains a bit
[11:34pm] dblevins: wait, looks like it's not in that one
[11:36pm] saintx: Yeah, the comment about scope made it into most of the poms in my project
[11:36pm] saintx: Good artists borrow, great artists steal. And your pom's are worth stealing david.
[11:37pm] saintx: s/pom's/poms
[11:37pm] dblevins: hehe
[11:37pm] dblevins: here's the doc about the empty ejb-jar.xml
http://openejb.apache.org/3.0/application-discovery-via-the-classpath.html[11:37pm] saintx: I'll look at it after I eat something. bbi10m
[11:37pm] saintx: tnx, btw
[11:38pm] dblevins: np
[12:13am] saintx: Dude, it worked.
[12:13am] saintx: HAHAAAH! Sweet.
[12:13am] dblevins: awesome!
[12:13am] saintx: I'm. so. jazzed. right. now.
[12:13am] saintx: This is great.
[12:13am] dblevins:
[12:14am] saintx: Essentially, it means that I can test my project libraries and keep on developing them my way, without interference from the persistence layer.
[12:14am] dblevins: sounds like a good quip for a short "this is cool" blog entry
[12:15am] saintx: Yeah, I think you're right.
[12:15am] saintx: I just wrote one, so what's another? It's been a "this is cool" sort of evening.
[12:15am] dblevins: and right, that's exactly what we're offering... the ability to develop ejb applications like you would any other application
[12:16am] saintx: EXACTLY.
[12:16am] saintx: Every time I asked about some funky weird thing that wasn't mentioned explicitly in the specs, first I got "umm . . . we're not sure", but then, sure enough, it . . . just . . . worked.
[12:17am] saintx: It's so cool.
[12:17am] saintx: I mean it, man. Thank you.
[12:17am] saintx: OpenJPA guys get heaps of cred, too.
[12:18am] dblevins: help us spread the word, that's thanks enough
[12:18am] dblevins: i'm quite proud that you can embedd openejb in really anything
[12:18am] saintx: Well, let's get a venue lined up then. Where's the biggest spot for java readers these days? Is it still TheServerSide, or did Java.net ever catch up?
[12:19am] dblevins: we don't need a special plugin for every environment you may wish to develop ini
[12:19am] dblevins: s/ini/in/
[12:19am] saintx: Yeah, it really is awesome.
[12:19am] dblevins: you don't need a maven plugin
[12:19am] dblevins: you don't need special ant tasks
[12:19am] dblevins: you don't need an eclipse plugin
[12:19am] dblevins: you don't need an intellij plugin
[12:20am] dblevins: you don't need to treat us any differently than any other library you'd use
[12:20am] saintx: This allows me to break down my component library like I would normally do, and configure the persistence units from my client jars, WARs, or even target/test-classes directory in the project build.
[12:21am] saintx: Which, ultimately means I give NO GROUND to EJB3 in my architecture. It's very compliant.
[12:22am] saintx: And the in-container testing (container-driven testing?) is so ultra simple. I didn't even run your example, by the way--just ported it over to my app.
[12:23am] saintx: Dain's idea to make hsqldb run completely in memory is a good one.
[12:23am] saintx: Especially for testing.
[12:24am] dblevins: yea, the example you didn't run does that
[12:25am] saintx: This one might too--I just changed the name of the entity, wrote my own persistence manager as a stateful bean.
[12:25am] saintx: Basically a complete analog to the example.
[12:26am] saintx: I can't seem to find the hsqldb files kicking around here, anywhere...
[12:27am] dblevins: do you have something like this in your test case?
[12:27am] dblevins: p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
[12:27am] saintx: hep.
[12:27am] saintx: s/hep/yep
[12:27am] saintx: Oh, sure.
[12:27am] saintx: mem
[12:27am] saintx: One more free feature.
I just finished my CORM project build migration to Maven2. It was surprisingly painless. The POM structure is very intuitive, and the stripped down nature of the existing CORM build helped out quite a bit. Major kudos go out to Wendy Smoak at the Codehaus for answering a couple of questions for me related to the multiproject functionality and building a new POM for the JScience.org project jar.
I think I'll wait a day or two and check with the other developers before I junk the old maven 1.x build for CORM. This should help us achieve container-driven testing in OpenEJB and get us out of servlet hell for the rest of the entity component library development cycle. I'm psyched.
Thanks again, Wendy ;-)