Author: curtisr7
Date: Sun Jul 24 12:54:45 2011
New Revision: 1150346
URL: http://svn.apache.org/viewvc?rev=1150346&view=rev
Log:
OPENJPA-2035: Allow distributed cache(s) operate without needing domain classes.
Added:
openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/
openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestPCDataSerialization.java
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Id.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java?rev=1150346&r1=1150345&r2=1150346&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java
Sun Jul 24 12:54:45 2011
@@ -18,10 +18,11 @@
*/
package org.apache.openjpa.kernel;
+import java.security.AccessController;
import java.util.BitSet;
-import java.util.Map;
import org.apache.openjpa.datacache.DataCache;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
@@ -36,7 +37,8 @@ public class PCDataImpl
extends AbstractPCData {
private final Object _oid;
- private final Class<?> _type;
+ private transient Class<?> _type;
+ private String _typeStr;
private final String _cache;
private final Object[] _data;
private final BitSet _loaded;
@@ -54,6 +56,7 @@ public class PCDataImpl
public PCDataImpl(Object oid, ClassMetaData meta, String name) {
_oid = oid;
_type = meta.getDescribedType();
+ _typeStr = _type.getName();
_cache = name;
int len = meta.getFields().length;
@@ -66,8 +69,21 @@ public class PCDataImpl
}
public Class<?> getType() {
+ if (_type == null) {
+ ClassLoader ccl =
AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
+ try {
+ _type = ccl.loadClass(_typeStr);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
return _type;
}
+
+ public void setType(Class<?> t){
+ _type = t;
+ _typeStr = t.getName();
+ }
public BitSet getLoaded() {
return _loaded;
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Id.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Id.java?rev=1150346&r1=1150345&r2=1150346&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Id.java
(original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Id.java
Sun Jul 24 12:54:45 2011
@@ -84,7 +84,7 @@ public final class Id
else {
int dash = str.indexOf(TYPE_VALUE_SEP);
try {
- type = Class.forName(str.substring(0, dash), true, loader);
+ setManagedInstanceType(Class.forName(str.substring(0, dash),
true, loader));
} catch (Throwable t) {
throw new UserException(_loc.get("string-id", str), t);
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java?rev=1150346&r1=1150345&r2=1150346&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java
Sun Jul 24 12:54:45 2011
@@ -19,7 +19,9 @@
package org.apache.openjpa.util;
import java.io.Serializable;
+import java.security.AccessController;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.ReferenceMap;
import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap;
@@ -29,16 +31,17 @@ import org.apache.openjpa.lib.util.concu
* @author Steve Kim
*/
@SuppressWarnings("serial")
-public abstract class OpenJPAId
- implements Comparable, Serializable {
+public abstract class OpenJPAId implements Comparable, Serializable {
public static final char TYPE_VALUE_SEP = '-';
// cache the types' generated hash codes
private static ConcurrentReferenceHashMap _typeCache =
new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD);
- protected Class type;
- protected boolean subs = true;
+ private transient Class<?> _type;
+ private String _typeStr;
+
+ protected boolean _subs = true;
// type hash is based on the least-derived non-object class so that
// user-given ids with non-exact types match ids with exact types
@@ -47,20 +50,31 @@ public abstract class OpenJPAId
protected OpenJPAId() {
}
- protected OpenJPAId(Class type) {
- this.type = type;
+ protected OpenJPAId(Class<?> type) {
+ _type = type;
+ _typeStr = type.getName();
+
}
- protected OpenJPAId(Class type, boolean subs) {
- this.type = type;
- this.subs = subs;
+ protected OpenJPAId(Class<?> type, boolean subs) {
+ _type = type;
+ _typeStr = type.getName();
+ _subs = subs;
}
/**
* Return the persistent class which this id instance represents.
*/
- public Class getType() {
- return type;
+ public final Class<?> getType() {
+ if (_type == null) {
+ ClassLoader ccl =
AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
+ try {
+ _type = ccl.loadClass(_typeStr);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return _type;
}
/**
@@ -68,22 +82,22 @@ public abstract class OpenJPAId
* Defaults to true.
*/
public boolean hasSubclasses() {
- return subs;
+ return _subs;
}
/**
* Set the exact type of the described instance once it is known.
*/
- public void setManagedInstanceType(Class type) {
+ public void setManagedInstanceType(Class<?> type) {
setManagedInstanceType(type, false);
}
/**
* Set the exact type of the described instance once it is known.
*/
- public void setManagedInstanceType(Class type, boolean subs) {
- this.type = type;
- this.subs = subs;
+ public void setManagedInstanceType(Class<?> type, boolean subs) {
+ _type = type;
+ _subs = subs;
}
/**
@@ -107,16 +121,16 @@ public abstract class OpenJPAId
*/
public int hashCode() {
if (_typeHash == 0) {
- Integer typeHashInt = (Integer) _typeCache.get(type);
+ Integer typeHashInt = (Integer) _typeCache.get(getType());
if (typeHashInt == null) {
- Class base = type;
- Class superclass = base.getSuperclass();
+ Class<?> base = getType();
+ Class<?> superclass = base.getSuperclass();
while (superclass != null && superclass != Object.class) {
base = base.getSuperclass();
superclass = base.getSuperclass();
}
_typeHash = base.hashCode();
- _typeCache.put(type, Integer.valueOf(_typeHash));
+ _typeCache.put(getType(), Integer.valueOf(_typeHash));
} else {
_typeHash = typeHashInt.intValue();
}
@@ -131,12 +145,12 @@ public abstract class OpenJPAId
return false;
OpenJPAId id = (OpenJPAId) o;
- return idEquals(id) && (id.type.isAssignableFrom(type)
- || (subs && type.isAssignableFrom(id.type)));
+ return idEquals(id)
+ && (id.getType().isAssignableFrom(getType()) || (_subs &&
getType().isAssignableFrom(id.getType())));
}
public String toString() {
- return type.getName() + TYPE_VALUE_SEP + getIdObject();
+ return getType().getName() + TYPE_VALUE_SEP + getIdObject();
}
public int compareTo(Object other) {
@@ -144,6 +158,6 @@ public abstract class OpenJPAId
return 0;
if (other == null)
return 1;
- return ((Comparable) getIdObject()).compareTo(((OpenJPAId)
other).getIdObject ());
- }
+ return ((Comparable) getIdObject()).compareTo(((OpenJPAId)
other).getIdObject());
+ }
}
Added:
openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestPCDataSerialization.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestPCDataSerialization.java?rev=1150346&view=auto
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestPCDataSerialization.java
(added)
+++
openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/TestPCDataSerialization.java
Sun Jul 24 12:54:45 2011
@@ -0,0 +1,156 @@
+/*
+ * 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.kernel;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.TestCase;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
+import org.apache.openjpa.enhance.DynamicStorage;
+import org.apache.openjpa.enhance.DynamicStorageGenerator;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.util.IntId;
+import org.apache.openjpa.util.OpenJPAId;
+
+/**
+ * This test ensures that we can stream a PCData and OpenJPAId to a client
which may not have the Entities on it's
+ * classpath. In a real use case we would have multiple processes, but for the
sake of unit testing this behavior is
+ * simulated via multiple classloaders.
+ */
+public class TestPCDataSerialization extends TestCase {
+ ClassLoader _ccl;
+
+ @Override
+ protected void setUp() throws Exception {
+ _ccl = Thread.currentThread().getContextClassLoader();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ Thread.currentThread().setContextClassLoader(_ccl);
+ }
+
+ public void test() throws Exception {
+ // Generate a new class. This will create a new class, and load it
with a new classloader.
+ Class<?> cls = generateClass();
+ ClassLoader newLoader = cls.getClassLoader();
+ Thread.currentThread().setContextClassLoader(newLoader);
+
+ // Create moc objects
+ MetaDataRepository repo = new DummyMetaDataRepository();
+ ClassMetaData cmd = new DummyClassMetaData(cls, repo);
+
+ OpenJPAId oid = new IntId(cls, 7);
+ PCDataImpl pcdi = new PCDataImpl(oid, cmd);
+
+ // Write the object out using the newly created classloader
+ byte[] bytes = writeObject(pcdi);
+
+ // Switch contextclassloader back to the original and try to
deserialize
+ Thread.currentThread().setContextClassLoader(_ccl);
+
+ pcdi = (PCDataImpl) readObject(bytes);
+ assertNotNull(pcdi);
+ try {
+ // This will throw a wrapped ClassNotFoundException because the
domain class isn't available.
+ pcdi.getType();
+ fail("Should have thrown an exception.");
+ } catch (RuntimeException cnfe) {
+ // expected
+ }
+ // Write object without the class
+ bytes = writeObject(pcdi);
+
+ // Switch to loader that has the new class and make sure we find it
again.
+ Thread.currentThread().setContextClassLoader(newLoader);
+ pcdi = (PCDataImpl) readObject(bytes);
+ assertNotNull(pcdi);
+ assertEquals(cls, pcdi.getType());
+
+ }
+
+ private byte[] writeObject(Object o) throws Exception {
+ ByteArrayOutputStream baos = null;
+ ObjectOutputStream oos = null;
+ try {
+ baos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+
+ return baos.toByteArray();
+ } finally {
+ if (oos != null) {
+ oos.close();
+ }
+ if (baos != null) {
+ baos.close();
+ }
+ }
+ }
+
+ private Object readObject(byte[] bytes) throws Exception {
+ ByteArrayInputStream bais = null;
+ ObjectInputStream ois = null;
+ try {
+ bais = new ByteArrayInputStream(bytes);
+ ois = new ObjectInputStream(bais);
+ return ois.readObject();
+ } finally {
+ if (ois != null) {
+ ois.close();
+ }
+ if (bais != null) {
+ bais.close();
+ }
+ }
+ }
+
+ private Class<?> generateClass() {
+ DynamicStorageGenerator gen = new DynamicStorageGenerator();
+ int[] types =
+ new int[] { JavaTypes.BOOLEAN, JavaTypes.BYTE, JavaTypes.CHAR,
JavaTypes.INT, JavaTypes.SHORT,
+ JavaTypes.LONG, JavaTypes.FLOAT, JavaTypes.DOUBLE,
JavaTypes.STRING, JavaTypes.OBJECT };
+ DynamicStorage storage = gen.generateStorage(types,
"org.apache.openjpa.enhance.Test");
+ storage = storage.newInstance();
+
+ return storage.getClass();
+ }
+
+ @SuppressWarnings("serial")
+ class DummyClassMetaData extends ClassMetaData {
+ public DummyClassMetaData(Class<?> cls, MetaDataRepository repo) {
+ super(cls, repo);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ class DummyMetaDataRepository extends MetaDataRepository {
+ @Override
+ public OpenJPAConfiguration getConfiguration() {
+ return new OpenJPAConfigurationImpl();
+ }
+ }
+}