/*
 * Copyright 2002-2008 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 test.feature.lifecycle.factorybean;

import static org.junit.Assert.assertTrue;

import org.junit.Test;

import org.springframework.beans.factory.FactoryBean;

import org.springframework.config.java.annotation.Bean;
import org.springframework.config.java.annotation.Configuration;
import org.springframework.config.java.context.JavaConfigApplicationContext;
import org.springframework.config.java.support.ConfigurationSupport;

import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;

import test.common.beans.TestBean;


/**
 * Integration tests for {@link FactoryBean} support.
 *
 * @author  Chris Beams
 * @author  Rod Johnson
 */
public class FactoryBeanTests {

    @Test
    public void testFactoryBean() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ContainsFactoryBean.class);
        assertTrue("Factory bean must return created type", ctx.getBean("factoryBean") instanceof TestBean);
    }

    @Configuration
    public static class ContainsFactoryBean {
        @Bean
        public DummyFactory factoryBean() { return new DummyFactory(); }
    }

    /**
     * First solution to Kuvera's FactoryBean question posted at
     * http://forum.springframework.org/showthread.php?t=56101.
     *
     * <p>This approach is the closest in semantics to XML use of FactoryBeans. It instantiates and
     * configures the FactoryBean in question (HttpInvokerProxyFactoryBean) and returns that FB
     * instance directly from the #remoteService() method.</p>
     *
     * <p>From the remoteServiceQueue method, the approach is to request that factory-bean-created
     * remoteService bean through a regular getBean() call. Here we're extending
     * {@link ConfigurationSupport}, so we get access to it's #getBean() method.</p>
     *
     * @see  #testForumIssueVariation2() for the more preferred approach.
     */
    @Test
    public void testForumIssueVariation1() { new JavaConfigApplicationContext(Config1.class); }

    @Configuration
    static class Config1 extends ConfigurationSupport {
        @Bean
        public HttpInvokerProxyFactoryBean remoteService() {
            HttpInvokerProxyFactoryBean fb = new HttpInvokerProxyFactoryBean();
            fb.setServiceUrl("http://localhost/batchService");
            fb.setServiceInterface(IBatchService.class);
            return fb;
        }

        @Bean
        public IServiceQueue remoteServiceQueue() {
            IServiceQueue o = new SwtServiceQueue();
            o.setService(getBean(IBatchService.class, "remoteService"));
            return o;
        }
    }

    /**
     * Second (and preferred) solution to Kuvera's FactoryBean question posted at
     * http://forum.springframework.org/showthread.php?t=56101.
     *
     * <p>In this approach we extend {@link ConfigurationSupport} as above, but encapsulate the fact
     * that a FactoryBean is being used within the remoteService() method. By calling
     * ConfigurationSupport's getConfigured() method, all lifecycle callbacks are invoked on the
     * FactoryBean.</p>
     *
     * <p>This is the cleaner of the two approaches, and thus recommended.</p>
     */
    @Test
    public void testForumIssueVariation2() { new JavaConfigApplicationContext(Config2.class); }

    @Configuration
    static class Config2 extends ConfigurationSupport {
        @Bean
        public IBatchService remoteService() {
            HttpInvokerProxyFactoryBean fb = new HttpInvokerProxyFactoryBean();
            fb.setServiceUrl("http://localhost/batchService");
            fb.setServiceInterface(IBatchService.class);

            // Two options here:
            // 1) directly invoke the factory bean's #afterPropertiesSet method:
            /* o.afterPropertiesSet(); */

            // 2) extend ConfigurationSupport and call #getConfigured() on the FactoryBean
            // instance.  This is the 'safest' route, as it will be sure to call any and all
            // callbacks required by the object:
            this.getConfigured(fb);

            // return the factory-created object directly.
            return (IBatchService) fb.getObject();
        }

        @Bean
        public IServiceQueue remoteServiceQueue() {
            IServiceQueue o = new SwtServiceQueue();
            o.setService(remoteService());
            return o;
        }
    }

    static interface IServiceQueue {
        void setService(IBatchService remoteService);
    }

    static class SwtServiceQueue implements IServiceQueue {
        public void setService(IBatchService remoteService) { }
    }

    static interface IBatchService { }

}
