001// Copyright 2008-2013 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.spring.modules;
016
017import org.apache.tapestry5.commons.MappedConfiguration;
018import org.apache.tapestry5.commons.OrderedConfiguration;
019import org.apache.tapestry5.http.services.ApplicationInitializer;
020import org.apache.tapestry5.http.services.ApplicationInitializerFilter;
021import org.apache.tapestry5.http.services.Context;
022import org.apache.tapestry5.internal.spring.SymbolBeanFactoryPostProcessor;
023import org.apache.tapestry5.ioc.annotations.Contribute;
024import org.apache.tapestry5.ioc.annotations.ImportModule;
025import org.apache.tapestry5.ioc.annotations.Marker;
026import org.apache.tapestry5.ioc.annotations.Primary;
027import org.apache.tapestry5.ioc.services.ChainBuilder;
028import org.apache.tapestry5.ioc.services.FactoryDefaults;
029import org.apache.tapestry5.ioc.services.SymbolProvider;
030import org.apache.tapestry5.ioc.services.SymbolSource;
031import org.apache.tapestry5.modules.TapestryModule;
032import org.apache.tapestry5.spring.ApplicationContextCustomizer;
033import org.apache.tapestry5.spring.SpringConstants;
034import org.slf4j.Logger;
035import org.springframework.context.ApplicationContext;
036import org.springframework.core.SpringVersion;
037import org.springframework.web.context.ConfigurableWebApplicationContext;
038
039import javax.servlet.ServletContext;
040import java.util.List;
041
042/**
043 * Module for Tapestry/Spring Integration. This module exists to force the load of the Spring ApplicationContext as part
044 * of Tapestry application initialization.
045 *
046 * @since 5.1.0.0
047 */
048@ImportModule(TapestryModule.class)
049public class SpringModule
050{
051    private final Logger logger;
052
053    public SpringModule(Logger logger)
054    {
055        this.logger = logger;
056    }
057
058    @Contribute(ApplicationInitializer.class)
059    @Primary
060    public void reportSpringContextDetailsAtStartup(
061            OrderedConfiguration<ApplicationInitializerFilter> configuration, final ApplicationContext springContext)
062    {
063        ApplicationInitializerFilter filter = new ApplicationInitializerFilter()
064        {
065            @Override
066            public void initializeApplication(Context context, ApplicationInitializer initializer)
067            {
068                logger.info(String.format("Spring version %s with %,d defined beans.",
069                        SpringVersion.getVersion(),
070                        springContext.getBeanDefinitionCount()));
071
072                initializer.initializeApplication(context);
073            }
074        };
075
076        configuration.add("SpringContextInitialization", filter);
077    }
078
079    @Contribute(SymbolProvider.class)
080    @FactoryDefaults
081    public static void defaultExternalSpringContextOff(MappedConfiguration<String, Object> configuration)
082    {
083        configuration.add(SpringConstants.USE_EXTERNAL_SPRING_CONTEXT, false);
084    }
085
086    /**
087     * Defines a chain-of-command for handling application context customization. This allows the Spring context to be
088     * configured before it is initially {@linkplain org.springframework.context.ConfigurableApplicationContext#refresh()
089     * refreshed}.
090     */
091    @Marker(Primary.class)
092    public static ApplicationContextCustomizer buildApplicationContextCustomizer(
093            List<ApplicationContextCustomizer> configuration,
094            ChainBuilder builder)
095    {
096        return builder.build(ApplicationContextCustomizer.class, configuration);
097    }
098
099    @Contribute(ApplicationContextCustomizer.class)
100    public static void addSymbolSourceAsPropertyCustomizerForSpringBeans(
101            OrderedConfiguration<ApplicationContextCustomizer> configuration,
102            final SymbolSource symbolSource)
103    {
104        ApplicationContextCustomizer beanFactoryPostProcessorCustomizer = new ApplicationContextCustomizer()
105        {
106
107            @Override
108            public void customizeApplicationContext(ServletContext servletContext,
109                                                    ConfigurableWebApplicationContext applicationContext)
110            {
111                applicationContext.addBeanFactoryPostProcessor(new SymbolBeanFactoryPostProcessor(symbolSource));
112
113            }
114        };
115
116        configuration.add("BeanFactoryPostProcessorCustomizer", beanFactoryPostProcessorCustomizer);
117    }
118}