All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openejb.persistence.JtaEntityManagerRegistry Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * 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.openejb.persistence;


import org.apache.openejb.util.Geronimo;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TransactionRequiredException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.TransactionSynchronizationRegistry;
import java.util.HashMap;
import java.util.Map;

/**
 * The JtaEntityManagerRegistry tracks JTA entity managers for transaction and extended scoped
 * entity managers.  A single instance of this object should be created and shared by all
 * JtaEntityManagers in the server instance.  Failure to do this will result in multiple entity
 * managers being created for a single persistence until, and that will result in cache
 * incoherence.
 */
public class JtaEntityManagerRegistry {

    private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB.createChild("persistence"), JtaEntityManager.class);
    
    /**
     * Registry of transaction associated entity managers.
     */
    private final TransactionSynchronizationRegistry transactionRegistry;

    /**
     * Registry of extended context entity managers.
     */
    private final ThreadLocal extendedRegistry = new ThreadLocal() {
        protected ExtendedRegistry initialValue() {
            return new ExtendedRegistry();
        }
    };

    /**
     * Creates a JtaEntityManagerRegistry using the specified transactionSynchronizationRegistry for the registry
     * if transaction associated entity managers.
     */
    public JtaEntityManagerRegistry(TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
        this.transactionRegistry = transactionSynchronizationRegistry;
    }

    /**
     * Gets an entity manager instance from the transaction registry, extended registry or for a transaction scoped
     * entity manager, creates a new one when an existing instance is not found.
     * 

* It is important that a component adds extended scoped entity managers to this registry when the component is * entered and removes them when exited. If this registration is not performed, an IllegalStateException will * be thrown when entity manger is fetched. * @param entityManagerFactory the entity manager factory from which an entity manager is required * @param properties the properties passed to the entity manager factory when an entity manager is created * @param extended is the entity manager an extended context * @param unitName * @return the new entity manager * @throws IllegalStateException if the entity manger is extended and there is not an existing entity manager * instance already registered */ @Geronimo public EntityManager getEntityManager(EntityManagerFactory entityManagerFactory, Map properties, boolean extended, String unitName) throws IllegalStateException { if (entityManagerFactory == null) throw new NullPointerException("entityManagerFactory is null"); EntityManagerTxKey txKey = new EntityManagerTxKey(entityManagerFactory); boolean transactionActive = isTransactionActive(); // if we have an active transaction, check the tx registry if (transactionActive) { EntityManager entityManager = (EntityManager) transactionRegistry.getResource(txKey); if (entityManager != null) { return entityManager; } } // if extended context, there must be an entity manager already registered with the tx if (extended) { EntityManagerTracker entityManagerTracker = getInheritedEntityManager(entityManagerFactory); if (entityManagerTracker == null || entityManagerTracker.getEntityManager() == null) { throw new IllegalStateException("InternalError: an entity manager should already be registered for this extended persistence unit"); } EntityManager entityManager = entityManagerTracker.getEntityManager(); // if transaction is active, we need to register the entity manager with the transaction manager if (transactionActive) { entityManager.joinTransaction(); transactionRegistry.putResource(txKey, entityManager); } return entityManager; } else { // create a new entity manager EntityManager entityManager; if (properties != null) { entityManager = entityManagerFactory.createEntityManager(properties); } else { entityManager = entityManagerFactory.createEntityManager(); } logger.debug("Created EntityManager(unit=" + unitName + ", hashCode=" + entityManager.hashCode() + ")"); // if we are in a transaction associate the entity manager with the transaction; otherwise it is // expected the caller will close this entity manager after use if (transactionActive) { transactionRegistry.registerInterposedSynchronization(new CloseEntityManager(entityManager, unitName)); transactionRegistry.putResource(txKey, entityManager); } return entityManager; } } /** * Adds the entity managers for the specified component to the registry. This should be called when the component * is entered. * @param deploymentId the id of the component * @param entityManagers the entity managers to register * @throws EntityManagerAlreadyRegisteredException if an entity manager is already registered with the transaction * for one of the supplied entity manager factories; for EJBs this should be caught and rethown as an EJBException */ public void addEntityManagers(String deploymentId, Object primaryKey, Map entityManagers) throws EntityManagerAlreadyRegisteredException { extendedRegistry.get().addEntityManagers(new InstanceId(deploymentId, primaryKey), entityManagers); } /** * Removed the registered entity managers for the specified component. * @param deploymentId the id of the component * @return EntityManager map we are removing */ public Map removeEntityManagers(String deploymentId, Object primaryKey) { return extendedRegistry.get().removeEntityManagers(new InstanceId(deploymentId, primaryKey)); } /** * Gets an exiting extended entity manager created by a component down the call stack. * @param entityManagerFactory the entity manager factory from which an entity manager is needed * @return the existing entity manager or null if one is not found */ public EntityManagerTracker getInheritedEntityManager(EntityManagerFactory entityManagerFactory) { return extendedRegistry.get().getInheritedEntityManager(entityManagerFactory); } /** * Notifies the registry that a user transaction has been started or the specified component. When a transaction * is started for a component with registered extended entity managers, the entity managers are enrolled in the * transaction. * @param deploymentId the id of the component */ public void transactionStarted(String deploymentId, Object primaryKey) { extendedRegistry.get().transactionStarted(new InstanceId(deploymentId, primaryKey)); } /** * Is a transaction active? * @return true if a transaction is active; false otherwise */ public boolean isTransactionActive() { int txStatus = transactionRegistry.getTransactionStatus(); boolean transactionActive = txStatus == Status.STATUS_ACTIVE || txStatus == Status.STATUS_MARKED_ROLLBACK; return transactionActive; } private class ExtendedRegistry { private final Map> entityManagersByDeploymentId = new HashMap>(); private void addEntityManagers(InstanceId instanceId, Map entityManagers) throws EntityManagerAlreadyRegisteredException { if (instanceId == null) { throw new NullPointerException("instanceId is null"); } if (entityManagers == null) { throw new NullPointerException("entityManagers is null"); } if (isTransactionActive()) { for (Map.Entry entry : entityManagers.entrySet()) { EntityManagerFactory entityManagerFactory = entry.getKey(); EntityManager entityManager = entry.getValue().getEntityManager(); EntityManagerTxKey txKey = new EntityManagerTxKey(entityManagerFactory); EntityManager oldEntityManager = (EntityManager) transactionRegistry.getResource(txKey); if (entityManager == oldEntityManager) { break; } if (oldEntityManager != null) { throw new EntityManagerAlreadyRegisteredException("Another entity manager is already registered for this persistence unit"); } entityManager.joinTransaction(); transactionRegistry.putResource(txKey, entityManager); } } entityManagersByDeploymentId.put(instanceId, entityManagers); } private Map removeEntityManagers(InstanceId instanceId) { if (instanceId == null) { throw new NullPointerException("InstanceId is null"); } return entityManagersByDeploymentId.remove(instanceId); } private EntityManagerTracker getInheritedEntityManager(EntityManagerFactory entityManagerFactory) { if (entityManagerFactory == null) { throw new NullPointerException("entityManagerFactory is null"); } for (Map entityManagers : entityManagersByDeploymentId.values()) { EntityManagerTracker entityManagerTracker = entityManagers.get(entityManagerFactory); if (entityManagerTracker != null) { return entityManagerTracker; } } return null; } private void transactionStarted(InstanceId instanceId) { if (instanceId == null) { throw new NullPointerException("instanceId is null"); } if (!isTransactionActive()) { throw new TransactionRequiredException(); } Map entityManagers = entityManagersByDeploymentId.get(instanceId); if (entityManagers == null) { return; } for (Map.Entry entry : entityManagers.entrySet()) { EntityManagerFactory entityManagerFactory = entry.getKey(); EntityManager entityManager = entry.getValue().getEntityManager(); entityManager.joinTransaction(); EntityManagerTxKey txKey = new EntityManagerTxKey(entityManagerFactory); transactionRegistry.putResource(txKey, entityManager); } } } private static class InstanceId { private final String deploymentId; private final Object primaryKey; public InstanceId(String deploymentId, Object primaryKey) { if (deploymentId == null) { throw new NullPointerException("deploymentId is null"); } if (primaryKey == null) { throw new NullPointerException("primaryKey is null"); } this.deploymentId = deploymentId; this.primaryKey = primaryKey; } public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final InstanceId that = (InstanceId) o; return deploymentId.equals(that.deploymentId) && primaryKey.equals(that.primaryKey); } public int hashCode() { int result; result = deploymentId.hashCode(); result = 29 * result + primaryKey.hashCode(); return result; } } /** * This object is used track all EntityManagers inherited in order * to effectively close it when the latest Extended persistence context * is no more accessed. */ public static class EntityManagerTracker { // must take care of the first inheritance level private transient int counter; private EntityManager entityManager; public EntityManagerTracker(EntityManager entityManager) { if (entityManager == null) throw new NullPointerException("entityManager is null."); this.counter = 0; this.entityManager = entityManager; } public int incCounter() { return counter++; } public int decCounter() { return counter--; } public EntityManager getEntityManager() { return entityManager; } } private static class CloseEntityManager implements Synchronization { private final EntityManager entityManager; private String unitName; public CloseEntityManager(EntityManager entityManager, String unitName) { this.entityManager = entityManager; this.unitName = unitName; } public void beforeCompletion() { } public void afterCompletion(int i) { entityManager.close(); logger.debug("Closed EntityManager(unit=" + unitName + ", hashCode=" + entityManager.hashCode() + ")"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy