You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2007/11/16 02:09:28 UTC

svn commit: r595510 - in /openjpa/trunk: openjpa-kernel-5/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-persistence-jdbc/ openjpa-persi...

Author: pcl
Date: Thu Nov 15 17:09:26 2007
New Revision: 595510

URL: http://svn.apache.org/viewvc?rev=595510&view=rev
Log:
OPENJPA-316

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestUnenhancedOneToMany.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedMany.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedOne.java
Removed:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/DynamicPCHelper.java
Modified:
    openjpa/trunk/openjpa-kernel-5/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/RedefinitionHelper.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
    openjpa/trunk/openjpa-persistence-jdbc/pom.xml

Modified: openjpa/trunk/openjpa-kernel-5/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel-5/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java?rev=595510&r1=595509&r2=595510&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel-5/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java (original)
+++ openjpa/trunk/openjpa-kernel-5/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java Thu Nov 15 17:09:26 2007
@@ -18,6 +18,7 @@
  */
 package org.apache.openjpa.enhance;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -34,6 +35,7 @@
 import org.apache.openjpa.lib.util.BytecodeWriter;
 import org.apache.openjpa.lib.util.JavaVersions;
 import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Files;
 import org.apache.openjpa.lib.util.Localizer.Message;
 import org.apache.openjpa.meta.ClassMetaData;
 import org.apache.openjpa.meta.FieldMetaData;
@@ -249,15 +251,35 @@
             // but do set the metadata accordingly.
             if (enhancer.isAlreadyRedefined())
                 ints.add(bc.getType());
-            else if (JavaVersions.VERSION >= 5)
+            else if (JavaVersions.VERSION >= 5) {
                 map.put(bc.getType(), bc.toByteArray());
+                debugBytecodes(bc);
+            }
         } else {
             if (!enhancer.isAlreadySubclassed()) {
+                debugBytecodes(bc);
+                
                 // this is the new subclass
                 ClassLoader loader = GeneratedClasses.getMostDerivedLoader(
                     cls, PersistenceCapable.class);
                 subs.add(GeneratedClasses.loadBCClass(bc, loader));
             }
+        }
+    }
+
+    private static void debugBytecodes(BCClass bc) throws IOException {
+        // Write the bytecodes to disk for debugging purposes.
+        if ("true".equals(System.getProperty(
+            ManagedClassSubclasser.class.getName() + ".dumpBytecodes")))
+        {
+            File tmp = new File(System.getProperty("java.io.tmpdir"));
+            File dir = new File(tmp, "openjpa");
+            dir = new File(dir, "pcsubclasses");
+            dir.mkdirs();
+            dir = Files.getPackageFile(dir, bc.getPackageName(), true);
+            File f = new File(dir, bc.getClassName() + ".class");
+            System.err.println("Writing to " + f);
+            bc.write(f);
         }
     }
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/RedefinitionHelper.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/RedefinitionHelper.java?rev=595510&r1=595509&r2=595510&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/RedefinitionHelper.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/RedefinitionHelper.java Thu Nov 15 17:09:26 2007
@@ -18,8 +18,15 @@
  */
 package org.apache.openjpa.enhance;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+
 import org.apache.openjpa.kernel.OpenJPAStateManager;
 import org.apache.openjpa.kernel.StateManagerImpl;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.util.ImplHelper;
 
 /**
@@ -184,5 +191,47 @@
         if (sm != null)
             sm.settingObjectField(pc, idx, cur, next,
                 OpenJPAStateManager.SET_USER);
+    }
+
+    /**
+     * Create a container instance that will delegate back to the state
+     * manager to emulate lazy loading. This is used by PC subclasses for
+     * unenhanced types that could not be redefined, and thus do not have
+     * field-interception capabilities. Do this for all collection and
+     * map field types, even if they are in the dfg, in case the fetch
+     * groups are reset at runtime.
+     *
+     * @since 1.1.0
+     */
+    public static void assignLazyLoadProxies(StateManagerImpl sm) {
+        FieldMetaData[] fmds = sm.getMetaData().getFields();
+        for (int i = 0; i < fmds.length; i++) {
+            switch (fmds[i].getTypeCode()) {
+                case JavaTypes.COLLECTION:
+                case JavaTypes.MAP:
+                    PersistenceCapable pc = sm.getPersistenceCapable();
+                    Field field = (Field) fmds[i].getBackingMember();
+                    Reflection.set(pc, field,
+                        newLazyLoadingProxy(fmds[i].getDeclaredType(), i, sm));
+                    break;
+            }
+        }
+    }
+
+    private static Object newLazyLoadingProxy(Class type, final int idx,
+        final StateManagerImpl sm) {
+        InvocationHandler handler = new InvocationHandler() {
+
+            public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+                // this will replace the field in the instance, so the dynamic
+                // proxy should only be called the first time a
+                // lazy-load-proxied field is used in normal usage.
+                Object delegate = sm.fetch(idx);
+                return method.invoke(delegate, args);
+            }
+        };
+        return Proxy.newProxyInstance(type.getClassLoader(),
+            new Class[] { type }, handler);
     }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java?rev=595510&r1=595509&r2=595510&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java Thu Nov 15 17:09:26 2007
@@ -21,16 +21,25 @@
 import java.io.IOException;
 import java.io.ObjectOutput;
 import java.lang.reflect.Modifier;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TimeZone;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.enhance.DynamicPersistenceCapable;
 import org.apache.openjpa.enhance.FieldManager;
+import org.apache.openjpa.enhance.ManagedInstanceProvider;
 import org.apache.openjpa.enhance.PCRegistry;
 import org.apache.openjpa.enhance.PersistenceCapable;
+import org.apache.openjpa.enhance.RedefinitionHelper;
 import org.apache.openjpa.enhance.StateManager;
-import org.apache.openjpa.enhance.ManagedInstanceProvider;
-import org.apache.openjpa.enhance.DynamicPersistenceCapable;
 import org.apache.openjpa.event.LifecycleEvent;
 import org.apache.openjpa.event.LifecycleEventManager;
 import org.apache.openjpa.lib.util.Localizer;
@@ -43,6 +52,7 @@
 import org.apache.openjpa.meta.ValueStrategies;
 import org.apache.openjpa.util.ApplicationIds;
 import org.apache.openjpa.util.Exceptions;
+import org.apache.openjpa.util.ImplHelper;
 import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.InvalidStateException;
 import org.apache.openjpa.util.ObjectNotFoundException;
@@ -50,7 +60,6 @@
 import org.apache.openjpa.util.ProxyManager;
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
 import org.apache.openjpa.util.UserException;
-import org.apache.openjpa.util.ImplHelper;
 import serp.util.Numbers;
 
 /**
@@ -306,8 +315,11 @@
 
         // if this is a non-tracking PC, add a hard ref to the appropriate data
         // sets and give it an opportunity to make a state snapshot.
-        if (!isIntercepting())
+        if (!isIntercepting()) {
             saveFields(true);
+            if (!isNew())
+                RedefinitionHelper.assignLazyLoadProxies(this);
+        }
     }
 
     /**
@@ -315,6 +327,8 @@
      * from {@link ClassMetaData#isIntercepting()} in that it checks for
      * property access + subclassing in addition to the redefinition /
      * enhancement checks.
+     *
+     * @since 1.0.0
      */
     public boolean isIntercepting() {
         if (getMetaData().isIntercepting())

Modified: openjpa/trunk/openjpa-persistence-jdbc/pom.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/pom.xml?rev=595510&r1=595509&r2=595510&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/pom.xml (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/pom.xml Thu Nov 15 17:09:26 2007
@@ -398,6 +398,7 @@
                                 <fileset id="enhance.path.ref"
                                     dir="${project.build.testOutputDirectory}">
                                     <include name="**/*.class"/>
+                                    <exclude name="**/Test*.class"/>
                                     <exclude name="**/inheritance/serializable/*.class"/>
                                     <exclude name="**/xml/*.class"/>
                                     <exclude name="**/Unenhanced*.class"/>

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestUnenhancedOneToMany.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestUnenhancedOneToMany.java?rev=595510&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestUnenhancedOneToMany.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestUnenhancedOneToMany.java Thu Nov 15 17:09:26 2007
@@ -0,0 +1,118 @@
+/*
+ * 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.enhance;
+
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+
+import org.apache.openjpa.persistence.test.SingleEMTestCase;
+
+public class TestUnenhancedOneToMany extends SingleEMTestCase {
+
+    public void setUp() {
+        setUp(UnenhancedOne.class, UnenhancedMany.class, CLEAR_TABLES);
+    }
+
+    public void testOneToMany() throws Exception {
+        assertFalse(PersistenceCapable.class.isAssignableFrom(
+            UnenhancedOne.class));
+        assertFalse(PersistenceCapable.class.isAssignableFrom(
+            UnenhancedMany.class));
+
+        em.getTransaction().begin();
+
+        UnenhancedOne one = new UnenhancedOne(1000);
+
+        UnenhancedMany manyA = new UnenhancedMany(1);
+        one.getMany().add(manyA);
+        manyA.setOne(one);
+
+        UnenhancedMany manyB = new UnenhancedMany(2);
+        one.getMany().add(manyB);
+        manyB.setOne(one);
+
+        UnenhancedMany manyC = new UnenhancedMany(3);
+        one.getMany().add(manyC);
+        manyC.setOne(one);
+
+        // em should not know about our entities
+        assertFalse(em.contains(one));
+        assertFalse(em.contains(manyA));
+        assertFalse(em.contains(manyB));
+        assertFalse(em.contains(manyC));
+
+        // persist the entity
+        em.persist(one);
+        em.persist(manyA);
+        em.persist(manyB);
+        em.persist(manyC);
+        em.flush();
+
+        // em should now be aware of our entity
+        assertTrue(em.contains(one));
+        assertTrue(em.contains(manyA));
+        assertTrue(em.contains(manyB));
+        assertTrue(em.contains(manyC));
+
+        em.getTransaction().commit();
+
+        // recreate entity manager to avoid caching
+        one = null;
+        manyA = null;
+        manyB = null;
+        manyC = null;
+        em.close();
+        em = emf.createEntityManager();
+        em.getTransaction().begin();
+
+        // reload one
+        one = em.find(UnenhancedOne.class, 1000);
+        assertNotNull("one is null", one);
+
+        // verify one.getMany(); ensure that it's a dynamic proxy before
+        // it is accessed
+        assertTrue(Proxy.isProxyClass(one.many.getClass()));
+        assertNotNull("one.getMany() is null", one.getMany());
+        Collection<UnenhancedMany> many = one.getMany();
+        assertEquals(3, many.size());
+
+        // reload the many
+        manyA = em.find(UnenhancedMany.class, 1);
+        assertNotNull("manyA is null", manyA);
+        manyB = em.find(UnenhancedMany.class, 2);
+        assertNotNull("manyB is null", manyA);
+        manyC = em.find(UnenhancedMany.class, 3);
+        assertNotNull("manyc is null", manyA);
+
+        // verify many.getOne()
+        assertNotNull("manyA.getOne() is null", manyA.getOne());
+        assertEquals(one, manyA.getOne());
+        assertNotNull("manyB.getOne() is null", manyB.getOne());
+        assertEquals(one, manyB.getOne());
+        assertNotNull("manyC.getOne() is null", manyC.getOne());
+        assertEquals(one, manyC.getOne());
+
+        // verify collection contains each many
+        assertTrue(many.contains(manyA));
+        assertTrue(many.contains(manyB));
+        assertTrue(many.contains(manyC));
+
+        em.getTransaction().commit();
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedMany.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedMany.java?rev=595510&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedMany.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedMany.java Thu Nov 15 17:09:26 2007
@@ -0,0 +1,69 @@
+/**
+ *
+ * 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.enhance;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import java.io.Serializable;
+
+@Entity
+public class UnenhancedMany implements Serializable, Cloneable {
+    private static final long serialVersionUID = 4041356744771116705L;
+
+    @Id
+    private int id;
+
+    @ManyToOne
+    private UnenhancedOne one;
+
+    public UnenhancedMany() {
+    }
+
+    public UnenhancedMany(int id) {
+        this.id = id;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public UnenhancedOne getOne() {
+        return one;
+    }
+
+    public void setOne(UnenhancedOne one) {
+        this.one = one;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (!getClass().isAssignableFrom(o.getClass())) return false;
+
+        return id == ((UnenhancedMany) o).id;
+    }
+
+    public int hashCode() {
+        return id;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedOne.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedOne.java?rev=595510&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedOne.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/UnenhancedOne.java Thu Nov 15 17:09:26 2007
@@ -0,0 +1,67 @@
+/**
+ *
+ * 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.enhance;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+
+@Entity
+public class UnenhancedOne implements Serializable, Cloneable {
+    private static final long serialVersionUID = -5834998517804641711L;
+
+    @Id
+    private int id;
+
+    @OneToMany
+    Collection<UnenhancedMany> many = new HashSet<UnenhancedMany>();
+
+    public UnenhancedOne() {
+    }
+
+    public UnenhancedOne(int id) {
+        this.id = id;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public Collection<UnenhancedMany> getMany() {
+        return many;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (!getClass().isAssignableFrom(o.getClass())) return false;
+
+        return id == ((UnenhancedOne) o).id;
+    }
+
+    public int hashCode() {
+        return id;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+}