View Javadoc

1   /*
2    * Copyright 2008 FatWire Corporation. All Rights Reserved.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.fatwire.gst.foundation.facade.runtag.render;
18  
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Map.Entry;
25  
26  import COM.FutureTense.Interfaces.FTValList;
27  import COM.FutureTense.Interfaces.ICS;
28  import COM.FutureTense.Interfaces.Utilities;
29  import COM.FutureTense.Util.ftErrors;
30  import COM.FutureTense.Util.ftMessage;
31  
32  import com.fatwire.assetapi.data.AssetId;
33  import com.fatwire.gst.foundation.facade.RenderUtils;
34  import com.fatwire.gst.foundation.facade.runtag.TagRunnerRuntimeException;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  /**
40   * CallTemplate tag with many improvements around context and style.
41   * <p/>
42   * <code>
43   * &lt;RENDER.CALLTEMPLATE SITE="site name"
44   * SLOTNAME="name of slot"
45   * TID="caller Template or CSElement id" [TTYPE="caller Template or CSElement"]
46   * [C="asset type"] [CID="asset id"] [TNAME="target Template or CSElement name"]
47   * [CONTEXT="context override"] [STYLE="pagelet or element"]
48   * [VARIANT="template variant name"] [PACKEDARGS="packed arguments"]&gt;
49   * <p/>
50   * [&lt;RENDER.ARGUMENT NAME="variable1" VALUE="value1"/&gt;]
51   * <p/>
52   * &lt;/RENDER.CALLTEMPLATE&gt;
53   * </code>
54   * 
55   * @author Tony Field
56   * @author Dolf Dijkstra
57   * @since Jun 10, 2010
58   */
59  public class CallTemplate extends TagRunnerWithRenderArguments {
60  
61      private static Log LOG = LogFactory.getLog(CallTemplate.class.getPackage().getName());
62  
63      static private boolean configLoaded = false;
64      /**
65       * The default style, used if present
66       */
67      static private Style defaultStyle = null;
68      /**
69       * Do not use the user provided value for style, override
70       */
71      static private boolean override = true;
72      static private boolean config_FixPageCriteria = false;
73      private boolean fixPageCriteria = false;
74      private String site, type, tname, cid;
75      private Style style;
76  
77      public enum Style {
78          element, pagelet, embedded
79      }
80  
81      public enum Type {
82          Template, CSElement
83      }
84  
85      public CallTemplate() {
86          super("RENDER.CALLTEMPLATE");
87      }
88  
89      /**
90       * Sets up CallTemplate with default <tt>Style.element</tt>
91       * 
92       * @param slotname
93       * @param tname
94       * @param type
95       */
96      public CallTemplate(final String slotname, final String tname, final Type type) {
97          super("RENDER.CALLTEMPLATE");
98          setSlotname(slotname);
99          setTname(tname);
100         setTtype(type);
101         setContext("");
102     }
103 
104     /**
105      * Checks the current settings and based on the current and target template
106      * state set the style to a best guess. This is only done if the developer
107      * didnot explicitly set the style.
108      */
109     @Override
110     protected void preExecute(final ICS ics) {
111         // consider to set site to ics.GetVar("site"); as helper for reasonable
112 
113         // default value
114         readConfig(ics);
115 
116         if (defaultStyle != null) {
117             setStyle(defaultStyle);
118         } else if (override || style == null) {
119             final Style newStyle = proposeStyle(ics);
120             setStyle(newStyle);
121         }
122         ics.ClearErrno();
123         super.preExecute(ics);
124     }
125 
126     @Override
127     protected void postExecute(ICS ics) {
128         site = null;
129         type = null;
130         tname = null;
131         cid = null;
132         style = null;
133         super.postExecute(ics);
134     }
135 
136     public void setSite(final String s) {
137         set("SITE", s);
138         site = s;
139     }
140 
141     public void setSlotname(final String s) {
142         set("SLOTNAME", s);
143     }
144 
145     public void setTid(final String s) {
146         set("TID", s);
147     }
148 
149     public void setTtype(final Type s) {
150         set("TTYPE", s.toString());
151     }
152 
153     public void setC(final String s) {
154         set("C", s);
155         type = s;
156     }
157 
158     public void setCid(final String s) {
159         set("CID", s);
160         cid = s;
161     }
162 
163     public void setTname(final String s) {
164         set("TNAME", s);
165         tname = s;
166     }
167 
168     public void setContext(final String s) {
169         set("CONTEXT", s);
170     }
171 
172     public void setStyle(final Style s) {
173         set("STYLE", s != null ? s.toString() : null);
174         style = s;
175     }
176 
177     public void setVariant(final String s) {
178         set("VARIANT", s);
179     }
180 
181     public void setPackedargs(final String s) {
182         // todo: this may need more work
183         set("PACKEDARGS", s);
184     }
185 
186     public void setAsset(final AssetId id) {
187         setC(id.getType());
188         setCid(Long.toString(id.getId()));
189     }
190 
191     void readConfig(final ICS ics) {
192         if (configLoaded) {
193             return;
194         }
195         final String tmp = getProperty(ics, "style");
196         if (tmp != null) {
197             CallTemplate.defaultStyle = Style.valueOf(tmp);
198         }
199         CallTemplate.override = "true".equals(getProperty(ics, "override"));
200         CallTemplate.config_FixPageCriteria = "true".equals(getProperty(ics, "config_FixPageCriteria"));
201         CallTemplate.configLoaded = true;
202 
203     }
204 
205     private String getProperty(final ICS ics, final String name) {
206         String val = System.getProperty(CallTemplate.class.getName() + "." + name);
207         if (!Utilities.goodString(val)) {
208             val = ics.GetProperty(CallTemplate.class.getName() + "." + name, "futuretense_xcel.ini", true);
209         }
210         LOG.trace(CallTemplate.class.getName() + "." + name + "=" + val);
211         return val;
212     }
213 
214     public Style proposeStyle(final ICS ics) {
215 
216         /**
217          * Considerations 1) Check target for parameter callstyle and use that
218          * 
219          */
220         String pname = getTargetPagename();
221 
222         // String targetStyle =(String)
223         // ics.getPageData(pname).getDefaultArguments().get("callstyle");
224         final boolean targetCached = RenderUtils.isCacheable(ics, pname);
225         final boolean currentCached = RenderUtils.isCacheable(ics, ics.GetVar(ftMessage.PageName));
226 
227         final Style proposal = calculateStyle(ics, pname, currentCached, targetCached);
228         if (LOG.isDebugEnabled()) {
229             LOG.debug("Setting style to '" + proposal + (style != null ? "' (user did set '" + style + "')" : "'")
230                     + " for calltemplate to '" + pname + "' with " + type + "," + cid + "," + getList()
231                     + " in element: '" + ics.ResolveVariables("CS.elementname") + "', caching: '" + currentCached + "/"
232                     + targetCached + "', page: " + ics.pageURL());
233         }
234         return proposal;
235 
236     }
237 
238     private String getTargetPagename() {
239         String pname;
240 
241         if (tname.startsWith("/")) // typeless
242         {
243             pname = site + tname;
244         } else {
245             pname = site + "/" + type + "/" + tname;
246         }
247         return pname;
248     }
249 
250     private Style calculateStyle(final ICS ics, final String pname, final boolean currentCache,
251             final boolean targetCache) {
252         if (currentCache == false) // we are not caching for the current pagelet
253         {
254             if (targetCache == false) {
255                 return Style.element; // call as element is target is also not
256                 // cacheable
257             } else {
258                 checkPageCriteria(ics, pname);
259                 return Style.pagelet; // otherwise call as pagelet
260             }
261 
262         } else { // currently we are caching
263 
264             if (targetCache == false) {
265                 checkPageCriteria(ics, pname);
266                 return Style.pagelet;
267             } else {
268                 // LOG.debug("getvar.cid=" + ics.GetVar("cid") + " at " +
269                 // ics.pageURL());
270 
271                 final FTValList m = COM.FutureTense.Interfaces.Utilities.getParams(ics.pageURL());
272                 final String pageCid = m.getValString("cid");
273                 if (pageCid != null && !pageCid.equals(ics.GetVar("cid"))) {
274                     LOG.warn(ics.GetVar("cid") + " does not match cid (" + pageCid + ") in " + ics.pageURL());
275                 }
276                 // should we check if cid is current page criteria, we are a
277                 // Template??
278                 if (cid != null && cid.equals(pageCid)) {
279                     // if c/cid does not change than we call this as an element,
280                     // as reuse is unlikely
281                     if (LOG.isTraceEnabled()) {
282                         LOG.trace("Calling " + pname + " as an element from " + ics.ResolveVariables("CS.elementname")
283                                 + " because cid is same as on current pagelet.");
284                     }
285                     return Style.element;
286                 } else {
287                     checkPageCriteria(ics, pname);
288                     return Style.embedded; // this is calltemplate, assuming
289                     // that
290                     // headers/footers/leftnavs etc will be
291                     // via CSElements/SiteEntry
292                 }
293             }
294 
295         }
296     }
297 
298     protected static final List<String> CALLTEMPLATE_EXCLUDE_VARS = Collections.unmodifiableList(Arrays.asList("TNAME",
299             "C", "CID", "EID", "SEID", "PACKEDARGS", "VARIANT", "CONTEXT", "SITE", "TID", "rendermode", "ft_ss",
300             "SystemAssetsRoot", "cshttp", "errno", "tablename", "empty", "errdetail", "null"));
301 
302     @SuppressWarnings("unchecked")
303     private void checkPageCriteria(final ICS ics, final String target) {
304         final FTValList o = getList();
305 
306         if (o != null) {
307             String[] pc = ics.pageCriteriaKeys(target);
308             if (pc == null) {
309                 pc = new String[0];
310             }
311             final Map<String, ?> m = o;
312             for (final Iterator<?> i = m.entrySet().iterator(); i.hasNext();) {
313                 final Entry<String, ?> e = (Entry<String, ?>) i.next();
314                 final String key = e.getKey();
315                 // only inspect arguments that start with ARGS_
316                 if (key.startsWith(ARGS)) {
317 
318                     String shortKey = key.substring(ARGS.length());
319                     boolean found = CALLTEMPLATE_EXCLUDE_VARS.contains(shortKey);
320                     if (!found) {
321                         for (final String c : pc) {
322                             if (c.equalsIgnoreCase(shortKey)) {
323                                 found = true;
324                                 break;
325                             }
326                         }
327                     }
328                     if (!found) {
329                         LOG.error("Argument '" + key + "' not found as PageCriterium on " + target
330                                 + ". Calling element is " + ics.ResolveVariables("CS.elementname")
331                                 + ". Arguments are: " + m.keySet().toString() + ". PageCriteria: " + Arrays.asList(pc),
332                                 new Exception());
333                         // we could correct this by calling as an element
334                         // or by removing the argument
335                         if (isFixPageCriteria() || config_FixPageCriteria) {
336                             i.remove();
337                             LOG.warn("Argument '" + key + "' is removed from the call to '" + target
338                                     + "' as it is not a PageCriterium.");
339                         }
340 
341                     }
342                 }
343             }
344 
345         }
346 
347     }
348 
349     protected void handleError(ICS ics) {
350 
351         int errno = ics.GetErrno();
352         if (errno != -10004) // error checking was too aggressive, any error set
353                              // by an element would leak into this handler
354             return;
355         FTValList arguments = list;
356         ftErrors complexError = ics.getComplexError();
357         String pagename = ics.GetVar("pagename");
358         String elementname = ics.ResolveVariables("CS.elementname");
359         String msg = "ics.runTag(RENDER.CALLTEMPLATE) failed for tname: " + list.getValString("TNAME") + " for asset: "
360                 + list.getValString("C") + ":" + list.getValString("CID") + " within pagename:" + pagename;
361         if (elementname != null)
362             msg += " and element:" + elementname;
363         msg += ".";
364         ics.ClearErrno();
365         throw new TagRunnerRuntimeException(msg, errno, arguments, complexError, pagename, elementname);
366     }
367 
368     /**
369      * @return the fixPageCriteria
370      */
371     public boolean isFixPageCriteria() {
372         return fixPageCriteria;
373     }
374 
375     /**
376      * @param fixPageCriteria the fixPageCriteria to set
377      */
378     public void setFixPageCriteria(boolean fixPageCriteria) {
379         this.fixPageCriteria = fixPageCriteria;
380     }
381 
382 }