/*
 * 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;


	/**
	 * Creates a new instance of the {@link ScopedProxyFactoryBean} class.
	 */
	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);
		boolean isPrivate = Modifier.isPrivate(beanType.getModifiers());
		if (isPrivate || 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;
	}

}

@@ -2233,39 +2233,15 @@
 eate
-s
  a new 
-instance of the %7B@link 
 Scop
@@ -2262,15 +2262,17 @@
 Bean
-%7D class
+ instance
 .%0A%09 
@@ -3153,28 +3153,220 @@
 ;%0A%09%09
-boolean isPrivate = 
+if (beanType == null) %7B%0A%09%09%09throw new IllegalStateException(%22Cannot create scoped proxy for bean '%22 + this.targetBeanName +%0A%09%09%09%09%09%22': Target type could not be determined at the time of proxy creation.%22);%0A%09%09%7D%0A%09%09if (
 Modi
@@ -3408,25 +3408,8 @@
 s())
-;%0A%09%09if (isPrivate
  %7C%7C 

throw IllegalStateException if it couldn't determine the type of the target bean (avoid NPE)