Author: epbernard Date: 2006-05-03 19:13:22 -0400 (Wed, 03 May 2006) New Revision: 9863 Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/LogListener.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/OtherLogListener.java Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationConfiguration.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/ReflectionManager.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/EJB3OverridenAnnotationReader.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/xml/XMLContext.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/Administration.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/EJB3OverridenAnnotationReaderTest.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/orm.xml Log: EJB-165 entity listeners and default entity listeners Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationConfiguration.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationConfiguration.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationConfiguration.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -45,8 +45,8 @@ import org.xml.sax.SAXException; /** - * Add JSR 175 configuration capability. - * For now, only programmatic configuration is available. + * Similar to the {@link Configuration} object but handles EJB3 and Hibernate + * specific annotations as a metadata facility * * @author Emmanuel Bernard */ @@ -584,4 +584,9 @@ } } } + + //not a public API + public ReflectionManager getReflectionManager() { + return reflectionManager; + } } Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/ReflectionManager.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/ReflectionManager.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/ReflectionManager.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -1,6 +1,7 @@ package org.hibernate.reflection; import java.util.Map; +import java.lang.reflect.Method; /** * The entry point to the reflection layer (a.k.a. the X* layer). @@ -17,6 +18,8 @@ public Class toClass(XClass xClazz); + public Method toMethod(XMethod method); + public <T> XClass classForName(String name, Class<T> caller) throws ClassNotFoundException; public XPackage packageForName(String packageName) throws ClassNotFoundException; Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/EJB3OverridenAnnotationReader.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/EJB3OverridenAnnotationReader.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/EJB3OverridenAnnotationReader.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -69,6 +69,14 @@ import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import javax.persistence.Version; +import javax.persistence.EntityListeners; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; +import javax.persistence.PostRemove; +import javax.persistence.PostPersist; +import javax.persistence.PostUpdate; +import javax.persistence.PostLoad; import org.dom4j.Attribute; import org.dom4j.Element; @@ -153,7 +161,14 @@ annotationToXml.put( JoinColumns.class, "join-column" ); annotationToXml.put( MapKey.class, "map-key" ); annotationToXml.put( OrderBy.class, "order-by" ); - + annotationToXml.put( EntityListeners.class, "entity-listeners" ); + annotationToXml.put( PrePersist.class, "pre-persist" ); + annotationToXml.put( PreRemove.class, "pre-remove" ); + annotationToXml.put( PreUpdate.class, "pre-update" ); + annotationToXml.put( PostPersist.class, "post-persist" ); + annotationToXml.put( PostRemove.class, "post-remove" ); + annotationToXml.put( PostUpdate.class, "post-update" ); + annotationToXml.put( PostLoad.class, "post-load" ); } private XMLContext xmlContext; @@ -305,6 +320,8 @@ if ( current != null ) annotationList.add( current ); current = getAssociationOverrides( tree, defaults ); if ( current != null ) annotationList.add( current ); + current = getEntityListeners( tree, defaults ); + if ( current != null ) annotationList.add( current ); this.annotations = annotationList.toArray( new Annotation[ annotationList.size() ] ); } else if ( className != null && propertyName != null ) { @@ -337,6 +354,7 @@ getAssociation( OneToMany.class, annotationList, defaults ); getAssociation( ManyToMany.class, annotationList, defaults ); } + processEventAnnotations(annotationList, defaults); this.annotations = annotationList.toArray( new Annotation[ annotationList.size() ] ); } else { @@ -345,6 +363,96 @@ } } + private void processEventAnnotations(List<Annotation> annotationList, XMLContext.Default defaults) { + boolean eventElement = false; + for ( Element element : elementsForProperty ) { + String elementName = element.getName(); + if ( "pre-persist".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PrePersist.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "pre-remove".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PreRemove.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "pre-update".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PreUpdate.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-persist".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostPersist.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-remove".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostRemove.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-update".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostUpdate.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-load".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostLoad.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + } + if ( ! eventElement && defaults.canUseJavaAnnotations() ) { + Annotation ann = super.getAnnotation(PrePersist.class); + if (ann != null) annotationList.add( ann ); + ann = super.getAnnotation(PreRemove.class); + if (ann != null) annotationList.add( ann ); + ann = super.getAnnotation(PreUpdate.class); + if (ann != null) annotationList.add( ann ); + ann = super.getAnnotation(PostPersist.class); + if (ann != null) annotationList.add( ann ); + ann = super.getAnnotation(PostRemove.class); + if (ann != null) annotationList.add( ann ); + ann = super.getAnnotation(PostUpdate.class); + if (ann != null) annotationList.add( ann ); + ann = super.getAnnotation(PostLoad.class); + if (ann != null) annotationList.add( ann ); + } + } + + private EntityListeners getEntityListeners(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( "entity-listeners" ) : null; + if ( element != null) { + List<Class> entityListenerClasses = new ArrayList<Class>(); + for (Element subelement : (List<Element>) element.elements( "entity-listener" ) ) { + String className = subelement.attributeValue( "class" ); + try { + entityListenerClasses.add( + ReflectHelper.classForName( + XMLContext.buildSafeClassName( className, defaults ), + this.getClass() + ) + ); + } + catch (ClassNotFoundException e) { + throw new AnnotationException( + "Unable to find " + element.getPath() + ".class: " + className, e + ); + } + } + AnnotationDescriptor ad = new AnnotationDescriptor( EntityListeners.class ); + ad.setValue( "value", entityListenerClasses.toArray( new Class[ entityListenerClasses.size() ] ) ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return super.getAnnotation( EntityListeners.class ); + } + else { + return null; + } + } + private JoinTable overridesDefaultsInJoinTable(XMLContext.Default defaults) { //no element but might have some default or some annotation final Class<JoinTable> annotationType = JoinTable.class; @@ -765,6 +873,7 @@ private void preCalculateElementsForProperty(Element tree) { elementsForProperty = new ArrayList<Element>(); Element element = tree != null ? tree.element( "attributes" ) : null; + //put entity.attributes elements if ( element != null ) { for ( Element subelement : (List<Element>) element.elements() ) { if ( propertyName.equals( subelement.attributeValue( "name" ) ) ) { @@ -772,6 +881,14 @@ } } } + //add pre-* etc from entity and pure entity listener classes + if (tree != null) { + for ( Element subelement : (List<Element>) tree.elements() ) { + if ( propertyName.equals( subelement.attributeValue( "method-name" ) ) ) { + elementsForProperty.add( subelement ); + } + } + } } private void getId(List<Annotation> annotationList, XMLContext.Default defaults) { Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -1,6 +1,7 @@ package org.hibernate.reflection.java; import java.lang.reflect.Member; +import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; @@ -12,6 +13,7 @@ import javax.persistence.SequenceGenerator; import javax.persistence.SqlResultSetMapping; import javax.persistence.TableGenerator; +import javax.persistence.EntityListeners; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -88,6 +90,13 @@ return (Class) ( (JavaXClass) xClazz ).toAnnotatedElement(); } + public Method toMethod(XMethod xMethod) { + if ( ! ( xMethod instanceof JavaXMethod ) ) { + throw new IllegalArgumentException( "XMethod not coming from this ReflectionManager implementation" ); + } + return (Method) ( (JavaXAnnotatedElement) xMethod ).toAnnotatedElement(); + } + public XClass classForName(String name, Class caller) throws ClassNotFoundException { return toXClass( ReflectHelper.classForName( name, caller ) ); } @@ -100,7 +109,18 @@ if (defaults == null) { defaults = new HashMap(); XMLContext.Default xmlDefaults = xmlContext.getDefault( null ); + List<Class> entityListeners = new ArrayList<Class>(); + for ( String className : xmlContext.getDefaultEntityListeners() ) { + try { + entityListeners.add( ReflectHelper.classForName( className, this.getClass() ) ); + } + catch (ClassNotFoundException e) { + throw new IllegalStateException( "Default entity listener class not found: " + className ); + } + } + defaults.put( EntityListeners.class, entityListeners ); for( Element element : xmlContext.getAllDocuments() ) { + List<Element> elements = element.elements( "sequence-generator" ); List<SequenceGenerator> sequenceGenerators = (List<SequenceGenerator>) defaults.get(SequenceGenerator.class); if (sequenceGenerators == null) { Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/xml/XMLContext.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/xml/XMLContext.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/xml/XMLContext.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -21,11 +21,13 @@ private Map<String, Element> classOverriding = new HashMap<String, Element>(); private Map<String, Default> defaultsOverriding = new HashMap<String, Default>(); private List<Element> defaultElements = new ArrayList<Element>(); + private List<String> defaultEntityListeners = new ArrayList<String>(); /** * Add a document and return the list of added classes names */ public List<String> addDocument(Document doc) { + List<String> addedClasses = new ArrayList<String>(); Element root = doc.getRootElement(); //global defaults Element metadata = root.element( "persistence-unit-metadata" ); @@ -47,11 +49,11 @@ globalDefaults.setAccess( unitElement != null ? unitElement.getTextTrim() : null ); unitElement = defaultElement.element( "cascade-persist" ); globalDefaults.setCascadePersist( unitElement != null ? Boolean.TRUE : null ); - //TODO entity listeners + defaultEntityListeners.addAll( addEntityListenerClasses( defaultElement, null, addedClasses ) ); } } else { - log.warn( "Found more than one <persistence-unit-metadata>, ignored" ); + log.warn( "Found more than one <persistence-unit-metadata>, subsequent ignored" ); } } @@ -68,7 +70,6 @@ entityMappingDefault.setAccess( unitElement != null ? unitElement.getTextTrim() : null ); defaultElements.add(root); - List<String> addedClasses = new ArrayList<String>(); List<Element> entities = (List<Element>) root.elements( "entity" ); addClass( entities, packageName, entityMappingDefault, addedClasses ); @@ -100,9 +101,39 @@ defaultsOverriding.put( className, localDefault ); log.debug( "Adding XML overriding information for " + className ); + addEntityListenerClasses( element, packageName, addedClasses ); } } + private List<String> addEntityListenerClasses(Element element, String packageName, List<String> addedClasses) { + List<String> localAddedClasses = new ArrayList<String>(); + Element listeners = element.element( "entity-listeners" ); + if (listeners != null) { + List<Element> elements = (List<Element>) listeners.elements( "entity-listener" ); + for ( Element listener : elements ) { + String listenerClassName = buildSafeClassName( listener.attributeValue( "class" ), packageName ); + if ( classOverriding.containsKey( listenerClassName ) ) { + //maybe switch it to warn? + if ( "entity-listener".equals( classOverriding.get( listenerClassName).getName() ) ) { + log.info( + "entity-listener duplication, first event definition will be used: " + + listenerClassName + ); + continue; + } + else { + throw new IllegalStateException( "Duplicate XML entry for " + listenerClassName ); + } + } + localAddedClasses.add( listenerClassName ); + classOverriding.put( listenerClassName, listener ); + } + } + log.debug( "Adding XML overriding information for listener: " + listeners ); + addedClasses.addAll( localAddedClasses ); + return localAddedClasses; + } + private static String buildSafeClassName(String className, String defaultPackageName) { if ( className.indexOf( '.' ) < 0 && defaultPackageName != null ) { className = StringHelper.qualify( defaultPackageName, className ); @@ -205,6 +236,9 @@ if ( globalDefault.getCascadePersist() != null ) cascadePersist = globalDefault.getCascadePersist(); } } + } + public List<String> getDefaultEntityListeners() { + return defaultEntityListeners; } } Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/Administration.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/Administration.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/Administration.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -7,6 +7,7 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.OneToOne; +import javax.persistence.PostLoad; import javax.persistence.SecondaryTable; import javax.persistence.Table; @@ -68,4 +69,9 @@ public void setLastname(String lastname) { this.lastname = lastname; } + + @PostLoad + public void calculate() { + //... + } } Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/EJB3OverridenAnnotationReaderTest.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/EJB3OverridenAnnotationReaderTest.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/EJB3OverridenAnnotationReaderTest.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -51,6 +51,10 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; import javax.persistence.Version; +import javax.persistence.PrePersist; +import javax.persistence.EntityListeners; +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; import junit.framework.TestCase; import org.dom4j.DocumentException; @@ -361,6 +365,31 @@ assertEquals( "maxSpeed", reader.getAnnotation( OrderBy.class ).value() ); } + public void testEntityListeners() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/reflection/java/xml/orm.xml" ); + + Method method = Administration.class.getDeclaredMethod( "calculate" ); + EJB3OverridenAnnotationReader reader = new EJB3OverridenAnnotationReader( method, context ); + assertTrue( reader.isAnnotationPresent( PrePersist.class ) ); + + reader = new EJB3OverridenAnnotationReader( Administration.class, context ); + assertTrue( reader.isAnnotationPresent( EntityListeners.class ) ); + assertEquals( 1, reader.getAnnotation( EntityListeners.class ).value().length ); + assertEquals( LogListener.class, reader.getAnnotation( EntityListeners.class ).value()[0] ); + + method = LogListener.class.getDeclaredMethod( "noLog", Object.class ); + reader = new EJB3OverridenAnnotationReader( method, context ); + assertTrue( reader.isAnnotationPresent( PostLoad.class ) ); + + method = LogListener.class.getDeclaredMethod( "log", Object.class ); + reader = new EJB3OverridenAnnotationReader( method, context ); + assertTrue( reader.isAnnotationPresent( PrePersist.class ) ); + assertFalse( reader.isAnnotationPresent( PostPersist.class ) ); + + assertEquals( 1, context.getDefaultEntityListeners().size() ); + assertEquals( OtherLogListener.class.getName(), context.getDefaultEntityListeners().get(0) ); + } + private XMLContext buildContext(String ormfile) throws SAXException, DocumentException, IOException { XMLHelper xmlHelper = new XMLHelper(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/LogListener.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/LogListener.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/LogListener.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -0,0 +1,26 @@ +//$Id: $ +package org.hibernate.test.reflection.java.xml; + +import javax.persistence.PrePersist; +import javax.persistence.PostPersist; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Emmanuel Bernard + */ +public class LogListener { + Log log = LogFactory.getLog( LogListener.class ); + + @PrePersist + @PostPersist + public void log(Object entity) { + log.debug( "Logging entity " + entity.getClass().getName() + " with hashCode: " + entity.hashCode() ); + } + + + public void noLog(Object entity) { + log.debug( "NoLogging entity " + entity.getClass().getName() + " with hashCode: " + entity.hashCode() ); + } +} Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/OtherLogListener.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/OtherLogListener.java 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/OtherLogListener.java 2006-05-03 23:13:22 UTC (rev 9863) @@ -0,0 +1,26 @@ +//$Id: $ +package org.hibernate.test.reflection.java.xml; + +import javax.persistence.PrePersist; +import javax.persistence.PostPersist; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Emmanuel Bernard + */ +public class OtherLogListener { + Log log = LogFactory.getLog( OtherLogListener.class ); + + @PrePersist + @PostPersist + public void log(Object entity) { + log.debug( "Logging entity " + entity.getClass().getName() + " with hashCode: " + entity.hashCode() ); + } + + + public void noLog(Object entity) { + log.debug( "NoLogging entity " + entity.getClass().getName() + " with hashCode: " + entity.hashCode() ); + } +} Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/orm.xml =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/orm.xml 2006-05-03 14:44:42 UTC (rev 9862) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/xml/orm.xml 2006-05-03 23:13:22 UTC (rev 9863) @@ -10,6 +10,11 @@ <schema>myschema</schema> <catalog>mycatalog</catalog> <cascade-persist/> + <entity-listeners> + <entity-listener class="org.hibernate.test.reflection.java.xml.OtherLogListener"> + <post-update method-name="log"/> + </entity-listener> + </entity-listeners> </persistence-unit-defaults> </persistence-unit-metadata> <package>org.hibernate.test.reflection.java.xml</package> @@ -28,6 +33,13 @@ </secondary-table> <sequence-generator name="seqhilo" sequence-name="seqhilo"/> <table-generator name="table" table="tablehilo"/> + <entity-listeners> + <entity-listener class="LogListener"> + <pre-persist method-name="log"/> + <post-load method-name="noLog"/> + </entity-listener> + </entity-listeners> + <pre-persist method-name="calculate"/> <attributes> <id name="id"> <column name="fld_id"/> @@ -75,6 +87,12 @@ </sql-result-set-mapping> <exclude-default-listeners/> <exclude-superclass-listeners/> + <entity-listeners> + <entity-listener class="LogListener"> + <pre-persist method-name="log"/> + <post-load method-name="noLog"/> + </entity-listener> + </entity-listeners> <attributes> <embedded name="playerASSN"/> </attributes> |