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 }