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}