/*
* Licensed 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 openbook.server;
import java.io.Serializable;
import java.util.concurrent.locks.ReentrantLock;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContextType;
/**
* An abstract utility for JPA based service.
* This thin wrapper over a {@link EntityManagerFactory Persistence Unit} maintains
* <LI>per-thread persistence context
* <LI>relinquishes direct transaction control under a managed environment
*
* @see #getEntityManager()
* @see #newEntityManager()
*
* @author Pinaki Poddar
*
*/
@SuppressWarnings("serial")
abstract class PersistenceService implements Serializable {
private final EntityManagerFactory emf;
private final String unitName;
private final boolean isManaged;
private final PersistenceContextType scope;
private ThreadLocal<EntityManager> thread = new ThreadLocal<EntityManager>();
private ReentrantLock lock = new ReentrantLock();
protected PersistenceService(String unit, EntityManagerFactory emf, boolean managed,
PersistenceContextType scope) {
this.unitName = unit;
this.emf = emf;
this.isManaged = managed;
this.scope = scope;
}
public final EntityManagerFactory getUnit() {
return emf;
}
public final String getUnitName() {
return unitName;
}
public final boolean isManaged() {
return isManaged;
}
public final PersistenceContextType getContextType() {
return scope;
}
/**
* Gets an entity manager associated with the current thread. If the
* current thread is not associated with any entity manager or the
* associated entity manager has been closed, creates a new entity manager
* and associates with the current thread.
*
* @return an entity manager associated with the current thread.
*/
protected EntityManager getEntityManager() {
try {
lock.lock();
EntityManager em = thread.get();
if (em == null || !em.isOpen()) {
em = emf.createEntityManager();
thread.set(em);
}
return em;
} finally {
lock.unlock();
}
}
/**
* Creates a new entity manager. The entity manager is not associated with
* the current thread.
*/
protected EntityManager newEntityManager() {
return emf.createEntityManager();
}
/**
* Begins a transaction on the current thread. If the thread is associated
* with a persistence context, then a transaction is started if necessary.
* If the thread is not associated with a persistence context, then a new
* context is created, associated with the thread, new transaction is
* started.
*
* @see #getEntityManager()
*/
protected EntityManager begin() {
try {
lock.lock();
EntityManager em = getEntityManager();
if (isManaged) {
em.joinTransaction();
} else {
if (!em.getTransaction().isActive()) {
em.getTransaction().begin();
}
}
return em;
} finally {
lock.unlock();
}
}
/**
* Commits a transaction on the current thread.
*/
protected void commit() {
try {
lock.lock();
EntityManager em = getEntityManager();
if (isManaged) {
em.flush();
} else {
assertActive();
em.getTransaction().commit();
}
if (scope == PersistenceContextType.TRANSACTION) {
em.clear();
}
} finally {
lock.unlock();
}
}
/**
* Rolls back a transaction on the current thread.
*/
protected void rollback() {
try {
lock.lock();
EntityManager em = getEntityManager();
if (isManaged) {
} else {
em.getTransaction().rollback();
}
if (scope == PersistenceContextType.TRANSACTION) {
em.clear();
}
} finally {
lock.unlock();
}
}
/**
* Assert current thread is associated with an active transaction.
*/
protected void assertActive() {
EntityManager em = thread.get();
String thread = Thread.currentThread().getName();
assertTrue("No persistent context is associated with " + thread, em != null);
assertTrue("Persistent context " + em + " associated with " + thread + " has been closed", em.isOpen());
if (!isManaged) {
assertTrue("Persistent context " + em + " associated with " + thread + " has no active transaction",
em.getTransaction().isActive());
}
}
protected void assertTrue(String s, boolean p) {
if (!p) {
System.err.println(s);
throw new RuntimeException(s);
}
}
}