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 }