View Javadoc

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 }