Home » openejb-3.1.2-src » org.apache » openejb » persistence » [javadoc | source]

    1   /**
    2    *
    3    * Licensed to the Apache Software Foundation (ASF) under one or more
    4    * contributor license agreements.  See the NOTICE file distributed with
    5    * this work for additional information regarding copyright ownership.
    6    * The ASF licenses this file to You under the Apache License, Version 2.0
    7    * (the "License"); you may not use this file except in compliance with
    8    * the License.  You may obtain a copy of the License at
    9    *
   10    *     http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    *  Unless required by applicable law or agreed to in writing, software
   13    *  distributed under the License is distributed on an "AS IS" BASIS,
   14    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15    *  See the License for the specific language governing permissions and
   16    *  limitations under the License.
   17    */
   18   package org.apache.openejb.persistence;
   19   
   20   
   21   import java.util.Map;
   22   import java.util.HashMap;
   23   import javax.persistence.EntityManager;
   24   import javax.persistence.EntityManagerFactory;
   25   import javax.persistence.TransactionRequiredException;
   26   import javax.transaction.Status;
   27   import javax.transaction.Synchronization;
   28   import javax.transaction.TransactionSynchronizationRegistry;
   29   
   30   /**
   31    * The JtaEntityManagerRegistry tracks JTA entity managers for transation and extended scoped
   32    * entity managers.  A signle instance of this object should be created and shared by all
   33    * JtaEntityManagers in the server instance.  Failure to do this will result in multiple entity
   34    * managers being created for a single persistence until, and that will result in cache
   35    * incoherence.
   36    */
   37   public class JtaEntityManagerRegistry {
   38       /**
   39        * Registry of transaction associated entity managers.
   40        */
   41       private final TransactionSynchronizationRegistry transactionRegistry;
   42   
   43       /**
   44        * Registry of entended context entity managers.
   45        */
   46       private final ThreadLocal<ExtendedRegistry> extendedRegistry = new ThreadLocal<ExtendedRegistry>() {
   47           protected ExtendedRegistry initialValue() {
   48               return new ExtendedRegistry();
   49           }
   50       };
   51   
   52       /**
   53        * Creates a JtaEntityManagerRegistry using the specified transactionSynchronizationRegistry for the registry
   54        * if transaction associated entity managers.
   55        */
   56       public JtaEntityManagerRegistry(TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
   57           this.transactionRegistry = transactionSynchronizationRegistry;
   58       }
   59   
   60       /**
   61        * Gets an entity manager instance from the transaction registry, extended regitry or for a transaction scoped
   62        * entity manager, creates a new one when an exisitng instance is not found.
   63        * </p>
   64        * It is important that a component adds extended scoped entity managers to this registry when the component is
   65        * entered and removes them when exited.  If this registration is not preformed, an IllegalStateException will
   66        * be thrown when entity manger is fetched.
   67        * @param entityManagerFactory the entity manager factory from which an entity manager is required
   68        * @param properties the properties passed to the entity manager factory when an entity manager is created
   69        * @param extended is the entity manager an extended context
   70        * @return the new entity manager
   71        * @throws IllegalStateException if the entity manger is extended and there is not an existing entity manager
   72        * instance already registered
   73        */
   74       public EntityManager getEntityManager(EntityManagerFactory entityManagerFactory, Map properties, boolean extended) throws IllegalStateException {
   75           if (entityManagerFactory == null) throw new NullPointerException("entityManagerFactory is null");
   76           EntityManagerTxKey txKey = new EntityManagerTxKey(entityManagerFactory);
   77           boolean transactionActive = isTransactionActive();
   78   
   79           // if we have an active transaction, check the tx registry
   80           if (transactionActive) {
   81               EntityManager entityManager = (EntityManager) transactionRegistry.getResource(txKey);
   82               if (entityManager != null) {
   83                   return entityManager;
   84               }
   85           }
   86   
   87           // if extended context, there must be an entity manager already registered with the tx
   88           if (extended) {
   89               EntityManager entityManager = getInheritedEntityManager(entityManagerFactory);
   90               if (entityManager == null) {
   91                   throw new IllegalStateException("InternalError: an entity manager should already be registered for this extended persistence unit");
   92               }
   93   
   94               // if transaction is active, we need to register the entity manager with the transaction manager
   95               if (transactionActive) {
   96                   entityManager.joinTransaction();
   97                   transactionRegistry.putResource(txKey, entityManager);
   98               }
   99   
  100               return entityManager;
  101           } else {
  102               // create a new entity manager
  103               EntityManager entityManager;
  104               if (properties != null) {
  105                   entityManager = entityManagerFactory.createEntityManager(properties);
  106               } else {
  107                   entityManager = entityManagerFactory.createEntityManager();
  108               }
  109   
  110               // if we are in a transaction associate the entity manager with the transaction; otherwise it is
  111               // expected the caller will close this entity manager after use
  112               if (transactionActive) {
  113                   transactionRegistry.registerInterposedSynchronization(new CloseEntityManager(entityManager));
  114                   transactionRegistry.putResource(txKey, entityManager);
  115               }
  116               return entityManager;
  117           }
  118       }
  119   
  120       /**
  121        * Adds the entity managers for the specified component to the registry.  This should be called when the component
  122        * is entered.
  123        * @param deploymentId the id of the component
  124        * @param entityManagers the entity managers to register
  125        * @throws EntityManagerAlreadyRegisteredException if an entity manager is already registered with the transaction
  126        * for one of the supplied entity manager factories; for EJBs this should be caught and rethown as an EJBException
  127        */
  128       public void addEntityManagers(String deploymentId, Object primaryKey, Map<EntityManagerFactory, EntityManager> entityManagers) throws EntityManagerAlreadyRegisteredException {
  129           extendedRegistry.get().addEntityManagers(new InstanceId(deploymentId, primaryKey), entityManagers);
  130       }
  131   
  132       /**
  133        * Removed the registered entity managers for the specified component.
  134        * @param deploymentId the id of the component
  135        */
  136       public void removeEntityManagers(String deploymentId, Object primaryKey) {
  137           extendedRegistry.get().removeEntityManagers(new InstanceId(deploymentId, primaryKey));
  138       }
  139   
  140       /**
  141        * Gets an exiting extended entity manager created by a component down the call stack.
  142        * @param entityManagerFactory the entity manager factory from which an entity manager is needed
  143        * @return the existing entity manager or null if one is not found
  144        */
  145       public EntityManager getInheritedEntityManager(EntityManagerFactory entityManagerFactory) {
  146           return extendedRegistry.get().getInheritedEntityManager(entityManagerFactory);
  147       }
  148   
  149       /**
  150        * Notifies the registry that a user transaction has been started or the specified component.  When a transaction
  151        * is started for a component with registered extended entity managers, the entity managers are enrolled in the
  152        * transaction.
  153        * @param deploymentId the id of the component
  154        */
  155       public void transactionStarted(String deploymentId, Object primaryKey) {
  156           extendedRegistry.get().transactionStarted(new InstanceId(deploymentId, primaryKey));
  157       }
  158   
  159       /**
  160        * Is a transaction active?
  161        * @return true if a transaction is active; false otherwise
  162        */
  163       public boolean isTransactionActive() {
  164           int txStatus = transactionRegistry.getTransactionStatus();
  165           boolean transactionActive = txStatus == Status.STATUS_ACTIVE || txStatus == Status.STATUS_MARKED_ROLLBACK;
  166           return transactionActive;
  167       }
  168   
  169       private class ExtendedRegistry {
  170           private final Map<InstanceId, Map<EntityManagerFactory, EntityManager>> entityManagersByDeploymentId =
  171                   new HashMap<InstanceId, Map<EntityManagerFactory, EntityManager>>();
  172   
  173           private void addEntityManagers(InstanceId instanceId, Map<EntityManagerFactory, EntityManager> entityManagers) throws EntityManagerAlreadyRegisteredException {
  174               if (instanceId == null) {
  175                   throw new NullPointerException("instanceId is null");
  176               }
  177               if (entityManagers == null) {
  178                   throw new NullPointerException("entityManagers is null");
  179               }
  180   
  181               if (isTransactionActive()) {
  182                   for (Map.Entry<EntityManagerFactory, EntityManager> entry : entityManagers.entrySet()) {
  183                       EntityManagerFactory entityManagerFactory = entry.getKey();
  184                       EntityManager entityManager = entry.getValue();
  185                       EntityManagerTxKey txKey = new EntityManagerTxKey(entityManagerFactory);
  186                       EntityManager oldEntityManager = (EntityManager) transactionRegistry.getResource(txKey);
  187                       if (entityManager == oldEntityManager) {
  188                           break;
  189                       }
  190                       if (oldEntityManager != null) {
  191                           throw new EntityManagerAlreadyRegisteredException("Another entity manager is already registered for this persistence unit");
  192                       }
  193   
  194                       entityManager.joinTransaction();
  195                       transactionRegistry.putResource(txKey, entityManager);
  196                   }
  197               }
  198               entityManagersByDeploymentId.put(instanceId, entityManagers);
  199           }
  200   
  201           private void removeEntityManagers(InstanceId instanceId) {
  202               if (instanceId == null) {
  203                   throw new NullPointerException("InstanceId is null");
  204               }
  205   
  206               entityManagersByDeploymentId.remove(instanceId);
  207           }
  208   
  209           private EntityManager getInheritedEntityManager(EntityManagerFactory entityManagerFactory) {
  210               if (entityManagerFactory == null) {
  211                   throw new NullPointerException("entityManagerFactory is null");
  212               }
  213   
  214               for (Map<EntityManagerFactory, EntityManager> entityManagers : entityManagersByDeploymentId.values()) {
  215                   EntityManager entityManager = entityManagers.get(entityManagerFactory);
  216                   if (entityManager != null) {
  217                       return entityManager;
  218                   }
  219               }
  220               return null;
  221           }
  222   
  223           private void transactionStarted(InstanceId instanceId) {
  224               if (instanceId == null) {
  225                   throw new NullPointerException("instanceId is null");
  226               }
  227               if (!isTransactionActive()) {
  228                   throw new TransactionRequiredException();
  229               }
  230   
  231               Map<EntityManagerFactory, EntityManager> entityManagers = entityManagersByDeploymentId.get(instanceId);
  232               if (entityManagers == null) {
  233                   return;
  234               }
  235   
  236               for (Map.Entry<EntityManagerFactory, EntityManager> entry : entityManagers.entrySet()) {
  237                   EntityManagerFactory entityManagerFactory = entry.getKey();
  238                   EntityManager entityManager = entry.getValue();
  239                   entityManager.joinTransaction();
  240                   EntityManagerTxKey txKey = new EntityManagerTxKey(entityManagerFactory);
  241                   transactionRegistry.putResource(txKey, entityManager);
  242               }
  243           }
  244       }
  245   
  246       private static class InstanceId {
  247           private final String deploymentId;
  248           private final Object primaryKey;
  249   
  250           public InstanceId(String deploymentId, Object primaryKey) {
  251               if (deploymentId == null) {
  252                   throw new NullPointerException("deploymentId is null");
  253               }
  254               if (primaryKey == null) {
  255                   throw new NullPointerException("primaryKey is null");
  256               }
  257               this.deploymentId = deploymentId;
  258               this.primaryKey = primaryKey;
  259           }
  260   
  261           public boolean equals(Object o) {
  262               if (this == o) {
  263                   return true;
  264               }
  265               if (o == null || getClass() != o.getClass()) {
  266                   return false;
  267               }
  268   
  269               final InstanceId that = (InstanceId) o;
  270               return deploymentId.equals(that.deploymentId) &&
  271                       primaryKey.equals(that.primaryKey);
  272   
  273           }
  274   
  275           public int hashCode() {
  276               int result;
  277               result = deploymentId.hashCode();
  278               result = 29 * result + primaryKey.hashCode();
  279               return result;
  280           }
  281       }
  282   
  283       private static class CloseEntityManager implements Synchronization {
  284           private final EntityManager entityManager;
  285   
  286           public CloseEntityManager(EntityManager entityManager) {
  287               this.entityManager = entityManager;
  288           }
  289   
  290           public void beforeCompletion() {
  291           }
  292   
  293           public void afterCompletion(int i) {
  294               entityManager.close();
  295           }
  296       }
  297   }

Home » openejb-3.1.2-src » org.apache » openejb » persistence » [javadoc | source]