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  package org.apache.myfaces.orchestra.conversation.spring;
    20
    21  import org.apache.commons.logging.Log;
    22  import org.apache.commons.logging.LogFactory;
    23  import org.springframework.aop.TargetSource;
    24  import org.springframework.aop.framework.Advised;
    25  import org.springframework.aop.framework.AopInfrastructureBean;
    26  import org.springframework.aop.framework.ProxyFactory;
    27  import org.springframework.aop.scope.DefaultScopedObject;
    28  import org.springframework.aop.scope.ScopedObject;
    29  import org.springframework.aop.support.DelegatingIntroductionInterceptor;
    30  import org.springframework.beans.factory.BeanFactory;
    31  import org.springframework.beans.factory.ObjectFactory;
    32  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    33
    34  /**
    35   * <p>Various Spring utilities used by the conversation framework</p>
    36   * <p>Notice: this class is not meant to be used outside of this library</p>
    37   */
    38  public final class _SpringUtils
    39  {
    40      // Equal to ScopedProxyUtils.TARGET_NAME_PREFIX, but that is private.
    41      private final static String SCOPED_TARGET_PREFIX = "scopedTarget.";
    42
    43      private _SpringUtils()
    44      {
    45      }
    46
    47      /**
    48       * Detect the case where a class includes a nested aop:scoped-target tag.
    49       * <p>
    50       * When a definition is present in the config file like this:
    51       * <pre>
    52       *  &lt;bean name="foo" class="example.Foo" orchestra:foo="foo"&gt;
    53       *   &lt;....&gt;
    54       *   &lt;aop:scopedProxy/&gt;
    55       *  &lt;/bean&gt;
    56       * </pre>
    57       * then what Spring actually does is create two BeanDefinition objects, one
    58       * with name "foo" that creates a proxy object, and one with name "scopedTarget.foo"
    59       * that actually defines the bean of type example.Foo (see Spring class ScopedProxyUtils
    60       * for details.
    61       * <p>
    62       * Using this pattern is very common with Orchestra, so we need to detect it and
    63       * look for orchestra settings on the renamed bean definition rather than the
    64       * one with the original name.
    65       *
    66       * @since 1.1
    67       */
    68      public static boolean isModifiedBeanName(String beanName)
    69      {
    70          return beanName.startsWith(SCOPED_TARGET_PREFIX);
    71      }
    72
    73      /**
    74       * Given the name of a BeanDefinition, if this is a fake name created
    75       * by spring because an aop:scoped-proxy is now wrapping this object,
    76       * then return the name of that scoped-proxy bean (ie the name that the
    77       * user accesses this bean by).
    78       *
    79       * @since 1.1
    80       */
    81      public static String getOriginalBeanName(String beanName)
    82      {
    83          if (beanName != null && isModifiedBeanName(beanName))
    84          {
    85              return beanName.substring(SCOPED_TARGET_PREFIX.length());
    86          }
    87
    88          return beanName;
    89      }
    90
    91      /**
    92       * Given a bean name "foo", if it refers to a scoped proxy then the bean
    93       * definition that holds the original settings is now under another name,
    94       * so return that other name so the original BeanDefinition can be found.
    95       *
    96       * @since 1.1
    97       */
    98      public static String getModifiedBeanName(String beanName)
    99      {
    100         if (beanName != null && !isModifiedBeanName(beanName))
    101         {
    102             return SCOPED_TARGET_PREFIX + beanName;
    103         }
    104
    105         return beanName;
    106     }
    107
    108     /** @deprecated use isModifiedBeanName instead. */
    109     public static boolean isAlternateBeanName(String beanName)
    110     {
    111         return isModifiedBeanName(beanName);
    112     }
    113
    114     /** @deprecated use getModifiedBeanName instead. */
    115     public static String getAlternateBeanName(String beanName)
    116     {
    117         return getModifiedBeanName(beanName);
    118     }
    119
    120     /** @deprecated use getOriginalBeanName instead. */
    121     public static String getRealBeanName(String beanName)
    122     {
    123         return getOriginalBeanName(beanName);
    124     }
    125
    126     /**
    127      * Create an object that subclasses the concrete class of the BeanDefinition
    128      * for the specified targetBeanName, and when invoked delegates to an instance
    129      * of that type fetched from a scope object.
    130      * <p>
    131      * The proxy returned currently also currently implements the standard Spring
    132      * ScopedObject interface; this is experimental and may be removed if not useful.
    133      *
    134      * @since 1.1
    135      */
    136     public static Object newProxy(
    137             AbstractSpringOrchestraScope scope,
    138             String conversationName,
    139             String targetBeanName,
    140             ObjectFactory objectFactory,
    141             BeanFactory beanFactory)
    142     {
    143         // based on code from  Spring ScopedProxyFactoryBean (APL2.0)
    144         final Log log = LogFactory.getLog(_SpringUtils.class);
    145
    146         if (log.isDebugEnabled())
    147         {
    148             log.debug("newProxy invoked for " + targetBeanName);
    149         }
    150
    151         if (!(beanFactory instanceof ConfigurableBeanFactory))
    152         {
    153             throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
    154         }
    155         ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
    156
    157         ScopedBeanTargetSource scopedTargetSource = new ScopedBeanTargetSource(
    158                 scope, conversationName, targetBeanName, objectFactory, beanFactory);
    159
    160         ProxyFactory pf = new ProxyFactory();
    161         pf.setProxyTargetClass(true);
    162         pf.setTargetSource(scopedTargetSource);
    163
    164         Class beanType = beanFactory.getType(targetBeanName);
    165         if (beanType == null)
    166         {
    167             throw new IllegalStateException("Cannot create scoped proxy for bean '" + targetBeanName +
    168                     "': Target type could not be determined at the time of proxy creation.");
    169         }
    170
    171         // Add an introduction that implements only the methods on ScopedObject.
    172         // Not sure if this is useful...
    173         ScopedObject scopedObject = new DefaultScopedObject(cbf, scopedTargetSource.getTargetBeanName());
    174         pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
    175
    176         // Add the AopInfrastructureBean marker to indicate that the scoped proxy
    177         // itself is not subject to auto-proxying! Only its target bean is.
    178         // Not sure if this is needed....
    179         pf.addInterface(AopInfrastructureBean.class);
    180
    181         return pf.getProxy(cbf.getBeanClassLoader());
    182     }
    183
    184     /**
    185      * Given a proxy object, return the underlying object that it currently refers to.
    186      * <p>
    187      * This method is currently experimental; it works for the current Spring implementation
    188      * of Orchestra but at the current time it is not known whether this functionality can
    189      * be implemented for all dependency-injection frameworks. If it does, then it might
    190      * later make sense to promote this up to the public Orchestra API.
    191      * <p>
    192      * Note that invoking this method will create the "target object" if it does not yet exist.
    193      * There is currently no way of testing whether this will happen, ie null will never
    194      * be returned from this method.
    195      *
    196      * @since 1.3
    197      */
    198     public static Object getTargetObject(Object proxy) throws Exception
    199     {
    200         Advised advised = (Advised) proxy;
    201         TargetSource targetSource = advised.getTargetSource();
    202         Object target = targetSource.getTarget();
    203
    204         // Possibly we could add a method on the ScopedBeanTargetSource class to test
    205         // whether the target bean exists. Then here we could cast TargetSource to
    206         // ScopedBeanTargetSource and return null if the target does not exist. This
    207         // might be useful, but let's leave that until someone actually has a use-case
    208         // for that functionality.
    209         return target;
    210     }
    211 }