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.viewController.spring;
21  
22  import org.apache.myfaces.orchestra.conversation.Conversation;
23  import org.apache.myfaces.orchestra.conversation.ConversationContext;
24  import org.apache.myfaces.orchestra.conversation.spring.AbstractSpringOrchestraScope;
25  import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
26  import org.apache.myfaces.orchestra.lib.OrchestraException;
27  import org.apache.myfaces.orchestra.viewController.DefaultViewControllerManager;
28  import org.apache.myfaces.orchestra.viewController.ViewControllerManager;
29  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
30  
31  /**
32   * This hooks into the Spring2.x scope-handling mechanism to provide a dummy scope type
33   * which will place a bean configured for it into the same conversation that the current
34   * viewController lives in.
35   * <p>
36   * To use this, in the spring config file add an element with id=N that defines a spring
37   * scope with this class as its controller. Then define managed beans with scope="N". When
38   * code references such a bean, then the current "view controller" bean is located
39   * (ie the bean handling lifecycle events for the current view), and the instance of
40   * the target bean from the same conversation is returned. If no such instance currently
41   * exists, then one is created and added to that conversation (even when an instance
42   * already exists in a different scope).
43   * <p>
44   * Note that this means a bean configured with a scope of this type will actually
45   * have a separate instance per conversation.
46   * <p>
47   * In particular, this works well with spring aop-proxy, where the proxy looks up the
48   * bean on each method call, and so always returns the instance in the conversation
49   * associated with the current view.
50   * <p>
51   * One use for this is implementing custom JSF converters or validators that access
52   * persistent objects. When accessing the database they need to use the same
53   * PersistenceContext that the beans handing this view use. Defining the converter
54   * using this scope type ensures that this happens.
55   * <p>
56   * It is an error (ie an exception is thrown) if a bean of this scope is referenced
57   * but there is no "view controller" bean associated with the current view.
58   */
59  public class SpringViewControllerScope extends AbstractSpringOrchestraScope
60  {
61      private final static ViewControllerManager DEFAULT_VCM = new DefaultViewControllerManager();
62  
63      public SpringViewControllerScope()
64      {
65      }
66  
67      public Conversation createConversation(ConversationContext context, String conversationName)
68      {
69          throw new IllegalStateException("the viewController scope is not supposed to start a conversation on its own. conversation to start: " + conversationName);
70      }
71  
72      protected void assertSameScope(String beanName, Conversation conversation)
73      {
74          // since we do not start any conversation, there is no need to check
75          // if this scope uses the same conversationFactory
76      }
77  
78      /**
79       * Find the conversation-controller bean for the current view, then return the conversation that
80       * is configured for that controller bean.
81       * <p>
82       * The parameter is completely ignored; the conversation-name returned is that associated with the
83       * controller bean, not the specified bean at all.
84       */
85      protected String getConversationNameForBean(String beanName)
86      {
87          ViewControllerManager viewControllerManager = getViewControllerManager();
88          String viewId = FrameworkAdapter.getCurrentInstance().getCurrentViewId();
89          String viewControllerName = viewControllerManager.getViewControllerName(viewId);
90          if (viewControllerName == null)
91          {
92              throw new OrchestraException("no view controller name found for view " + viewId);
93          }
94  
95          String conversationName = super.getConversationNameForBean(viewControllerName);
96          if (conversationName == null)
97          {
98              throw new OrchestraException("no view controller definition found for view " + viewId);
99          }
100 
101         return conversationName;
102     }
103 
104     /**
105      * Look for a Spring bean definition that defines a custom ViewControllerManager;
106      * if not found then return the default instance.
107      * <p>
108      * Never returns null.
109      */
110     private ViewControllerManager getViewControllerManager()
111     {
112         try
113         {
114             return (ViewControllerManager)
115                 getApplicationContext().getBean(
116                     ViewControllerManager.VIEW_CONTROLLER_MANAGER_NAME, 
117                     ViewControllerManager.class);
118         }
119         catch(NoSuchBeanDefinitionException e)
120         {
121             return DEFAULT_VCM;
122         }
123     }
124 }