/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.processor.idempotent.jpa;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.apache.camel.Exchange;
import org.apache.camel.api.management.ManagedAttribute;
import org.apache.camel.api.management.ManagedOperation;
import org.apache.camel.api.management.ManagedResource;
import org.apache.camel.spi.ExchangeIdempotentRepository;
import org.apache.camel.support.ServiceSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import static org.apache.camel.component.jpa.JpaHelper.getTargetEntityManager;
/**
* @version
*/
@ManagedResource(description = "JPA based message id repository")
public class JpaMessageIdRepository extends ServiceSupport implements ExchangeIdempotentRepository<String> {
protected static final String QUERY_STRING = "select x from " + MessageProcessed.class.getName() + " x where x.processorName = ?1 and x.messageId = ?2";
protected static final String QUERY_CLEAR_STRING = "select x from " + MessageProcessed.class.getName() + " x where x.processorName = ?1";
private static final Logger LOG = LoggerFactory.getLogger(JpaMessageIdRepository.class);
private final String processorName;
private final EntityManagerFactory entityManagerFactory;
private final TransactionTemplate transactionTemplate;
private boolean joinTransaction = true;
private boolean sharedEntityManager;
public JpaMessageIdRepository(EntityManagerFactory entityManagerFactory, String processorName) {
this(entityManagerFactory, createTransactionTemplate(entityManagerFactory), processorName);
}
public JpaMessageIdRepository(EntityManagerFactory entityManagerFactory, TransactionTemplate transactionTemplate, String processorName) {
this.entityManagerFactory = entityManagerFactory;
this.processorName = processorName;
this.transactionTemplate = transactionTemplate;
}
public static JpaMessageIdRepository jpaMessageIdRepository(String persistenceUnit, String processorName) {
return jpaMessageIdRepository(Persistence.createEntityManagerFactory(persistenceUnit), processorName);
}
public static JpaMessageIdRepository jpaMessageIdRepository(EntityManagerFactory entityManagerFactory, String processorName) {
return new JpaMessageIdRepository(entityManagerFactory, processorName);
}
private static TransactionTemplate createTransactionTemplate(EntityManagerFactory entityManagerFactory) {
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(new JpaTransactionManager(entityManagerFactory));
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return transactionTemplate;
}
@ManagedOperation(description = "Adds the key to the store")
public boolean add(String messageId) {
return add(null, messageId);
}
@Override
public boolean add(final Exchange exchange, final String messageId) {
final EntityManager entityManager = getTargetEntityManager(exchange, entityManagerFactory, true, sharedEntityManager, true);
// Run this in single transaction.
Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
public Boolean doInTransaction(TransactionStatus status) {
if (isJoinTransaction()) {
entityManager.joinTransaction();
}
List<?> list = query(entityManager, messageId);
if (list.isEmpty()) {
MessageProcessed processed = new MessageProcessed();
processed.setProcessorName(processorName);
processed.setMessageId(messageId);
processed.setCreatedAt(new Date());
entityManager.persist(processed);
entityManager.flush();
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
});
LOG.debug("add {} -> {}", messageId, rc);
return rc;
}
@ManagedOperation(description = "Does the store contain the given key")
public boolean contains(String messageId) {
return contains(null, messageId);
}
@Override
public boolean contains(final Exchange exchange, final String messageId) {
final EntityManager entityManager = getTargetEntityManager(exchange, entityManagerFactory, true, sharedEntityManager, true);
// Run this in single transaction.
Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
public Boolean doInTransaction(TransactionStatus status) {
if (isJoinTransaction()) {
entityManager.joinTransaction();
}
List<?> list = query(entityManager, messageId);
if (list.isEmpty()) {
return Boolean.FALSE;
} else {
return Boolean.TRUE;
}
}
});
LOG.debug("contains {} -> {}", messageId, rc);
return rc;
}
@ManagedOperation(description = "Remove the key from the store")
public boolean remove(String messageId) {
return remove(null, messageId);
}
@Override
public boolean remove(final Exchange exchange, final String messageId) {
final EntityManager entityManager = getTargetEntityManager(exchange, entityManagerFactory, true, sharedEntityManager, true);
Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
public Boolean doInTransaction(TransactionStatus status) {
if (isJoinTransaction()) {
entityManager.joinTransaction();
}
List<?> list = query(entityManager, messageId);
if (list.isEmpty()) {
return Boolean.FALSE;
} else {
MessageProcessed processed = (MessageProcessed) list.get(0);
entityManager.remove(processed);
entityManager.flush();
return Boolean.TRUE;
}
}
});
LOG.debug("remove {}", messageId);
return rc;
}
@Override
public boolean confirm(String messageId) {
return confirm(null, messageId);
}
@Override
public boolean confirm(final Exchange exchange, String messageId) {
LOG.debug("confirm {} -> true", messageId);
return true;
}
@ManagedOperation(description = "Clear the store")
public void clear() {
final EntityManager entityManager = getTargetEntityManager(null, entityManagerFactory, true, sharedEntityManager, true);
Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
public Boolean doInTransaction(TransactionStatus status) {
if (isJoinTransaction()) {
entityManager.joinTransaction();
}
List<?> list = queryClear(entityManager);
if (!list.isEmpty()) {
Iterator it = list.iterator();
while (it.hasNext()) {
Object item = it.next();
entityManager.remove(item);
}
entityManager.flush();
}
return Boolean.TRUE;
}
});
LOG.debug("clear the store {}", MessageProcessed.class.getName());
}
private List<?> query(final EntityManager entityManager, final String messageId) {
Query query = entityManager.createQuery(QUERY_STRING);
query.setParameter(1, processorName);
query.setParameter(2, messageId);
return query.getResultList();
}
private List<?> queryClear(final EntityManager entityManager) {
Query query = entityManager.createQuery(QUERY_CLEAR_STRING);
query.setParameter(1, processorName);
return query.getResultList();
}
@ManagedAttribute(description = "The processor name")
public String getProcessorName() {
return processorName;
}
@ManagedAttribute(description = "Whether to join existing transaction")
public boolean isJoinTransaction() {
return joinTransaction;
}
public void setJoinTransaction(boolean joinTransaction) {
this.joinTransaction = joinTransaction;
}
@ManagedAttribute(description = "Whether to use shared EntityManager")
public boolean isSharedEntityManager() {
return sharedEntityManager;
}
public void setSharedEntityManager(boolean sharedEntityManager) {
this.sharedEntityManager = sharedEntityManager;
}
@Override
protected void doStart() throws Exception {
// noop
}
@Override
protected void doStop() throws Exception {
// noop
}
}