openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Kevin Sutter" <kwsut...@gmail.com>
Subject Re: svn commit: r557089 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/ openjpa-lib/src/main/ja
Date Wed, 18 Jul 2007 15:38:15 GMT
Yes, and the Sun JDK works just fine.  So, for the majority of the folks,
there is no problem...  :-(

On 7/18/07, Craig L Russell <Craig.Russell@sun.com> wrote:
>
> I don't suppose you have run this with the Sun JDK (that I've tested
> on).
>
> Craig
>
> On Jul 18, 2007, at 8:00 AM, Kevin Sutter wrote:
>
> > Craig,
> > I haven't taken the time to figure out the source of the problem
> > yet, but I
> > am experiencing two NPE's in our test bucket when running with the
> > IBM JDK (
> > 1.5.0 SR5).  I thought I would post a heads-up for any IBM JDK
> > users...
> > Here are the errors that I received:
> >
> > Tests run: 4, Failures: 0, Errors: 2, Skipped: 0, Time elapsed:
> > 0.031 sec
> > <<< FAILURE!
> > testBackEdges(org.apache.openjpa.lib.graph.TestDepthFirstAnalysis)
> > Time
> > elapsed: 0 sec  <<< ERROR!
> > java.lang.NullPointerException
> >        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.visit(
> > DepthFirstAnalysis.java:97)
> >        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.<init>(
> > DepthFirstAnalysis.java:74)
> >        at
> > org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.setUpGraph2(
> > TestDepthFirstAnalysis.java:78)
> >        at
> > org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.testBackEdges
> > (TestDepthFirstAnalysis.java:105)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke(
> > NativeMethodAccessorImpl.java:64)
> >        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> > DelegatingMethodAccessorImpl.java:43)
> >        at java.lang.reflect.Method.invoke(Method.java:615)
> >        at junit.framework.TestCase.runTest(TestCase.java:154)
> >        at junit.framework.TestCase.runBare(TestCase.java:127)
> >        at junit.framework.TestResult$1.protect(TestResult.java:106)
> >        at junit.framework.TestResult.runProtected(TestResult.java:124)
> >        at junit.framework.TestResult.run(TestResult.java:109)
> >        at junit.framework.TestCase.run(TestCase.java:118)
> >        at org.apache.openjpa.lib.test.AbstractTestCase.run(
> > AbstractTestCase.java:178)
> >        at junit.framework.TestSuite.runTest(TestSuite.java:208)
> >        at junit.framework.TestSuite.run(TestSuite.java:203)
> >        at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
> >        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> > DelegatingMethodAccessorImpl.java:43)
> >        at java.lang.reflect.Method.invoke(Method.java:615)
> >        at org.apache.maven.surefire.junit.JUnitTestSet.execute(
> > JUnitTestSet.java:210)
> >        at
> > org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTest
> > Set(
> > AbstractDirectoryTestSuite.java:135)
> >        at
> > org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(
> > AbstractDirectoryTestSuite.java:122)
> >        at org.apache.maven.surefire.Surefire.run(Surefire.java:129)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke(
> > NativeMethodAccessorImpl.java:64)
> >        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> > DelegatingMethodAccessorImpl.java:43)
> >        at java.lang.reflect.Method.invoke(Method.java:615)
> >        at
> > org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(
> > SurefireBooter.java:225)
> >        at org.apache.maven.surefire.booter.SurefireBooter.main(
> > SurefireBooter.java:747)
> >
> > testForwardEdges
> > (org.apache.openjpa.lib.graph.TestDepthFirstAnalysis)  Time
> > elapsed: 0 sec  <<< ERROR!
> > java.lang.NullPointerException
> >        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.visit(
> > DepthFirstAnalysis.java:97)
> >        at org.apache.openjpa.lib.graph.DepthFirstAnalysis.<init>(
> > DepthFirstAnalysis.java:74)
> >        at
> > org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.setUpGraph2(
> > TestDepthFirstAnalysis.java:78)
> >        at
> > org.apache.openjpa.lib.graph.TestDepthFirstAnalysis.testForwardEdges(
> > TestDepthFirstAnalysis.java:129)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke(
> > NativeMethodAccessorImpl.java:64)
> >        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> > DelegatingMethodAccessorImpl.java:43)
> >        at java.lang.reflect.Method.invoke(Method.java:615)
> >        at junit.framework.TestCase.runTest(TestCase.java:154)
> >        at junit.framework.TestCase.runBare(TestCase.java:127)
> >        at junit.framework.TestResult$1.protect(TestResult.java:106)
> >        at junit.framework.TestResult.runProtected(TestResult.java:124)
> >        at junit.framework.TestResult.run(TestResult.java:109)
> >        at junit.framework.TestCase.run(TestCase.java:118)
> >        at org.apache.openjpa.lib.test.AbstractTestCase.run(
> > AbstractTestCase.java:178)
> >        at junit.framework.TestSuite.runTest(TestSuite.java:208)
> >        at junit.framework.TestSuite.run(TestSuite.java:203)
> >        at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
> >        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> > DelegatingMethodAccessorImpl.java:43)
> >        at java.lang.reflect.Method.invoke(Method.java:615)
> >        at org.apache.maven.surefire.junit.JUnitTestSet.execute(
> > JUnitTestSet.java:210)
> >        at
> > org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTest
> > Set(
> > AbstractDirectoryTestSuite.java:135)
> >        at
> > org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(
> > AbstractDirectoryTestSuite.java:122)
> >        at org.apache.maven.surefire.Surefire.run(Surefire.java:129)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >        at sun.reflect.NativeMethodAccessorImpl.invoke(
> > NativeMethodAccessorImpl.java:64)
> >        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
> > DelegatingMethodAccessorImpl.java:43)
> >        at java.lang.reflect.Method.invoke(Method.java:615)
> >        at
> > org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(
> > SurefireBooter.java:225)
> >        at org.apache.maven.surefire.booter.SurefireBooter.main(
> > SurefireBooter.java:747)
> >
> > I have also verified these problems with Mike (who also uses the
> > IBM JDK).
> > Neither of us have taken the time to debug the problem yet, but we are
> > willing to accept assistance...  :-)
> >
> > Later,
> > Kevin
> >
> >
> > On 7/17/07, clr@apache.org <clr@apache.org> wrote:
> >>
> >> Author: clr
> >> Date: Tue Jul 17 16:56:45 2007
> >> New Revision: 557089
> >>
> >> URL: http://svn.apache.org/viewvc?view=rev&rev=557089
> >> Log:
> >> OPENJPA-235 break-nullable-patch contributed by Markus Fuchs
> >>
> >> Added:
> >>
> >>     openjpa/trunk/openjpa-lib/src/main/resources/org/apache/
> >> openjpa/lib/graph/
> >>     openjpa/trunk/openjpa-lib/src/main/resources/org/apache/
> >> openjpa/lib/graph/localizer.properties
> >> (with props)
> >>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/
> >> apache/openjpa/jdbc/kernel/EntityE.java
> >> (with props)
> >> Modified:
> >>
> >>     openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/
> >> jdbc/kernel/ConstraintUpdateManager.java
> >>
> >>     openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/
> >> jdbc/schema/ForeignKey.java
> >>
> >>     openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/
> >> openjpa/jdbc/kernel/localizer.properties
> >>
> >>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/DepthFirstAnalysis.java
> >>
> >>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/Edge.java
> >>
> >>     openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/
> >> graph/TestDepthFirstAnalysis.java
> >>
> >>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/
> >> apache/openjpa/jdbc/kernel/EntityB.java
> >>
> >>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/
> >> apache/openjpa/jdbc/kernel/EntityC.java
> >>
> >>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/
> >> apache/openjpa/jdbc/kernel/EntityD.java
> >>
> >>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/
> >> apache/openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/
> >> kernel/ConstraintUpdateManager.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/
> >> java/org/apache/openjpa/jdbc/kernel/ConstraintUpdateManager.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/
> >> kernel/ConstraintUpdateManager.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/
> >> kernel/ConstraintUpdateManager.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -24,6 +24,7 @@
> >> import java.util.HashMap;
> >> import java.util.Iterator;
> >> import java.util.LinkedList;
> >> +import java.util.List;
> >> import java.util.Map;
> >>
> >> import org.apache.openjpa.jdbc.meta.ClassMapping;
> >> @@ -43,6 +44,7 @@
> >> import org.apache.openjpa.lib.util.Localizer;
> >> import org.apache.openjpa.util.InternalException;
> >> import org.apache.openjpa.util.OpenJPAException;
> >> +import org.apache.openjpa.util.UserException;
> >>
> >> /**
> >>   * <p>Standard update manager, capable of foreign key constraint
> >> evaluation.</p>
> >> @@ -163,7 +165,7 @@
> >>              row2 = getInsertRow(insertMap, rowMgr, row);
> >>              if (row2 != null) {
> >>                  ignoreUpdates = false;
> >> -                graphs[1] = addEdge(graphs[1], row, (PrimaryRow)
> >> row2,
> >> null);
> >> +                graphs[1] = addEdge(graphs[1], (PrimaryRow) row2,
> >> row,
> >> null);
> >>              }
> >>
> >>              // now check this row's fks against other deletes
> >> @@ -180,7 +182,7 @@
> >>                  row2 = rowMgr.getRow(fks[j].getPrimaryKeyTable(),
> >>                      Row.ACTION_DELETE, fkVal, false);
> >>                  if (row2 != null && row2.isValid() && row2 != row)
> >> -                    graphs[1] = addEdge(graphs[1], row, (PrimaryRow)
> >> row2,
> >> +                    graphs[1] = addEdge(graphs[1], (PrimaryRow)
> >> row2,
> >> row,
> >>                          fks[j]);
> >>              }
> >>          }
> >> @@ -249,7 +251,7 @@
> >>                      Row.ACTION_INSERT, row.getForeignKeySet(fks[j]),
> >> false);
> >>                  if (row2 != null && row2.isValid() && (row2 != row
> >>                      || fks[j].isDeferred() || fks[j].isLogical()))
> >> -                    graph = addEdge(graph, (PrimaryRow) row2, row,
> >> fks[j]);
> >> +                    graph = addEdge(graph, row, (PrimaryRow) row2,
> >> fks[j]);
> >>              }
> >>
> >>              // see if there are any relation id columns dependent on
> >> @@ -263,7 +265,7 @@
> >>                  row2 = rowMgr.getRow(getBaseTable(sm),
> >> Row.ACTION_INSERT,
> >>                      sm, false);
> >>                  if (row2 != null && row2.isValid())
> >> -                    graph = addEdge(graph, (PrimaryRow) row2, row,
> >> cols[j]);
> >> +                    graph = addEdge(graph, row, (PrimaryRow) row2,
> >> cols[j]);
> >>              }
> >>          }
> >>          return graph;
> >> @@ -318,80 +320,205 @@
> >>              return;
> >>
> >>          DepthFirstAnalysis dfa = newDepthFirstAnalysis(graph,
> >> autoAssign);
> >> +        Collection insertUpdates = new LinkedList();
> >> +        Collection deleteUpdates = new LinkedList();
> >> +        boolean recalculate;
> >> +
> >> +        // Handle circular constraints:
> >> +        // - if deleted row A has a ciricular fk to deleted row B,
> >> +        //   then use an update statement to null A's fk to B before
> >> flushing,
> >> +        //   and then flush
> >> +        // - if inserted row A has a circular fk to updated/
> >> inserted row
> >> B,
> >> +        //   then null the fk in the B row object, then flush,
> >> +        //   and after flushing, use an update to set the fk back
> >> to A
> >> +        // Depending on where circular dependencies are broken, the
> >> +        // topological order of the graph nodes has to be re-
> >> calculated.
> >> +        recalculate = resolveCycles(graph, dfa.getEdges
> >> (Edge.TYPE_BACK),
> >> +                deleteUpdates, insertUpdates);
> >> +        recalculate |= resolveCycles(graph, dfa.getEdges(
> >> Edge.TYPE_FORWARD),
> >> +                deleteUpdates, insertUpdates);
> >> +
> >> +        if (recalculate) {
> >> +            dfa = recalculateDepthFirstAnalysis(graph, autoAssign);
> >> +        }
> >> +
> >> +        // flush delete updates to null fks, then all rows in
> >> order, then
> >> +        // the insert updates to set circular fk values
> >> +        flush(deleteUpdates, psMgr);
> >>          Collection nodes = dfa.getSortedNodes();
> >> -        Collection backs = dfa.getEdges(Edge.TYPE_BACK);
> >> +        for (Iterator itr = nodes.iterator(); itr.hasNext();)
> >> +            psMgr.flush((RowImpl) itr.next());
> >> +        flush(insertUpdates, psMgr);
> >> +    }
> >>
> >> -        // handle circular constraints:
> >> -        // - if deleted row A has a ciricular fk to deleted row
> >> B, then
> >> use an
> >> -        //   update statement to null A's fk to B
> >> -        // - if inserted row A has a circular fk to updated/
> >> inserted row
> >> B,
> >> -        //   then null the fk in the B row object, and after
> >> flushing,
> >> use
> >> -        //   an update to set the fk to back to A
> >> -        Collection insertUpdates = null;
> >> -        Collection deleteUpdates = null;
> >> +    /**
> >> +     * Break a circular dependency caused by delete operations.
> >> +     * If deleted row A has a ciricular fk to deleted row B, then
> >> use an
> >> update
> >> +     * statement to null A's fk to B before deleting B, then
> >> delete A.
> >> +     * @param edge Edge in the dependency graph corresponding to a
> >> foreign key
> >> +     * constraint. This dependency is broken by nullifying the
> >> foreign
> >> key.
> >> +     * @param deleteUpdates Collection of update statements that are
> >> executed
> >> +     * before the delete operations are flushed
> >> +     */
> >> +    private void addDeleteUpdate(Edge edge, Collection
> >> deleteUpdates)
> >> +        throws SQLException {
> >>          PrimaryRow row;
> >>          RowImpl update;
> >> -        Edge edge;
> >> +        ForeignKey fk;
> >> +
> >> +        // copy where conditions into new update that nulls the fk
> >> +        row = (PrimaryRow) edge.getTo();
> >> +        update = new PrimaryRow(row.getTable(),
> >> Row.ACTION_UPDATE, null);
> >> +        row.copyInto(update, true);
> >> +        if (edge.getUserObject() instanceof ForeignKey) {
> >> +            fk = (ForeignKey) edge.getUserObject();
> >> +            update.setForeignKey(fk, row.getForeignKeyIO(fk), null);
> >> +        } else
> >> +            update.setNull((Column) edge.getUserObject());
> >> +
> >> +        deleteUpdates.add(update);
> >> +    }
> >> +
> >> +    /**
> >> +     * Break a circular dependency caused by insert operations.
> >> +     * If inserted row A has a circular fk to updated/inserted
> >> row B,
> >> +     * then null the fk in the B row object, then flush,
> >> +     * and after flushing, use an update to set the fk back to A.
> >> +     * @param row Row to be flushed
> >> +     * @param edge Edge in the dependency graph corresponding to a
> >> foreign key
> >> +     * constraint. This dependency is broken by nullifying the
> >> foreign
> >> key.
> >> +     * @param insertUpdates Collection of update statements that are
> >> executed
> >> +     * after the insert/update operations are flushed
> >> +     */
> >> +    private void addInsertUpdate(PrimaryRow row, Edge edge,
> >> +        Collection insertUpdates) throws SQLException {
> >> +        RowImpl update;
> >>          ForeignKey fk;
> >>          Column col;
> >> -        for (Iterator itr = backs.iterator(); itr.hasNext();) {
> >> -            edge = (Edge) itr.next();
> >> -            if (edge.getUserObject() == null)
> >> -                throw new InternalException(_loc.get("del-ins-
> >> cycle"));
> >> -
> >> -            // use a primary row update to prevent setting pk and fk
> >> values
> >> -            // until after flush, to get latest auto-increment
> >> values
> >> -            row = (PrimaryRow) edge.getTo();
> >> -            if (row.getAction() == Row.ACTION_DELETE) {
> >> -                // copy where conditions into new update that
> >> nulls the
> >> fk
> >> -                row = (PrimaryRow) edge.getFrom();
> >> -                update = new PrimaryRow(row.getTable(),
> >> Row.ACTION_UPDATE
> >> ,null);
> >> -                row.copyInto(update, true);
> >> -                if (edge.getUserObject() instanceof ForeignKey) {
> >> -                    fk = (ForeignKey) edge.getUserObject();
> >> -                    update.setForeignKey(fk, row.getForeignKeyIO
> >> (fk),
> >> null);
> >> -                } else
> >> -                    update.setNull((Column) edge.getUserObject());
> >> -
> >> -                if (deleteUpdates == null)
> >> -                    deleteUpdates = new LinkedList();
> >> -                deleteUpdates.add(update);
> >> -            } else {
> >> -                // copy where conditions into new update that
> >> sets the fk
> >> -                update = new PrimaryRow(row.getTable(),
> >> Row.ACTION_UPDATE
> >> ,null);
> >> -                if (row.getAction() == Row.ACTION_INSERT) {
> >> -                    if (row.getPrimaryKey() == null)
> >> -                        throw new
> >> InternalException(_loc.get("ref-cycle"));
> >> -                    update.wherePrimaryKey(row.getPrimaryKey());
> >> -                } else
> >> -                    row.copyInto(update, true);
> >> -                if (edge.getUserObject() instanceof ForeignKey) {
> >> -                    fk = (ForeignKey) edge.getUserObject();
> >> -                    update.setForeignKey(fk, row.getForeignKeyIO
> >> (fk),
> >> -                        row.getForeignKeySet(fk));
> >> -                    row.clearForeignKey(fk);
> >> -                } else {
> >> -                    col = (Column) edge.getUserObject();
> >> -                    update.setRelationId(col, row.getRelationIdSet
> >> (col),
> >> -                        row.getRelationIdCallback(col));
> >> -                    row.clearRelationId(col);
> >> -                }
> >>
> >> -                if (insertUpdates == null)
> >> -                    insertUpdates = new LinkedList();
> >> -                insertUpdates.add(update);
> >> +        // copy where conditions into new update that sets the fk
> >> +        update = new PrimaryRow(row.getTable(),
> >> Row.ACTION_UPDATE, null);
> >> +        if (row.getAction() == Row.ACTION_INSERT) {
> >> +            if (row.getPrimaryKey() == null)
> >> +                throw new InternalException(_loc.get("ref-cycle"));
> >> +            update.wherePrimaryKey(row.getPrimaryKey());
> >> +        } else {
> >> +            // Row.ACTION_UPDATE
> >> +            row.copyInto(update, true);
> >> +        }
> >> +        if (edge.getUserObject() instanceof ForeignKey) {
> >> +            fk = (ForeignKey) edge.getUserObject();
> >> +            update.setForeignKey(fk, row.getForeignKeyIO(fk),
> >> +                row.getForeignKeySet(fk));
> >> +            row.clearForeignKey(fk);
> >> +        } else {
> >> +            col = (Column) edge.getUserObject();
> >> +            update.setRelationId(col, row.getRelationIdSet(col),
> >> +                row.getRelationIdCallback(col));
> >> +            row.clearRelationId(col);
> >> +        }
> >> +
> >> +        insertUpdates.add(update);
> >> +    }
> >> +
> >> +    /**
> >> +     * Finds a nullable foreign key by walking the dependency cycle.
> >> +     * Circular dependencies can be broken at this point.
> >> +     * @param cycle Cycle in the dependency graph.
> >> +     * @return Edge corresponding to a nullable foreign key.
> >> +     */
> >> +    private Edge findBreakableLink(List cycle) {
> >> +        Edge breakableLink = null;
> >> +        for (Iterator iter = cycle.iterator(); iter.hasNext(); ) {
> >> +            Edge edge = (Edge) iter.next();
> >> +            Object userObject = edge.getUserObject();
> >> +            if (userObject instanceof ForeignKey) {
> >> +                 if (!((ForeignKey) userObject).hasNotNullColumns
> >> ()) {
> >> +                     breakableLink = edge;
> >> +                     break;
> >> +                 }
> >> +            } else if (userObject instanceof Column) {
> >> +                if (!((Column) userObject).isNotNull()) {
> >> +                    breakableLink = edge;
> >> +                    break;
> >> +                }
> >>              }
> >>          }
> >> +        return breakableLink;
> >> +    }
> >>
> >> -        // flush delete updates to null fks, then all rows in
> >> order, then
> >> -        // the insert updates to set circular fk values
> >> -        if (deleteUpdates != null)
> >> -            flush(deleteUpdates, psMgr);
> >> -        for (Iterator itr = nodes.iterator(); itr.hasNext();)
> >> -            psMgr.flush((RowImpl) itr.next());
> >> -        if (insertUpdates != null)
> >> -            flush(insertUpdates, psMgr);
> >> -       }
> >> +    /**
> >> +     * Re-calculates the DepthFirstSearch analysis of the graph
> >> +     * after some of the edges have been removed. Ensures
> >> +     * that the dependency graph is cycle free.
> >> +     * @param graph The graph of statements to be walked
> >> +     * @param autoAssign Whether any of the rows in the graph
> >> have any
> >> +     * auto-assign constraints
> >> +     */
> >> +    private DepthFirstAnalysis recalculateDepthFirstAnalysis
> >> (Graph graph,
> >> +        boolean autoAssign) {
> >> +        DepthFirstAnalysis dfa;
> >> +        // clear previous traversal data
> >> +        graph.clearTraversal();
> >> +        dfa = newDepthFirstAnalysis(graph, autoAssign);
> >> +        // make sure that the graph is non-cyclic now
> >> +        assert (dfa.hasNoCycles()): _loc.get("graph-not-cycle-
> >> free");
> >> +        return dfa;
> >> +    }
> >> +
> >> +    /**
> >> +     * Resolve circular dependencies by identifying and breaking
> >> +     * a nullable foreign key.
> >> +     * @param graph Dependency graph.
> >> +     * @param edges Collection of edges. Each edge indicates a
> >> possible
> >> +     * circular dependency
> >> +     * @param deleteUpdates Collection of update operations
> >> (nullifying
> >> +     * foreign keys) to be filled. These updates will be executed
> >> before
> >> +     * the rows in the dependency graph are flushed
> >> +     * @param insertUpdates CCollection of update operations
> >> (nullifying
> >> +     * foreign keys) to be filled. These updates will be executed
> >> after
> >> +     * the rows in the dependency graph are flushed
> >> +     * @return Depending on where circular dependencies are
> >> broken, the
> >> +     * topological order of the graph nodes has to be re-calculated.
> >> +     */
> >> +    private boolean resolveCycles(Graph graph, Collection edges,
> >> +        Collection deleteUpdates, Collection insertUpdates)
> >> +        throws SQLException {
> >> +        boolean recalculate = false;
> >> +        for (Iterator itr = edges.iterator(); itr.hasNext();) {
> >> +            Edge edge = (Edge) itr.next();
> >> +            List cycle = edge.getCycle();
> >> +
> >> +            if (cycle != null) {
> >> +                // find a nullable foreign key
> >> +                Edge breakableLink = findBreakableLink(cycle);
> >> +                if (breakableLink == null) {
> >> +                    throw new UserException(_loc.get("no-nullable-
> >> fk"));
> >> +                }
> >> +
> >> +                // topologic node order must be re-calculated,
> >> if the
> >> +                // breakable link is different from the edge where
> >> +                // the circular dependency was originally detected
> >> +                if (edge != breakableLink) {
> >> +                    recalculate = true;
> >> +                }
> >> +
> >> +                if (!breakableLink.isRemovedFromGraph()) {
> >> +
> >> +                    // use a primary row update to prevent
> >> setting pk and
> >> fk values
> >> +                    // until after flush, to get latest auto-
> >> increment
> >> values
> >> +                    PrimaryRow row = (PrimaryRow)
> >> breakableLink.getFrom
> >> ();
> >> +                    if (row.getAction() == Row.ACTION_DELETE) {
> >> +                        addDeleteUpdate(breakableLink,
> >> deleteUpdates);
> >> +                    } else {
> >> +                        addInsertUpdate(row, breakableLink,
> >> insertUpdates);
> >> +                    }
> >> +                    graph.removeEdge(breakableLink);
> >> +                }
> >> +            }
> >> +        }
> >> +        return recalculate;
> >> +    }
> >>
> >>      /**
> >>       * Create a new {@link DepthFirstAnalysis} suitable for the
> >> given
> >> graph
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/
> >> schema/ForeignKey.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/
> >> java/org/apache/openjpa/jdbc/schema/ForeignKey.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/
> >> schema/ForeignKey.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/
> >> schema/ForeignKey.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -701,6 +701,19 @@
> >>              && match(getPrimaryKeyColumns(), fkPKCols);
> >>      }
> >>
> >> +    /**
> >> +     * Checks for non-nullable local columns.
> >> +     */
> >> +    public boolean hasNotNullColumns() {
> >> +      Column[] columns = getColumns();
> >> +      for (int j = 0; j < columns.length; j++) {
> >> +          if (columns[j].isNotNull()) {
> >> +              return true;
> >> +          }
> >> +      }
> >> +      return false;
> >> +    }
> >> +
> >>      private static boolean match(Column[] cols, Column[] fkCols) {
> >>          if (cols.length != fkCols.length)
> >>              return false;
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/
> >> jdbc/kernel/localizer.properties
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/
> >> resources/org/apache/openjpa/jdbc/kernel/localizer.properties?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/
> >> jdbc/kernel/localizer.properties
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/
> >> jdbc/kernel/localizer.properties
> >> Tue Jul 17 16:56:45 2007
> >> @@ -98,7 +98,15 @@
> >>         \t[-properties/-p <properties file or resource>]\n\
> >>         \t[-<property name> <property value>]*
> >> bad-level: Invalid isolation level. Valid levels are -1, \
> >> -    Connection.TRANSACTION_NONE,
> >> Connection.TRANSACTION_READ_UNCOMMITTED,
> >> \
> >> +    Connection.TRANSACTION_NONE,
> >> Connection.TRANSACTION_READ_UNCOMMITTED,
> >> \
> >>      Connection.TRANSACTION_READ_COMMITTED, \
> >>      Connection.TRANSACTION_REPEATABLE_READ, or \
> >>      Connection.TRANSACTION_SERIALIZABLE. Specified value: {0}.
> >> +no-nullable-fk: No nullable foreign key found to resolve circular
> >> flush\n\
> >> +       dependency. During flush processing, changes to instances,
> >> new\n\
> >> +       instances, and deleted instances must be processed in a
> >> specific
> >> sequence\n\
> >> +       to avoid foreign key constraint violations. The changes
> >> required
> >> in this\n\
> >> +       transaction cannot be reordered because none of the
> >> foreign key
> >> constraints\n\
> >> +       is nullable (optional).
> >> +graph-not-cycle-free: A circular flush dependency has been found
> >> after
> >> all \
> >> +    circular dependencies should have been resolved.
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/DepthFirstAnalysis.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/
> >> java/org/apache/openjpa/lib/graph/DepthFirstAnalysis.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/DepthFirstAnalysis.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/DepthFirstAnalysis.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -18,6 +18,8 @@
> >>   */
> >> package org.apache.openjpa.lib.graph;
> >>
> >> +import org.apache.openjpa.lib.util.Localizer;
> >> +
> >> import java.util.AbstractList;
> >> import java.util.ArrayList;
> >> import java.util.Arrays;
> >> @@ -26,6 +28,7 @@
> >> import java.util.Comparator;
> >> import java.util.HashMap;
> >> import java.util.Iterator;
> >> +import java.util.LinkedList;
> >> import java.util.List;
> >> import java.util.Map;
> >>
> >> @@ -42,6 +45,9 @@
> >>   */
> >> public class DepthFirstAnalysis {
> >>
> >> +    private static final Localizer _loc = Localizer.forPackage
> >> +        (DepthFirstAnalysis.class);
> >> +
> >>      private final Graph _graph;
> >>      private final Map _nodeInfo = new HashMap();
> >>      private Comparator _comp;
> >> @@ -65,14 +71,15 @@
> >>              node = itr.next();
> >>              info = (NodeInfo) _nodeInfo.get(node);
> >>              if (info.color == NodeInfo.COLOR_WHITE)
> >> -                visit(graph, node, info, 0);
> >> +                visit(graph, node, info, 0, new LinkedList());
> >>          }
> >>      }
> >>
> >>      /**
> >>       * Visit a node.  See Introduction to Algorithms book for
> >> details.
> >>       */
> >> -    private int visit(Graph graph, Object node, NodeInfo info,
> >> int time)
> >> {
> >> +    private int visit(Graph graph, Object node, NodeInfo info,
> >> int time,
> >> +        List path) {
> >>          // discover node
> >>          info.color = NodeInfo.COLOR_GRAY;
> >>
> >> @@ -89,14 +96,24 @@
> >>              otherInfo = (NodeInfo) _nodeInfo.get(other);
> >>              if (otherInfo.color == NodeInfo.COLOR_WHITE) {
> >>                  // undiscovered node; recurse into it
> >> -                childTime = visit(graph, other, otherInfo, time);
> >> +                path.add(edge);
> >> +                childTime = visit(graph, other, otherInfo, time,
> >> path);
> >> +                path.remove(edge);
> >>                  edge.setType(Edge.TYPE_TREE);
> >>              } else if (otherInfo.color == NodeInfo.COLOR_GRAY) {
> >>                  childTime = -1;
> >>                  edge.setType(Edge.TYPE_BACK);
> >> +                // calculate the cycle including this edge
> >> +                edge.setCycle(cycleForBackEdge(edge, path));
> >>              } else {
> >>                  childTime = otherInfo.finished;
> >>                  edge.setType(Edge.TYPE_FORWARD);
> >> +                // find the cycle including this edge
> >> +                List cycle = new LinkedList();
> >> +                cycle.add(edge);
> >> +                if (cycleForForwardEdge(graph, other, node,
> >> cycle)) {
> >> +                    edge.setCycle(cycle);
> >> +                }
> >>              }
> >>              maxChildTime = Math.max(maxChildTime, childTime);
> >>          }
> >> @@ -134,7 +151,7 @@
> >>      /**
> >>       * Return all edges of the given type.  This method can be
> >> used to
> >>       * discover all edges that cause cycles in the graph by
> >> passing it
> >> -     * the {@link #EDGE_BACK} edge type.
> >> +     * the {@link Edge#TYPE_BACK} or {@link Edge#TYPE_FORWARD}
> >> edge type.
> >>       */
> >>      public Collection getEdges(int type) {
> >>          Collection typed = null;
> >> @@ -167,6 +184,132 @@
> >>      }
> >>
> >>      /**
> >> +     * Returns a list of graph edges forming a cycle. The cycle
> >> begins
> >> +     * with a type {@link Edge#TYPE_BACK} edge.
> >> +     * @param backEdge "Starting" edge of the cycle
> >> +     * @param path Continuous list of graph edges, may be null
> >> +     * @param pos Index of the first edge in path continuing the
> >> cycle
> >> +     * @return Cycle starting with a type {@link Edge#TYPE_BACK}
> >> edge
> >> +     */
> >> +    private List buildCycle(Edge backEdge, List path, int pos) {
> >> +        int length = path != null ? path.size() - pos : 0;
> >> +        List cycle = new ArrayList(length + 1);
> >> +        cycle.add(0, backEdge);
> >> +        for (int i = 0; i < length; i++) {
> >> +            cycle.add(i + 1, path.get(pos + i));
> >> +        }
> >> +        return cycle;
> >> +    }
> >> +
> >> +    /**
> >> +     * Computes the list of edges forming a cycle. The cycle always
> >> exists for
> >> +     * a type {@link Edge#TYPE_BACK} edge. This method should
> >> only be
> >> called
> >> +     * for type {@link Edge#TYPE_BACK} edges.
> >> +     * @param edge Edge where the cycle was detected
> >> +     * @param path Path consisting of edges to the edge's
> >> starting node
> >> +     * @return Cycle starting with a type {@link Edge#TYPE_BACK}
> >> edge
> >> +     */
> >> +    private List cycleForBackEdge(Edge edge, List path) {
> >> +        if (edge.getType() != Edge.TYPE_BACK) {
> >> +            return null;
> >> +        }
> >> +
> >> +        List cycle;
> >> +        int pos = 0;
> >> +        if (path != null && edge.getFrom() != edge.getTo()) {
> >> +            // Not a single edge loop
> >> +            pos = findNodeInPath(edge.getTo(), path);
> >> +            assert (pos >= 0): _loc.get("node-not-on-path", edge,
> >> edge.getTo());
> >> +        } else {
> >> +            assert (edge.getFrom() == edge.getTo()):
> >> +                    _loc.get("edge-no-loop", edge).getMessage();
> >> +            path = null;
> >> +        }
> >> +        cycle = buildCycle(edge, path, pos);
> >> +        assert (cycle != null): _loc.get("cycle-null",
> >> edge).getMessage();
> >> +        return cycle;
> >> +    }
> >> +
> >> +    /**
> >> +     * Computes the cycle of edges including node cycleTo. The
> >> cycle must
> >> not
> >> +     * necessarily exist. This method should only be called for type
> >> +     * {@link Edge#TYPE_FORWARD} edges.
> >> +     * @param graph Graph
> >> +     * @param node Current node
> >> +     * @param cycleTo End node for loop
> >> +     * @param path Path from loop end node to current node
> >> +     * @return True if a cycle has been found. The cycle will be
> >> contained in
> >> +     * the <code>path</code> parameter.
> >> +     */
> >> +    private boolean cycleForForwardEdge(Graph graph, Object node,
> >> +        Object cycleTo, List path) {
> >> +        boolean found = false;
> >> +        Collection edges = graph.getEdgesFrom(node);
> >> +        for (Iterator itr = edges.iterator(); !found &&
> >> itr.hasNext();) {
> >> +            Edge edge = (Edge) itr.next();
> >> +            Object other = edge.getOther(node);
> >> +            // Single edge loops are ignored
> >> +            if (node != other) {
> >> +                if (other == cycleTo) {
> >> +                    // Cycle complete
> >> +                    path.add(edge);
> >> +                    found = true;
> >> +                } else if (!path.contains(edge)){
> >> +                    // Walk this edge
> >> +                    path.add(edge);
> >> +                    found = cycleForForwardEdge(graph, other,
> >> cycleTo,
> >> path);
> >> +                    if (!found) {
> >> +                        // Remove edge again
> >> +                        path.remove(edge);
> >> +                    }
> >> +                }
> >> +            }
> >> +        }
> >> +        return found;
> >> +    }
> >> +
> >> +    /**
> >> +     * Finds the position of the edge starting from a particular
> >> node in
> >> the
> >> +     * continuous list of edges.
> >> +     * @param node Node on the cycle.
> >> +     * @param path Continuous list of graph edges.
> >> +     * @return Edge index if found, -1 otherwise.
> >> +     */
> >> +    private int findNodeInPath(Object node, List path) {
> >> +        int pos = -1;
> >> +        if (path != null) {
> >> +            for (int i = 0; i < path.size(); i++) {
> >> +                if (((Edge)path.get(i)).getFrom() == node) {
> >> +                    pos = i;
> >> +                }
> >> +            }
> >> +        }
> >> +        return pos;
> >> +    }
> >> +
> >> +    /**
> >> +     * Test, if the analysis didn't find cycles.
> >> +     */
> >> +    public boolean hasNoCycles() {
> >> +        // a) there must not be any back edges
> >> +        if (!getEdges(Edge.TYPE_BACK).isEmpty()) {
> >> +            return false;
> >> +        }
> >> +        // b) there might be forward edges
> >> +        // make sure these don't indicate cycles
> >> +        Collection edges = getEdges(Edge.TYPE_FORWARD);
> >> +        if (!edges.isEmpty()) {
> >> +            for (Iterator itr = edges.iterator(); itr.hasNext();) {
> >> +                Edge edge = (Edge) itr.next();
> >> +                if (edge.getCycle() != null)  {
> >> +                    return false;
> >> +                }
> >> +            }
> >> +        }
> >> +        return true;
> >> +    }
> >> +
> >> +    /**
> >>       * Comparator for toplogically sorting entries in the node
> >> info map.
> >>       */
> >>      private static class NodeInfoComparator
> >> @@ -184,8 +327,8 @@
> >>              NodeInfo n1 = (NodeInfo) e1.getValue();
> >>              NodeInfo n2 = (NodeInfo) e2.getValue();
> >>
> >> -            // reverse finished order
> >> -            int ret = n2.finished - n1.finished;
> >> +            // sort by finished order
> >> +            int ret = n1.finished - n2.finished;
> >>              if (ret == 0 && _subComp != null)
> >>                  ret = _subComp.compare(e1.getKey(), e2.getKey());
> >>              return ret;
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/Edge.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/
> >> java/org/apache/openjpa/lib/graph/Edge.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/Edge.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/
> >> graph/Edge.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -18,6 +18,8 @@
> >>   */
> >> package org.apache.openjpa.lib.graph;
> >>
> >> +import java.util.List;
> >> +
> >> /**
> >>   * <p>A graph edge.  Includes the from and to nodes, an arbitrary
> >> user
> >> object,
> >>   * and a weight.  Edges can be either directed or undirected.</p>
> >> @@ -52,6 +54,8 @@
> >>      private int _type = 0;
> >>      private double _weight = 0;
> >>      private Object _userObj = null;
> >> +    private List _cycle = null;
> >> +    private boolean _removedFromGraph = false;
> >>
> >>      /**
> >>       * Constructor.
> >> @@ -176,10 +180,39 @@
> >>      }
> >>
> >>      /**
> >> +     * List of edges forming a cycle. Only set for TYPE_BACK and
> >> TYPE_FORWARD edges.
> >> +     */
> >> +    public List getCycle() {
> >> +        return _cycle;
> >> +    }
> >> +
> >> +    /**
> >> +     * List of edges forming a cycle. Only set for TYPE_BACK and
> >> TYPE_FORWARD edges.
> >> +     */
> >> +    public void setCycle(List cycle) {
> >> +        _cycle = cycle;
> >> +    }
> >> +
> >> +    /**
> >> +     * Returns if this edge is (still) part of the graph.
> >> +     */
> >> +    public boolean isRemovedFromGraph() {
> >> +        return _removedFromGraph;
> >> +    }
> >> +
> >> +    /**
> >> +     * Mark this edge as removed from the graph.
> >> +     */
> >> +    public void setRemovedFromGraph() {
> >> +        this._removedFromGraph = true;
> >> +    }
> >> +
> >> +    /**
> >>       * Clear traversal info.
> >>       */
> >>      public void clearTraversal() {
> >>          _type = 0;
> >> +        _cycle = null;
> >>      }
> >>
> >>      public String toString() {
> >>
> >> Added:
> >> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/
> >> lib/graph/localizer.properties
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/
> >> resources/org/apache/openjpa/lib/graph/localizer.properties?
> >> view=auto&rev=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/
> >> lib/graph/localizer.properties
> >> (added)
> >> +++
> >> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/
> >> lib/graph/localizer.properties
> >> Tue Jul 17 16:56:45 2007
> >> @@ -0,0 +1,22 @@
> >> +# Licensed to the Apache Software Foundation (ASF) under one
> >> +# or more contributor license agreements.  See the NOTICE file
> >> +# distributed with this work for additional information
> >> +# regarding copyright ownership.  The ASF licenses this file
> >> +# to you under the Apache License, Version 2.0 (the
> >> +# "License"); you may not use this file except in compliance
> >> +# with the License.  You may obtain a copy of the License at
> >> +#
> >> +# http://www.apache.org/licenses/LICENSE-2.0
> >> +#
> >> +# Unless required by applicable law or agreed to in writing,
> >> +# software distributed under the License is distributed on an
> >> +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> >> +# KIND, either express or implied.  See the License for the
> >> +# specific language governing permissions and limitations
> >> +# under the License.
> >> +
> >> +cycle-null: The cycle must not be null for edge {0}!
> >> +edge-no-loop: Edge {0} must be a single node loop, if the depth
> >> first
> >> search \
> >> +    path is null!
> >> +node-not-on-path: Could not find node {1} in the depth first
> >> search path
> >> \
> >> +    leading to edge {0}!
> >>
> >> Propchange:
> >> openjpa/trunk/openjpa-lib/src/main/resources/org/apache/openjpa/
> >> lib/graph/localizer.properties
> >>
> >> ---------------------------------------------------------------------
> >> ---------
> >>     svn:eol-style = LF
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/
> >> graph/TestDepthFirstAnalysis.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/test/
> >> java/org/apache/openjpa/lib/graph/TestDepthFirstAnalysis.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/
> >> graph/TestDepthFirstAnalysis.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/
> >> graph/TestDepthFirstAnalysis.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -20,6 +20,7 @@
> >>
> >> import java.util.Collection;
> >> import java.util.Iterator;
> >> +import java.util.List;
> >>
> >> import org.apache.openjpa.lib.test.AbstractTestCase;
> >>
> >> @@ -34,6 +35,10 @@
> >>      private DepthFirstAnalysis _dfa = null;
> >>
> >>      public void setUp() {
> >> +        setUpGraph1();
> >> +    }
> >> +
> >> +    public void setUpGraph1() {
> >>          Graph graph = new Graph();
> >>          Object node1 = new Object();
> >>          Object node2 = new Object();
> >> @@ -51,15 +56,37 @@
> >>          _dfa = new DepthFirstAnalysis(graph);
> >>      }
> >>
> >> +    public void setUpGraph2() {
> >> +        Graph graph = new Graph();
> >> +        Integer node1 = new Integer(5);
> >> +        Integer node2 = new Integer(4);
> >> +        Integer node3 = new Integer(3);
> >> +        Integer node4 = new Integer(2);
> >> +        Integer node5 = new Integer(1);
> >> +        graph.addNode(node1);
> >> +        graph.addNode(node2);
> >> +        graph.addNode(node3);
> >> +        graph.addNode(node4);
> >> +        graph.addNode(node5);
> >> +        graph.addEdge(new Edge(node1, node2, true));
> >> +        graph.addEdge(new Edge(node2, node3, true));
> >> +        graph.addEdge(new Edge(node3, node3, true));
> >> +        graph.addEdge(new Edge(node3, node4, true));
> >> +        graph.addEdge(new Edge(node4, node1, true));
> >> +        graph.addEdge(new Edge(node4, node2, true));
> >> +        graph.addEdge(new Edge(node5, node2, true));
> >> +        _dfa = new DepthFirstAnalysis(graph);
> >> +    }
> >> +
> >>      public void testNodeSorting() {
> >>          Collection nodes = _dfa.getSortedNodes();
> >>          assertEquals(4, nodes.size());
> >>
> >> -        int time = Integer.MAX_VALUE;
> >> +        int time = 0;
> >>          Object node;
> >>          for (Iterator itr = nodes.iterator(); itr.hasNext();) {
> >>              node = itr.next();
> >> -            assertTrue(time >= _dfa.getFinishedTime(node));
> >> +            assertTrue(time <= _dfa.getFinishedTime(node));
> >>              time = _dfa.getFinishedTime(node);
> >>          }
> >>      }
> >> @@ -74,6 +101,51 @@
> >>              || edge1.getTo() == edge1.getFrom());
> >>      }
> >>
> >> +    public void testBackEdges() {
> >> +        setUpGraph2();
> >> +        Collection edges = _dfa.getEdges(Edge.TYPE_BACK);
> >> +        assertEquals(2, edges.size());
> >> +        Iterator itr = edges.iterator();
> >> +        Edge edge0 = (Edge) itr.next();
> >> +        Edge edge1 = (Edge) itr.next();
> >> +        if (edge0.getTo() == edge0.getFrom()) {
> >> +            assertTrue(edge0.getCycle() != null && edge0.getCycle
> >> ().size()
> >> == 1);
> >> +            List cycle = edge1.getCycle();
> >> +            assertTrue(cycle != null && cycle.size() == 4);
> >> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
> >> ((Edge)cycle.get(3)).getTo());
> >> +        } else if (edge1.getTo() == edge1.getFrom()) {
> >> +            assertTrue(edge1.getCycle() != null && edge1.getCycle
> >> ().size()
> >> == 1);
> >> +            assertTrue(edge1 == edge1.getCycle());
> >> +            List cycle = edge0.getCycle();
> >> +            assertTrue(cycle != null && cycle.size() == 4);
> >> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
> >> ((Edge)cycle.get(3)).getTo());
> >> +        } else {
> >> +            // should not happen
> >> +            assertFalse(true);
> >> +        }
> >> +    }
> >> +
> >> +    public void testForwardEdges() {
> >> +        setUpGraph2();
> >> +        Collection edges = _dfa.getEdges(Edge.TYPE_FORWARD);
> >> +        assertEquals(2, edges.size());
> >> +        Iterator itr = edges.iterator();
> >> +        Edge edge0 = (Edge) itr.next();
> >> +        Edge edge1 = (Edge) itr.next();
> >> +        if (edge0.getCycle() == null) {
> >> +            List cycle = edge1.getCycle();
> >> +            assertTrue(cycle != null && cycle.size() == 3);
> >> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
> >> ((Edge)cycle.get(2)).getTo());
> >> +        } else if (edge1.getCycle() == null) {
> >> +            List cycle = edge0.getCycle();
> >> +            assertTrue(cycle != null && cycle.size() == 3);
> >> +            assertTrue(((Edge)cycle.get(0)).getFrom() ==
> >> ((Edge)cycle.get(2)).getTo());
> >> +        } else {
> >> +            // should not happen
> >> +            assertFalse(true);
> >> +        }
> >> +    }
> >> +
> >>      public static void main(String[] args) {
> >>          main(TestDepthFirstAnalysis.class);
> >>      }
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityB.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-
> >> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityB.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityB.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityB.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -40,7 +40,7 @@
> >>
> >>      private String name;
> >>
> >> -    @OneToOne(cascade = CascadeType.ALL)
> >> +    @OneToOne(cascade = CascadeType.ALL, optional = false)
> >>      @JoinColumn(name = "entityc_id", referencedColumnName =
> >> "entityc_id",
> >>          nullable = false)
> >>      @ForeignKey
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityC.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-
> >> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityC.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityC.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityC.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -41,7 +41,7 @@
> >>
> >>      private String name;
> >>
> >> -    @OneToOne(cascade = CascadeType.ALL)
> >> +    @OneToOne(cascade = CascadeType.ALL, optional = false)
> >>      @JoinColumn(name = "entityd_id", referencedColumnName =
> >> "entityd_id",
> >>          nullable = false)
> >>      @ForeignKey
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityD.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-
> >> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityD.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityD.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityD.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -18,12 +18,16 @@
> >>   */
> >> package org.apache.openjpa.jdbc.kernel;
> >>
> >> +import org.apache.openjpa.persistence.jdbc.ForeignKey;
> >> +
> >> import javax.persistence.Column;
> >> import javax.persistence.Entity;
> >> import javax.persistence.GeneratedValue;
> >> import javax.persistence.GenerationType;
> >> import javax.persistence.Id;
> >> import javax.persistence.Version;
> >> +import javax.persistence.OneToOne;
> >> +import javax.persistence.JoinColumn;
> >>
> >> @Entity
> >> public class EntityD {
> >> @@ -35,6 +39,16 @@
> >>
> >>      private String name;
> >>
> >> +    @OneToOne
> >> +    @JoinColumn(name = "entitya_id", referencedColumnName =
> >> "entitya_id")
> >> +    @ForeignKey
> >> +    private EntityA entityA;
> >> +
> >> +    @OneToOne
> >> +    @JoinColumn(name = "entityb_id", referencedColumnName =
> >> "entityb_id")
> >> +    @ForeignKey
> >> +    private EntityB entityB;
> >> +
> >>      @Version
> >>      private Integer optLock;
> >>
> >> @@ -49,6 +63,22 @@
> >>          return id;
> >>      }
> >>
> >> +    public EntityA getEntityA() {
> >> +        return this.entityA;
> >> +    }
> >> +
> >> +    public void setEntityA(EntityA entityA) {
> >> +        this.entityA = entityA;
> >> +    }
> >> +
> >> +    public EntityB getEntityB() {
> >> +        return entityB;
> >> +    }
> >> +
> >> +    public void setEntityB(EntityB entityB) {
> >> +        this.entityB = entityB;
> >> +    }
> >> +
> >>      public String getName() {
> >>          return this.name;
> >>      }
> >>
> >> Added:
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityE.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-
> >> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/EntityE.java?
> >> view=auto&rev=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityE.java
> >> (added)
> >> +++
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityE.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -0,0 +1,76 @@
> >> +/*
> >> + * Licensed to the Apache Software Foundation (ASF) under one
> >> + * or more contributor license agreements.  See the NOTICE file
> >> + * distributed with this work for additional information
> >> + * regarding copyright ownership.  The ASF licenses this file
> >> + * to you under the Apache License, Version 2.0 (the
> >> + * "License"); you may not use this file except in compliance
> >> + * with the License.  You may obtain a copy of the License at
> >> + *
> >> + * http://www.apache.org/licenses/LICENSE-2.0
> >> + *
> >> + * Unless required by applicable law or agreed to in writing,
> >> + * software distributed under the License is distributed on an
> >> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> >> + * KIND, either express or implied.  See the License for the
> >> + * specific language governing permissions and limitations
> >> + * under the License.
> >> + */
> >> +package org.apache.openjpa.jdbc.kernel;
> >> +
> >> +import javax.persistence.Column;
> >> +import javax.persistence.Entity;
> >> +import javax.persistence.GeneratedValue;
> >> +import javax.persistence.GenerationType;
> >> +import javax.persistence.Id;
> >> +import javax.persistence.JoinColumn;
> >> +import javax.persistence.OneToOne;
> >> +import javax.persistence.Version;
> >> +
> >> +import org.apache.openjpa.persistence.jdbc.ForeignKey;
> >> +
> >> +@Entity
> >> +public class EntityE {
> >> +
> >> +    @Id
> >> +    @Column(name = "entitye_id", nullable = false)
> >> +    @GeneratedValue(strategy = GenerationType.IDENTITY)
> >> +    private Integer id;
> >> +
> >> +    private String name;
> >> +
> >> +    @OneToOne
> >> +    @JoinColumn(name = "entityb_id", referencedColumnName =
> >> "entityb_id")
> >> +    @ForeignKey
> >> +    private EntityB entityB;
> >> +
> >> +    @Version
> >> +    private Integer optLock;
> >> +
> >> +    public EntityE() {
> >> +    }
> >> +
> >> +    public void setId(Integer id) {
> >> +        this.id = id;
> >> +    }
> >> +
> >> +    public Integer getId() {
> >> +        return id;
> >> +    }
> >> +
> >> +    public EntityB getEntityB() {
> >> +        return this.entityB;
> >> +    }
> >> +
> >> +    public void setEntityB(EntityB entityB) {
> >> +        this.entityB = entityB;
> >> +    }
> >> +
> >> +    public String getName() {
> >> +        return this.name;
> >> +    }
> >> +
> >> +    public void setName(String name) {
> >> +        this.name = name;
> >> +    }
> >> +}
> >>
> >> Propchange:
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/EntityE.java
> >>
> >> ---------------------------------------------------------------------
> >> ---------
> >>     svn:eol-style = LF
> >>
> >> Modified:
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
> >> URL:
> >> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-
> >> jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/
> >> TestNoForeignKeyViolation.java?
> >> view=diff&rev=557089&r1=557088&r2=557089
> >>
> >> =====================================================================
> >> =========
> >> ---
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
> >> (original)
> >> +++
> >> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/
> >> openjpa/jdbc/kernel/TestNoForeignKeyViolation.java
> >> Tue Jul 17 16:56:45 2007
> >> @@ -34,15 +34,20 @@
> >>      private EntityA entityA;
> >>      private EntityB entityB;
> >>      private EntityC entityC;
> >> +    private EntityD entityD;
> >>
> >>      public void setUp() {
> >> -        setUp(EntityA.class, EntityB.class, EntityC.class,
> >> EntityD.class
> >> );
> >> +        setUp(EntityA.class, EntityB.class, EntityC.class,
> >> EntityD.class,
> >> EntityE.class);
> >>
> >> +        createTestData();
> >> +    }
> >> +
> >> +    private void createTestData() {
> >>          entityA = new EntityA();
> >> +        entityB = new EntityB();
> >>          entityC = new EntityC();
> >> -        EntityD entityD = new EntityD();
> >> +        entityD = new EntityD();
> >>          entityA.setName("entityA");
> >> -        entityB = new EntityB();
> >>          entityB.setName("entityB");
> >>          entityC.setName("entityC");
> >>          entityD.setName("entityD");
> >> @@ -52,7 +57,6 @@
> >>      }
> >>
> >>      public void testSqlOrder() {
> >> -
> >>          EntityManager em = emf.createEntityManager();
> >>          try {
> >>              em.getTransaction().begin();
> >> @@ -70,12 +74,78 @@
> >>              EntityC newEntityC = new EntityC();
> >>              newEntityC.setName("newEntityC");
> >>              newEntityD = new EntityD();
> >> -            newEntityD.setName("newEntityD");
> >> +            newEntityD.setName("newNewEntityD");
> >>              newEntityC.setEntityD(newEntityD);
> >>              entityB.setEntityC(newEntityC);
> >>
> >>              em.getTransaction().begin();
> >>              em.merge(entityB);
> >> +            em.getTransaction().commit();
> >> +        }
> >> +        finally {
> >> +            if (em.getTransaction().isActive())
> >> +                em.getTransaction().rollback();
> >> +            em.close();
> >> +        }
> >> +    }
> >> +
> >> +    public void testSimpleCycle() {
> >> +        EntityManager em = emf.createEntityManager();
> >> +        try {
> >> +            em.getTransaction().begin();
> >> +            entityD.setEntityA(entityA);
> >> +            em.persist(entityA);
> >> +            em.getTransaction().commit();
> >> +        }
> >> +        finally {
> >> +            if (em.getTransaction().isActive())
> >> +                em.getTransaction().rollback();
> >> +            em.close();
> >> +        }
> >> +    }
> >> +
> >> +    public void testComplexCycle() {
> >> +        EntityManager em = emf.createEntityManager();
> >> +        try {
> >> +            EntityE entityE = new EntityE();
> >> +            entityE.setName("entityE");
> >> +            entityE.setEntityB(entityB);
> >> +
> >> +            em.getTransaction().begin();
> >> +            em.persist(entityE);
> >> +            entityD.setEntityA(entityA);
> >> +            em.persist(entityA);
> >> +            em.getTransaction().commit();
> >> +
> >> +            em.getTransaction().begin();
> >> +            em.remove(entityE);
> >> +            em.remove(entityA);
> >> +            em.getTransaction().commit();
> >> +        }
> >> +        finally {
> >> +            if (em.getTransaction().isActive())
> >> +                em.getTransaction().rollback();
> >> +            em.close();
> >> +        }
> >> +    }
> >> +
> >> +    public void testComplexTwoCycles() {
> >> +        EntityManager em = emf.createEntityManager();
> >> +        try {
> >> +            EntityE entityE = new EntityE();
> >> +            entityE.setName("entityE");
> >> +            entityE.setEntityB(entityB);
> >> +
> >> +            em.getTransaction().begin();
> >> +            em.persist(entityE);
> >> +            entityD.setEntityA(entityA);
> >> +            entityD.setEntityB(entityB);
> >> +            em.persist(entityA);
> >> +            em.getTransaction().commit();
> >> +
> >> +            em.getTransaction().begin();
> >> +            em.remove(entityE);
> >> +            em.remove(entityA);
> >>              em.getTransaction().commit();
> >>          }
> >>          finally {
> >>
> >>
> >>
>
> Craig Russell
> Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
> 408 276-5638 mailto:Craig.Russell@sun.com
> P.S. A good JDO? O, Gasp!
>
>
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message