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 20 package org.apache.myfaces.orchestra.conversation.spring; 21 22 import org.aopalliance.intercept.MethodInterceptor; 23 import org.aopalliance.intercept.MethodInvocation; 24 import org.apache.myfaces.orchestra.conversation.Conversation; 25 26 /** 27 * Maintain the appropriate persistence state for the current call-stack. 28 * <p> 29 * This class is a MethodInterceptor (an AOP Advice) which should be configured to intercept 30 * calls to all conversation-scoped beans. It ensures that the appropriate PersistenceContext 31 * object for this conversation is placed into the default location, by invoking the 32 * <i>bind</i> method on the persistence context object. Typically the bind method stores 33 * the persistence context into a thread-local variable, but that is implementation-specific. 34 * <p> 35 * When Spring code retrieves the persistence context in order to inject it into a bean, 36 * it then finds the correct context for the conversation that is associated with the 37 * nearest conversation-scoped bean in the callstack. 38 * <p> 39 * If no PersistenceContext yet exists for the conversation associated with the bean 40 * that is being invoked then one is created using {@link PersistenceContextFactory}. 41 * <p> 42 * A reference to the {@link PersistenceContext} is put into the conversation attribute 43 * map wrapped in an {@link PersistenceContextCloser} so that when the conversation 44 * ends a callback occurs on it which closes the underlying object. 45 */ 46 public class PersistenceContextConversationInterceptor implements MethodInterceptor 47 { 48 private final static String PERSISTENCE_CONTEXT_CONV_ATTRIBUTE = 49 PersistenceContextConversationInterceptor.class.getName() + ".PERSISTENCE_CONTEXT"; 50 public final static String REQUEST_ATTRIBUTE = 51 PersistenceContextConversationInterceptor.class.getName() + ".USED_PERSISTENCE_CONTEXTS"; 52 53 private PersistenceContextFactory persistenceContextFactory; 54 55 public PersistenceContextConversationInterceptor() 56 { 57 } 58 59 public void setPersistenceContextFactory(PersistenceContextFactory persistenceContextFactory) 60 { 61 this.persistenceContextFactory = persistenceContextFactory; 62 } 63 64 public Object invoke(MethodInvocation methodInvocation) throws Throwable 65 { 66 PersistenceContext persistenceContext = null; 67 68 // The CurrentConversationAdvice object is expected to have already been executed, 69 // so the "current conversation" is now the one associated with the bean being 70 // invoked. But the persistence context currently configured is still unchanged, 71 // so here we set up the context associated with this conversation (creating 72 // it if this is the first call to this conversation). 73 Conversation conversation = Conversation.getCurrentInstance(); 74 if (conversation != null) 75 { 76 PersistenceContextCloser persistenceContextCloser = (PersistenceContextCloser) 77 conversation.getAttribute(PERSISTENCE_CONTEXT_CONV_ATTRIBUTE); 78 if (persistenceContextCloser != null) 79 { 80 // This is not the first call to this conversation (the closer exists). So 81 // retrieve the existing PersistenceContext for this conversation from the 82 // closer object. 83 persistenceContext = persistenceContextCloser.getPersistenceContext(); 84 } 85 86 if (persistenceContext == null) 87 { 88 // This must be the first time any bean in this conversation has been 89 // invoked. Therefore create a PersistenceContext. Also create a 90 // PersistenceContextCloser and cache it in the conversation. 91 persistenceContext = persistenceContextFactory.create(); 92 93 conversation.setAttribute( 94 PERSISTENCE_CONTEXT_CONV_ATTRIBUTE, 95 new PersistenceContextCloser(persistenceContext)); 96 } 97 } 98 99 if (persistenceContext != null) 100 { 101 // Make the persistenceContext object the "current" one. All PersistenceContext 102 // instances that Spring injected are proxies that look up the actual target 103 // PersistenceContext on each method call; after this bind call, the object 104 // that they will retrieve is our new persistenceContext. 105 persistenceContext.bind(); 106 } 107 108 try 109 { 110 return methodInvocation.proceed(); 111 } 112 finally 113 { 114 if (persistenceContext != null) 115 { 116 persistenceContext.unbind(); 117 } 118 } 119 } 120 121 /* 122 protected void registerPersistenceContextUsage(PersistenceContext persistenceContext) 123 { 124 FrameworkAdapterInterface fai = FrameworkAdapter.getInstance(); 125 Set persistencesContexts = (Set) fai.getRequestAttribute(REQUEST_ATTRIBUTE); 126 if (persistencesContexts == null) 127 { 128 persistencesContexts = new HashSet(); 129 fai.setRequestAttribute(REQUEST_ATTRIBUTE, persistencesContexts); 130 } 131 if (!persistencesContexts.contains(persistenceContext)) 132 { 133 persistencesContexts.add(persistenceContext); 134 } 135 } 136 137 public static void cleanupPersistence() 138 { 139 FrameworkAdapterInterface fai = FrameworkAdapter.getInstance(); 140 Set persistencesContexts = (Set) fai.getRequestAttribute(REQUEST_ATTRIBUTE); 141 if (persistencesContexts == null) 142 { 143 return; 144 } 145 146 Iterator iterPersistencesContexts = persistencesContexts.iterator(); 147 while (iterPersistencesContexts.hasNext()) 148 { 149 PersistenceContext persistenceContext = (PersistenceContext) iterPersistencesContexts.next(); 150 persistenceContext.unbind(); 151 } 152 } 153 */ 154 }