You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2006/05/01 23:53:45 UTC
svn commit: r398704 - in /incubator/cayenne/jpa/trunk/cayenne-jpa: ./
src/main/java/org/apache/cayenne/jpa/conf/
src/main/java/org/apache/cayenne/jpa/cspi/
src/main/java/org/apache/cayenne/jpa/enhancer/
src/main/java/org/apache/cayenne/jpa/instrument/ ...
Author: aadamchik
Date: Mon May 1 14:53:41 2006
New Revision: 398704
URL: http://svn.apache.org/viewcvs?rev=398704&view=rev
Log:
class enhancer - first cut
Added:
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/CglibEnhancer.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectDelegate.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectPropertyInjector.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/InterfaceMethodInjector.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/PropertyInjector.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/UnitClassTranformer.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaProviderContext.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancerTest.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancingClassLoader.java
Removed:
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/PersistentEnhancementVisitor.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/PersistentEnhancer.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/InstrumentationContext.java
Modified:
incubator/cayenne/jpa/trunk/cayenne-jpa/pom.xml
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessor.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoader.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoaderContext.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapMergeProcessor.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/CjpaPersistenceProvider.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/CayenneAgent.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/InstrumentingUnit.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaUnit.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/bridge/DataMapConverterTest.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapAnnotationLoaderTest.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessorTest.java
incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/entity/cayenne/MockCayenneEntity1.java
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/pom.xml
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/pom.xml?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/pom.xml (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/pom.xml Mon May 1 14:53:41 2006
@@ -93,10 +93,10 @@
<artifactId>cayenne-nodeps</artifactId>
<version>1.2-SNAPSHOT</version>
</dependency>
- <dependency>
- <groupId>asm</groupId>
- <artifactId>asm-all</artifactId>
- <version>2.2</version>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ <version>2.1_3</version>
</dependency>
</dependencies>
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessor.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessor.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessor.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessor.java Mon May 1 14:53:41 2006
@@ -50,19 +50,16 @@
public class EntityMapDefaultsProcessor {
protected HierarchicalTreeVisitor visitor;
- protected EntityMapLoaderContext context;
+ protected transient EntityMapLoaderContext context;
- public EntityMapDefaultsProcessor(EntityMapLoaderContext context) {
+ public void applyDefaults(EntityMapLoaderContext context) throws JpaProviderException {
this.context = context;
- }
-
- public void applyDefaults(JpaEntityMap entityMap) throws JpaProviderException {
if (visitor == null) {
visitor = createVisitor();
}
- TraversalUtil.traverse(entityMap, visitor);
+ TraversalUtil.traverse(context.getEntityMap(), visitor);
}
/**
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoader.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoader.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoader.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoader.java Mon May 1 14:53:41 2006
@@ -61,7 +61,6 @@
static final String DESCRIPTOR_LOCATION = "META-INF/orm.xml";
- protected JpaEntityMap entityMap;
protected EntityMapLoaderContext context;
protected Map<String, JpaClassDescriptor> descriptors;
@@ -77,7 +76,7 @@
* Returns an entity map with entity
*/
public JpaEntityMap getEntityMap() {
- return entityMap;
+ return context.getEntityMap();
}
/**
@@ -87,13 +86,12 @@
protected void loadEntityMap(PersistenceUnitInfo persistenceUnit)
throws JpaProviderException {
- this.entityMap = new JpaEntityMap();
- this.context = new EntityMapLoaderContext(entityMap);
+ this.context = new EntityMapLoaderContext();
try {
loadFromAnnotations(persistenceUnit);
- updateFromXML(entityMap, persistenceUnit);
- updateFromDefaults(entityMap);
+ updateFromXML(persistenceUnit);
+ updateFromDefaults();
}
catch (JpaProviderException e) {
throw e;
@@ -106,8 +104,8 @@
/**
* Updates missing values with spec-compilant defaults.
*/
- protected void updateFromDefaults(JpaEntityMap baseMap) {
- new EntityMapDefaultsProcessor(context).applyDefaults(baseMap);
+ protected void updateFromDefaults() {
+ new EntityMapDefaultsProcessor().applyDefaults(context);
}
/**
@@ -123,10 +121,9 @@
* present anywhere on the classpath. An orm.xml file or other mapping file is loaded
* as a resource by the persistence provider.
*/
- protected void updateFromXML(JpaEntityMap baseMap, PersistenceUnitInfo persistenceUnit)
- throws IOException {
+ protected void updateFromXML(PersistenceUnitInfo persistenceUnit) throws IOException {
- EntityMapMergeProcessor merger = new EntityMapMergeProcessor(baseMap, context);
+ EntityMapMergeProcessor merger = new EntityMapMergeProcessor(context);
Set loadedLocations = new HashSet();
EntityMapXMLLoader loader = new EntityMapXMLLoader();
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoaderContext.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoaderContext.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoaderContext.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapLoaderContext.java Mon May 1 14:53:41 2006
@@ -21,6 +21,8 @@
import java.util.LinkedList;
import java.util.Map;
+import javax.persistence.spi.PersistenceUnitInfo;
+
import org.apache.cayenne.jpa.map.JpaEntityMap;
import org.objectstyle.cayenne.validation.SimpleValidationFailure;
import org.objectstyle.cayenne.validation.ValidationFailure;
@@ -36,10 +38,11 @@
protected Map<String, JpaClassDescriptor> descriptors;
protected ValidationResult conflicts;
protected JpaEntityMap entityMap;
+ protected PersistenceUnitInfo unit;
- public EntityMapLoaderContext(JpaEntityMap map) {
+ public EntityMapLoaderContext() {
this.conflicts = new ValidationResult();
- this.entityMap = map;
+ this.entityMap = new JpaEntityMap();
}
public JpaEntityMap getEntityMap() {
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapMergeProcessor.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapMergeProcessor.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapMergeProcessor.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/conf/EntityMapMergeProcessor.java Mon May 1 14:53:41 2006
@@ -24,11 +24,9 @@
*/
public class EntityMapMergeProcessor {
- protected JpaEntityMap baseMap;
protected EntityMapLoaderContext context;
- public EntityMapMergeProcessor(JpaEntityMap baseMap, EntityMapLoaderContext context) {
- this.baseMap = baseMap;
+ public EntityMapMergeProcessor(EntityMapLoaderContext context) {
this.context = context;
}
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/CjpaPersistenceProvider.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/CjpaPersistenceProvider.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/CjpaPersistenceProvider.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/cspi/CjpaPersistenceProvider.java Mon May 1 14:53:41 2006
@@ -28,6 +28,8 @@
import org.apache.cayenne.jpa.bridge.DataMapConverter;
import org.apache.cayenne.jpa.conf.EntityMapLoader;
import org.apache.cayenne.jpa.conf.EntityMapLoaderContext;
+import org.apache.cayenne.jpa.enhancer.CglibEnhancer;
+import org.apache.cayenne.jpa.enhancer.UnitClassTranformer;
import org.apache.cayenne.jpa.spi.JpaPersistenceProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -69,7 +71,7 @@
Configuration.configureCommonLogging();
this.logger = LogFactory.getLog(getClass());
-
+
this.configuration = new LazyConfiguration();
// set a singleton that may be used by Cayenne
@@ -102,10 +104,18 @@
if (domain == null) {
long t0 = System.currentTimeMillis();
+
+ // configure Cayenne domain
domain = new DataDomain(name);
configuration.addDomain(domain);
EntityMapLoader loader = new EntityMapLoader(info);
+
+ // now that all entities are resolved, add an enhancer...
+ info.addTransformer(new UnitClassTranformer(
+ loader.getEntityMap(),
+ new CglibEnhancer()));
+
DataMapConverter converter = new DataMapConverter();
DataMap cayenneMap = converter.toDataMap(name, loader.getContext());
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/CglibEnhancer.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/CglibEnhancer.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/CglibEnhancer.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/CglibEnhancer.java Mon May 1 14:53:41 2006
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import net.sf.cglib.asm.Attribute;
+import net.sf.cglib.asm.ClassReader;
+import net.sf.cglib.asm.ClassWriter;
+import net.sf.cglib.asm.attrs.Attributes;
+import net.sf.cglib.core.ClassGenerator;
+import net.sf.cglib.core.DebuggingClassWriter;
+import net.sf.cglib.transform.ClassReaderGenerator;
+import net.sf.cglib.transform.ClassTransformer;
+import net.sf.cglib.transform.ClassTransformerChain;
+import net.sf.cglib.transform.TransformingClassGenerator;
+
+import org.objectstyle.cayenne.CayenneRuntimeException;
+import org.objectstyle.cayenne.DataObject;
+
+/**
+ * A JPA class transformer based on Cglib library.
+ *
+ * @author Andrus Adamchik
+ */
+public class CglibEnhancer implements javax.persistence.spi.ClassTransformer {
+
+ public byte[] transform(
+ ClassLoader loader,
+ String className,
+ Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) throws IllegalClassFormatException {
+
+ ClassReader reader = new ClassReader(classfileBuffer);
+ ClassWriter writer = new DebuggingClassWriter(true);
+ try {
+ getGenerator(reader).generateClass(writer);
+ }
+ catch (Exception e) {
+ throw new CayenneRuntimeException("Error transforming class '"
+ + className
+ + "'", e);
+ }
+
+ return writer.toByteArray();
+ }
+
+ protected ClassGenerator getGenerator(ClassReader reader) {
+ ClassGenerator generator = new ClassReaderGenerator(
+ reader,
+ attributes(),
+ skipDebug());
+ return new TransformingClassGenerator(generator, createTransformer());
+ }
+
+ protected boolean skipDebug() {
+ return false;
+ }
+
+ protected Attribute[] attributes() {
+ return Attributes.getDefaultAttributes();
+ }
+
+ /**
+ * Creates a chain of transformers to make DataObjects out of POJOs.
+ */
+ public ClassTransformer createTransformer() {
+
+ ClassTransformer t1 = new DataObjectPropertyInjector();
+
+ Collection<String> excludes = new ArrayList<String>();
+ excludes.add("setSnapshotVersion");
+ excludes.add("getSnapshotVersion");
+ ClassTransformer t2 = new InterfaceMethodInjector(
+ DataObject.class,
+ DataObjectDelegate.class,
+ excludes);
+
+ return new ClassTransformerChain(new ClassTransformer[] {
+ t1, t2
+ });
+ }
+}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectDelegate.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectDelegate.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectDelegate.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectDelegate.java Mon May 1 14:53:41 2006
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.objectstyle.cayenne.CayenneRuntimeException;
+import org.objectstyle.cayenne.DataObject;
+import org.objectstyle.cayenne.ObjectContext;
+import org.objectstyle.cayenne.access.DataContext;
+import org.objectstyle.cayenne.access.DataNode;
+import org.objectstyle.cayenne.access.types.ExtendedTypeMap;
+import org.objectstyle.cayenne.map.DbAttribute;
+import org.objectstyle.cayenne.map.DbJoin;
+import org.objectstyle.cayenne.map.DbRelationship;
+import org.objectstyle.cayenne.map.EntityResolver;
+import org.objectstyle.cayenne.map.ObjAttribute;
+import org.objectstyle.cayenne.map.ObjEntity;
+import org.objectstyle.cayenne.map.ObjRelationship;
+import org.objectstyle.cayenne.property.ClassDescriptor;
+import org.objectstyle.cayenne.validation.BeanValidationFailure;
+import org.objectstyle.cayenne.validation.ValidationFailure;
+import org.objectstyle.cayenne.validation.ValidationResult;
+
+/**
+ * A static delegate for DataObject callbacks. It only implements DataContext retrieval
+ * and validation methods. Remaining methods should not be called by Cayenne runtime
+ * anymore.
+ *
+ * @author Andrus Adamchik
+ */
+public final class DataObjectDelegate {
+
+ public static DataContext getDataContext(DataObject object) {
+ ObjectContext context = object.getObjectContext();
+ if (context == null || context instanceof DataContext) {
+ return (DataContext) context;
+ }
+
+ throw new CayenneRuntimeException("ObjectContext is not a DataContext: "
+ + context);
+ }
+
+ public static void setDataContext(DataObject object, DataContext dataContext) {
+ object.setObjectContext(dataContext);
+ }
+
+ public static Object readNestedProperty(DataObject object, String path) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ /**
+ * @since 1.1
+ * @deprecated since 1.2 use 'getObjectContext().prepareForAccess(object)'
+ */
+ public static void resolveFault(DataObject object) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static Object readProperty(DataObject object, String propertyName) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static Object readPropertyDirectly(DataObject object, String propName) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void writeProperty(DataObject object, String propName, Object val) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void writePropertyDirectly(
+ DataObject object,
+ String propName,
+ Object val) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void removeToManyTarget(
+ DataObject object,
+ String relName,
+ DataObject value,
+ boolean setReverse) {
+
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void addToManyTarget(
+ DataObject object,
+ String relName,
+ DataObject value,
+ boolean setReverse) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void setToOneTarget(
+ DataObject object,
+ String relationshipName,
+ DataObject value,
+ boolean setReverse) {
+
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void fetchFinished(DataObject object) {
+ // noop
+ }
+
+ public static long getSnapshotVersion(DataObject object) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ public static void setSnapshotVersion(DataObject object, long snapshotVersion) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ protected static void validateForSave(
+ DataObject object,
+ ValidationResult validationResult) {
+
+ EntityResolver resolver = object.getObjectContext().getEntityResolver();
+ ObjEntity objEntity = resolver.lookupObjEntity(object);
+ if (objEntity == null) {
+ throw new CayenneRuntimeException(
+ "No ObjEntity mapping found for DataObject "
+ + object.getClass().getName());
+ }
+
+ DataNode node = getDataContext(object).getParentDataDomain().lookupDataNode(
+ objEntity.getDataMap());
+ if (node == null) {
+ throw new CayenneRuntimeException("No DataNode found for objEntity: "
+ + objEntity.getName());
+ }
+
+ ExtendedTypeMap types = node.getAdapter().getExtendedTypes();
+
+ // validate mandatory attributes
+
+ // handling a special case - meaningful mandatory FK... defer failures until
+ // relationship validation is done... This is just a temporary solution, as
+ // handling meaningful keys within the object lifecycle requires something more,
+ // namely read/write methods for relationships and direct values should be
+ // synchronous with each other..
+ Map failedDbAttributes = null;
+
+ ClassDescriptor descriptor = resolver.getClassDescriptor(objEntity.getName());
+ Iterator attributes = objEntity.getAttributes().iterator();
+ while (attributes.hasNext()) {
+ ObjAttribute objAttribute = (ObjAttribute) attributes.next();
+ DbAttribute dbAttribute = objAttribute.getDbAttribute();
+
+ Object value = descriptor
+ .getDeclaredProperty(objAttribute.getName())
+ .readPropertyDirectly(object);
+ if (dbAttribute.isMandatory()) {
+ ValidationFailure failure = BeanValidationFailure.validateNotNull(
+ object,
+ objAttribute.getName(),
+ value);
+
+ if (failure != null) {
+
+ if (failedDbAttributes == null) {
+ failedDbAttributes = new HashMap();
+ }
+
+ failedDbAttributes.put(dbAttribute.getName(), failure);
+ continue;
+ }
+ }
+
+ if (value != null) {
+
+ // TODO: should we pass null values for validation as well?
+ // if so, class can be obtained from ObjAttribute...
+
+ types.getRegisteredType(value.getClass()).validateProperty(
+ object,
+ objAttribute.getName(),
+ value,
+ dbAttribute,
+ validationResult);
+ }
+ }
+
+ // validate mandatory relationships
+ Iterator relationships = objEntity.getRelationships().iterator();
+ while (relationships.hasNext()) {
+ ObjRelationship relationship = (ObjRelationship) relationships.next();
+
+ if (relationship.isSourceIndependentFromTargetChange()) {
+ continue;
+ }
+
+ List dbRels = relationship.getDbRelationships();
+ if (dbRels.isEmpty()) {
+ // Wha?
+ continue;
+ }
+
+ // if db relationship is not based on a PK and is based on mandatory
+ // attributes, see if we have a target object set
+ boolean validate = true;
+ DbRelationship dbRelationship = (DbRelationship) dbRels.get(0);
+ Iterator joins = dbRelationship.getJoins().iterator();
+ while (joins.hasNext()) {
+ DbJoin join = (DbJoin) joins.next();
+ DbAttribute source = join.getSource();
+
+ if (source.isMandatory()) {
+ // clear attribute failures...
+ if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
+ failedDbAttributes.remove(source.getName());
+
+ // loop through all joins if there were previous mandatory
+
+ // attribute failures....
+ if (!failedDbAttributes.isEmpty()) {
+ continue;
+ }
+ }
+ }
+ else {
+ // do not validate if the relation is based on
+ // multiple keys with some that can be nullable.
+ validate = false;
+ }
+ }
+
+ if (validate) {
+ Object value = descriptor
+ .getDeclaredProperty(relationship.getName())
+ .readPropertyDirectly(object);
+ ValidationFailure failure = BeanValidationFailure.validateNotNull(
+ object,
+ relationship.getName(),
+ value);
+
+ if (failure != null) {
+ validationResult.addFailure(failure);
+ continue;
+ }
+ }
+
+ }
+
+ // deal with previously found attribute failures...
+ if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
+ Iterator failedAttributes = failedDbAttributes.values().iterator();
+ while (failedAttributes.hasNext()) {
+ validationResult.addFailure((ValidationFailure) failedAttributes.next());
+ }
+ }
+ }
+
+ public static void validateForInsert(
+ DataObject object,
+ ValidationResult validationResult) {
+ validateForSave(object, validationResult);
+ }
+
+ public static void validateForUpdate(
+ DataObject object,
+ ValidationResult validationResult) {
+ validateForSave(object, validationResult);
+ }
+
+ public static void validateForDelete(
+ DataObject object,
+ ValidationResult validationResult) {
+ // does nothing
+ }
+}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectPropertyInjector.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectPropertyInjector.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectPropertyInjector.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/DataObjectPropertyInjector.java Mon May 1 14:53:41 2006
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import org.objectstyle.cayenne.ObjectContext;
+import org.objectstyle.cayenne.ObjectId;
+
+public class DataObjectPropertyInjector extends PropertyInjector {
+
+ static final String OBJECT_ID_PROPERTY = "objectId";
+ static final String PERSISTENCE_STATE_PROPERTY = "persistenceState";
+ static final String OBJECT_CONTEXT_PROPERTY = "objectContext";
+
+ static final String SNAPSHOT_VERSION_PROPERTY = "snapshotVersion";
+
+ static final String[] PROPERTIES = new String[] {
+ OBJECT_ID_PROPERTY, PERSISTENCE_STATE_PROPERTY, OBJECT_CONTEXT_PROPERTY,
+ SNAPSHOT_VERSION_PROPERTY
+ };
+
+ static final Class[] TYPES = new Class[] {
+ ObjectId.class, Integer.TYPE, ObjectContext.class, Long.TYPE
+ };
+
+ public DataObjectPropertyInjector() {
+ super(PROPERTIES, TYPES);
+ }
+}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/InterfaceMethodInjector.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/InterfaceMethodInjector.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/InterfaceMethodInjector.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/InterfaceMethodInjector.java Mon May 1 14:53:41 2006
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import net.sf.cglib.asm.Type;
+import net.sf.cglib.core.CodeEmitter;
+import net.sf.cglib.core.Constants;
+import net.sf.cglib.core.Signature;
+import net.sf.cglib.transform.ClassEmitterTransformer;
+
+import org.objectstyle.cayenne.CayenneRuntimeException;
+
+/**
+ * A class transformer that delegates execution of the interface methods to the
+ * corresponding static delegate methods. Delegate method must be static and have the same
+ * name and return type as the interface method. It should have one extra parameter - the
+ * interface instance. E.g. MyInterface "String doSomething(int)" method will be mapped to
+ * a "static String doSomething(MyInterface, int)" delegate method.
+ *
+ * @author Andrus Adamchik
+ */
+public class InterfaceMethodInjector extends ClassEmitterTransformer {
+
+ protected Class delegatedInterface;
+ protected Type staticDelegate;
+ protected List<Signature> interfaceMethods;
+ protected List<Signature> delegateMethods;
+
+ public InterfaceMethodInjector(Class delegatedInterface, Class staticDelegate,
+ Collection<String> excludedMethods) {
+ this.staticDelegate = Type.getType(staticDelegate);
+ this.delegatedInterface = delegatedInterface;
+
+ Method[] methods = delegatedInterface.getDeclaredMethods();
+ interfaceMethods = new ArrayList<Signature>(methods.length);
+ delegateMethods = new ArrayList<Signature>(methods.length);
+ for (Method m : methods) {
+
+ if (!excludedMethods.contains(m.getName())) {
+ interfaceMethods.add(compileSignature(m));
+ delegateMethods.add(mapDelegateMethod(staticDelegate, m));
+ }
+ }
+ }
+
+ @Override
+ public void begin_class(
+ int version,
+ int access,
+ String className,
+ Type superType,
+ Type[] interfaces,
+ String sourceFile) {
+
+ interfaces = addInterface(interfaces, delegatedInterface);
+ super.begin_class(version, access, className, superType, interfaces, sourceFile);
+ }
+
+ @Override
+ public void end_class() {
+ addDelegateMethods();
+ super.end_class();
+ }
+
+ protected Signature mapDelegateMethod(Class staticDelegate, Method method) {
+ Class[] params = method.getParameterTypes();
+ Class[] delegateParams;
+
+ if (params.length > 0) {
+ delegateParams = new Class[params.length + 1];
+ delegateParams[0] = delegatedInterface;
+ System.arraycopy(params, 0, delegateParams, 1, params.length);
+ }
+ else {
+ delegateParams = new Class[] {
+ delegatedInterface
+ };
+ }
+
+ Method delegateMethod;
+ try {
+ delegateMethod = staticDelegate.getMethod(method.getName(), delegateParams);
+ }
+ catch (NoSuchMethodException e) {
+ throw new CayenneRuntimeException("Can't match interface method '"
+ + method.getName()
+ + "'");
+ }
+
+ if (!Modifier.isStatic(delegateMethod.getModifiers())) {
+ throw new CayenneRuntimeException("Delegate method must be static '"
+ + method.getName()
+ + "'");
+ }
+
+ if (!method.getReturnType().isAssignableFrom(delegateMethod.getReturnType())) {
+ throw new CayenneRuntimeException(
+ "Inompatible return type of the delegate method '"
+ + method.getName()
+ + "'");
+ }
+
+ return compileSignature(delegateMethod);
+ }
+
+ protected Signature compileSignature(Method method) {
+
+ Class[] params = method.getParameterTypes();
+ Type[] types;
+ if (params.length == 0) {
+ types = Constants.TYPES_EMPTY;
+ }
+ else {
+ types = new Type[params.length];
+ for (int i = 0; i < params.length; i++) {
+ types[i] = Type.getType(params[i]);
+ }
+ }
+
+ return new Signature(method.getName(), Type.getReturnType(method), types);
+ }
+
+ /**
+ * Adds a delegated interface unless it is already declared for the class.
+ */
+ protected Type[] addInterface(Type[] interfaces, Class iface) {
+ String name = iface.getName();
+
+ for (Type type : interfaces) {
+ if (name.equals(type.getClassName())) {
+ return interfaces;
+ }
+ }
+
+ Type[] newInterfaces = new Type[interfaces.length + 1];
+ System.arraycopy(interfaces, 0, newInterfaces, 1, interfaces.length);
+ newInterfaces[0] = Type.getType(iface);
+ return newInterfaces;
+ }
+
+ protected void addDelegateMethods() {
+
+ // TODO: andrus, 5/1/2006 - check if the interface is partially implemented...
+ for (int i = 0; i < interfaceMethods.size(); i++) {
+
+ CodeEmitter e = begin_method(
+ Constants.ACC_PUBLIC,
+ interfaceMethods.get(i),
+ null,
+ null);
+
+ e.load_this();
+ e.load_args();
+ e.invoke_static(staticDelegate, delegateMethods.get(i));
+ e.return_value();
+ e.end_method();
+ }
+ }
+}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/PropertyInjector.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/PropertyInjector.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/PropertyInjector.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/PropertyInjector.java Mon May 1 14:53:41 2006
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import net.sf.cglib.asm.Type;
+import net.sf.cglib.core.EmitUtils;
+import net.sf.cglib.transform.ClassEmitterTransformer;
+
+/**
+ * Loads common persistent fields.
+ *
+ * @author Andrus Adamchik
+ */
+public class PropertyInjector extends ClassEmitterTransformer {
+
+ protected String[] names;
+ protected Type[] types;
+
+ public PropertyInjector(String[] names, Class[] types) {
+ this.names = names;
+ this.types = new Type[types.length];
+ for (int i = 0; i < types.length; i++) {
+ this.types[i] = Type.getType(types[i]);
+ }
+ }
+
+ @Override
+ public void begin_class(
+ int version,
+ int access,
+ String className,
+ Type superType,
+ Type[] interfaces,
+ String sourceFile) {
+
+ super.begin_class(version, access, className, superType, interfaces, sourceFile);
+ EmitUtils.add_properties(this, names, types);
+ }
+}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/UnitClassTranformer.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/UnitClassTranformer.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/UnitClassTranformer.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/enhancer/UnitClassTranformer.java Mon May 1 14:53:41 2006
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+import javax.persistence.spi.ClassTransformer;
+
+import org.apache.cayenne.jpa.map.JpaEntityMap;
+
+/**
+ * A ClassTransformer decorator that passes through classes mentioned in the JpaEntityMap
+ * to the wrapped transformer, letting all other classes to go untransformed.
+ *
+ * @author Andrus Adamchik
+ */
+public class UnitClassTranformer implements ClassTransformer {
+
+ protected JpaEntityMap entityMap;
+ protected ClassTransformer transformer;
+
+ public UnitClassTranformer(JpaEntityMap entityMap, ClassTransformer transformer) {
+ this.entityMap = entityMap;
+ this.transformer = transformer;
+ }
+
+ public byte[] transform(
+ ClassLoader loader,
+ String className,
+ Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) throws IllegalClassFormatException {
+ return isManagedClass(className) ? transformer.transform(
+ loader,
+ className,
+ classBeingRedefined,
+ protectionDomain,
+ classfileBuffer) : null;
+ }
+
+ /**
+ * Returns true if a classname os a part of an entity map. Note that the class name is
+ * expected in the internal format, separated by "/", not ".".
+ */
+ protected boolean isManagedClass(String className) {
+ return true;
+ }
+}
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/CayenneAgent.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/CayenneAgent.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/CayenneAgent.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/CayenneAgent.java Mon May 1 14:53:41 2006
@@ -28,7 +28,7 @@
* the JVM with the "-javaagent:" option. E.g.:
*
* <pre>
- * java -javaagent:/path/to/cayenne-jpa-3.0.jar org.example.Main
+ * java -javaagent:/path/to/cayenne-jpa-3.0.jar org.example.Main
* </pre>
*
* @author Andrus Adamchik
@@ -41,8 +41,7 @@
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println("*** CayenneAgent starting...");
- InstrumentationContext.sharedInstance = new InstrumentationContext(
- instrumentation);
+ InstrumentingUnit.setInstrumentation(instrumentation);
System.setProperty(JpaPersistenceProvider.UNIT_FACTORY_PROPERTY, FACTORY_CLASS);
}
}
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/InstrumentingUnit.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/InstrumentingUnit.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/InstrumentingUnit.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/instrument/InstrumentingUnit.java Mon May 1 14:53:41 2006
@@ -15,8 +15,14 @@
*/
package org.apache.cayenne.jpa.instrument;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.security.ProtectionDomain;
+
import javax.persistence.spi.ClassTransformer;
+import org.apache.cayenne.jpa.spi.JpaProviderContext;
import org.apache.cayenne.jpa.spi.JpaUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -29,25 +35,49 @@
*/
public class InstrumentingUnit extends JpaUnit {
+ static final String INSTRUMENTATION_KEY = "cayenne.jpa.instrumentation";
+
+ public static Instrumentation getInstrumentation() {
+ return (Instrumentation) JpaProviderContext.getObject(INSTRUMENTATION_KEY);
+ }
+
+ public static void setInstrumentation(Instrumentation instance) {
+ JpaProviderContext.setObject(INSTRUMENTATION_KEY, instance);
+ }
+
protected Log logger;
@Override
- public void addTransformer(ClassTransformer transformer) {
+ public void addTransformer(final ClassTransformer transformer) {
// sanity check
- if (InstrumentationContext.getInstance() == null) {
+ if (getInstrumentation() == null) {
getLogger().warn(
- "*** No instrumentation context present. "
+ "*** No instrumentation instance present. "
+ "Check the -javaagent: option");
+ return;
}
- else {
- boolean added = InstrumentationContext.getInstance().addTransformer(
- transformer);
- if (!added) {
- getLogger().info("Duplicate transformer, ignored: " + transformer);
+ // wrap in a ClassFileTransformer
+ ClassFileTransformer transformerWrapper = new ClassFileTransformer() {
+
+ public byte[] transform(
+ ClassLoader loader,
+ String className,
+ Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) throws IllegalClassFormatException {
+
+ return transformer.transform(
+ loader,
+ className,
+ classBeingRedefined,
+ protectionDomain,
+ classfileBuffer);
}
- }
+ };
+
+ getInstrumentation().addTransformer(transformerWrapper);
}
protected Log getLogger() {
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaProviderContext.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaProviderContext.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaProviderContext.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaProviderContext.java Mon May 1 14:53:41 2006
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.spi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A singleton object storing shared provider information.
+ *
+ * @author Andrus Adamchik
+ */
+public class JpaProviderContext {
+
+ protected static Map context;
+
+ public static synchronized Object getObject(String key) {
+ return context == null ? null : context.get(key);
+ }
+
+ public static synchronized void setObject(String key, Object value) {
+
+ if (context == null) {
+
+ if (value == null) {
+ return;
+ }
+
+ context = new HashMap();
+ }
+
+ context.put(key, value);
+ }
+}
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaUnit.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaUnit.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaUnit.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/main/java/org/apache/cayenne/jpa/spi/JpaUnit.java Mon May 1 14:53:41 2006
@@ -59,7 +59,10 @@
this.jarFileUrls = new ArrayList<URL>(2);
this.managedClassNames = new ArrayList<String>(30);
this.properties = new Properties();
- this.classLoader = Thread.currentThread().getContextClassLoader();
+
+ // create a class loader that is separate from the app class loader, as we want
+ // all app class loader classes to go through the enhancers
+ this.classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
}
public String getPersistenceUnitName() {
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/bridge/DataMapConverterTest.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/bridge/DataMapConverterTest.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/bridge/DataMapConverterTest.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/bridge/DataMapConverterTest.java Mon May 1 14:53:41 2006
@@ -31,15 +31,15 @@
public class DataMapConverterTest extends TestCase {
public void testDataMapDefaults() {
- JpaEntityMap jpaMap = new JpaEntityMap();
+ EntityMapLoaderContext context = new EntityMapLoaderContext();
+ JpaEntityMap jpaMap = context.getEntityMap();
jpaMap.setPackageName("p1");
jpaMap.setSchema("s1");
// TODO: unsupported by DataMap
// jpaMap.setCatalog("c1");
- DataMap cayenneMap = new DataMapConverter().toDataMap("n1", new EntityMapLoaderContext(
- jpaMap));
+ DataMap cayenneMap = new DataMapConverter().toDataMap("n1", context);
assertEquals("n1", cayenneMap.getName());
assertEquals("p1", cayenneMap.getDefaultPackage());
assertEquals("s1", cayenneMap.getDefaultSchema());
@@ -49,8 +49,7 @@
* @see org.apache.cayenne.jpa.conf.EntityMapAnnotationLoaderTest#testLoadClassMapping()
*/
public void testLoadClassMapping() throws Exception {
- JpaEntityMap map = new JpaEntityMap();
- EntityMapLoaderContext context = new EntityMapLoaderContext(map);
+ EntityMapLoaderContext context = new EntityMapLoaderContext();
EntityMapAnnotationLoader loader = new EntityMapAnnotationLoader(context);
loader.loadClassMapping(MockCayenneEntity1.class);
@@ -61,7 +60,7 @@
loader.loadClassMapping(MockCayenneEntityMap1.class);
// apply defaults before conversion
- new EntityMapDefaultsProcessor(context).applyDefaults(map);
+ new EntityMapDefaultsProcessor().applyDefaults(context);
assertFalse("Found conflicts: " + context.getConflicts(), context
.getConflicts()
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapAnnotationLoaderTest.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapAnnotationLoaderTest.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapAnnotationLoaderTest.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapAnnotationLoaderTest.java Mon May 1 14:53:41 2006
@@ -46,7 +46,7 @@
public void testSortAnnotations() throws Exception {
EntityMapAnnotationLoader loader = new EntityMapAnnotationLoader(
- new EntityMapLoaderContext(new JpaEntityMap()));
+ new EntityMapLoaderContext());
Annotation[] a1 = new Annotation[3];
a1[0] = MockAnnotatedBean1.class.getAnnotation(NamedQuery.class);
@@ -65,11 +65,12 @@
* are both processed correctly.
*/
public void testAttributeOverride() {
- JpaEntityMap map = new JpaEntityMap();
- EntityMapLoaderContext context = new EntityMapLoaderContext(map);
+
+ EntityMapLoaderContext context = new EntityMapLoaderContext();
EntityMapAnnotationLoader loader = new EntityMapAnnotationLoader(context);
loader.loadClassMapping(MockAnnotatedBean2.class);
+ JpaEntityMap map = context.getEntityMap();
assertEquals(1, map.getEntities().size());
JpaEntity entity = map.getEntities().iterator().next();
assertEquals(1, entity.getAttributeOverrides().size());
@@ -95,8 +96,8 @@
* {@link EntityMapXMLLoaderTest#testDetails()}.
*/
public void testLoadClassMapping() throws Exception {
- JpaEntityMap map = new JpaEntityMap();
- EntityMapLoaderContext context = new EntityMapLoaderContext(map);
+
+ EntityMapLoaderContext context = new EntityMapLoaderContext();
EntityMapAnnotationLoader loader = new EntityMapAnnotationLoader(context);
loader.loadClassMapping(MockEntity1.class);
@@ -119,6 +120,6 @@
.getConflicts()
.hasFailures());
- new AnnotationMappingAssertion().testEntityMap(map);
+ new AnnotationMappingAssertion().testEntityMap(context.getEntityMap());
}
}
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessorTest.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessorTest.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessorTest.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/conf/EntityMapDefaultsProcessorTest.java Mon May 1 14:53:41 2006
@@ -36,17 +36,15 @@
// sanity check - test object must not be serializable to be rejected...
assertFalse(Serializable.class.isAssignableFrom(MockAnnotatedBean3.class));
- JpaEntityMap map = new JpaEntityMap();
- context = new EntityMapLoaderContext(map);
+ context = new EntityMapLoaderContext();
EntityMapAnnotationLoader loader = new EntityMapAnnotationLoader(context);
loader.loadClassMapping(MockAnnotatedBean1.class);
loader.loadClassMapping(MockAnnotatedBean3.class);
// apply defaults
- EntityMapDefaultsProcessor defaultsProcessor = new EntityMapDefaultsProcessor(
- context);
- defaultsProcessor.applyDefaults(map);
-
+ EntityMapDefaultsProcessor defaultsProcessor = new EntityMapDefaultsProcessor();
+ defaultsProcessor.applyDefaults(context);
+ JpaEntityMap map = context.getEntityMap();
entity = map.entityForClass(MockAnnotatedBean3.class);
assertNotNull(entity);
}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancerTest.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancerTest.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancerTest.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancerTest.java Mon May 1 14:53:41 2006
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+import org.objectstyle.cayenne.DataObject;
+import org.objectstyle.cayenne.ObjectId;
+import org.objectstyle.cayenne.PersistenceState;
+import org.objectstyle.cayenne.property.PropertyUtils;
+
+public class EnhancerTest extends TestCase {
+
+ static final String E1 = "org.apache.cayenne.jpa.entity.cayenne.MockCayenneEntity1";
+ static final String E2 = "org.apache.cayenne.jpa.entity.cayenne.MockCayenneEntity2";
+ static final String ET1 = "org.apache.cayenne.jpa.entity.cayenne.MockCayenneTargetEntity1";
+ static final String ET2 = "org.apache.cayenne.jpa.entity.cayenne.MockCayenneTargetEntity2";
+
+ protected EnhancingClassLoader loader;
+
+ @Override
+ protected void setUp() throws Exception {
+ Collection<String> managedClasses = new ArrayList<String>();
+ managedClasses.add(E1);
+ managedClasses.add(E2);
+ managedClasses.add(ET1);
+ managedClasses.add(ET2);
+
+ loader = new EnhancingClassLoader(new CglibEnhancer(), managedClasses);
+ }
+
+ public void testClassLoading() throws Exception {
+
+ Class e1Class = Class.forName(E1, true, loader);
+ assertNotNull(e1Class);
+ assertEquals(E1, e1Class.getName());
+
+ assertTrue(DataObject.class.isAssignableFrom(e1Class));
+
+ Method persistenceStateGetter = e1Class.getMethod("getPersistenceState");
+ assertNotNull(persistenceStateGetter);
+ assertFalse(Modifier.isAbstract(persistenceStateGetter.getModifiers()));
+ assertNotNull(e1Class.getMethod("setPersistenceState", Integer.TYPE));
+ }
+
+ public void testDataObject() throws Exception {
+
+ Class e1Class = Class.forName(E1, true, loader);
+ Object object = e1Class.newInstance();
+
+ assertTrue(object instanceof DataObject);
+ DataObject p = (DataObject) object;
+
+ int state = PersistenceState.DELETED;
+ p.setPersistenceState(state);
+ assertEquals(PersistenceState.DELETED, p.getPersistenceState());
+
+ ObjectId id = new ObjectId("X", "R", 55);
+ p.setObjectId(id);
+ assertSame(id, p.getObjectId());
+ }
+
+ public void testPropertyInterception() throws Exception {
+
+ Class e1Class = Class.forName(E1, true, loader);
+ Object object = e1Class.newInstance();
+ // DataObject dataObject = (DataObject) object;
+
+ assertEquals(new Integer(0), PropertyUtils.getProperty(object, "attribute2"));
+ PropertyUtils.setProperty(object, "attribute2", new Integer(4));
+ assertEquals(new Integer(4), PropertyUtils.getProperty(object, "attribute2"));
+
+ // assertEquals(new Integer(4), dataObject.readProperty("attribute2"));
+ }
+}
Added: incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancingClassLoader.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancingClassLoader.java?rev=398704&view=auto
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancingClassLoader.java (added)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/enhancer/EnhancingClassLoader.java Mon May 1 14:53:41 2006
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.cayenne.jpa.enhancer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.SecureClassLoader;
+import java.util.Collection;
+
+import javax.persistence.spi.ClassTransformer;
+
+public class EnhancingClassLoader extends SecureClassLoader {
+
+ protected ClassTransformer transformer;
+ protected Collection<String> classesToEnhance;
+
+ public EnhancingClassLoader(ClassTransformer transformer,
+ Collection<String> classesToEnhance) {
+ super(Thread.currentThread().getContextClassLoader());
+ this.transformer = transformer;
+ this.classesToEnhance = classesToEnhance;
+ }
+
+ /**
+ * Returns true if the class does not need to be enhanced.
+ */
+ protected boolean skipClassEnhancement(String className) {
+ return transformer == null || !classesToEnhance.contains(className);
+ }
+
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+
+ if (skipClassEnhancement(name)) {
+ return super.loadClass(name, resolve);
+ }
+
+ Class c = findLoadedClass(name);
+
+ if (c == null) {
+ c = findClass(name);
+ }
+
+ if (resolve) {
+ resolveClass(c);
+ }
+
+ return c;
+ }
+
+ /**
+ * If a class name is one of the managed classes, loads it
+ */
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (skipClassEnhancement(name)) {
+ return Class.forName(name, true, getParent());
+ }
+ else {
+ return findEnhancedClass(name);
+ }
+ }
+
+ /**
+ * Loads class bytes, and passes them through the registered ClassTransformers.
+ */
+ protected Class<?> findEnhancedClass(String name) throws ClassNotFoundException {
+ String path = name.replace('.', '/') + ".class";
+
+ InputStream in = getResourceAsStream(path);
+ if (in == null) {
+ return Class.forName(name, true, getParent());
+ }
+
+ try {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
+ byte[] buffer = new byte[1024];
+ int read;
+
+ while ((read = in.read(buffer, 0, 1024)) > 0) {
+ out.write(buffer, 0, read);
+ }
+
+ out.close();
+ byte[] classBytes = out.toByteArray();
+
+ byte[] bytes;
+ try {
+ bytes = transformer.transform(getParent(), name, null, null, classBytes);
+ }
+ catch (IllegalClassFormatException e) {
+ throw new ClassNotFoundException("Could not transform class '"
+ + name
+ + "' due to invalid format", e);
+ }
+
+ if (bytes != null) {
+ classBytes = bytes;
+ }
+
+ return defineClass(name, classBytes, 0, classBytes.length);
+ }
+ catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ finally {
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ // ignore close exceptions...
+ }
+ }
+ }
+}
Modified: incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/entity/cayenne/MockCayenneEntity1.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/entity/cayenne/MockCayenneEntity1.java?rev=398704&r1=398703&r2=398704&view=diff
==============================================================================
--- incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/entity/cayenne/MockCayenneEntity1.java (original)
+++ incubator/cayenne/jpa/trunk/cayenne-jpa/src/test/java/org/apache/cayenne/jpa/entity/cayenne/MockCayenneEntity1.java Mon May 1 14:53:41 2006
@@ -84,4 +84,34 @@
@Column(name = "column9")
@Temporal(TemporalType.DATE)
protected Date attribute9;
+
+
+ public String getAttribute1() {
+ return attribute1;
+ }
+
+
+ public void setAttribute1(String attribute1) {
+ this.attribute1 = attribute1;
+ }
+
+
+ public int getAttribute2() {
+ return attribute2;
+ }
+
+
+ public void setAttribute2(int attribute2) {
+ this.attribute2 = attribute2;
+ }
+
+
+ public int getAttribute3() {
+ return attribute3;
+ }
+
+
+ public void setAttribute3(int attribute3) {
+ this.attribute3 = attribute3;
+ }
}