package com.adaptrex.core.persistence.jpa;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.adaptrex.core.ext.ExtConfig;
import com.adaptrex.core.persistence.api.ORMModelDefinition;
import com.adaptrex.core.persistence.api.ORMModelInstance;
import com.adaptrex.core.persistence.api.ORMPersistenceManager;
import com.adaptrex.core.persistence.api.ORMStoreData;
import com.adaptrex.core.utilities.Inflector;
import com.adaptrex.core.utilities.StringUtilities;
public class JPAPersistenceManager implements ORMPersistenceManager {
private String factoryName;
private EntityManagerFactory factory;
private Map<String, Class<?>> entityClassCache = new HashMap<String, Class<?>>();
public JPAPersistenceManager() throws IOException, SAXException,
ParserConfigurationException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
List<InputStream> inputStreams = loadResources(
"META-INF/persistence.xml", cl);
if (inputStreams.size() > 1) {
throw new RuntimeException(
"Multiple persistence.xml descriptors found but no default has been configured");
}
SAXParserFactory.newInstance().newSAXParser()
.parse(inputStreams.get(0), new DefaultHandler() {
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) {
if (qName.equalsIgnoreCase("persistence-unit")) {
factoryName = attributes.getValue("name");
}
}
});
this.factory = Persistence.createEntityManagerFactory(factoryName);
}
public JPAPersistenceManager(String factoryName) throws IOException {
this.factory = Persistence.createEntityManagerFactory(factoryName);
}
@Override
public Object getFactory() {
return this.factory;
}
@Override
public void shutdown() {
this.factory.close();
}
@Override
public Object getSession() {
return this.factory.createEntityManager();
}
@Override
public Class<?> getEntityClass(String className) {
if (entityClassCache.containsKey(className)) {
return entityClassCache.get(className);
}
EntityManager em = null;
try {
em = this.factory.createEntityManager();
Metamodel meta = em.getMetamodel();
for (EntityType<?> et : meta.getEntities()) {
if (et.getJavaType().getName().endsWith("." + className)) {
Class<?> clazz = et.getJavaType();
entityClassCache.put(className, clazz);
return clazz;
}
}
return null;
} catch (Exception e) {
throw new RuntimeException("Could not load entity class ("
+ className + ")");
} finally {
if (em != null) {
em.close();
}
}
}
@Override
public Object getEntity(Class<?> clazz, Object id) {
EntityManager em = null;
try {
em = this.factory.createEntityManager();
return em.find(clazz, id);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
em.close();
}
}
@Override
public Object getEntity(Class<?> clazz, String key, Object value) {
return null;
}
@Override
public ORMModelDefinition getModelDefinition(ExtConfig extConfig) {
return new JPAModelDefinition(extConfig);
}
@Override
public ORMModelInstance getModelInstance(ExtConfig extConfig, Object entity) {
return new JPAModelInstance(extConfig, entity);
}
@Override
public ORMStoreData getStoreData(ExtConfig extConfig) {
return new JPAStoreData(extConfig);
}
@Override
public boolean isIdField(Class<?> clazz, String fieldName) {
return hasMemberAnnotation(clazz, fieldName, "Id");
}
@Override
public Object getEntityId(Object entity) {
Method idGetter;
try {
idGetter = entity.getClass().getDeclaredMethod("getId");
return idGetter.invoke(entity);
} catch (Exception e) {
throw new RuntimeException ("Could not find id getter for " + entity.getClass().getName(), e);
}
}
@Override
public boolean isOneToMany(Class<?> clazz, String fieldName) {
return hasMemberAnnotation(clazz, fieldName, "OneToMany");
}
@Override
public boolean isManyToOne(Class<?> clazz, String fieldName) {
return hasMemberAnnotation(clazz, fieldName, "ManyToOne");
}
@Override
public boolean isManyToMany(Class<?> clazz, String fieldName) {
return hasMemberAnnotation(clazz, fieldName, "ManyToMany");
}
@Override
public boolean isAssociated(Class<?> clazz, String associationName) {
String fieldName = StringUtilities.uncapitalize(associationName);
return isOneToMany(clazz, fieldName) || isManyToOne(clazz, fieldName)
|| isManyToMany(clazz, fieldName);
}
@Override
public Class<?> getFieldEntityClass(Class<?> clazz, String fieldName) {
Field field;
try {
field = clazz.getDeclaredField(fieldName);
} catch (Exception e) {
throw new RuntimeException("could not find field entity class");
}
if (field == null) {
throw new RuntimeException("could not find field entity class");
}
if (Collection.class.isAssignableFrom(field.getType())) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
return (Class<?>) pt.getActualTypeArguments()[0];
}
} else {
return field.getType();
}
return null;
}
@Override
public Object getFieldValue(Object entity, String fieldName) {
Method getter = null;
Class<?> clazz = entity.getClass();
try {
getter = clazz.getMethod("get" + StringUtilities.capitalize(fieldName));
} catch (Exception e) {
}
if (getter == null) {
try {
getter = clazz.getMethod("is" + StringUtilities.capitalize(fieldName));
} catch (Exception e) {
}
}
if (getter == null) {
return null;
}
try {
return getter.invoke(entity);
} catch (Exception e) {
//log.warn("Error", e);
return null;
}
}
private Map<String, String> fieldTypeCache = new HashMap<String, String>();
/*
* Get the type of field. Returns the Ext string for various data types Ext
* supports.
*
* (non-Javadoc)
*
* @see
* com.adaptrex.core.persistence.api.ORMPersistenceManager#getFieldType(
* java.lang.Class, java.lang.String)
*/
@Override
public String getFieldType(Class<?> clazz, String fieldName) {
String fieldKey = clazz.getName() + "." + fieldName;
String fieldType = fieldTypeCache.get(fieldKey);
if (fieldType != null)
return fieldType;
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
} catch (Exception e) {
}
if (field == null)
return null;
String columnType = field.getType().getSimpleName().toLowerCase();
if (columnType.equals("integer") || columnType.equals("long")) {
columnType = "int";
} else if (columnType.equals("double")) {
columnType = "float";
} else if (columnType.equals("time")) {
columnType = "time";
} else if (columnType.equals("calendar") || columnType.equals("date")) {
columnType = "date";
}
if (columnType.equals("date")) {
Temporal a = field.getAnnotation(Temporal.class);
if (a == null) {
try {
Method getter = clazz.getMethod("get"
+ StringUtilities.capitalize(fieldName));
if (getter != null) {
a = getter.getAnnotation(Temporal.class);
}
} catch (Exception e) {
}
}
if (a != null && a.value().equals(TemporalType.TIME)) {
columnType = "time";
}
}
fieldTypeCache.put(fieldKey, columnType);
return columnType;
}
/* *******************************************************************
*
* private utilities
*/
private Map<String, List<String>> memberAnnotationCache = new HashMap<String, List<String>>();
private boolean hasMemberAnnotation(Class<?> clazz, String member,
String annotationName) {
List<String> memberAnnotations = memberAnnotationCache.get(clazz
.getName() + "." + member);
if (memberAnnotations == null) {
memberAnnotations = getMemberAnnotations(clazz, member);
}
return memberAnnotations.contains(annotationName);
}
/*
* This gets a list of all annotations on a class field (or it's getter)
*/
private List<String> getMemberAnnotations(Class<?> clazz, String member) {
List<String> list = memberAnnotationCache.get(clazz.getName() + "."
+ member);
if (list != null) {
return list;
}
list = new ArrayList<String>();
try {
Field field = clazz.getDeclaredField(member);
if (field != null) {
for (Annotation annotation : field.getDeclaredAnnotations()) {
list.add(annotation.annotationType().getSimpleName());
}
}
} catch (Exception e) {
}
try {
Method getter = clazz.getMethod("get"
+ StringUtilities.capitalize(member));
if (getter != null) {
for (Annotation annotation : getter.getDeclaredAnnotations()) {
list.add(annotation.annotationType().getSimpleName());
}
}
} catch (Exception e) {
}
memberAnnotationCache.put(clazz.getName() + "." + member, list);
return list;
}
/*
* Ripped from: http://goo.gl/CYwbl
*/
private static List<InputStream> loadResources(final String name,
final ClassLoader classLoader) throws IOException {
final List<InputStream> list = new ArrayList<InputStream>();
final Enumeration<URL> systemResources = classLoader.getResources(name);
while (systemResources.hasMoreElements()) {
list.add(systemResources.nextElement().openStream());
}
return list;
}
@Override
public ORMModelInstance createModelinstance(ExtConfig extConfig,
Map<String, Object> data) {
try {
Object entity = extConfig.getEntityClass().newInstance();
JPAModelInstance model = new JPAModelInstance(extConfig, entity);
return model.update(data);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public ORMModelInstance updateModelInstance(ExtConfig extConfig, Object id,
Map<String, Object> data) {
try {
Object entity = this.getEntity(extConfig.getEntityClass(), id);
JPAModelInstance model = new JPAModelInstance(extConfig, entity);
return model.update(data);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public ORMModelInstance deleteModelInstance(ExtConfig extConfig, Object id) {
try {
Object entity = this.getEntity(extConfig.getEntityClass(), id);
JPAModelInstance model = new JPAModelInstance(extConfig, entity);
model.delete();
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}