/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.scope;

import java.lang.reflect.Modifier;

import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.aop.target.PrototypeTargetSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.util.ClassUtils;

/**
 * Convenient proxy factory bean for scoped objects.
 * 
 * <p>Proxies created using this factory bean are thread-safe singletons,
 * and may be injected, with transparent scoping behavior.
 *
 * <p>Proxies returned by this class implement the {@link ScopedObject}
 * interface. This presently allows removing corresponding object from
 * the scope, seamlessly creating a new instance in the scope on next
 * access.
 * 
 * <p>Please note that the proxies created by this factory are
 * <i>class-based</i> proxies.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 2.0
 */
public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean, BeanFactoryAware {

	/** The TargetSource that manages scoping */
	private final PrototypeTargetSource scopedTargetSource = new PrototypeTargetSource();

	private String targetBeanName;

	/** The cached singleton proxy */
	private Object proxy;


	/**
	 * Create a new ScopedProxyFactoryBean instance.
	 */
	public ScopedProxyFactoryBean() {
		setProxyTargetClass(true);
	}


	/**
	 * Sets the name of the bean that is to be scoped.
	 * @param targetBeanName the name of the bean that is to be scoped.
	 */
	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = targetBeanName;
		this.scopedTargetSource.setTargetBeanName(targetBeanName);
	}

	public void setBeanFactory(BeanFactory beanFactory) {
		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

		this.scopedTargetSource.setBeanFactory(beanFactory);

		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		pf.setTargetSource(this.scopedTargetSource);

		Class beanType = beanFactory.getType(this.targetBeanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
					"': Target type could not be determined at the time of proxy creation.");
		}
		if (Modifier.isPrivate(beanType.getModifiers()) || beanType.isInterface() || !isProxyTargetClass()) {
			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType));
		}

		// Add an introduction that implements only the methods on ScopedObject.
		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

		this.proxy = pf.getProxy();
	}


	public Object getObject() {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
		return this.proxy;
	}

	public Class getObjectType() {
		if (this.proxy != null) {
			return this.proxy.getClass();
		}
		if (this.scopedTargetSource != null) {
			return this.scopedTargetSource.getTargetClass();
		}
		return null;
	}

	public boolean isSingleton() {
		return true;
	}

}

@@ -21,9 +21,9 @@
 -200
-6
+7
  the
@@ -1430,17 +1430,16 @@
 ngletons
-,
 %0A * and 
@@ -1453,16 +1453,36 @@
 injected
+ into shared objects
 , with t
@@ -1584,19 +1584,16 @@
 dObject%7D
-%0A *
  interfa
@@ -1595,16 +1595,19 @@
 terface.
+%0A *
  This pr
@@ -1621,16 +1621,20 @@
  allows 
+for 
 removing
@@ -1634,16 +1634,20 @@
 emoving 
+the 
 correspo
@@ -1663,19 +1663,16 @@
 ect from
-%0A *
  the sco
@@ -1674,16 +1674,19 @@
 e scope,
+%0A *
  seamles
@@ -1733,19 +1733,16 @@
  on next
-%0A *
  access.
@@ -1838,16 +1838,115 @@
  proxies
+ by default. This can be customized%0A * through switching the %22proxyTargetClass%22 property to %22false%22
 .%0A *%0A * 
@@ -2006,16 +2006,45 @@
 nce 2.0%0A
+ * @see #setProxyTargetClass%0A
  */%0Apubl
@@ -2273,16 +2273,52 @@
 rce();%0A%0A
+%09/** The name of the target bean */%0A
 %09private
@@ -2547,78 +2547,8 @@
  Set
-s the name of the bean that is to be scoped.%0A%09 * @param targetBeanName
  the
@@ -3483,51 +3483,29 @@
 if (
-Modifier.isPrivate(beanType.getModifier
+!isProxyTargetClas
 s()
-)
  %7C%7C 
@@ -3534,30 +3534,52 @@
  %7C%7C 
-!isProxyTargetClas
+Modifier.isPrivate(beanType.getModifier
 s())
+)
  %7B%0A%09
@@ -3927,16 +3927,40 @@
 etProxy(
+cbf.getBeanClassLoader()
 );%0A%09%7D%0A%0A%0A

use the BeanFactory's bean ClassLoader for generating scoped proxies