OPENJPA-316

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@595510 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2007-11-16 01:09:26 +00:00
parent 634bdc5c92
commit 0071f7b0d8
8 changed files with 346 additions and 36 deletions

View File

@ -18,6 +18,7 @@
*/ */
package org.apache.openjpa.enhance; package org.apache.openjpa.enhance;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -34,6 +35,7 @@ import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.BytecodeWriter; import org.apache.openjpa.lib.util.BytecodeWriter;
import org.apache.openjpa.lib.util.JavaVersions; import org.apache.openjpa.lib.util.JavaVersions;
import org.apache.openjpa.lib.util.Localizer; 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.lib.util.Localizer.Message;
import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.FieldMetaData;
@ -249,10 +251,14 @@ public class ManagedClassSubclasser {
// but do set the metadata accordingly. // but do set the metadata accordingly.
if (enhancer.isAlreadyRedefined()) if (enhancer.isAlreadyRedefined())
ints.add(bc.getType()); ints.add(bc.getType());
else if (JavaVersions.VERSION >= 5) else if (JavaVersions.VERSION >= 5) {
map.put(bc.getType(), bc.toByteArray()); map.put(bc.getType(), bc.toByteArray());
debugBytecodes(bc);
}
} else { } else {
if (!enhancer.isAlreadySubclassed()) { if (!enhancer.isAlreadySubclassed()) {
debugBytecodes(bc);
// this is the new subclass // this is the new subclass
ClassLoader loader = GeneratedClasses.getMostDerivedLoader( ClassLoader loader = GeneratedClasses.getMostDerivedLoader(
cls, PersistenceCapable.class); cls, PersistenceCapable.class);
@ -261,6 +267,22 @@ public class ManagedClassSubclasser {
} }
} }
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);
}
}
private static void setIntercepting(OpenJPAConfiguration conf, private static void setIntercepting(OpenJPAConfiguration conf,
ClassLoader envLoader, Class cls) { ClassLoader envLoader, Class cls) {
ClassMetaData meta = conf.getMetaDataRepositoryInstance() ClassMetaData meta = conf.getMetaDataRepositoryInstance()

View File

@ -1,30 +0,0 @@
/*
* 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;
/**
* Helper methods for dynamically-redefined managed types.
*
* @since 1.0.0
*/
public class DynamicPCHelper {
public static boolean isDetached(Object o) {
throw new UnsupportedOperationException();
}
}

View File

@ -18,8 +18,15 @@
*/ */
package org.apache.openjpa.enhance; 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.OpenJPAStateManager;
import org.apache.openjpa.kernel.StateManagerImpl; import org.apache.openjpa.kernel.StateManagerImpl;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.ImplHelper;
/** /**
@ -185,4 +192,46 @@ public class RedefinitionHelper {
sm.settingObjectField(pc, idx, cur, next, sm.settingObjectField(pc, idx, cur, next,
OpenJPAStateManager.SET_USER); 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);
}
} }

View File

@ -21,16 +21,25 @@ package org.apache.openjpa.kernel;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutput; import java.io.ObjectOutput;
import java.lang.reflect.Modifier; 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.commons.lang.StringUtils;
import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.enhance.DynamicPersistenceCapable;
import org.apache.openjpa.enhance.FieldManager; import org.apache.openjpa.enhance.FieldManager;
import org.apache.openjpa.enhance.ManagedInstanceProvider;
import org.apache.openjpa.enhance.PCRegistry; import org.apache.openjpa.enhance.PCRegistry;
import org.apache.openjpa.enhance.PersistenceCapable; import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.RedefinitionHelper;
import org.apache.openjpa.enhance.StateManager; 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.LifecycleEvent;
import org.apache.openjpa.event.LifecycleEventManager; import org.apache.openjpa.event.LifecycleEventManager;
import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Localizer;
@ -43,6 +52,7 @@ import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.meta.ValueStrategies; import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ApplicationIds; import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.Exceptions; import org.apache.openjpa.util.Exceptions;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.InvalidStateException; import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.ObjectNotFoundException; import org.apache.openjpa.util.ObjectNotFoundException;
@ -50,7 +60,6 @@ import org.apache.openjpa.util.OpenJPAId;
import org.apache.openjpa.util.ProxyManager; import org.apache.openjpa.util.ProxyManager;
import org.apache.openjpa.util.RuntimeExceptionTranslator; import org.apache.openjpa.util.RuntimeExceptionTranslator;
import org.apache.openjpa.util.UserException; import org.apache.openjpa.util.UserException;
import org.apache.openjpa.util.ImplHelper;
import serp.util.Numbers; import serp.util.Numbers;
/** /**
@ -306,8 +315,11 @@ public class StateManagerImpl
// if this is a non-tracking PC, add a hard ref to the appropriate data // 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. // sets and give it an opportunity to make a state snapshot.
if (!isIntercepting()) if (!isIntercepting()) {
saveFields(true); saveFields(true);
if (!isNew())
RedefinitionHelper.assignLazyLoadProxies(this);
}
} }
/** /**
@ -315,6 +327,8 @@ public class StateManagerImpl
* from {@link ClassMetaData#isIntercepting()} in that it checks for * from {@link ClassMetaData#isIntercepting()} in that it checks for
* property access + subclassing in addition to the redefinition / * property access + subclassing in addition to the redefinition /
* enhancement checks. * enhancement checks.
*
* @since 1.0.0
*/ */
public boolean isIntercepting() { public boolean isIntercepting() {
if (getMetaData().isIntercepting()) if (getMetaData().isIntercepting())

View File

@ -398,6 +398,7 @@
<fileset id="enhance.path.ref" <fileset id="enhance.path.ref"
dir="${project.build.testOutputDirectory}"> dir="${project.build.testOutputDirectory}">
<include name="**/*.class"/> <include name="**/*.class"/>
<exclude name="**/Test*.class"/>
<exclude name="**/inheritance/serializable/*.class"/> <exclude name="**/inheritance/serializable/*.class"/>
<exclude name="**/xml/*.class"/> <exclude name="**/xml/*.class"/>
<exclude name="**/Unenhanced*.class"/> <exclude name="**/Unenhanced*.class"/>

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}