package org.mobicents.slee.container.deployment.profile.jpa;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.slee.InvalidArgumentException;
import javax.slee.SLEEException;
import javax.slee.profile.AttributeTypeMismatchException;
import javax.slee.profile.UnrecognizedAttributeException;
import javax.slee.profile.UnrecognizedQueryNameException;
import javax.slee.profile.query.QueryExpression;
import javax.transaction.Transaction;
import org.apache.log4j.Logger;
import org.hibernate.cfg.Environment;
import org.hibernate.ejb.HibernatePersistence;
import org.hibernate.ejb.QueryImpl;
import org.jboss.jpa.deployment.PersistenceUnitInfoImpl;
import org.jboss.metadata.jpa.spec.PersistenceUnitMetaData;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.component.ProfileSpecificationComponent;
import org.mobicents.slee.container.component.profile.ProfileAttribute;
import org.mobicents.slee.container.component.profile.ProfileEntity;
import org.mobicents.slee.container.component.profile.ProfileEntityFactory;
import org.mobicents.slee.container.component.profile.ProfileEntityFramework;
import org.mobicents.slee.container.management.jmx.MobicentsManagement;
import org.mobicents.slee.runtime.transaction.SleeTransactionManager;
import org.mobicents.slee.runtime.transaction.TransactionalAction;
/**
*
* The profile entity framework implementation that uses JPA to manage SLEE
* profile data.
*
* @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
* @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
* @author martins
*/
public class JPAProfileEntityFramework implements ProfileEntityFramework {
private static Logger logger = Logger
.getLogger(JPAProfileEntityFramework.class);
private static final String DEFAULT_PROFILE_NAME = "";
private static final SleeContainer sleeContainer = SleeContainer
.lookupFromJndi();
/**
* the concrete jpa profile entity class of the framework
*/
private Class<?> profileEntityClass;
/**
* runtime caching the profile entity class name, used many times in jpa
* queries
*/
private String profileEntityClassName;
/**
* the concrete jpa profile entity factory class of the framework
*/
private Class<?> profileEntityFactoryClass;
/**
* a map containing the concrete jpa profile entity array attr value class
* of the framework, per attribute name
*/
private Map<String, Class<?>> profileEntityArrayAttrValueClassMap;
/**
* the profile spec component related with the framework
*/
private final ProfileSpecificationComponent component;
/**
* the jpa entity manager factory of the framework
*/
private EntityManagerFactory entityManagerFactory;
/**
* the shared instance of the profile entity factory of the framework
*/
private ProfileEntityFactory profileEntityFactory;
/**
*
* @param component
*/
public JPAProfileEntityFramework(ProfileSpecificationComponent component) {
this.component = component;
this.component.setProfileEntityFramework(this);
}
// GETTERS / SETTERS
/**
*
*/
public Class<?> getProfileEntityClass() {
return profileEntityClass;
}
/**
*
* @param profileEntityClass
*/
public void setProfileEntityClass(Class<?> profileEntityClass) {
this.profileEntityClass = profileEntityClass;
}
/**
*
* @return
*/
public String getProfileEntityClassName() {
return profileEntityClassName;
}
/**
*
* @param profileEntityClassName
*/
public void setProfileEntityClassName(String profileEntityClassName) {
this.profileEntityClassName = profileEntityClassName;
}
/**
*
* @return
*/
public Map<String, Class<?>> getProfileEntityArrayAttrValueClassMap() {
return profileEntityArrayAttrValueClassMap;
}
/**
*
* @param profileEntityArrayAttrValueClassMap
*/
public void setProfileEntityArrayAttrValueClassMap(
Map<String, Class<?>> profileEntityArrayAttrValueClassMap) {
this.profileEntityArrayAttrValueClassMap = profileEntityArrayAttrValueClassMap;
}
/**
*
* @return
*/
public Class<?> getProfileEntityFactoryClass() {
return profileEntityFactoryClass;
}
// ProfileEntityFramework IMPL
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #getProfileEntityFactory()
*/
public ProfileEntityFactory getProfileEntityFactory() {
return profileEntityFactory;
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #findAll(java.lang.String)
*/
public Collection<ProfileEntity> findAll(String profileTable) {
return findProfilesByAttribute(profileTable, null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #findProfilesByAttribute(java.lang.String,
* org.mobicents.slee.container.component.profile.ProfileAttribute,
* java.lang.Object)
*/
@SuppressWarnings("unchecked")
public Collection<ProfileEntity> findProfilesByAttribute(
String profileTable, ProfileAttribute profileAttribute,
Object attributeValue) {
if (logger.isDebugEnabled()) {
logger.debug("findProfilesByAttribute( profileTable = "
+ profileTable + " , profileAttribute = "
+ profileAttribute + " , attributeValue = "
+ attributeValue + " )");
}
EntityManager em = getEntityManager();
Query query = null;
if (profileAttribute == null) {
query = em
.createQuery(
"SELECT x FROM "
+ profileEntityClassName
+ " x WHERE x.tableName = :tableName")
.setParameter("tableName", profileTable);
} else {
if (profileAttribute.isArray()) {
query = em
.createQuery(
"SELECT x FROM "
+ profileEntityClassName
+ " x , IN (x.c"
+ profileAttribute.getName()
+ ") y WHERE x.tableName = :tableName AND y.string = :attrValue")
.setParameter("tableName", profileTable).setParameter(
"attrValue", attributeValue.toString());
} else {
// TODO handle Address objects in this use case, they can't be
// binary for search
query = em
.createQuery(
"SELECT x FROM "
+ profileEntityClassName
+ " x WHERE x.tableName = :tableName AND x.c"
+ profileAttribute.getName()
+ " = :attrValue").setParameter(
"tableName", profileTable).setParameter(
"attrValue", attributeValue);
}
}
Collection<ProfileEntity> result = query.getResultList();
if (logger.isDebugEnabled()) {
logger.debug("findProfilesByAttribute : query = "
+ ((QueryImpl) query).getHibernateQuery().getQueryString()
+ " , result = " + result);
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #findProfile(java.lang.String, java.lang.String)
*/
public ProfileEntity findProfile(String profileTable, String profileName) {
EntityManager em = getEntityManager();
Query query = em.createQuery(
"FROM " + profileEntityClassName
+ " WHERE tableName = ?1 AND profileName = ?2")
.setParameter(1, profileTable).setParameter(2, profileName);
List<?> resultList = query.getResultList();
if (resultList.isEmpty()) {
return null;
} else {
return (ProfileEntity) resultList.get(0);
}
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #getProfilesByStaticQuery(java.lang.String, java.lang.String,
* java.lang.Object[])
*/
@SuppressWarnings("unchecked")
public Collection<ProfileEntity> getProfilesByStaticQuery(
String profileTable, String queryName, final Object[] parameters)
throws NullPointerException, UnrecognizedQueryNameException,
AttributeTypeMismatchException, InvalidArgumentException {
// TODO check for exceptions
final QueryWrapper wQuery = JPAQueryBuilder.getQuery(queryName);
final EntityManager em = getEntityManager();
if(System.getSecurityManager()==null)
{
Query staticQuery = em.createQuery(wQuery
.getQuerySQL(profileEntityClassName));
if (wQuery.getMaxMatches() > 0)
staticQuery.setMaxResults((int) wQuery.getMaxMatches());
for (int i = 0; i < parameters.length; i++) {
try {
staticQuery.setParameter(i + 1, parameters[i]);
} catch (Exception ignore) {
// We don't care, it's because there's no such parameter.
}
}
return staticQuery.getResultList();
}else
{
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Collection<ProfileEntity>>(){
public Collection<ProfileEntity> run() throws Exception {
Query staticQuery = em.createQuery(wQuery
.getQuerySQL(profileEntityClassName));
if (wQuery.getMaxMatches() > 0)
staticQuery.setMaxResults((int) wQuery.getMaxMatches());
for (int i = 0; i < parameters.length; i++) {
try {
staticQuery.setParameter(i + 1, parameters[i]);
} catch (Exception ignore) {
// We don't care, it's because there's no such parameter.
}
}
return staticQuery.getResultList();
}});
} catch (PrivilegedActionException e) {
Throwable t = e.getCause();
if(t instanceof NullPointerException)
throw (NullPointerException )t;
if(t instanceof UnrecognizedQueryNameException)
throw (UnrecognizedQueryNameException )t;
if(t instanceof AttributeTypeMismatchException)
throw (AttributeTypeMismatchException )t;
if(t instanceof InvalidArgumentException)
throw (InvalidArgumentException )t;
//?
throw new SLEEException("",t);
}
}
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #getProfilesByDynamicQuery(java.lang.String,
* javax.slee.profile.query.QueryExpression)
*/
@SuppressWarnings("unchecked")
public Collection<ProfileEntity> getProfilesByDynamicQuery(
String profileTable, QueryExpression expr)
throws UnrecognizedAttributeException,
AttributeTypeMismatchException {
// TODO check for exceptions
QueryWrapper wQuery = JPAQueryBuilder.parseDynamicQuery(expr);
EntityManager em = getEntityManager();
Query dynamicQuery = em.createQuery(wQuery
.getQuerySQL(profileEntityClassName));
int i = 1;
for (Object param : wQuery.getDynamicParameters()) {
dynamicQuery.setParameter(i++, param);
}
if (wQuery.getMaxMatches() > 0)
dynamicQuery.setMaxResults((int) wQuery.getMaxMatches());
return dynamicQuery.getResultList();
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #persistProfile
* (org.mobicents.slee.container.component.profile.ProfileEntity)
*/
public void persistProfile(ProfileEntity profileEntity) {
EntityManager em = null;
// if(checkUniqueFields(profileObject))
// {
em = getEntityManager();
em.persist(profileEntity);
/*
* } else { // FIXME: We need to throw this PVException! //throw new
* ProfileVerificationException
* ("Failed to persist profile due to uniqueness constraint."); throw
* new
* SLEEException("Failed to persist profile due to uniqueness constraint."
* ); }
*/
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #retrieveProfile(java.lang.String, java.lang.String)
*/
public ProfileEntity retrieveProfile(String profileTable, String profileName) {
if (logger.isDebugEnabled()) {
logger.debug("retrieveProfile( profileTableName = " + profileTable
+ " , profileName = " + profileName + " )");
}
if (profileName == null) {
profileName = DEFAULT_PROFILE_NAME;
}
EntityManager em = getEntityManager();
final Query q = em.createQuery(
"FROM " + profileEntityClassName
+ " WHERE tableName = ?1 AND profileName = ?2")
.setParameter(1, profileTable).setParameter(2, profileName);
if(System.getSecurityManager()==null)
{
List<?> resultList = q.getResultList();
if (resultList.size() > 0) {
if (logger.isDebugEnabled()) {
logger.debug("ProfileEntity retrieved -> " + resultList.get(0));
}
return (ProfileEntity) resultList.get(0);
} else {
return null;
}
}else
{
return AccessController.doPrivileged(new PrivilegedAction<ProfileEntity>(){
public ProfileEntity run() {
List<?> resultList = q.getResultList();
if (resultList.size() > 0) {
if (logger.isDebugEnabled()) {
logger.debug("ProfileEntity retrieved -> " + resultList.get(0));
}
return (ProfileEntity) resultList.get(0);
} else {
return null;
}
}});
}
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #removeprofile
* (org.mobicents.slee.container.component.profile.ProfileEntity)
*/
public void removeprofile(ProfileEntity profileEntity) {
EntityManager em = getEntityManager();
em.remove(profileEntity);
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #install()
*/
public void install() {
synchronized (this) {
// generate profile entity & related classes
new ConcreteProfileEntityGenerator(component, this)
.generateClasses();
// this one is just a runtime optimization for faster query building
// now, later to use named queries
profileEntityClassName = profileEntityClass.getName();
profileEntityFactoryClass = new ConcreteProfileEntityFactoryGenerator(
component, profileEntityClass,
profileEntityArrayAttrValueClassMap).generateClass();
try {
profileEntityFactory = (ProfileEntityFactory) profileEntityFactoryClass
.newInstance();
} catch (Throwable e) {
throw new SLEEException(e.getMessage(), e);
}
// 1. Generate CMP Interface Impl with JPA Annotations
component.setProfileCmpConcreteClass(new ConcreteProfileGenerator(
component).generateConcreteProfile());
// 2. Create the corresponding JPA PU -- FIXME: Should be somewhere
// else?
createPersistenceUnit(component);
new JPAQueryBuilder(component).parseStaticQueries();
}
}
/*
* (non-Javadoc)
*
* @see
* org.mobicents.slee.container.component.profile.ProfileEntityFramework
* #uninstall()
*/
public void uninstall() {
synchronized (this) {
SleeTransactionManager sleeTransactionManager = sleeContainer
.getTransactionManager();
Transaction tx = null;
try {
tx = sleeTransactionManager.suspend();
} catch (Throwable e) {
throw new SLEEException(e.getMessage(), e);
}
if (entityManagerFactory != null) {
entityManagerFactory.close();
}
try {
sleeTransactionManager.resume(tx);
} catch (Throwable e) {
throw new SLEEException(e.getMessage(), e);
}
component.setProfileEntityFramework(null);
}
}
// AUX METHODS
/**
*
* @param profileComponent
*/
@SuppressWarnings("unchecked")
private void createPersistenceUnit(
ProfileSpecificationComponent profileComponent) {
try {
HibernatePersistence hp = new HibernatePersistence();
PersistenceUnitMetaData pumd = new PersistenceUnitMetaData();
pumd.setProvider("org.hibernate.ejb.HibernatePersistence");
pumd.setJtaDataSource("java:/DefaultDS");
pumd.setExcludeUnlistedClasses(false);
boolean persistProfiles = MobicentsManagement.persistProfiles;
Map pumdProps = new HashMap();
pumdProps.put(Environment.HBM2DDL_AUTO, persistProfiles ? "update" : "create-drop");
pumdProps.put(Environment.DIALECT,
"org.hibernate.dialect.HSQLDialect");
pumd.setProperties(pumdProps);
pumd.setName("JSLEEProfiles"
+ profileComponent.getComponentID().hashCode());
Set classes = new HashSet<String>();
classes.add(profileEntityClass.getName());
for (Class<?> clazz : profileEntityArrayAttrValueClassMap.values()) {
classes.add(clazz.getName());
}
pumd.setClasses(classes);
Properties properties = new Properties();
properties.setProperty(Environment.DATASOURCE, "java:/DefaultDS");
properties
.setProperty(Environment.TRANSACTION_STRATEGY,
"org.hibernate.ejb.transaction.JoinableCMTTransactionFactory");
properties
.setProperty(Environment.CONNECTION_PROVIDER,
"org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider");
properties.setProperty(
"hibernate.jndi.java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
properties.setProperty(Environment.CACHE_PROVIDER,
"org.hibernate.cache.HashtableCacheProvider");
properties.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY,
"org.hibernate.transaction.JBossTransactionManagerLookup");
properties.setProperty(
"hibernate.jndi.java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
properties.setProperty(Environment.DIALECT,
"org.hibernate.dialect.HSQLDialect");
// FIXME: Should be Environment.JACC_CONTEXTID but it's
// hibernate.jacc_context_id vs hibernate.jacc.ctx.id. Bug?
properties.setProperty("hibernate.jacc.ctx.id", "persistence.xml");
properties.setProperty(Environment.CACHE_REGION_PREFIX,
"persistence.unit:unitName=#" + pumd.getName());
properties.setProperty(Environment.SESSION_FACTORY_NAME,
"persistence.unit:unitName=#" + pumd.getName());
properties.setProperty(Environment.HBM2DDL_AUTO, persistProfiles ? "update" : "create-drop");
properties.setProperty(Environment.USE_REFLECTION_OPTIMIZER,
"false");
properties.setProperty(Environment.BYTECODE_PROVIDER, "javassist");
properties.setProperty(Environment.STATEMENT_BATCH_SIZE, "0");
properties.setProperty(Environment.SHOW_SQL, "false");
properties.setProperty(Environment.FORMAT_SQL, "false");
ClassLoader newClassLoader = profileComponent.getClassLoader();
Thread.currentThread().setContextClassLoader(newClassLoader);
for (String className : pumd.getClasses()) {
Thread.currentThread().getContextClassLoader().loadClass(
className);
}
PersistenceUnitInfoImpl pi = new PersistenceUnitInfoImpl(pumd,
properties, Thread.currentThread().getContextClassLoader(),
ClassLoader.getSystemResource("."), new ArrayList<URL>(),
new InitialContext());
Transaction tx = null;
try {
tx = sleeContainer.getTransactionManager().suspend();
this.entityManagerFactory = hp
.createContainerEntityManagerFactory(pi, null);
} catch (Exception e) {
logger.error("Failure creating Persistence Unit.", e);
} catch (Throwable t) {
logger.error("Failure creating Persistence Unit.", t);
} finally {
if (tx != null)
sleeContainer.getTransactionManager().resume(tx);
}
} catch (Exception e) {
logger.error("Failure creating Persistence Unit.", e);
}
}
/**
* the string key used to store entity manager of this profile spec in tx
* data
*/
private String txDataKey = null;
/**
* Retrieves the entity manager for the current tx and the framework profile
* spec
*
* @return
*/
@SuppressWarnings("unchecked")
private EntityManager getEntityManager() {
if (txDataKey == null) {
txDataKey = "jpapef.em."
+ component.getProfileSpecificationID().toString();
}
// look in tx
Map transactionContextData = null;
try {
transactionContextData = sleeContainer.getTransactionManager()
.getTransactionContext().getData();
} catch (Throwable e1) {
throw new SLEEException(e1.getMessage(), e1);
}
EntityManager result = (EntityManager) transactionContextData
.get(txDataKey);
if (result == null) {
// create using factory
result = entityManagerFactory.createEntityManager();
// store in tx context data
transactionContextData.put(txDataKey, result);
// add a tx action to close it before tx commits
final EntityManager em = result;
TransactionalAction action = new TransactionalAction() {
public void execute() {
try {
em.close();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
};
try {
sleeContainer.getTransactionManager().addBeforeCommitAction(
action);
} catch (Throwable e1) {
throw new SLEEException(e1.getMessage(), e1);
}
}
return result;
}
}