001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.tags.template;
020:
021: import org.apache.beehive.netui.tags.AbstractClassicTag;
022: import org.apache.beehive.netui.tags.AbstractPageError;
023: import org.apache.beehive.netui.tags.IErrorReporter;
024: import org.apache.beehive.netui.tags.html.Html;
025: import org.apache.beehive.netui.tags.rendering.TagRenderingBase;
026: import org.apache.beehive.netui.util.Bundle;
027: import org.apache.beehive.netui.util.logging.Logger;
028: import org.apache.beehive.netui.pageflow.internal.InternalUtils;
029:
030: import javax.servlet.RequestDispatcher;
031: import javax.servlet.ServletException;
032: import javax.servlet.ServletRequest;
033: import javax.servlet.ServletResponse;
034: import javax.servlet.http.HttpServletRequest;
035: import javax.servlet.jsp.JspException;
036: import javax.servlet.jsp.JspWriter;
037: import javax.servlet.jsp.tagext.Tag;
038: import java.io.IOException;
039: import java.net.MalformedURLException;
040: import java.net.URL;
041: import java.util.ArrayList;
042: import java.util.HashMap;
043:
044: /**
045: * This tags defines the template to use within a content page. The
046: * content page interacts with the template page through children tags
047: * of the
048: * <code>Template</code> tag. The legal children are as follows:
049: * <ul>
050: * <li> <code>setAttribute</code> -- A Tag that will set an Attribute on the
051: * template.
052: * <li> <code>section</code> -- A tag that defines the content of a section
053: * defined in the template.
054: * </ul>
055: * The URL of the template file is set as the <code>templatePage</code>
056: * attribute on the
057: * <code>Template</code> tag. The Template file is included from
058: * the <code>Template</code> tag and will include sections defined
059: * in the content page. The content is contained in one or more
060: * <code>Section</code> tags, which are children of the
061: * <code>Template</code> tag. In addition, the content page can set
062: * attributes of the template page.
063:
064: * @jsptagref.tagdescription
065: * Points a content page at its template page. A
066: * content page interacts with its template page through children tags
067: * of the
068: * <netui-template:template> tag. The legal children are as follows:
069: * <blockquote>
070: * <ul>
071: * <li> {@link SetAttribute} -- a tag that fills a
072: * {@link Attribute} placeholder with content.
073: * <li> {@link Section} -- a tag that fills a
074: * {@link IncludeSection} placeholder with content.
075: * </ul>
076: * </blockquote>
077: * <p>The URL of the template page is specified by the <code>templatePage</code>
078: * attribute on the
079: * <netui-template:template> tag.
080: *
081: * @example
082: * The following example shows a content page that adopts the template.jsp page as its template.
083: * The content page also sets the "title" attribute on the template.
084: *
085: * <pre> <netui-template:template templatePage="./template.jsp">
086: * ...
087: * <netui-template:setAttribute name="title" value="Template Tags Sample"/>
088: * ...
089: * </netui-template:template></pre>
090: *
091: * @netui:tag name="template"
092: * description="Use this tag to associate a JSP page with a particular template file."
093: */
094: public class Template extends AbstractClassicTag implements
095: TemplateConstants, IErrorReporter {
096: private static final Logger logger = Logger
097: .getInstance(Template.class);
098:
099: public static class TemplateContext {
100: HashMap secs = null;
101: }
102:
103: /**
104: * Saved context for the nested case
105: */
106: private TemplateContext _savedContext = null;
107:
108: private boolean _fatalError = false;
109:
110: /**
111: * Inner class that is exposed to handle errors
112: */
113: private org.apache.beehive.netui.tags.IErrorReporter _innerErrors;
114:
115: /**
116: * Returns the name of the Tag. This is used to
117: * identify the type of tag reporting errors.
118: */
119: public String getTagName() {
120: return "Template";
121: }
122:
123: /**
124: * The URL of the template file...
125: */
126: private String _templatePage;
127:
128: /**
129: * boolean indicating the error reporting.
130: */
131: private boolean _reportErrors = false;
132:
133: /**
134: * Set the boolean indicating that the template should report errors. The
135: * errors will be reported through a HTML tag that must be found
136: * in the template JSP.
137: * @param reportErrors boolean indicating that errors should be reported
138: *
139: * @jsptagref.attributedescription
140: * Boolean. Determines if the template should report errors.
141: * The errors will be reported inline on the JSP page.
142: *
143: * @jsptagref.databindable false
144: *
145: * @jsptagref.attributesyntaxvalue <i>boolean_reportErrors</i>
146: *
147: * @netui:attribute required="false" rtexprvalue="true"
148: * description="Determines if the template should report errors.
149: * The errors will be reported inline on the JSP page."
150: */
151: public void setReportErrors(boolean reportErrors) {
152: _reportErrors = reportErrors;
153: }
154:
155: /**
156: * Set the URL of the template to use. The <code>templatePage</code>
157: * is an URL which
158: * identifies the JSP template page.
159: * @param templatePage - a URL pointing to a JSP page that represents the
160: * template.
161: *
162: * @jsptagref.attributedescription
163: * The URL of the template page to use.
164: *
165: * @jsptagref.databindable false
166: *
167: * @jsptagref.attributesyntaxvalue <i>string_urlToTemplatePage</i>
168: *
169: * @netui:attribute required="true" rtexprvalue="true"
170: * description="The URL of the template page to use."
171: */
172: public void setTemplatePage(String templatePage) {
173: _templatePage = templatePage;
174: }
175:
176: /**
177: * @netui:attribute required="false" rtexprvalue="true"
178: * description="Set the document type (html4-loose or xhtml1-transitional) of the document."
179: */
180: public void setDocumentType(String docType) {
181: int rendering = 0;
182: if (docType != null) {
183: if (docType.equals(Html.HTML_401))
184: rendering = TagRenderingBase.HTML_RENDERING;
185: else if (docType.equals(Html.HTML_401_QUIRKS))
186: rendering = TagRenderingBase.HTML_RENDERING_QUIRKS;
187: else if (docType.equals(Html.XHTML_10))
188: rendering = TagRenderingBase.XHTML_RENDERING;
189: else
190: rendering = TagRenderingBase.getDefaultDocType();
191: } else {
192: rendering = TagRenderingBase.getDefaultDocType();
193: }
194: pageContext.getRequest().setAttribute(Html.DOC_TYPE_OVERRIDE,
195: new Integer(rendering));
196: }
197:
198: /**
199: * the tag extension lifecycle method called when the tag is first
200: * encountered. This will cause the body of
201: * the tag to be evaluated.
202: * @return int indicating that the body should be evaluated.
203: * @throws JspException on errors.
204: */
205: public int doStartTag() throws JspException {
206:
207: Tag parent = getParent();
208: if (parent != null) {
209: String s = Bundle.getString("TempExcp_ContainedTemplate");
210: registerTagError(s, null);
211: reportErrors();
212: _fatalError = true;
213: return SKIP_BODY;
214: }
215:
216: ServletRequest req = pageContext.getRequest();
217: _savedContext = (TemplateContext) req
218: .getAttribute(TEMPLATE_SECTIONS);
219: TemplateContext ctxt = new TemplateContext();
220: req.setAttribute(TEMPLATE_SECTIONS, ctxt);
221:
222: return EVAL_BODY_INCLUDE;
223: }
224:
225: /**
226: * The tag extension lifecycle method called after the tag has
227: * processed the body. This method will include the template
228: * JSP page specified by the <code>templatePage</code> attribute. The
229: * contents of the template are made available to the template page.
230: * @return SKIP_PAGE to skip all processing after the template.
231: * @throws JspException on all errors. The most common error is
232: * an error indicating that the JSP page representing the Template
233: * isn't found.
234: */
235: public int doEndTag() throws JspException {
236:
237: // if there was an error, exit
238: if (_fatalError) {
239: localRelease();
240: return EVAL_PAGE;
241: }
242:
243: // get the request and response
244: ServletRequest req = pageContext.getRequest();
245: ServletResponse resp = pageContext.getResponse();
246:
247: if (_innerErrors != null) {
248: req.setAttribute(CONTAINER_ERRORS, _innerErrors);
249: }
250:
251: String realURI = getRealURI((HttpServletRequest) req,
252: _templatePage);
253: if (!templateExists(realURI)) {
254: String s = Bundle.getString("TempExcp_MissingTemplate",
255: new Object[] { _templatePage });
256: registerTagError(s, null);
257: reportErrors();
258: localRelease();
259: return SKIP_BODY;
260: }
261:
262: RequestDispatcher rd = req.getRequestDispatcher(realURI);
263: if (rd == null) {
264: String s = Bundle.getString(
265: "Temp_RequestDispatcherReturnNull",
266: new Object[] { "realURI" });
267: registerTagError(s, null);
268: reportErrors();
269: localRelease();
270: return SKIP_PAGE;
271: }
272: try {
273: // dispatch to the template itself...
274: JspWriter out = pageContext.getOut();
275: out.flush();
276: // We have to make sure that the Page Flow framework doesn't use the Servlet Include path as the request
277: // URI while we're rendering a template page. This is so that rendered URLs are relative to the current
278: // page, not the included template page.
279: InternalUtils.setIgnoreIncludeServletPath(req, true);
280: try {
281: rd.include(req, resp);
282: } finally {
283: InternalUtils.setIgnoreIncludeServletPath(req, false);
284: }
285: } catch (IOException e) {
286: String s = Bundle.getString(
287: "TempExcp_ExceptIncludeTemplate", new Object[] {
288: "IOException", _templatePage });
289: registerTagError(s, null);
290: reportErrors();
291: localRelease();
292: return SKIP_PAGE;
293: } catch (ServletException se) {
294:
295: System.err.println("Servlet Excepiton");
296:
297: // Report the servlet exception
298: String s = Bundle.getString("TempLog_ServletException",
299: new Object[] { se.getMessage() });
300: registerTagError(s, null);
301:
302: // walk the servlet hierarchy
303: Throwable t = se.getRootCause();
304: if (t == null) {
305: s = Bundle
306: .getString("TempLog_ServletError",
307: new Object[] { _templatePage,
308: se.getMessage() });
309: registerTagError(s, null);
310: reportErrors();
311: localRelease();
312: return SKIP_PAGE;
313: }
314:
315: // Walk all of the errors
316: while (t != null
317: && (t instanceof ServletException || t instanceof JspException)) {
318:
319: s = Bundle.getString("TempLog_Cause", new Object[] {
320: t.getClass().getName(), t.getMessage() });
321: logger.error(s);
322:
323: if (t.getMessage() == null) {
324: logger.error("Unwinding Servlet Exception", t);
325: t.printStackTrace();
326: }
327: if (t instanceof ServletException)
328: t = ((ServletException) t).getRootCause();
329: else
330: t = ((JspException) t).getRootCause();
331: }
332: if (t == null) {
333: s = Bundle
334: .getString("TempLog_ServletError",
335: new Object[] { _templatePage,
336: se.getMessage() });
337: registerTagError(s, null);
338: reportErrors();
339: logger.error(s);
340: localRelease();
341: return SKIP_PAGE;
342: }
343: if (t instanceof AssertionError) {
344: s = Bundle.getString("TempLog_AssertCause",
345: new Object[] { t.getStackTrace().toString(), });
346: registerTagError(s, null);
347: } else {
348: s = Bundle.getString("TempLog_Cause", new Object[] {
349: t.getClass().getName(), t.getMessage() });
350: registerTagError(s, null);
351: }
352: s = Bundle.getString("TempExcp_ExceptIncludeTemplate",
353: new Object[] { "ServletException", _templatePage });
354: registerTagError(s, null);
355: reportErrors();
356: localRelease();
357: return SKIP_PAGE;
358: }
359:
360: // skip the page because on this pass we forwarded to the template
361: // for rendering...
362: req.setAttribute(TEMPLATE_SECTIONS, _savedContext);
363: localRelease();
364: return SKIP_PAGE;
365: }
366:
367: private boolean templateExists(String realURI) throws JspException {
368: try {
369: URL uri = pageContext.getServletContext().getResource(
370: realURI);
371: return (uri != null);
372: } catch (MalformedURLException e) {
373: String s = Bundle.getString(
374: "TempExcp_ExceptIncludeDefault", new Object[] {
375: "MalformedURLException", realURI });
376: logger.error(s, e);
377: JspException jspException = new JspException(s, e);
378: // todo: future cleanup
379: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
380: // this will cause an IllegalStateException on the following call.
381: if (jspException.getCause() == null) {
382: jspException.initCause(e);
383: }
384: throw jspException;
385: }
386: }
387:
388: static String getRealURI(HttpServletRequest req, String uri) {
389: if (uri.charAt(0) == '/') {
390: return uri;
391: }
392: String path = req.getServletPath();
393: int pos = path.lastIndexOf('/');
394: if (pos != -1) {
395: path = path.substring(0, pos + 1);
396: }
397: return path + uri;
398: }
399:
400: /**
401: * Reset all of the fields of the tag.
402: */
403: protected void localRelease() {
404: super .localRelease();
405: _fatalError = false;
406: _templatePage = null;
407: _innerErrors = null;
408: _reportErrors = false;
409: _savedContext = null;
410: }
411:
412: /**
413: * Add an error to the errors being reported by this tag.
414: * @param ape - The AbstractPageError to add
415: */
416: public void addError(AbstractPageError ape) {
417: if (_innerErrors == null) {
418: _innerErrors = new InnerErrorReporter();
419: }
420: _innerErrors.addError(ape);
421: }
422:
423: /**
424: * Return an ArrayList of the errors
425: * @return an <code>ArrayList</code> of all errors.
426: */
427: public ArrayList returnErrors() {
428: if (_innerErrors == null) {
429: _innerErrors = new InnerErrorReporter();
430: }
431: return _innerErrors.returnErrors();
432: }
433:
434: /**
435: * This boolean indicates if an ErrorReporter is reporting errors
436: * or not. The caller should check this before calling addError
437: * because the ErrorReporter may be off for some reason.
438: * @return a boolean indicating if the tag is reporting errors or not.
439: */
440: public boolean isReporting() {
441: return _reportErrors;
442: }
443:
444: static class InnerErrorReporter implements
445: org.apache.beehive.netui.tags.IErrorReporter {
446: /**
447: * The errors reported by contained tags
448: */
449: private ArrayList _errors;
450:
451: /**
452: * Add an error to the errors being reported by this tag.
453: * @param ape - The AbstractPageError to add
454: */
455: public void addError(AbstractPageError ape) {
456: assert (ape != null);
457: if (_errors == null) {
458: _errors = new ArrayList();
459: }
460:
461: // add the error and update it
462: _errors.add(ape);
463: ape.errorNo = _errors.size();
464: }
465:
466: /**
467: * This boolean indicates if an ErrorReporter is reporting errors
468: * or not. The caller should check this before calling addError
469: * because the ErrorReporter may be off for some reason.
470: * @return a boolean indicating if the tag is reporting errors or not.
471: */
472: public boolean isReporting() {
473: return true;
474: }
475:
476: /**
477: * Return an ArrayList of the errors
478: * @return an <code>ArrayList</code> of all errors.
479: */
480: public ArrayList returnErrors() {
481: ArrayList ret = _errors;
482: _errors = null;
483: return ret;
484: }
485: }
486: }
|