View Javadoc

1   /*
2    * Copyright 2010 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  package com.fatwire.gst.foundation.controller;
17  
18  import java.util.Arrays;
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Map.Entry;
24  
25  import COM.FutureTense.Interfaces.ICS;
26  import COM.FutureTense.Util.ftErrors;
27  import COM.FutureTense.Util.ftMessage;
28  
29  import com.fatwire.gst.foundation.CSRuntimeException;
30  import com.fatwire.gst.foundation.facade.RenderUtils;
31  import com.fatwire.gst.foundation.facade.logging.LogUtil;
32  import com.fatwire.gst.foundation.facade.runtag.render.CallTemplate;
33  import com.fatwire.gst.foundation.facade.runtag.render.CallTemplate.Style;
34  import com.fatwire.gst.foundation.facade.runtag.render.SatellitePage;
35  import com.fatwire.gst.foundation.url.WraPathTranslationService;
36  import com.fatwire.gst.foundation.wra.Alias;
37  import com.fatwire.gst.foundation.wra.AliasCoreFieldDao;
38  import com.fatwire.gst.foundation.wra.VanityAsset;
39  import com.fatwire.gst.foundation.wra.VanityAssetBean;
40  import com.fatwire.gst.foundation.wra.WraCoreFieldDao;
41  import com.openmarket.xcelerate.publish.PubConstants;
42  
43  import org.apache.commons.lang.StringUtils;
44  import org.apache.commons.logging.Log;
45  
46  import static COM.FutureTense.Interfaces.Utilities.goodString;
47  
48  /**
49   * 
50   * The BaseRenderPage class in the implementation that renders an asset. It's
51   * intended use is to be called from a Controller (outer wrapper).
52   * <p>
53   * For the passing of arguments it makes heavy use of page criteria on the
54   * calling Template
55   * 
56   * @author Tony Field
57   * @author Dolf Dijkstra
58   * @since June 2010
59   * 
60   */
61  public class BaseRenderPage {
62      public static final String URL_PATH = "url-path";
63  
64      public static final String VIRTUAL_WEBROOT = "virtual-webroot";
65  
66      public static final String PACKEDARGS = "packedargs";
67  
68      protected static final Log LOG = LogUtil.getLog(BaseRenderPage.class);
69      protected WraPathTranslationService pathTranslationService;
70      protected WraCoreFieldDao wraCoreFieldDao;
71      protected AliasCoreFieldDao aliasCoreFieldDao;
72      protected ICS ics;
73      public static final List<String> CALLTEMPLATE_EXCLUDE_VARS = Collections.unmodifiableList(Arrays.asList("c", "cid",
74              "eid", "seid", PACKEDARGS, "variant", "context", "pagename", "childpagename", "site", "tid",
75              VIRTUAL_WEBROOT, URL_PATH, "SystemAssetsRoot", "rendermode", "cshttp", "errno", "tablename", "empty",
76              "ft_ss", "errdetail", "null"));
77  
78      public BaseRenderPage() {
79          super();
80      }
81  
82      protected void callPage(AssetIdWithSite id, String pagename, String packedArgs) {
83          final SatellitePage sp = new SatellitePage(pagename);
84          if (StringUtils.isNotBlank(packedArgs))
85              sp.setPackedArgs(packedArgs);
86          sp.setArgument(PubConstants.c, id.getType());
87          sp.setArgument(PubConstants.cid, id.getId());
88          String[] pc = ics.pageCriteriaKeys(pagename);
89          if (pc != null) {
90              for (String p : pc) {
91                  if (!CALLTEMPLATE_EXCLUDE_VARS.contains(p)) {
92                      // add only if page critera and not excluded
93                      sp.setArgument(p, ics.GetVar(p));
94                  }
95              }
96          }
97          String s = sp.execute(ics);
98          if (s != null) {
99              ics.StreamText(s);
100         }
101 
102     }
103 
104     protected void callTemplate(final AssetIdWithSite id, final String tname) {
105         final CallTemplate ct = new CallTemplate();
106         ct.setSite(id.getSite());
107         ct.setSlotname("wrapper");
108         String tid = ics.GetVar("eid");// renderpage action is intended to be
109                                        // rendered from the controller
110         if (StringUtils.isBlank(tid)) {
111             if (LOG.isDebugEnabled()) {
112                 LOG.debug("CSVar eid is not found, compensating with eid=1. Is this element not a CSElement");
113             }
114             tid = "1";
115         }
116         ct.setTid(tid);
117         ct.setAsset(id);
118         ct.setTname(tname);
119         ct.setContext("");
120         ct.setArgument("site", id.getSite());
121 
122         final String packedargs = ics.GetVar(PACKEDARGS);
123         if (StringUtils.isNotBlank(packedargs)) {
124             if (LOG.isDebugEnabled()) {
125                 LOG.debug("packedargs is " + packedargs);
126             }
127             ct.setPackedargs(massagePackedArgs(packedargs));
128         }
129 
130         // typeless or not...
131         final String targetPagename = tname.startsWith("/") ? (id.getSite() + tname) : (id.getSite() + "/"
132                 + id.getType() + "/" + tname);
133         if (LOG.isDebugEnabled()) {
134             LOG.debug("Target pagename is " + targetPagename);
135         }
136 
137         // create a list of parameters that can be specified as arguments to the
138         // CallTemplate tag.
139         final Map<String, String> arguments = new HashMap<String, String>();
140 
141         // Prime the map with the ics variable scope for the architect to make
142         // the controller as transparent as possible
143         String[] pageKeys = ics.pageCriteriaKeys(targetPagename);
144         if (pageKeys != null) {
145             for (final String pcVarName : pageKeys) {
146                 if (!CALLTEMPLATE_EXCLUDE_VARS.contains(pcVarName) && StringUtils.isNotBlank(ics.GetVar(pcVarName))) {
147                     arguments.put(pcVarName, ics.GetVar(pcVarName));
148                 }
149             }
150         } else {
151             if (LOG.isDebugEnabled()) {
152                 LOG.debug("PageCriteria for " + targetPagename + " is null.");
153             }
154         }
155 
156         // allow users to set their own
157         getCallTemplateArguments(id, arguments);
158 
159         // add them to the tag
160         for (final Entry<String, String> e : arguments.entrySet()) {
161             ct.setArgument(e.getKey(), e.getValue());
162             if (LOG.isTraceEnabled()) {
163                 LOG.trace("CallTemplate param added: " + e.getKey() + "=" + e.getValue());
164             }
165         }
166 
167         // override calltemplate call style
168         imposeCallTemplateStyle(ct, targetPagename);
169 
170         final String s = ct.execute(ics);
171         if (s != null) {
172             ics.StreamText(s);
173         }
174     }
175 
176     /**
177      * Add p to the input parameters, if it is known or knowable. First check to
178      * see if it has been explicitly set, then look it up if it hasn't been. The
179      * variable is not guaranteed to be found.
180      * 
181      * @param id asset id with site
182      * @param arguments calltemplate arguments
183      */
184     protected final void findAndSetP(final AssetIdWithSite id, final Map<String, String> arguments) {
185         final String pVar = ics.GetVar("p");
186         if (pVar != null && pVar.length() > 0) {
187             arguments.put("p", pVar);
188         } else {
189             final long p = wraCoreFieldDao.findP(id);
190             if (p > 0L) {
191                 arguments.put("p", Long.toString(p));
192             }
193         }
194     }
195 
196     /**
197      * This method collects additional arguments for the CallTemplate call. New
198      * arguments are added to the map as name-value pairs.
199      * 
200      * @param id AssetIdWithSite object
201      * @param arguments Map<String,String> containing arguments for the nested
202      *            CallTemplate call
203      */
204     protected void getCallTemplateArguments(AssetIdWithSite id, Map<String, String> arguments) {
205         findAndSetP(id, arguments);
206     }
207 
208     protected Style getCallTemplateCallStyle(String target) {
209         if (RenderUtils.isCacheable(ics, ics.GetVar(ftMessage.PageName))) {
210             // call as element when current is caching.
211             // note that it may be useful to set this to "embedded" in some
212             // cases.
213             // override it in that situation
214             return Style.element;
215         } else if (RenderUtils.isCacheable(ics, target)) {
216             // call as embedded when current is not caching and target is
217             return Style.embedded;
218         } else {
219             // call as element when current is not caching and target is not
220             return Style.element;
221         }
222     }
223 
224     /**
225      * Load the WRA, or the alias, and return it for use by the controller. If
226      * the asset is not found, an exception is thrown
227      * 
228      * @param id asset id
229      * @return WRA, never null. May be an instance of an Alias
230      */
231     protected VanityAsset getWraAndResolveAlias(AssetIdWithSite id) {
232         try {
233             if (Alias.ALIAS_ASSET_TYPE_NAME.equals(id.getType())) {
234                 if (LOG.isTraceEnabled())
235                     LOG.trace("Loading alias: " + id);
236                 Alias alias = aliasCoreFieldDao.getAlias(id);
237                 VanityAssetBean wra = new VanityAssetBean(alias);
238 
239                 if (LOG.isDebugEnabled())
240                     LOG.debug("Loaded alias: " + id + " which resolved to " + wra.getId());
241                 return wra;
242             } else {
243                 if (LOG.isTraceEnabled())
244                     LOG.trace("Loading wra: " + id);
245                 return wraCoreFieldDao.getVanityWra(id);
246             }
247         } catch (IllegalArgumentException e) {
248             throw new CSRuntimeException("Web-Referenceable Asset " + id + " is not valid", ftErrors.pagenotfound);
249         }
250     }
251 
252     /**
253      * This method allows developers to override the automatically-selected
254      * CallTemplate call style. By default, no override is done and the
255      * CallTemplate class determines the best call style.
256      * 
257      * @param ct CallTemplate tag
258      * @param targetPagename target pagename
259      * @see CallTemplate #setStyle(CallTemplate.Style)
260      */
261     protected void imposeCallTemplateStyle(final CallTemplate ct, final String targetPagename) {
262     }
263 
264     protected String massagePackedArgs(String packedargs) {
265         return packedargs;
266     }
267 
268     /**
269      * Record compositional dependencies that are required for the controller
270      */
271     protected void recordCompositionalDependencies() {
272         RenderUtils.recordBaseCompositionalDependencies(ics);
273     }
274 
275     /**
276      * This method is the entry of the class, it does the rendering of the page.
277      */
278     protected void renderPage() {
279         // the preview code path is adding all the additional arguments in
280         // packedargs.
281         // unpack here so we can use
282         String pa = unpackPackedArgs();//ics.GetVar(PubConstants.PACKEDARGS);
283         
284         final AssetIdWithSite id = resolveAssetId();
285         if (id == null || id.getSite() == null) {
286             throw new CSRuntimeException("Asset or site not found: '" + id + "' for url " + ics.pageURL(),
287                     ftErrors.pagenotfound);
288         }
289         if (LOG.isDebugEnabled())
290             LOG.debug("RenderPage found a valid asset and site: " + id);
291 
292         // if childpagename is passed in (preview code path) we use this and do
293         // a render:satellitepage
294         if (ics.GetVar(PubConstants.CHILDPAGENAME) != null) {
295             // preview UI support
296             callPage(id, ics.GetVar(PubConstants.CHILDPAGENAME), pa);
297         } else {
298             // alternatively, render the live logic.
299             VanityAsset wra = getWraAndResolveAlias(id);
300 
301             callTemplate(new AssetIdWithSite(wra.getId().getType(), wra.getId().getId(), id.getSite()),
302                     wra.getTemplate());
303 
304         }
305         LOG.debug("RenderPage execution complete");
306     }
307 
308     /**
309      * In this method the AssetId and the site are resolved. Based on either
310      * c/cid or the virtual-webroot/url-path arguments the site which this asset
311      * belongs to is discovered from the AssetPublication table.
312      * 
313      * @return the AssetId and the site that the asset belongs to.
314      */
315     protected AssetIdWithSite resolveAssetId() {
316         final AssetIdWithSite id;
317         if (goodString(ics.GetVar(VIRTUAL_WEBROOT)) && goodString(ics.GetVar(URL_PATH))) {
318             id = pathTranslationService.resolveAsset(ics.GetVar(VIRTUAL_WEBROOT), ics.GetVar(URL_PATH));
319         } else if (goodString(ics.GetVar("c")) && goodString(ics.GetVar("cid"))) {
320             // handle these to be nice
321             // Look up site because we can't trust the wrapper's resarg.
322             String site = wraCoreFieldDao.resolveSite(ics.GetVar("c"), ics.GetVar("cid"));
323 
324             if (site == null)
325                 throw new CSRuntimeException("No site found for asset (" + ics.GetVar("c") + ":" + ics.GetVar("cid")
326                         + " ).", ftErrors.pagenotfound);
327 
328             id = new AssetIdWithSite(ics.GetVar("c"), Long.parseLong(ics.GetVar("cid")), site);
329         } else if (goodString(ics.GetVar(VIRTUAL_WEBROOT)) || goodString(ics.GetVar(URL_PATH))) {
330             // (but not both)
331             throw new CSRuntimeException("Missing required param virtual-webroot & url-path.", ftErrors.pagenotfound);
332         } else {
333             throw new CSRuntimeException("Missing required param c, cid.", ftErrors.pagenotfound);
334         }
335         return id;
336     }
337 
338     protected String unpackPackedArgs() {
339         // for RenderPage unpacking and throwing away packedargs seems the
340         // correct thing to do
341         final String packedargs = ics.GetVar(PACKEDARGS);
342         if (StringUtils.isNotBlank(packedargs)) {
343             if (LOG.isDebugEnabled()) {
344                 LOG.debug("packedargs is " + packedargs);
345             }
346             Map<String, String> map = new HashMap<String, String>();
347             ics.decode(packedargs, map);
348             for (Map.Entry<String, String> e : map.entrySet()) {
349                 ics.SetVar(e.getKey(), e.getValue());
350 
351             }
352             ics.RemoveVar(PACKEDARGS);
353         }
354         return packedargs;
355 
356     }
357 
358 }