001: /**
002: * $Id: RenderingUtil.java,v 1.27 2005/09/21 10:49:17 dg154973 Exp $
003: * Copyright 2002 Sun Microsystems, Inc. All
004: * rights reserved. Use of this product is subject
005: * to license terms. Federal Acquisitions:
006: * Commercial Software -- Government Users
007: * Subject to Standard License Terms and
008: * Conditions.
009: *
010: * Sun, Sun Microsystems, the Sun logo, and iPlanet
011: * are trademarks or registered trademarks of Sun Microsystems,
012: * Inc. in the United States and other countries.
013: */package com.sun.portal.wireless.providers.rendering;
014:
015: import com.aligo.util.Cache;
016: import com.aligo.engine.Content;
017: import com.aligo.portal.wireless.services.rendering.RenderingEngine;
018: import com.aligo.portal.wireless.services.rendering.RenderingEngineFactory;
019: import com.aligo.portal.wireless.services.rendering.RenderingException;
020: import com.iplanet.services.cdm.ClientsManager;
021: import com.iplanet.services.cdm.Client;
022: import com.iplanet.sso.SSOException;
023: import com.iplanet.sso.SSOToken;
024: import com.iplanet.sso.SSOTokenManager;
025: import com.iplanet.am.util.Debug;
026: import com.sun.portal.log.common.PortalLogger;
027:
028: import com.sun.portal.providers.containers.ContainerProvider;
029: import com.sun.portal.providers.context.ContainerProviderContext;
030: import com.sun.portal.providers.context.ProviderContextException;
031: import com.sun.portal.providers.ProviderException;
032: import com.sun.portal.wireless.providers.containers.rendering.RenderingContainerProvider;
033: import com.sun.mobile.responsebuffer.ResponseBufferEntry;
034: import com.sun.mobile.responsebuffer.ResponseBufferService;
035: import com.sun.mobile.responsebuffer.StaleResponseBufferDataException;
036:
037: import javax.servlet.http.HttpServletRequest;
038: import java.io.File;
039: import java.util.Hashtable;
040: import java.util.logging.Level;
041: import java.util.logging.Logger;
042:
043: /**
044: * The <code>RenderingUtil</code> class acts as a wrapper to the
045: * <code>RenderingEngine</code> interface. It also provides a method to do the
046: * modified fileLookup to get the appropriate AML templates from the file
047: * system. It provides static methods for the
048: * <code>JSPRenderingContainerProvider</code> and the
049: * <code>JSPRenderingProvider</code to make use of the rendering
050: * functionality. These utility methods can be also used by Containers
051: * or Providers which do not extend from the
052: * <code>JSPRenderingContainerProvider</code> or
053: * <code>JSPRenderingProvider</code> classes.
054: *
055: */
056:
057: public class RenderingUtil {
058:
059: private static Logger logger = PortalLogger
060: .getLogger(RenderingUtil.class);
061:
062: /**
063: * This method is used to do the rendering of the content that the
064: * Rendering Providers or Containers have retrieved. The logic of whether
065: * to do the conversion of AML document into device specific markup is
066: * implemented in this method. It also decides whether the
067: * Rendering Engine should cache the response buffer based on whether the
068: * container calling it is a parent container or not. <p>
069: * The logic of whether to do the rendering or not is driven by the
070: * following tables.
071:
072: <div align="center">
073: <div align="center"><b>Behaviour of JSPRenderingContainerProvider</b><br>
074:
075: <div align="center">
076: <table cellpadding="2" cellspacing="2" border="1" width="60%">
077: <tbody>
078: <tr>
079: <td valign="top" rowspan="2" colspan="2" border="0"><br>
080: </td>
081: <th valign="top" rowspan="1" colspan="3" align="center">
082: <font color="#000000">Parent Container</font></th>
083: </tr>
084: <tr>
085: <td valign="top" ><font color="#000000">Rendering
086: Container<br>
087: </font></td>
088: <td valign="top" ><font color="#000000">Native
089: Container</font><br>
090: </td>
091: <td valign="top" ><font color="#000000">Null - Top level
092: <br>
093: </font></td>
094: </tr>
095: <tr>
096: <th valign="top" rowspan="2" colspan="1">Client FilePath </th>
097: <td valign="top">Native filepath e.g. 'wml'<br>
098: </td>
099: <td valign="top" align="center" >NOOP<br>
100: </td>
101: <td valign="top" >Render<br>
102: </td>
103: <td valign="top" align="center" >Not applicable<br>
104: </td>
105: </tr>
106: <tr>
107: <td valign="top">Starts with 'aml'<br>
108: </td>
109: <td valign="top" align="center" >NOOP<br>
110: </td>
111: <td valign="top" >NOOP<br>
112: </td>
113: <td valign="top" align="center" >Render<br>
114: </td>
115: </tr>
116: <tr>
117: </tr>
118:
119: </tbody>
120: </table>
121: <br>
122: </div>
123: <br>
124: </div>
125: <b>Behaviour of JSPRenderingProvider</b><br>
126:
127: <div align="center">
128: <table cellpadding="2" cellspacing="2" border="1" width="60%">
129: <tbody>
130: <tr>
131: <td valign="top" rowspan="2" colspan="2" border="0"><br>
132: </td>
133: <th valign="top" rowspan="1" colspan="2" align="center">
134: <font color="#000000">Parent Container</font></th>
135: </tr>
136: <tr>
137: <td valign="top" ><font color="#000000">Rendering
138: Container<br>
139: </font></td>
140: <td valign="top" ><font color="#000000">Native
141: Container</font><br>
142: </td>
143: </tr>
144: <tr>
145: <th valign="top" rowspan="2" colspan="1">Client FilePath </th>
146: <td valign="top">Native filepath e.g. 'wml'<br>
147: </td>
148: <td valign="top" align="center" >NOOP<br>
149: </td>
150: <td valign="top" >Render<br>
151: </td>
152: </tr>
153: <tr>
154: <td valign="top">Starts with 'aml'<br>
155: </td>
156: <td valign="top" align="center" >NOOP<br>
157: </td>
158: <td valign="top" >NOOP<br>
159: </td>
160: </tr>
161: <tr>
162: </tr>
163:
164: </tbody>
165: </table>
166: <br>
167: </div>
168: <br>
169: </div>
170:
171: * @param request The Http Servlet Request object that gets passed to
172: * the getContent call of the Provider/Container.
173: * @param cpc This is the ContainerProviderContext obtained from
174: * getProviderContext() call while in Provider/Container.
175: * @param name Name of the channel being rendered
176: * @param sb The StringBuffer representing the Content obtained
177: * from the Templates/JSPs.
178: * @return StringBuffer The return content driven by the above
179: * mentioned logic.
180: * @throws ProviderException
181: */
182:
183: public static StringBuffer renderContent(
184: HttpServletRequest request, ContainerProviderContext cpc,
185: String name, StringBuffer sb) throws ProviderException {
186: // Render content
187:
188: String parent = cpc.getParentContainerName(name);
189: if (logger.isLoggable(Level.FINEST)) {
190: String[] param = { name, cpc.getParentContainerName(name),
191: sb.toString() };
192: logger.log(Level.FINEST, "PSMA_CSPWPR0002", param);
193: }
194:
195: if (parent != null) {
196:
197: ContainerProvider cp = (ContainerProvider) cpc.getProvider(
198: request, null, parent);
199: if (cp instanceof RenderingContainerProvider) {
200:
201: // Parent container is a Rendering Container
202: // NOOP .. return AML
203: return sb;
204: } else {
205: // Parent container is a Native Container
206: if (cpc.getClientPath().startsWith("/aml")
207: || cpc.getClientPath().startsWith("aml")) {
208: // NOOP .. return AML
209: return sb;
210: } else {
211: // Render .. return device specific markup
212: // Not a parent .. so cache is false, forgive is
213: // false, fragment is true
214:
215: return RenderingUtil.doRender(request, cpc, name,
216: sb, false, true);
217: }
218: }
219: } else {
220:
221: // This is a top level container
222: // Render .. return device specific markup
223: // cache is true, forgive is false, fragment is false
224: // as this is top level
225: return RenderingUtil.doRender(request, cpc, name, sb, true,
226: false);
227: }
228: }
229:
230: /**
231: * If the channel is a top level edit channel, it renders its contents.
232: * Whereas if it is contained in some other container, then the edit
233: * channel only renders if the parent is a Native container.
234: * renderEditContent determines whether to render or not based on the
235: * HTTP attributes set by the parent ContainerProvider -
236: * '<parent_container>.isTopLevel' and '<parent_container>.shouldRender'.
237: * If these values are not set, then shouldRender is assumed to be
238: * true and isTopLevel assumed to be false.
239: * <br>
240: * A RenderingContainer would sets shouldRender=false and isTopLevel=false.
241: * So the channels in the RenderingContainer pass back AML to the
242: * container. A NativeContainer on the other hand does not know to set
243: * these these attributes, so they are assumed to be isTopLevel=false
244: * and shouldRender=true. Thus the edit channel passes back device
245: * specific markup back to the Native parent container.
246: * <p>
247: * @param request The Http Servlet Request object that gets passed to
248: * the getContent call of the Provider/Container.
249: * @param cpc This is the ContainerProviderContext obtained from
250: * getProviderContext() call while in Provider/Container.
251: * @param name Name of the channel being rendered
252: * @param sb The StringBuffer representing the Content obtained
253: * from the Templates/JSPs.
254: * @return StringBuffer The return content driven by the above
255: * mentioned logic.
256: * @throws ProviderException
257: *
258: */
259:
260: public static StringBuffer renderEditContent(
261: HttpServletRequest request, ContainerProviderContext cpc,
262: String name, StringBuffer sb) throws ProviderException {
263: // Render content
264:
265: if (logger.isLoggable(Level.FINEST)) {
266: String[] param = { name, cpc.getParentContainerName(name),
267: sb.toString() };
268: logger.log(Level.FINEST, "PSMA_CSPWPR0002", param);
269: }
270:
271: String parent = request.getParameter("provider");
272: Boolean tmp = (Boolean) request.getAttribute(parent
273: + ".shouldRender");
274: // Default value of shouldRender is true as native containers
275: // dont know to set this value.
276: boolean shouldRender = true;
277: if (tmp != null) {
278: shouldRender = tmp.booleanValue();
279: }
280:
281: tmp = (Boolean) request.getAttribute(name + ".isTopLevel");
282: // Is not top level unless specifically stated.
283: boolean isTopLevel = false;
284: if (tmp != null) {
285: isTopLevel = tmp.booleanValue();
286: }
287:
288: if (logger.isLoggable(Level.FINEST)) {
289: String[] param = { "channel", name };
290: logger.log(Level.FINEST, "PSMA_CSPWPR0001", param);
291: param[0] = "is topLevel";
292: param[1] = "" + isTopLevel;
293: logger.log(Level.FINEST, "PSMA_CSPWPR0001", param);
294: param[0] = "shouldRender";
295: param[1] = "" + shouldRender;
296: logger.log(Level.FINEST, "PSMA_CSPWPR0001", param);
297: }
298:
299: if (isTopLevel) {
300: // Top level container
301: // Render irrespective of shouldRender .. return device
302: // specific markup
303: // Top Level .. so cache is true, forgive is
304: // false, fragment is false
305: return RenderingUtil.doRender(request, cpc, name, sb, true,
306: false);
307: } else {
308: if (!shouldRender) {
309:
310: // Parent container is a Rendering Container
311: // NOOP .. return AML
312: return sb;
313:
314: } else {
315: // Parent container is a Native Container
316: // Render .. return device specific markup
317: // Not a parent .. so cache is false, forgive is
318: // false, fragment is true
319: return RenderingUtil.doRender(request, cpc, name, sb,
320: false, true);
321: }
322: }
323: }
324:
325: /**
326: * The method that gets the instance of the Rendering Engine and
327: * transforms the AML to device specific markup by calling the
328: * RenderingEngine.renderContent.
329: *
330: * <p>
331: * @param request The Http Servlet Request object that gets passed to
332: * the getContent call of the Provider/Container.
333: * @param cpc This is the ContainerProviderContext obtained from
334: * getProviderContext() call while in Provider/Container.
335: * @param name Name of the channel being rendered
336: * @param sb The StringBuffer representing the Content obtained
337: * from the Templates/JSPs.
338: * @param cache Boolean indicating whether to ask rendering engine to
339: * buffer the response or not.
340: * @param fragment Boolean indicating whether the AML being rendered is
341: * a complete AML document or a fragment.
342: * @return StringBuffer The device specific markup based on the
343: * client type
344: * @throws ProviderException
345: */
346:
347: public static StringBuffer doRender(HttpServletRequest request,
348: ContainerProviderContext cpc, String name, StringBuffer sb,
349: boolean cache, boolean fragment) throws ProviderException {
350: String clientType = null;
351: Client client = null;
352: SSOToken ssotoken = null;
353: // Get client type
354: try {
355: clientType = cpc.getClientType();
356: client = ClientsManager.getInstance(clientType);
357: } catch (Exception ce) {
358: if (logger.isLoggable(Level.INFO))
359: logger.log(Level.INFO, "PSMA_CSPWPR0003", ce);
360: clientType = "genericHTML";
361: }
362:
363: if (clientType == null) {
364: clientType = "genericHTML";
365: }
366:
367: try {
368: ssotoken = SSOTokenManager.getInstance().createSSOToken(
369: request);
370: } catch (SSOException ssoe) {
371: if (logger.isLoggable(Level.INFO))
372: logger.log(Level.INFO, "PSMA_CSPWPR0004", ssoe);
373: ssotoken = null;
374: cache = false;
375: //return sb;
376: }
377:
378: // Create a ResponseBufferEntry to make the buffering system manage
379: // the life cycle of this Cache.
380: ResponseBufferEntry entry = null;
381:
382: // For authless anonymous desktop ssotoken is the same, so
383: // disabling response buffering for authless anonymous users.
384: // If ssotoken is null, send null entry object to rendering eng.
385: if (ssotoken != null) {
386: // this is the request url which is generating the content
387: //String request_url = cpc.getDesktopURL(request);
388: StringBuffer request_url = new StringBuffer(request
389: .getRequestURI());
390: if (request.getQueryString() != null) {
391: request_url = request_url.append("?");
392: request_url = request_url.append(request
393: .getQueryString());
394: }
395: // Get an instance of ResponseBufferService
396: ResponseBufferService service = ResponseBufferService
397: .getInstance();
398:
399: try {
400: entry = service.createEntry(ssotoken, request_url
401: .toString(), request);
402: } catch (SSOException ssoe) {
403: if (logger.isLoggable(Level.INFO))
404: logger.log(Level.INFO, "PSMA_CSPWPR0005", ssoe);
405:
406: return sb;
407: }
408: }
409:
410: Cache entry_cache = null;
411: try {
412: if (entry != null) {
413: entry_cache = entry.getCache();
414: }
415: } catch (StaleResponseBufferDataException srbde) {
416: entry_cache = null;
417: }
418:
419: RenderingEngine engine = RenderingEngineFactory.getInstance()
420: .getEngine(entry_cache, name);
421:
422: String renderedContent = null;
423: try {
424: if (logger.isLoggable(Level.WARNING)) {
425: String[] param = { "" + cache, "" + fragment };
426: logger.log(Level.WARNING, "PSMA_CSPWPR0006");
427: }
428:
429: Content xformedContent = engine.renderContent(
430: sb.toString(), client, cache, false, fragment);
431: if (xformedContent != null) {
432: renderedContent = xformedContent.getContents();
433: }
434:
435: if (logger.isLoggable(Level.WARNING)) {
436: logger.log(Level.WARNING, "PSMA_CSPWPR0007",
437: renderedContent);
438: }
439: } catch (RenderingException re) {
440: throw new ProviderException(re.getMessage());
441: }
442: return new StringBuffer(renderedContent);
443:
444: }
445:
446: /**
447: * Gets a desktop template relative to the filepath specified. It
448: * internally calls ProviderContext.getTemplate to get the template.
449: *
450: * @param cpc - ContainerProviderContext of the calling provider
451: * @param name - Channel name
452: * @param file - Template name to return.
453: *
454: * @return Buffer containng the template.
455: */
456:
457: public static StringBuffer getTemplate(
458: ContainerProviderContext cpc, String name, String file)
459: throws ProviderContextException {
460: String filepath = cpc.getClientPath();
461: if (!(filepath.startsWith("/aml"))
462: && !(filepath.startsWith("aml"))) {
463:
464: // This rendering component is a part of the native desktop.
465: // Have to add 'aml' in order to access AML templates of the
466: // rendering component.
467: filepath = "/aml/" + filepath;
468: }
469:
470: return cpc.getTemplate(cpc.getDesktopType(), cpc.getLocale()
471: .toString(), name, filepath, file, cpc
472: .getConfigProperty("templateBaseDir"));
473:
474: }
475:
476: /**
477: * Gets and tag swaps a desktop template relative to the filepath
478: * specified. It internally calls ProviderContext.getTemplate to get
479: * the template.
480: *
481: * @param cpc - ContainerProviderContext of the calling provider
482: * @param name - Channel name
483: * @param file - Template name to return.
484: * @param table table to use for tag swapping
485: *
486: * @return Buffer containng the template.
487: */
488: public static StringBuffer getTemplate(
489: ContainerProviderContext cpc, String name, String file,
490: Hashtable table) throws ProviderContextException {
491: String filepath = cpc.getClientPath();
492: if (!(filepath.startsWith("/aml"))
493: && !(filepath.startsWith("aml"))) {
494:
495: // This rendering component is a part of the native desktop.
496: // Have to add 'aml' in order to access AML templates of the
497: // rendering component.
498: filepath = "/aml/" + filepath;
499: }
500:
501: return cpc.getTemplate(cpc.getDesktopType(), cpc.getLocale()
502: .toString(), name, filepath, file, table, cpc
503: .getConfigProperty("templateBaseDir"));
504: }
505:
506: /**
507: * Gets the most specific template path for the given channel name,
508: * the given template name and the given filepath. The file returned may
509: * not exist at all. If non-null, the key returned by this method will map
510: * to the most specific template file. The file may or may not exist. It
511: * internally calls ProviderContext.getTemplateMostSpecificPath to get
512: * the template path.
513: *
514: * @param cpc - ContainerProviderContext of the calling provider
515: * @param name - Channel name
516: * @param file - Template name
517: *
518: * @return Path key to access the template
519: */
520: public static File getTemplateMostSpecificPath(
521: ContainerProviderContext cpc, String name, String file)
522: throws ProviderContextException {
523: String filepath = cpc.getClientPath();
524: if (!(filepath.startsWith("/aml"))
525: && !(filepath.startsWith("aml"))) {
526:
527: // This rendering component is a part of the native desktop.
528: // Have to add 'aml' in order to access AML templates of the
529: // rendering component.
530: filepath = "/aml/" + filepath;
531: }
532:
533: return cpc.getTemplateMostSpecificPath(cpc.getDesktopType(),
534: cpc.getLocale().toString(), name, filepath, file, cpc
535: .getConfigProperty("templateBaseDir"));
536:
537: }
538:
539: /**
540: * Gets the template path for the given channel name, the given template
541: * name and the given filepath. If non-null, the key returned by this
542: * method will map to a valid template file. It internally calls
543: * ProviderContext.getTemplateMostSpecificPath to get the template path.
544: *
545: * @param cpc - ContainerProviderContext of the calling provider
546: * @param name - Channel name
547: * @param file - Template name
548: *
549: * @return Path key to access the template
550: */
551: public static File getTemplatePath(ContainerProviderContext cpc,
552: String name, String file) throws ProviderContextException {
553: String filepath = cpc.getClientPath();
554: if (!(filepath.startsWith("/aml"))
555: && !(filepath.startsWith("aml"))) {
556:
557: // This rendering component is a part of the native desktop.
558: // Have to add 'aml' in order to access AML templates of the
559: // rendering component.
560: filepath = "/aml/" + filepath;
561: }
562:
563: return cpc.getTemplatePath(cpc.getDesktopType(), cpc
564: .getLocale().toString(), name, filepath, file, cpc
565: .getConfigProperty("templateBaseDir"));
566: }
567: }
|