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

import static org.junit.Assert.*;

import static test.common.util.Assert.getNonInternalBeanDefinitionCount;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import org.junit.Test;

import org.springframework.aop.support.AopUtils;

import org.springframework.beans.factory.annotation.Autowire;

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.plugin.aop.AspectJAutoProxy;

import test.common.beans.DependsOnTestBean;
import test.common.beans.TestBean;

import test.feature.aop.AspectJConfigurationProcessorTests.AroundAdviceWithNamedPointcut;


/**
 * System tests for autowiring when AOP is in the mix.
 *
 * @author  Chris Beams
 */
public class AspectAutowiringTests {

    @Test
    public void testAutowiringOnProxiedBean() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(AdvisedAutowiring.class);
        Husband husband = ctx.getBean(Husband.class, "husband");
        assertTrue(AopUtils.isAopProxy(husband));
        assertNotNull("Advised object should have still been autowired", husband.getWife());
    }


    @Test
    public void testAutowiringOnNonProxiedBean() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(NonAdvisedAutowiring.class);
        Husband husband = ctx.getBean(Husband.class, "husband");
        assertFalse(AopUtils.isAopProxy(husband));
        assertNotNull("Non-advised object should have been autowired", husband.getWife());
    }

    @Test
    public void testAutowireOnProxiedBeanDefinition() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ProxiesDotb.class);

        ProxiesDotb.count = 0;
        DependsOnTestBean sarah = (DependsOnTestBean) ctx.getBean("sarah");
        assertTrue("bean 'sarah' should have been an AOP proxy", AopUtils.isAopProxy(sarah));
        assertTrue("bean 'sarah' should have been an CGLIB proxy", AopUtils.isCglibProxy(sarah));
        assertNotNull("autowiring did not complete successfully", sarah.getTestBean());

        assertEquals(1, ProxiesDotb.count);
    }


    @Test
    public void testAutowireOnBeanDefinition() {
        JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(AroundAdviceWithNamedPointcut.class);

        ctx.getBean("dotb");
        DependsOnTestBean dotb1 = (DependsOnTestBean) ctx.getBean("dotb");
        DependsOnTestBean dotb2 = (DependsOnTestBean) ctx.getBean("dotb");
        assertSame(dotb1, dotb2);
        assertSame(dotb1.getTestBean(), dotb2.getTestBean());
        assertNotNull("autowiring failed", dotb1.getTestBean());
    }

    @Test
    public void testBeanDefinitionCount() throws Exception {
        // 2 @Bean + 1 @Configuration + 1 internalAutoProxyCreator
        assertEquals(4, getNonInternalBeanDefinitionCount(new JavaConfigApplicationContext(AdvisedAutowiring.class)));
    }

    static interface Husband {
        Wife getWife();
    }

    @AspectJAutoProxy(proxyTargetClass=true)
    @Aspect
    @Configuration
    static class AdvisedAutowiring {
        @Bean(autowire = Autowire.BY_TYPE)
        public Husband husband() { return new HusbandImpl(); }

        @Bean
        public Wife wife() { return new Wife(); }

        @Before("execution(* getWife())")
        protected void log() { /* nothing */
        }
    }

    @Configuration
    public static class NonAdvisedAutowiring {
        @Bean(autowire = Autowire.BY_TYPE)
        public Husband husband() { return new HusbandImpl(); }

        @Bean
        public Wife wife() { return new Wife(); }
    }

    static class Wife { }

    static class HusbandImpl implements Husband {
        private Wife wife;

        public Wife getWife() { return wife; }

        public void setWife(Wife wife) { this.wife = wife; }
    }

    @AspectJAutoProxy(proxyTargetClass=true)
    @Aspect
    @Configuration
    static class ProxiesDotb {
        public static int count = 0;

        @Bean
        public TestBean adrian() { return new TestBean("adrian", 34); }

        @Bean(autowire = Autowire.BY_TYPE)
        public DependsOnTestBean sarah() { return new DependsOnTestBean(); }

        @Before("execution(* getTestBean())")
        public void println() { ++count; }
    }

}
