1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with 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, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.myfaces.orchestra.conversation.spring; 20 21 import org.springframework.orm.jpa.EntityManagerHolder; 22 import org.springframework.transaction.support.TransactionSynchronizationManager; 23 24 import javax.persistence.EntityManager; 25 import javax.persistence.EntityManagerFactory; 26 import javax.persistence.FlushModeType; 27 import java.util.Stack; 28 29 /** 30 * A factory for PersistenceContext objects which integrates with Spring's JPA 31 * support. 32 * <p> 33 * When a bean is invoked which is associated with a conversation, but the conversation 34 * does not yet have a PersistenceContext, then this factory is used to create a 35 * PersistenceContext. 36 * <p> 37 * The returned object knows how to configure itself as the "current persistence context" 38 * within Spring when a method on that bean is invoked, and how to restore the earlier 39 * "current persistence context" after the method returns. 40 */ 41 public class JpaPersistenceContextFactory implements PersistenceContextFactory 42 { 43 private EntityManagerFactory entityManagerFactory; 44 45 public PersistenceContext create() 46 { 47 final EntityManager em = entityManagerFactory.createEntityManager(); 48 em.setFlushMode(FlushModeType.COMMIT); 49 50 return new PersistenceContext() 51 { 52 private final Stack bindings = new Stack(); 53 54 // Store the current EntityManagerHolder on a stack, then install our own as the 55 // current EntityManagerHolder. Storing the old one allows us to restore it when 56 // this context is "unbound". 57 // 58 // Orchestra calls bind every time a method is invoked on a bean which has 59 // a conversation with a persistence context. The unbind is invoked when the 60 // invoked method on that bean returns. 61 // 62 // When a bean has a property that has been annotated as @PersistenceContext, 63 // Spring injects a proxy that looks up the "current" persistence context whenever 64 // a method is invoked on it. Because Orchestra has called persistencecontext.bind 65 // when the bean was first entered, and this object's bind method has installed 66 // itself as the "current" spring persistence context object, the bean sees the 67 // persistence context that is associated with its conversation. 68 // 69 // TODO: what happens when a bean invokes a method on itself? Does bind get called 70 // again? If so, then this implementation is inefficient as it will push itself 71 // onto the stack over and over again. This could be optimised by checking whether 72 // this is the current context, and if so just incrementing a counter rather than 73 // pushing onto a stack... 74 public void bind() 75 { 76 synchronized(bindings) 77 { 78 EntityManagerHolder current = (EntityManagerHolder) 79 TransactionSynchronizationManager.getResource(entityManagerFactory); 80 81 if (current != null) 82 { 83 TransactionSynchronizationManager.unbindResource(entityManagerFactory); 84 } 85 86 bindings.push(current); 87 88 TransactionSynchronizationManager.bindResource(entityManagerFactory, 89 new EntityManagerHolder(em)); 90 } 91 } 92 93 public void unbind() 94 { 95 synchronized(bindings) 96 { 97 if (TransactionSynchronizationManager.hasResource(entityManagerFactory)) 98 { 99 TransactionSynchronizationManager.unbindResource(entityManagerFactory); 100 } 101 102 Object holder = null; 103 if (bindings.size() > 0) 104 { 105 holder = bindings.pop(); 106 } 107 if (holder != null) 108 { 109 TransactionSynchronizationManager.bindResource(entityManagerFactory, 110 holder); 111 } 112 } 113 } 114 115 public void close() 116 { 117 em.close(); 118 } 119 }; 120 } 121 122 public EntityManagerFactory getEntityManagerFactory() 123 { 124 return entityManagerFactory; 125 } 126 127 public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) 128 { 129 this.entityManagerFactory = entityManagerFactory; 130 } 131 }