/**
* This file was automatically generated by the Mule Development Kit
*/
package es.twentymobile.mule.modules.jpa;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.Connect;
import org.mule.api.annotations.Source;
import org.mule.api.annotations.ValidateConnection;
import org.mule.api.annotations.ConnectionIdentifier;
import org.mule.api.annotations.Disconnect;
import org.mule.api.ConnectionException;
import org.mule.api.MuleContext;
import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.Optional;
import org.mule.api.callback.SourceCallback;
import org.mule.api.context.MuleContextAware;
import org.mule.api.execution.ExecutionCallback;
import org.mule.api.execution.ExecutionTemplate;
import org.mule.api.transaction.Transaction;
import org.mule.api.transaction.TransactionConfig;
import org.mule.api.transaction.TransactionException;
import org.mule.execution.TransactionalExecutionTemplate;
import org.mule.transaction.MuleTransactionConfig;
import org.mule.transaction.TransactionCoordination;
import org.mule.transaction.XaTransaction;
import org.mule.transaction.XaTransactionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import es.twentymobile.mule.modules.jpa.dsl.CustomJPAQuery;
import es.twentymobile.mule.modules.jpa.dsl.JPACountQueryResult;
import es.twentymobile.mule.modules.jpa.exceptions.JpaException;
import es.twentymobile.mule.modules.jpa.strategy.EntityManagerStrategy;
import es.twentymobile.mule.modules.jpa.strategy.EntityManagerStrategyFactory;
import es.twentymobile.mule.modules.jpa.strategy.EntityManagerStrategyFactory.ReceiveAction;
import es.twentymobile.mule.modules.jpa.xa.JpaXaResource;
import es.twentymobile.mule.modules.jpa.xa.JpaXaResourceForTomcatAndOpenJpa;
/**
* Cloud Connector
*
* @author Jose Maria Alvarez Fernandez at 20:20 Mobile Spain, S.A.
* @author Adolfo Gonzalaez Blazquez at 20:20 Mobile Spain, S.A.
*/
@Connector(name = "jpa", schemaVersion = "1.0")
public class JpaConnector implements MuleContextAware {
private static final String DEFAULT_TRANSACTION_TIMEOUT = "60000";
/**
* The entityManagerFactory to use to create entityManagers
*/
@Configurable
private EntityManagerFactory entityManagerFactory;
/**
* The default max results to return from select queries.
*/
@Configurable
private Integer defaultMaxResults;
/**
* Property to set when the connection pool is not managed, and you want to
* use XA Transactions. Depends on OpenJPA. Other implementations may be
* used Overriding <a>es.twentymobile.mule.modules.jpa.xa.JpaXaResource</a>
* class.
*/
@Configurable
@Optional
@Default("false")
private Boolean isUnmanagedPoolAndOpenJpa;
/**
* The properties of JPA Entity Manager
*/
@Configurable
@Optional
private Map<String, Object> jpaProperties;
@Inject
private MuleContext muleContext;
private static Logger log = LoggerFactory.getLogger(JpaConnector.class);
/**
* @return the isUnmanagedPoolAndOpenJpa
*/
public Boolean getIsUnmanagedPoolAndOpenJpa() {
return isUnmanagedPoolAndOpenJpa;
}
/**
* @param isUnmanagedPoolAndOpenJpa
* the isUnmanagedPoolAndOpenJpa to set
*/
public void setIsUnmanagedPoolAndOpenJpa(Boolean isUnmanagedPoolAndOpenJpa) {
this.isUnmanagedPoolAndOpenJpa = isUnmanagedPoolAndOpenJpa;
}
/**
* @return the jpaProperties
*/
public Map<String, Object> getJpaProperties() {
return jpaProperties;
}
/**
* @param jpaProperties
* the jpaProperties to set
*/
public void setJpaProperties(Map<String, Object> jpaProperties) {
this.jpaProperties = jpaProperties;
}
public Integer getDefaultMaxResults() {
return defaultMaxResults;
}
public void setDefaultMaxResults(Integer defaultMaxResults) {
this.defaultMaxResults = defaultMaxResults;
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
/**
* Connect
*
* @throws ConnectionException
*/
@Connect
public void connect() throws ConnectionException {
}
/**
* Disconnect
*/
@Disconnect
public void disconnect() {
if (entityManagerFactory != null && entityManagerFactory.isOpen()) {
entityManagerFactory.close();
}
}
/**
* Are we connected
*/
@ValidateConnection
public boolean isConnected() {
return true;
}
/**
* Are we connected
*/
@ConnectionIdentifier
public String connectionId() {
return "001";
}
/**
* Query database as message source
*
* @param query
* the query to execute
* @param maxResults
* the maximum number of results to return
* @param startPosition
* the position of the first result to retrieve
* @param startTransaction
* if we must start a transaction
* @param transactionTimeout
* the transaction timeout in miliseconds
* @param callback
* the callback to start the process flow
* @return the objects result of the query
* @throws JpaException
* if something goes wrong
*
* {@sample.xml ../../../doc/jpa-connector.xml.sample
* jpa:query-database}
*/
@Source
public Object queryDatabase(final String query, Boolean startTransaction, @Optional @Default(DEFAULT_TRANSACTION_TIMEOUT) Integer transactionTimeout,
final SourceCallback callback, @Optional String maxResults, @Optional String startPosition) throws JpaException {
final Integer maxRes = parseMaxResults(maxResults);
final Integer startPos = parseStartPosition(startPosition);
if (startTransaction) {
TransactionConfig config = new MuleTransactionConfig();
config.setFactory(new XaTransactionFactory());
config.setAction(TransactionConfig.ACTION_ALWAYS_BEGIN);
ExecutionTemplate<Object> executionTemplate = TransactionalExecutionTemplate.createTransactionalExecutionTemplate(muleContext, config);
try {
return executionTemplate.execute(new ExecutionCallback<Object>() {
public Object process() throws Exception {
return callback.process(executeJpaQuery(query, Boolean.FALSE, null, maxRes, startPos));
}
});
} catch (Exception e) {
log.error("Error processing jpa query " + query + " with transaction", e);
throw new JpaException("Error processing jpa query", e);
}
} else {
try {
return callback.process(executeJpaQuery(query, Boolean.FALSE, null, maxRes, startPos));
} catch (Exception e) {
log.error("Error processing jpa query " + query + " without transaction", e);
throw new JpaException("Error processing jpa query", e);
}
}
}
private Object executeJpaQuery(String query, Boolean singleResult, Map<String, Object> parameters, Integer maxResults, Integer startPosition)
throws JpaException {
EntityManager em = null;
EntityTransaction etx = null;
try {
Transaction tx = TransactionCoordination.getInstance().getTransaction();
em = getEntityManager();
if (tx == null) {
log.info("No transaction available, so open a new transaction");
etx = em.getTransaction();
}
if (!em.isOpen()) {
throw new Exception("Cannot open entityManager.");
}
if (etx != null && !etx.isActive()) {
etx.begin();
}
Object resultSet = null;
Query jpaQuery = em.createQuery(query);
if (maxResults != 0) {
jpaQuery.setMaxResults(maxResults);
}
if (startPosition != null) {
jpaQuery.setFirstResult(startPosition);
}
if (parameters != null) {
for (String key : parameters.keySet()) {
jpaQuery.setParameter(key, parameters.get(key));
}
}
if (singleResult) {
resultSet = jpaQuery.getSingleResult();
} else {
resultSet = jpaQuery.getResultList();
}
return resultSet;
} catch (Exception e) {
log.error("Error querying with JPA Transport", e);
if (etx != null && etx.isActive()) {
etx.rollback();
}
throw new JpaException("Error querying with JPA. Query: " + query, e);
}
}
/**
* Persist object in database
*
* @param object
* the object(s) to persist
* @param flush
* if flush() must be invoked after the operation
* @return the objects persisted
* @throws JpaException
* if something fails
*
* {@sample.xml ../../../doc/jpa-connector.xml.sample
* jpa:persist}
*/
@Processor
public Object persist(Object object, @Optional Boolean flush) throws JpaException {
EntityManagerStrategy emStrategy;
if (flush != null) {
emStrategy = EntityManagerStrategyFactory.createEntityManagerCommand(getEntityManager(), ReceiveAction.PERSIST, flush);
} else {
emStrategy = EntityManagerStrategyFactory.createEntityManagerCommand(getEntityManager(), ReceiveAction.PERSIST, Boolean.FALSE);
}
return executeStrategy(emStrategy, object);
}
/**
* Merge object in database
*
* @param object
* the object(s) to merge
* @param flush
* if flush() must be invoked after the operation
* @return the objects merged
* @throws JpaException
* if something fails
*
* {@sample.xml ../../../doc/jpa-connector.xml.sample jpa:merge}
*/
@Processor
public Object merge(Object object, @Optional Boolean flush) throws JpaException {
EntityManagerStrategy emStrategy;
if (flush != null) {
emStrategy = EntityManagerStrategyFactory.createEntityManagerCommand(getEntityManager(), ReceiveAction.MERGE, flush);
} else {
emStrategy = EntityManagerStrategyFactory.createEntityManagerCommand(getEntityManager(), ReceiveAction.MERGE, Boolean.FALSE);
}
return executeStrategy(emStrategy, object);
}
/**
* Delete object from database
*
* @param object
* the object(s) to delete
* @param flush
* if flush() must be invoked after the operation
* @return the objects del
* @throws JpaException
* if something fails
*
* {@sample.xml ../../../doc/jpa-connector.xml.sample
* jpa:delete}
*/
@Processor
public void delete(Object object, @Optional Boolean flush) throws JpaException {
EntityManagerStrategy emStrategy;
if (flush != null) {
emStrategy = EntityManagerStrategyFactory.createEntityManagerCommand(getEntityManager(), ReceiveAction.DELETE, flush);
} else {
emStrategy = EntityManagerStrategyFactory.createEntityManagerCommand(getEntityManager(), ReceiveAction.DELETE, Boolean.FALSE);
}
executeStrategy(emStrategy, object);
}
/**
* Queries the database and populate the payload with the result
*
* @param query
* the query to execute
* @param singleResult
* if we must return a single result
* @param maxResults
* the maximum number of results to return
* @param startPosition
* the position of the first result to retrieve
* @param parameters
* if the query has parameters, parameters names and values
* @return the List of objects
* @throws JpaException
* if something fails
*
*
* {@sample.xml ../../../doc/jpa-connector.xml.sample
* jpa:execute-query}
*/
@Processor
public Object executeQuery(String query, @Optional Map<String, Object> parameters, @Optional Boolean singleResult, @Optional String maxResults,
@Optional String startPosition) throws JpaException {
Integer maxRes = parseMaxResults(maxResults);
Integer startPos = parseStartPosition(startPosition);
if (singleResult == null) {
return executeJpaQuery(query, Boolean.FALSE, parameters, maxRes, startPos);
} else {
return executeJpaQuery(query, singleResult, parameters, maxRes, startPos);
}
}
/**
* Queries the database based on a dynamic DSL query and populate the
* payload with the result
*
* @param jpaQuery
* the object that contains the query
* @param maxResults
* the maximum number of results to return
* @param singleResult
* if we must return a list or a single result object (default is
* false)
* @param distinct
* if we must add distinct to the restrictions
* @param returnCount
* if we must return the result and the count without limit of
* the possibly limited result
*
* @return the List of objects
*
* {@sample.xml ../../../doc/jpa-connector.xml.sample
* jpa:execute-dsl-query}
*/
@Processor
public Object executeDslQuery(QueryDslQuery jpaQuery, @Optional String maxResults, @Optional Boolean singleResult, @Optional Boolean distinct,
@Optional Boolean returnCount) throws JpaException {
EntityManager em = null;
EntityTransaction etx = null;
Transaction tx = TransactionCoordination.getInstance().getTransaction();
Integer maxRes = parseMaxResults(maxResults);
try {
em = getEntityManager();
if (tx == null) {
log.info("No transaction available, so open a new transaction");
etx = em.getTransaction();
}
if (!em.isOpen()) {
throw new Exception("Cannot open entityManager.");
}
if (etx != null && !etx.isActive()) {
etx.begin();
}
CustomJPAQuery query = jpaQuery.getQuery().clone(em);
if (distinct != null && distinct.equals(Boolean.TRUE)) {
query = query.distinct();
}
Object result;
if (singleResult == null || singleResult.equals(Boolean.FALSE)) {
if (maxRes != 0) {
if (!returnCount) {
result = query.limit(maxRes).list(jpaQuery.getBasePath());
} else {
JPACountQueryResult queryResult = new JPACountQueryResult();
queryResult.setResult(query.limit(maxRes).list(jpaQuery.getBasePath()));
queryResult.setCount(query.count());
result = queryResult;
}
} else {
result = query.list(jpaQuery.getBasePath());
}
} else {
result = query.uniqueResult(jpaQuery.getBasePath());
}
if (etx != null && etx.isActive()) {
etx.commit();
}
return result;
} catch (Exception e) {
log.error("Error executing JPA query", e);
if (etx != null && etx.isActive()) {
etx.rollback();
}
throw new JpaException("Error querying database with QueryDSL: " + jpaQuery.getQuery(), e);
}
}
private Object executeStrategy(EntityManagerStrategy emStrategy, Object payload) throws JpaException {
EntityManager em = null;
EntityTransaction etx = null;
Transaction tx = TransactionCoordination.getInstance().getTransaction();
try {
em = emStrategy.getEntityManager();
if (tx == null) {
log.info("No transaction available, so open a new transaction");
etx = em.getTransaction();
}
if (!em.isOpen()) {
throw new Exception("Cannot open entityManager.");
}
if (etx != null && !etx.isActive()) {
etx.begin();
}
Object result = null;
if (payload instanceof List) {
log.debug("payload type is list.");
List<?> payloadList = (List<?>) payload;
List<Object> resultList = new ArrayList<Object>();
for (Iterator<?> payloadIter = payloadList.iterator(); payloadIter.hasNext();) {
Object entity = payloadIter.next();
log.debug("entity is " + entity.getClass() + ".");
Object iterResult = emStrategy.execute(entity);
resultList.add((Object) iterResult);
}
result = resultList;
} else {
log.debug("entity is " + payload.getClass() + ".");
result = emStrategy.execute(payload);
}
if (etx != null && etx.isActive()) {
etx.commit();
}
return result;
} catch (Exception e) {
log.error("Error executing JPA query", e);
if (etx != null && etx.isActive()) {
etx.rollback();
}
throw new JpaException("Error executing jpa strategy " + emStrategy, e);
}
}
private EntityManager getEntityManager() {
Transaction tx = TransactionCoordination.getInstance().getTransaction();
if (tx != null) {
if (tx.hasResource(entityManagerFactory)) {
log.debug("Retrieving session from current transaction");
Object r = tx.getResource(entityManagerFactory);
if (r instanceof JpaXaResource) {
return ((JpaXaResource) r).getEntityManager();
} else {
return (EntityManager) r;
}
}
}
log.debug("Retrieving new entityManager from factory");
EntityManager entityManager;
if (jpaProperties != null && !jpaProperties.isEmpty()) {
entityManager = entityManagerFactory.createEntityManager(jpaProperties);
} else {
entityManager = entityManagerFactory.createEntityManager();
}
if (tx != null) {
log.debug("Binding session to current transaction");
try {
Object r;
if (tx instanceof XaTransaction) {
if (isUnmanagedPoolAndOpenJpa) {
r = new JpaXaResourceForTomcatAndOpenJpa(entityManager);
} else {
r = new JpaXaResource(entityManager);
}
} else {
r = entityManager;
}
tx.bindResource(entityManagerFactory, r);
} catch (TransactionException e) {
throw new RuntimeException("Could not bind connection to current transaction", e);
}
}
return entityManager;
}
private Integer parseMaxResults(String maxResults) {
Integer maxRes = getDefaultMaxResults();
try {
maxRes = Integer.valueOf(maxResults);
} catch (NumberFormatException e) {
log.debug("Invalid maxResults value. Setting default connector value " + getDefaultMaxResults());
}
return maxRes;
}
private Integer parseStartPosition(String startPosition) {
Integer startPos = null;
try {
startPos = Integer.valueOf(startPosition);
} catch (NumberFormatException e) {
log.debug("Invalid startPosition value. Setting default to null");
}
return startPos;
}
@Override
public void setMuleContext(MuleContext context) {
this.muleContext = context;
}
}