    001// Copyright 2009 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.services;
    016
    017import java.util.HashSet;
    018import java.util.Set;
    019
    020import org.apache.tapestry5.SymbolConstants;
    021import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType;
    022import org.apache.tapestry5.ioc.annotations.Symbol;
    023import org.apache.tapestry5.model.ComponentModel;
    024import org.apache.tapestry5.services.ComponentClassResolver;
    025
    026public class ComponentModelSourceImpl implements ComponentModelSource
    027{
    028    private final ComponentClassResolver resolver;
    029
    030    private final ComponentInstantiatorSource source;
    031
    032    private final ComponentDependencyRegistry componentDependencyRegistry;
    033
    034    private final PageSource pageSource;
    035
    036    private final boolean multipleClassLoaders;
    037
    038    private final static ThreadLocal<Set<String>> CALL_STACK =
    039            ThreadLocal.withInitial(HashSet::new);
    040
    041    public ComponentModelSourceImpl(ComponentClassResolver resolver, ComponentInstantiatorSource source,
    042            ComponentDependencyRegistry componentDependencyRegistry,
    043            PageSource pageSource,
    044            @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode,
    045            @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) boolean multipleClassLoaders)
    046    {
    047        this.resolver = resolver;
    048        this.source = source;
    049        this.componentDependencyRegistry = componentDependencyRegistry;
    050        this.pageSource = pageSource;
    051        this.multipleClassLoaders = !productionMode && multipleClassLoaders;
    052    }
    053
    054    public ComponentModel getModel(String componentClassName)
    055    {
    056        if (multipleClassLoaders && isPage(componentClassName))
    057        {
    058            final Set<String> superclasses = componentDependencyRegistry.getDependencies(
    059                    componentClassName, DependencyType.SUPERCLASS);
    060
    061            if (!superclasses.isEmpty())
    062            {
    063                final String superclass = superclasses.iterator().next();
    064                final Set<String> callStack = CALL_STACK.get();
    065                if (!callStack.contains(superclass) && isPage(superclass))
    066                {
    067                    callStack.add(superclass);
    068                    getModel(superclass);
    069                    try
    070                    {
    071                        pageSource.getPage(resolver.getLogicalName(componentClassName));
    072                    }
    073                    catch (IllegalStateException e)
    074                    {
    075                        // This can be thrown in PageSourceImpl in case an
    076                        // infinite method call recursion is detected. In
    077                        // that case, the page instance is already created,
    078                        // so the objective of the line above is already
    079                        // fulfilled and we can safely ignore the exception
    080                    }
    081                    callStack.remove(superclass);
    082                }
    083            }
    084        }
    085        return source.getInstantiator(componentClassName).getModel();
    086    }
    087
    088    public ComponentModel getPageModel(String pageName)
    089    {
    090        return getModel(resolver.resolvePageNameToClassName(pageName));
    091    }
    092
    093    private boolean isPage(String componentClassName)
    094    {
    095        return componentClassName.contains(".pages.");
    096    }
    097}