Package org.apache.openejb.persistence

Source Code of org.apache.openejb.persistence.JtaEntityManagerRegistry$CloseEntityManager

/**
*
* 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 java.util.Map;
import java.util.HashMap;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TransactionRequiredException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.TransactionSynchronizationRegistry;

/**
* The JtaEntityManagerRegistry tracks JTA entity managers for transation and extended scoped
* entity managers.  A signle 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 {
    /**
     * Registry of transaction associated entity managers.
     */
    private final TransactionSynchronizationRegistry transactionRegistry;

    /**
     * Registry of entended context entity managers.
     */
    private final ThreadLocal<ExtendedRegistry> extendedRegistry = new ThreadLocal<ExtendedRegistry>() {
        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 regitry or for a transaction scoped
     * entity manager, creates a new one when an exisitng instance is not found.
     * </p>
     * 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 preformed, 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
     * @return the new entity manager
     * @throws IllegalStateException if the entity manger is extended and there is not an existing entity manager
     * instance already registered
     */
    public EntityManager getEntityManager(EntityManagerFactory entityManagerFactory, Map properties, boolean extended) 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) {
            EntityManager entityManager = getInheritedEntityManager(entityManagerFactory);
            if (entityManager == null) {
                throw new IllegalStateException("InternalError: an entity manager should already be registered for this extended persistence unit");
            }

            // 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();
            }

            // 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));
                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<EntityManagerFactory, EntityManager> 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
     */
    public void removeEntityManagers(String deploymentId, Object primaryKey) {
        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 EntityManager 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<InstanceId, Map<EntityManagerFactory, EntityManager>> entityManagersByDeploymentId =
                new HashMap<InstanceId, Map<EntityManagerFactory, EntityManager>>();

        private void addEntityManagers(InstanceId instanceId, Map<EntityManagerFactory, EntityManager> 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<EntityManagerFactory, EntityManager> entry : entityManagers.entrySet()) {
                    EntityManagerFactory entityManagerFactory = entry.getKey();
                    EntityManager entityManager = entry.getValue();
                    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 void removeEntityManagers(InstanceId instanceId) {
            if (instanceId == null) {
                throw new NullPointerException("InstanceId is null");
            }

            entityManagersByDeploymentId.remove(instanceId);
        }

        private EntityManager getInheritedEntityManager(EntityManagerFactory entityManagerFactory) {
            if (entityManagerFactory == null) {
                throw new NullPointerException("entityManagerFactory is null");
            }

            for (Map<EntityManagerFactory, EntityManager> entityManagers : entityManagersByDeploymentId.values()) {
                EntityManager entityManager = entityManagers.get(entityManagerFactory);
                if (entityManager != null) {
                    return entityManager;
                }
            }
            return null;
        }

        private void transactionStarted(InstanceId instanceId) {
            if (instanceId == null) {
                throw new NullPointerException("instanceId is null");
            }
            if (!isTransactionActive()) {
                throw new TransactionRequiredException();
            }

            Map<EntityManagerFactory, EntityManager> entityManagers = entityManagersByDeploymentId.get(instanceId);
            if (entityManagers == null) {
                return;
            }

            for (Map.Entry<EntityManagerFactory, EntityManager> entry : entityManagers.entrySet()) {
                EntityManagerFactory entityManagerFactory = entry.getKey();
                EntityManager entityManager = entry.getValue();
                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;
        }
    }

    private static class CloseEntityManager implements Synchronization {
        private final EntityManager entityManager;

        public CloseEntityManager(EntityManager entityManager) {
            this.entityManager = entityManager;
        }

        public void beforeCompletion() {
        }

        public void afterCompletion(int i) {
            entityManager.close();
        }
    }
}
TOP

Related Classes of org.apache.openejb.persistence.JtaEntityManagerRegistry$CloseEntityManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.