"Fossies" - the Fresh Open Source Software Archive

Member "apidocs/src-html/org/apache/tapestry5/internal/spring/SpringModuleDef.html" (9 Sep 2020, 37145 Bytes) of package /linux/www/apache-tapestry-5.6.1-apidocs.zip:


Caution: In this restricted "Fossies" environment the current HTML page may not be correctly presentated and may have some non-functional links. You can here alternatively try to browse the pure source code or just view or download the uninterpreted raw source code. If the rendering is insufficient you may try to find and view the page on the project site itself.

001// Copyright 2007, 2008, 2009, 2010, 2011 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.internal.spring;
016
017import org.apache.tapestry5.internal.AbstractContributionDef;
018import org.apache.tapestry5.ioc.*;
019import org.apache.tapestry5.ioc.annotations.Primary;
020import org.apache.tapestry5.ioc.def.ContributionDef;
021import org.apache.tapestry5.ioc.def.DecoratorDef;
022import org.apache.tapestry5.ioc.def.ModuleDef;
023import org.apache.tapestry5.ioc.def.ServiceDef;
024import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
025import org.apache.tapestry5.ioc.internal.util.InternalUtils;
026import org.apache.tapestry5.ioc.services.RegistryShutdownHub;
027import org.apache.tapestry5.plastic.PlasticUtils;
028import org.apache.tapestry5.spring.ApplicationContextCustomizer;
029import org.apache.tapestry5.spring.SpringConstants;
030import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
031import org.springframework.context.ApplicationContext;
032import org.springframework.context.ConfigurableApplicationContext;
033import org.springframework.core.SpringVersion;
034import org.springframework.web.context.ConfigurableWebApplicationContext;
035import org.springframework.web.context.WebApplicationContext;
036import org.springframework.web.context.support.WebApplicationContextUtils;
037
038import javax.servlet.ServletContext;
039import java.util.Collections;
040import java.util.Map;
041import java.util.Set;
042import java.util.concurrent.atomic.AtomicBoolean;
043
044/**
045 * A wrapper that converts a Spring {@link ApplicationContext} into a set of service definitions,
046 * compatible with
047 * Tapestry 5 IoC, for the beans defined in the context, as well as the context itself.
048 */
049public class SpringModuleDef implements ModuleDef
050{
051    static final String SERVICE_ID = "ApplicationContext";
052
053    private final Map<String, ServiceDef> services = CollectionFactory.newMap();
054
055    private final boolean compatibilityMode;
056
057    private final AtomicBoolean applicationContextCreated = new AtomicBoolean(false);
058
059    private final ServletContext servletContext;
060
061    private ApplicationContext locateExternalContext()
062    {
063        ApplicationContext context = locateApplicationContext(servletContext);
064
065        applicationContextCreated.set(true);
066
067        return context;
068    }
069
070    /**
071     * Invoked to obtain the Spring ApplicationContext, presumably stored in the ServletContext.
072     * This method is only used in Tapestry 5.0 compatibility mode (in Tapestry 5.1 and above,
073     * the default is for Tapestry to <em>create</em> the ApplicationContext).
074     *
075     * @param servletContext used to locate the ApplicationContext
076     * @return the ApplicationContext itself
077     * @throws RuntimeException if the ApplicationContext could not be located or is otherwise invalid
078     * @since 5.2.0
079     */
080    protected ApplicationContext locateApplicationContext(ServletContext servletContext)
081    {
082        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
083
084        if (context == null)
085        {
086            throw new NullPointerException(
087                    String
088                            .format(
089                                    "No Spring ApplicationContext stored in the ServletContext as attribute '%s'. "
090                                            + "You should either re-enable Tapestry as the creator of the ApplicationContext, or "
091                                            + "add a Spring ContextLoaderListener to web.xml.",
092                                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
093        }
094
095        return context;
096    }
097
098    public SpringModuleDef(ServletContext servletContext)
099    {
100        this.servletContext = servletContext;
101
102        compatibilityMode = Boolean.parseBoolean(servletContext
103                .getInitParameter(SpringConstants.USE_EXTERNAL_SPRING_CONTEXT));
104
105        final ApplicationContext externalContext = compatibilityMode ? locateExternalContext()
106                : null;
107
108        if (compatibilityMode)
109            addServiceDefsForSpringBeans(externalContext);
110
111        ServiceDef applicationContextServiceDef = new ServiceDef()
112        {
113            @Override
114            public ObjectCreator createServiceCreator(final ServiceBuilderResources resources)
115            {
116                if (compatibilityMode)
117                    return new StaticObjectCreator(externalContext,
118                            "externally configured Spring ApplicationContext");
119
120                ApplicationContextCustomizer customizer = resources.getService(
121                        "ApplicationContextCustomizer", ApplicationContextCustomizer.class);
122
123                return constructObjectCreatorForApplicationContext(resources, customizer);
124            }
125
126            @Override
127            public String getServiceId()
128            {
129                return SERVICE_ID;
130            }
131
132            @Override
133            public Set<Class> getMarkers()
134            {
135                return Collections.emptySet();
136            }
137
138            @Override
139            public Class getServiceInterface()
140            {
141                return compatibilityMode ? externalContext.getClass()
142                        : ConfigurableWebApplicationContext.class;
143            }
144
145            @Override
146            public String getServiceScope()
147            {
148                return ScopeConstants.DEFAULT;
149            }
150
151            @Override
152            public boolean isEagerLoad()
153            {
154                return false;
155            }
156        };
157
158        services.put(SERVICE_ID, applicationContextServiceDef);
159    }
160
161    private void addServiceDefsForSpringBeans(ApplicationContext context)
162    {
163        ConfigurableListableBeanFactory beanFactory = null;
164        if (context instanceof ConfigurableApplicationContext)
165        {
166            beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory();
167        }
168
169        for (final String beanName : context.getBeanDefinitionNames())
170        {
171            boolean isAbstract = false;
172            if (beanFactory != null)
173            {
174                isAbstract = beanFactory.getBeanDefinition(beanName).isAbstract();
175            }
176
177            if (!isAbstract)
178            {
179                String trueName = beanName.startsWith("&") ? beanName.substring(1) : beanName;
180
181                services.put(trueName, new SpringBeanServiceDef(trueName, context));
182            }
183        }
184    }
185
186    private ObjectCreator constructObjectCreatorForApplicationContext(
187            final ServiceBuilderResources resources, @Primary
188    ApplicationContextCustomizer customizer)
189    {
190        final CustomizingContextLoader loader = new CustomizingContextLoader(customizer);
191
192        final Runnable shutdownListener = new Runnable()
193        {
194            @Override
195            public void run()
196            {
197                loader.closeWebApplicationContext(servletContext);
198            }
199        };
200
201        final RegistryShutdownHub shutdownHub = resources.getService(RegistryShutdownHub.class);
202
203        return new ObjectCreator()
204        {
205            @Override
206            public Object createObject()
207            {
208                return resources.getTracker().invoke(
209                        "Creating Spring ApplicationContext via ContextLoader",
210                        new Invokable<Object>()
211                        {
212                            @Override
213                            public Object invoke()
214                            {
215                                resources.getLogger().info(
216                                        String.format("Starting Spring (version %s)", SpringVersion
217                                                .getVersion()));
218
219                                WebApplicationContext context = loader
220                                        .initWebApplicationContext(servletContext);
221
222                                shutdownHub.addRegistryShutdownListener(shutdownListener);
223
224                                applicationContextCreated.set(true);
225
226                                return context;
227                            }
228                        });
229            }
230
231            @Override
232            public String toString()
233            {
234                return "ObjectCreator for Spring ApplicationContext";
235            }
236        };
237    }
238
239    @Override
240    public Class getBuilderClass()
241    {
242        return null;
243    }
244
245    /**
246     * Returns a contribution, "SpringBean", to the MasterObjectProvider service. It is ordered
247     * after the built-in
248     * contributions.
249     */
250    @Override
251    public Set<ContributionDef> getContributionDefs()
252    {
253        ContributionDef def = createContributionToMasterObjectProvider();
254
255        return CollectionFactory.newSet(def);
256    }
257
258    private ContributionDef createContributionToMasterObjectProvider()
259    {
260
261        ContributionDef def = new AbstractContributionDef()
262        {
263            @Override
264            public String getServiceId()
265            {
266                return "MasterObjectProvider";
267            }
268
269            @Override
270            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
271                                   OrderedConfiguration configuration)
272            {
273                final OperationTracker tracker = resources.getTracker();
274
275                final ApplicationContext context = resources.getService(SERVICE_ID,
276                        ApplicationContext.class);
277
278                final ObjectProvider springBeanProvider = new ObjectProvider()
279                {
280                    @Override
281                    public <T> T provide(Class<T> objectType,
282                                         AnnotationProvider annotationProvider, ObjectLocator locator)
283                    {
284
285                        Map beanMap = context.getBeansOfType(objectType);
286
287                        switch (beanMap.size())
288                        {
289                            case 0:
290                                return null;
291
292                            case 1:
293
294                                Object bean = beanMap.values().iterator().next();
295
296                                return objectType.cast(bean);
297
298                            default:
299
300                                String message = String
301                                        .format(
302                                                "Spring context contains %d beans assignable to type %s: %s.",
303                                                beanMap.size(), PlasticUtils.toTypeName(objectType), InternalUtils
304                                                .joinSorted(beanMap.keySet()));
305
306                                throw new IllegalArgumentException(message);
307                        }
308                    }
309                };
310
311                final ObjectProvider springBeanProviderInvoker = new ObjectProvider()
312                {
313                    @Override
314                    public <T> T provide(final Class<T> objectType,
315                                         final AnnotationProvider annotationProvider, final ObjectLocator locator)
316                    {
317                        return tracker.invoke(
318                                "Resolving dependency by searching Spring ApplicationContext",
319                                new Invokable<T>()
320                                {
321                                    @Override
322                                    public T invoke()
323                                    {
324                                        return springBeanProvider.provide(objectType,
325                                                annotationProvider, locator);
326                                    }
327                                });
328                    }
329                };
330
331                ObjectProvider outerCheck = new ObjectProvider()
332                {
333                    @Override
334                    public <T> T provide(Class<T> objectType,
335                                         AnnotationProvider annotationProvider, ObjectLocator locator)
336                    {
337                        // I think the following line is the only reason we put the
338                        // SpringBeanProvider here,
339                        // rather than in SpringModule.
340
341                        if (!applicationContextCreated.get())
342                            return null;
343
344                        return springBeanProviderInvoker.provide(objectType, annotationProvider,
345                                locator);
346                    }
347                };
348
349
350                configuration.add("SpringBean", outerCheck, "after:AnnotationBasedContributions", "after:ServiceOverride");
351            }
352        };
353
354        return def;
355    }
356
357    /**
358     * Returns an empty set.
359     */
360    @Override
361    public Set<DecoratorDef> getDecoratorDefs()
362    {
363        return Collections.emptySet();
364    }
365
366    @Override
367    public String getLoggerName()
368    {
369        return SpringModuleDef.class.getName();
370    }
371
372    @Override
373    public ServiceDef getServiceDef(String serviceId)
374    {
375        return services.get(serviceId);
376    }
377
378    @Override
379    public Set<String> getServiceIds()
380    {
381        return services.keySet();
382    }
383}