package org.jboss.seam;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.Version;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.init.EjbDescriptor;
import org.jboss.seam.init.EjbEntityDescriptor;
import org.jboss.seam.util.Reflections;
/**
* Metamodel class for entity classes.
*
* A class will be identified as an entity class if it has an @Entity annotation.
*
* @author Gavin King
*
*/
public class Entity extends Model
{
private Method preRemoveMethod;
private Method prePersistMethod;
private Method preUpdateMethod;
private Method postLoadMethod;
private Method identifierGetter;
private Field identifierField;
private Method versionGetter;
private Field versionField;
private String name;
/**
*
* @param beanClass
*
* Use Entity.forBean() or Entity.forClass
*/
@Deprecated
public Entity(Class<?> beanClass)
{
super(beanClass);
EjbDescriptor descriptor = Seam.getEjbDescriptor(beanClass);
if (descriptor instanceof EjbEntityDescriptor)
{
mergeAnnotationAndOrmXml((EjbEntityDescriptor) descriptor);
}
else
{
mergeAnnotationAndOrmXml(null);
}
}
public Method getPostLoadMethod()
{
return postLoadMethod;
}
public Method getPrePersistMethod()
{
return prePersistMethod;
}
public Method getPreRemoveMethod()
{
return preRemoveMethod;
}
public Method getPreUpdateMethod()
{
return preUpdateMethod;
}
@Deprecated
public Field getIdentifierField()
{
return identifierField;
}
@Deprecated
public Method getIdentifierGetter()
{
return identifierGetter;
}
@Deprecated
public Field getVersionField()
{
return versionField;
}
@Deprecated
public Method getVersionGetter()
{
return versionGetter;
}
public Object getIdentifier(Object entity)
{
if (identifierGetter != null)
{
return Reflections.invokeAndWrap(identifierGetter, entity);
}
else if (identifierField != null)
{
return Reflections.getAndWrap(identifierField, entity);
}
else
{
throw new IllegalStateException("@Id attribute not found for entity class: " + getBeanClass().getName());
}
}
public Object getVersion(Object entity)
{
if (versionGetter != null)
{
return Reflections.invokeAndWrap(versionGetter, entity);
}
else if (versionField != null)
{
return Reflections.getAndWrap(versionField, entity);
}
else
{
return null;
}
}
public String getName()
{
return name;
}
public static Entity forBean(Object bean)
{
return forClass(bean.getClass());
}
public static Entity forClass(Class clazz)
{
if (!Contexts.isApplicationContextActive())
{
throw new IllegalStateException("No application context active");
}
Class entityClass = Seam.getEntityClass(clazz);
if (entityClass == null)
{
throw new NotEntityException("Not an entity class: " + clazz.getName());
}
String name = getModelName(entityClass);
Model model = (Model) Contexts.getApplicationContext().get(name);
if (model == null || !(model instanceof Entity))
{
Entity entity = new Entity(entityClass);
Contexts.getApplicationContext().set(name, entity);
return entity;
}
else
{
return (Entity) model;
}
}
private void mergeAnnotationAndOrmXml(EjbEntityDescriptor descriptor)
{
// Lookup the name of the Entity from XML, annotation or default
this.name = lookupName(getBeanClass(), descriptor);
if (this.name == null)
{
throw new NotEntityException("Unable to establish name of entity " + getBeanClass());
}
if (descriptor != null)
{
// Set any methods and fields we need metadata for from the XML
// descriptor. These take priority over annotations
this.preRemoveMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPreRemoveMethodName());
this.prePersistMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPrePersistMethodName());
this.preUpdateMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPreUpdateMethodName());
this.postLoadMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPostLoadMethodName());
this.identifierField = descriptor.getIdentifierFieldName() != null ? Reflections.getField(getBeanClass(), descriptor.getIdentifierFieldName()) : null;
this.identifierGetter = descriptor.getIdentifierPropertyName() != null ? Reflections.getGetterMethod(getBeanClass(), descriptor.getIdentifierPropertyName()) : null;
this.versionField = descriptor.getVersionFieldName() != null ? Reflections.getField(getBeanClass(), descriptor.getVersionFieldName()) : null;
this.versionGetter = descriptor.getVersionPropertyName() != null ? Reflections.getGetterMethod(getBeanClass(), descriptor.getVersionPropertyName()) : null;
}
if (descriptor == null || !descriptor.isMetaDataComplete())
{
for ( Class<?> clazz=getBeanClass(); clazz!=Object.class; clazz = clazz.getSuperclass() )
{
for ( Method method: clazz.getDeclaredMethods() )
{
//TODO: does the spec allow multiple lifecycle method
// in the entity class heirarchy?
if (this.preRemoveMethod == null && method.isAnnotationPresent(PreRemove.class))
{
this.preRemoveMethod = method;
}
if (this.prePersistMethod == null && method.isAnnotationPresent(PrePersist.class) )
{
this.prePersistMethod = method;
}
if (preUpdateMethod == null && method.isAnnotationPresent(PreUpdate.class) )
{
preUpdateMethod = method;
}
if (postLoadMethod == null && method.isAnnotationPresent(PostLoad.class) )
{
postLoadMethod = method;
}
if (identifierField == null && identifierGetter == null && method.isAnnotationPresent(Id.class) || method.isAnnotationPresent(EmbeddedId.class))
{
identifierGetter = method;
}
if (versionField == null && versionGetter == null && method.isAnnotationPresent(Version.class) )
{
versionGetter = method;
}
}
if ( ( identifierGetter == null && identifierField == null ) || ( versionField == null && versionGetter == null ) )
{
for ( Field field: clazz.getDeclaredFields() )
{
if ( identifierGetter == null && identifierField == null && (field.isAnnotationPresent(Id.class) || field.isAnnotationPresent(EmbeddedId.class)))
{
identifierField = field;
}
if ( versionGetter == null && versionField == null && field.isAnnotationPresent(Version.class) )
{
versionField = field;
}
}
}
}
}
setAccessible(this.preRemoveMethod);
setAccessible(this.prePersistMethod);
setAccessible(this.preUpdateMethod);
setAccessible(this.postLoadMethod);
setAccessible(this.identifierField);
setAccessible(this.identifierGetter);
setAccessible(this.versionField);
setAccessible(this.versionGetter);
}
private void setAccessible(AccessibleObject accessibleObject)
{
if (accessibleObject != null)
{
accessibleObject.setAccessible(true);
}
}
private static String lookupName(Class<?> beanClass, EjbEntityDescriptor descriptor)
{
if (descriptor != null && descriptor.getEjbName() != null)
{
// XML overrides annotations
return descriptor.getEjbName();
}
else if ( (descriptor == null || !descriptor.isMetaDataComplete()) && beanClass.isAnnotationPresent(javax.persistence.Entity.class) && !"".equals(beanClass.getAnnotation(javax.persistence.Entity.class).name()))
{
// Is a name specified?
return beanClass.getAnnotation(javax.persistence.Entity.class).name();
}
else if (descriptor != null || beanClass.isAnnotationPresent(javax.persistence.Entity.class))
{
// Use the default name if either a descriptor is specified or the
// annotation is present
return beanClass.getName();
}
else
{
return null;
}
}
private static Method getEntityCallbackMethod(Class beanClass, String callbackMethodName)
{
try
{
if (callbackMethodName != null)
{
return Reflections.getMethod(beanClass, callbackMethodName);
}
else
{
return null;
}
}
catch (IllegalArgumentException e)
{
throw new IllegalArgumentException("Unable to find Entity callback method specified in orm.xml", e);
}
}
public static class NotEntityException extends IllegalArgumentException
{
public NotEntityException(String string)
{
super(string);
}
}
}