/*
 * Copyright 2002-2007 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.beans.factory.config;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;

/**
 * Simple template superclass for {@link FactoryBean} implementations that
 * creates a singleton or a prototype object, depending on a flag.
 *
 * <p>If the "singleton" flag is <code>true</code> (the default),
 * this class will create the object that it creates exactly once
 * on initialization and subsequently return said singleton instance
 * on all calls to the {@link #getObject()} method.
 * 
 * <p>Else, this class will create a new instance every time the
 * {@link #getObject()} method is invoked. Subclasses are responsible
 * for implementing the abstract {@link #createInstance()} template
 * method to actually create the object(s) to expose.
 *
 * @author Juergen Hoeller
 * @author Keith Donald
 * @since 1.0.2
 * @see #setSingleton(boolean)
 * @see #createInstance()
 */
public abstract class AbstractFactoryBean implements FactoryBean, InitializingBean, DisposableBean {

	/** Logger available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());

	private boolean singleton = true;

	private boolean initialized = false;

	private Object singletonInstance;

	private Object earlySingletonInstance;


	/**
	 * Set if a singleton should be created, or a new object
	 * on each request else. Default is <code>true</code>  (a singleton).
	 */
	public void setSingleton(boolean singleton) {
		this.singleton = singleton;
	}

	public boolean isSingleton() {
		return this.singleton;
	}

	/**
	 * Eagerly create the singleton instance, if necessary.
	 */
	public void afterPropertiesSet() throws Exception {
		if (isSingleton()) {
			this.initialized = true;
			this.singletonInstance = createInstance();
			this.earlySingletonInstance = null;
		}
	}


	/**
	 * Expose the singleton instance or create a new prototype instance.
	 * @see #createInstance()
	 * @see #getEarlySingletonInterfaces()
	 */
	public final Object getObject() throws Exception {
		if (isSingleton()) {
			return (!this.initialized ? getEarlySingletonInstance() : getSingletonInstance());
		}
		else {
			return createInstance();
		}
	}

	/**
	 * Determine an 'eager singleton' instance, exposed in case of a
	 * circular reference. Not called in a non-circular scenario.
	 */
	private Object getEarlySingletonInstance() throws Exception {
		Class[] ifcs = getEarlySingletonInterfaces();
		if (ifcs == null) {
			throw new FactoryBeanNotInitializedException(
					getClass().getName() + " does not support circular references");
		}
		if (this.earlySingletonInstance == null) {
			this.earlySingletonInstance = Proxy.newProxyInstance(getClass().getClassLoader(), ifcs,
				new InvocationHandler() {
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						try {
							return method.invoke(getSingletonInstance(), args);
						}
						catch (InvocationTargetException ex) {
							throw ex.getTargetException();
						}
					}
				});
		}
		return this.earlySingletonInstance;
	}

	/**
	 * Expose the singleton instance, if any.
	 */
	private Object getSingletonInstance() {
		return this.singletonInstance;
	}

	/**
	 * Destroy the singleton instance, if any.
	 * @see #destroyInstance(Object)
	 */
	public void destroy() throws Exception {
		if (isSingleton()) {
			destroyInstance(this.singletonInstance);
		}
	}


	/**
	 * This abstract method declaration shadows the method in the FactoryBean interface.
	 * This is necessary to make the <code>getEarlySingletonInterfaces</code> implementation
	 * in this class work on Sun's JDK 1.3 classic VM, which can't find the method
	 * when executing <code>getEarlySingletonInterfaces</code> else.
	 * @see org.springframework.beans.factory.FactoryBean#getObjectType()
	 * @see #getEarlySingletonInterfaces()
	 */
	public abstract Class getObjectType();

	/**
	 * Template method that subclasses must override to construct
	 * the object returned by this factory.
	 * <p>Invoked on initialization of this FactoryBean in case of
	 * a singleton; else, on each {@link #getObject()} call.
	 * @return the object returned by this factory
	 * @throws Exception if an exception occured during object creation
	 * @see #getObject()
	 */
	protected abstract Object createInstance() throws Exception;

	/**
	 * Return an array of interfaces that a singleton object exposed by this
	 * FactoryBean is supposed to implement, for use with an 'early singleton
	 * proxy' that will be exposed in case of a circular reference.
	 * <p>The default implementation returns this FactoryBean's object type,
	 * provided that it is an interface, or <code>null</code> else. The latter
	 * indicates that early singleton access is not supported by this FactoryBean.
	 * This will lead to a FactoryBeanNotInitializedException getting thrown.
	 * @return the interfaces to use for 'early singletons',
	 * or <code>null</code> to indicate a FactoryBeanNotInitializedException
	 * @see org.springframework.beans.factory.FactoryBeanNotInitializedException
	 */
	protected Class[] getEarlySingletonInterfaces() {
		Class type = getObjectType();
		return (type != null && type.isInterface() ? new Class[] {type} : null);
	}

	/**
	 * Callback for destroying a singleton instance. Subclasses may
	 * override this to destroy the previously created instance.
	 * <p>The default implementation is empty.
	 * @param instance the singleton instance, as returned by
	 * {@link #createInstance()}
	 * @throws Exception in case of shutdown errors
	 * @see #createInstance()
	 */
	protected void destroyInstance(Object instance) throws Exception {
	}

}

@@ -3091,17 +3091,16 @@
 return (
-!
 this.ini
@@ -3110,25 +3110,22 @@
 lized ? 
-getEarlyS
+this.s
 ingleton
@@ -3136,16 +3136,19 @@
 ance
-()
  : get
+Early
 Sing
@@ -4137,63 +4137,384 @@
 ance
-, if any.%0A%09 */%0A%09private Object getSingletonInstance() %7B
+ (for access through the 'early singleton' proxy).%0A%09 * @return the singleton instance that this FactoryBean holds%0A%09 * @throws IllegalStateException if the singleton instance is not initialized%0A%09 */%0A%09private Object getSingletonInstance() throws IllegalStateException %7B%0A%09%09if (!this.initialized) %7B%0A%09%09%09throw new IllegalStateException(%22Singleton instance not initialized yet%22);%0A%09%09%7D
 %0A%09%09r

throw IllegalStateException if 'early singleton' proxy methods get invoked too early