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 }