001    // Copyright 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    
015    package org.apache.tapestry5.internal.dynamic;
016    
017    import org.apache.tapestry5.internal.services.PageSource;
018    import org.apache.tapestry5.internal.services.TemplateParser;
019    import org.apache.tapestry5.ioc.Resource;
020    import org.apache.tapestry5.ioc.annotations.PostInjection;
021    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
022    import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
023    import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
024    import org.apache.tapestry5.services.BindingSource;
025    import org.apache.tapestry5.services.UpdateListener;
026    import org.apache.tapestry5.services.UpdateListenerHub;
027    import org.apache.tapestry5.services.dynamic.DynamicTemplate;
028    import org.apache.tapestry5.services.dynamic.DynamicTemplateParser;
029    
030    import java.util.Map;
031    
032    public class DynamicTemplateParserImpl implements DynamicTemplateParser, UpdateListener
033    {
034        private final Map<Resource, DynamicTemplate> cache = CollectionFactory.newConcurrentMap();
035    
036        private final BindingSource bindingSource;
037    
038        private final PageSource pageSource;
039    
040        private final URLChangeTracker tracker;
041    
042        private final TemplateParser componentTemplateParser;
043    
044        public DynamicTemplateParserImpl(ClasspathURLConverter converter, BindingSource bindingSource, PageSource pageSource, TemplateParser componentTemplateParser)
045        {
046            this.bindingSource = bindingSource;
047            this.pageSource = pageSource;
048            this.componentTemplateParser = componentTemplateParser;
049    
050            tracker = new URLChangeTracker(converter);
051        }
052    
053        @PostInjection
054        public void registerAsUpdateListener(UpdateListenerHub hub)
055        {
056            hub.addUpdateListener(this);
057        }
058    
059        public DynamicTemplate parseTemplate(Resource resource)
060        {
061            DynamicTemplate result = cache.get(resource);
062    
063            if (result == null)
064            {
065                result = doParse(resource);
066                cache.put(resource, result);
067    
068                tracker.add(resource.toURL());
069            }
070    
071            return result;
072        }
073    
074        private DynamicTemplate doParse(Resource resource)
075        {
076            return new DynamicTemplateSaxParser(resource, bindingSource, componentTemplateParser.getDTDURLMappings()).parse();
077        }
078    
079        public void checkForUpdates()
080        {
081            if (tracker.containsChanges())
082            {
083                tracker.clear();
084                cache.clear();
085    
086                // A typical case is that a "context:" or "asset:" binding is used with the Dynamic component's template
087                // parameter. This causes the Asset to be converted to a Resource and parsed. However, those are invariant
088                // bindings, so even if it is discovered that the underlying file has changed, the parsed template
089                // is still cached inside the component. Clearing the page pool forces the page instance to be
090                // rebuilt, which is a crude way of clearing out that data. Other alternatives exist, such as
091                // yielding up a proxy to the DynamicTemplate that is more change-aware.
092    
093                pageSource.clearCache();
094            }
095        }
096    
097    }