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 015package org.apache.tapestry5.internal.dynamic; 016 017import org.apache.tapestry5.internal.services.PageSource; 018import org.apache.tapestry5.internal.services.TemplateParser; 019import org.apache.tapestry5.ioc.Resource; 020import org.apache.tapestry5.ioc.annotations.PostInjection; 021import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 022import org.apache.tapestry5.ioc.internal.util.URLChangeTracker; 023import org.apache.tapestry5.ioc.services.ClasspathURLConverter; 024import org.apache.tapestry5.services.BindingSource; 025import org.apache.tapestry5.services.UpdateListener; 026import org.apache.tapestry5.services.UpdateListenerHub; 027import org.apache.tapestry5.services.dynamic.DynamicTemplate; 028import org.apache.tapestry5.services.dynamic.DynamicTemplateParser; 029 030import java.util.Map; 031 032public 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}