001: /*
002: * Created on Sep 19, 2005
003: */
004: package uk.org.ponder.rsf.templateresolver;
005:
006: import java.io.InputStream;
007: import java.util.List;
008: import java.util.Map;
009:
010: import org.springframework.core.io.ResourceLoader;
011:
012: import uk.org.ponder.reflect.ReflectiveCache;
013: import uk.org.ponder.rsf.flow.errors.SilentRedirectException;
014: import uk.org.ponder.rsf.template.TPIAggregator;
015: import uk.org.ponder.rsf.template.XMLCompositeViewTemplate;
016: import uk.org.ponder.rsf.template.XMLViewTemplate;
017: import uk.org.ponder.rsf.template.XMLViewTemplateParser;
018: import uk.org.ponder.rsf.view.ViewTemplate;
019: import uk.org.ponder.rsf.viewstate.ViewParameters;
020: import uk.org.ponder.springutil.CachingInputStreamSource;
021: import uk.org.ponder.stringutil.StringList;
022: import uk.org.ponder.util.Logger;
023: import uk.org.ponder.util.UniversalRuntimeException;
024:
025: /**
026: * A basic template resolver accepting a TemplateExtensionInferrer and a
027: * TemplateResolverStrategy to load a view template.
028: *
029: * @author Antranig Basman (antranig@caret.cam.ac.uk)
030: *
031: */
032: public class BasicTemplateResolver implements TemplateResolver {
033:
034: private TemplateExtensionInferrer tei;
035: private int cachesecs;
036: private TPIAggregator aggregator;
037: private List strategies;
038:
039: public void setResourceLoader(ResourceLoader resourceLoader) {
040: cachingiis = new CachingInputStreamSource(resourceLoader,
041: cachesecs);
042: }
043:
044: /**
045: * Set the lag in seconds at which the filesystem will be polled for changes
046: * in the view template. If this value is 0 or the resource is not a
047: * filesystem resource, it will always be reloaded.
048: */
049: public void setCacheSeconds(int cachesecs) {
050: this .cachesecs = cachesecs;
051: }
052:
053: public void setTemplateExtensionInferrer(
054: TemplateExtensionInferrer tei) {
055: this .tei = tei;
056: }
057:
058: public void setTemplateResolverStrategies(List strategies) {
059: this .strategies = strategies;
060: }
061:
062: public void setTPIAggregator(TPIAggregator aggregator) {
063: this .aggregator = aggregator;
064: }
065:
066: public void setReflectiveCache(ReflectiveCache reflectiveCache) {
067: templates = reflectiveCache.getConcurrentMap(1);
068: }
069:
070: // this is a map of viewID onto template file.
071: private Map templates;
072:
073: private CachingInputStreamSource cachingiis;
074:
075: public ViewTemplate locateTemplate(ViewParameters viewparams) {
076:
077: // NB if we really want this optimisation, it must be based on #
078: // of RETURNED templates, not the number of strategies!
079: XMLCompositeViewTemplate xcvt = strategies.size() == 1 ? null
080: : new XMLCompositeViewTemplate();
081: int highestpriority = 0;
082: StringList tried = new StringList();
083:
084: for (int i = 0; i < strategies.size(); ++i) {
085: TemplateResolverStrategy trs = (TemplateResolverStrategy) strategies
086: .get(i);
087: int this pri = trs instanceof RootAwareTRS ? ((RootAwareTRS) trs)
088: .getRootResolverPriority()
089: : 1;
090:
091: boolean isexpected = trs instanceof ExpectedTRS ? ((ExpectedTRS) trs)
092: .isExpected()
093: : true;
094: boolean ismultiple = trs instanceof MultipleTemplateResolverStrategy ? ((MultipleTemplateResolverStrategy) trs)
095: .isMultiple()
096: : false;
097:
098: StringList bases = trs.resolveTemplatePath(viewparams);
099:
100: StringList[] usebases;
101: if (ismultiple) {
102: usebases = new StringList[bases.size()];
103: for (int j = 0; j < usebases.length; ++j) {
104: usebases[j] = new StringList(bases.stringAt(j));
105: }
106: } else {
107: usebases = new StringList[] { bases };
108: }
109:
110: for (int j = 0; j < usebases.length; ++j) {
111: XMLViewTemplate template = locateTemplate(viewparams,
112: trs, usebases[j], isexpected ? tried : null,
113: ismultiple && isexpected);
114: if (template != null) {
115: if (xcvt != null) {
116: if (trs.isStatic()) {
117: xcvt.globalmap
118: .aggregate(template.rootlump.downmap);
119: } else {
120: xcvt.globalmap
121: .aggregate(template.globalmap);
122: }
123: if (template.mustcollectmap != null) {
124: xcvt.mustcollectmap
125: .aggregate(template.mustcollectmap);
126: }
127:
128: if (this pri == highestpriority && this pri != 0) {
129: if (xcvt.roottemplate != null) {
130: Logger.log
131: .warn("Duplicate root TemplateResolverStrategy "
132: + trs
133: + " found at priority "
134: + this pri
135: + ", using first entry");
136: }
137: }
138: if (this pri > highestpriority) {
139: xcvt.roottemplate = template;
140: highestpriority = this pri;
141: }
142:
143: } else {
144: return template;
145: }
146: } // end if template returned
147: }
148: }
149:
150: if (xcvt != null && xcvt.roottemplate == null) {
151: Exception eclass = viewparams.viewID.trim().length() == 0 ? (Exception) new SilentRedirectException()
152: : new IllegalArgumentException();
153: throw UniversalRuntimeException
154: .accumulate(
155: eclass,
156: "No template found for view "
157: + viewparams.viewID
158: + ": tried paths (expected) "
159: + tried.toString()
160: + " from all TemplateResolverStrategy which were marked as a root resolver (rootPriority > 0) ");
161: }
162: return xcvt;
163: }
164:
165: public XMLViewTemplate locateTemplate(ViewParameters viewparams,
166: TemplateResolverStrategy strs, StringList bases,
167: StringList tried, boolean logfailure) {
168: String resourcebase = "/";
169: if (strs instanceof BaseAwareTemplateResolverStrategy) {
170: BaseAwareTemplateResolverStrategy batrs = (BaseAwareTemplateResolverStrategy) strs;
171: resourcebase = batrs.getTemplateResourceBase();
172: }
173:
174: String extension = tei.inferTemplateExtension(viewparams);
175: InputStream is = null;
176: String fullpath = null;
177: for (int i = 0; i < bases.size(); ++i) {
178: fullpath = resourcebase + bases.stringAt(i) + "."
179: + extension;
180: if (tried != null) {
181: tried.add(fullpath);
182: }
183: is = cachingiis.openStream(fullpath);
184: if (is != null)
185: break;
186: if (is == null && logfailure) {
187: // This is not a real failure for other content types - see RSF-21
188: // Logger.log.warn("Failed to load template from " + fullpath);
189: }
190: }
191: if (is == null) {
192: return null;
193: }
194:
195: XMLViewTemplate template = null;
196: if (is == CachingInputStreamSource.UP_TO_DATE) {
197: template = (XMLViewTemplate) templates.get(fullpath);
198: }
199: if (template == null) {
200: List tpis = aggregator.getFilteredTPIs();
201: try {
202: // possibly the reason is it had a parse error last time, which may have
203: // been corrected
204: if (is == CachingInputStreamSource.UP_TO_DATE) {
205: is = cachingiis.getNonCachingResolver().openStream(
206: fullpath);
207: }
208: XMLViewTemplateParser parser = new XMLViewTemplateParser();
209: parser.setTemplateParseInterceptors(tpis);
210: template = (XMLViewTemplate) parser.parse(is);
211: // there WILL be one slash in the path.
212: int lastslashpos = fullpath.lastIndexOf('/');
213: String resourcebaseext = fullpath.substring(1,
214: lastslashpos + 1);
215: if (strs instanceof BaseAwareTemplateResolverStrategy) {
216: BaseAwareTemplateResolverStrategy batrs = (BaseAwareTemplateResolverStrategy) strs;
217: String extresourcebase = batrs.getExternalURLBase();
218: template.setExtResourceBase(extresourcebase);
219: }
220: if (strs instanceof ForceContributingTRS) {
221: ForceContributingTRS fctrs = (ForceContributingTRS) strs;
222: if (fctrs.getMustContribute()) {
223: template.mustcollectmap = template.collectmap;
224: }
225: }
226: template.setRelativeResourceBase(resourcebaseext);
227: template.fullpath = fullpath;
228: template.isstatictemplate = strs.isStatic();
229: templates.put(fullpath, template);
230: } catch (Exception e) {
231: throw UniversalRuntimeException.accumulate(e,
232: "Error parsing view template file " + fullpath);
233: }
234: }
235: return template;
236: }
237:
238: }
|