504 lines
17 KiB
Java
504 lines
17 KiB
Java
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* $Header:$
|
|
*/
|
|
package org.apache.beehive.netui.tags.template;
|
|
|
|
import org.apache.beehive.netui.tags.AbstractClassicTag;
|
|
import org.apache.beehive.netui.tags.AbstractPageError;
|
|
import org.apache.beehive.netui.tags.IErrorReporter;
|
|
import org.apache.beehive.netui.tags.html.Html;
|
|
import org.apache.beehive.netui.tags.rendering.TagRenderingBase;
|
|
import org.apache.beehive.netui.util.Bundle;
|
|
import org.apache.beehive.netui.util.logging.Logger;
|
|
import org.apache.beehive.netui.pageflow.internal.InternalUtils;
|
|
|
|
import javax.servlet.RequestDispatcher;
|
|
import javax.servlet.ServletException;
|
|
import javax.servlet.ServletRequest;
|
|
import javax.servlet.ServletResponse;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.jsp.JspException;
|
|
import javax.servlet.jsp.JspWriter;
|
|
import javax.servlet.jsp.tagext.Tag;
|
|
import java.io.IOException;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
|
|
/**
|
|
* This tags defines the template to use within a content page. The
|
|
* content page interacts with the template page through children tags
|
|
* of the
|
|
* <code>Template</code> tag. The legal children are as follows:
|
|
* <ul>
|
|
* <li> <code>setAttribute</code> -- A Tag that will set an Attribute on the
|
|
* template.
|
|
* <li> <code>section</code> -- A tag that defines the content of a section
|
|
* defined in the template.
|
|
* </ul>
|
|
* The URL of the template file is set as the <code>templatePage</code>
|
|
* attribute on the
|
|
* <code>Template</code> tag. The Template file is included from
|
|
* the <code>Template</code> tag and will include sections defined
|
|
* in the content page. The content is contained in one or more
|
|
* <code>Section</code> tags, which are children of the
|
|
* <code>Template</code> tag. In addition, the content page can set
|
|
* attributes of the template page.
|
|
|
|
* @jsptagref.tagdescription
|
|
* Points a content page at its template page. A
|
|
* content page interacts with its template page through children tags
|
|
* of the
|
|
* <netui-template:template> tag. The legal children are as follows:
|
|
* <blockquote>
|
|
* <ul>
|
|
* <li> {@link SetAttribute} -- a tag that fills a
|
|
* {@link Attribute} placeholder with content.
|
|
* <li> {@link Section} -- a tag that fills a
|
|
* {@link IncludeSection} placeholder with content.
|
|
* </ul>
|
|
* </blockquote>
|
|
* <p>The URL of the template page is specified by the <code>templatePage</code>
|
|
* attribute on the
|
|
* <netui-template:template> tag.
|
|
*
|
|
* @example
|
|
* The following example shows a content page that adopts the template.jsp page as its template.
|
|
* The content page also sets the "title" attribute on the template.
|
|
*
|
|
* <pre> <netui-template:template templatePage="./template.jsp">
|
|
* ...
|
|
* <netui-template:setAttribute name="title" value="Template Tags Sample"/>
|
|
* ...
|
|
* </netui-template:template></pre>
|
|
*
|
|
* @netui:tag name="template"
|
|
* description="Use this tag to associate a JSP page with a particular template file."
|
|
*/
|
|
public class Template extends AbstractClassicTag
|
|
implements TemplateConstants, IErrorReporter
|
|
{
|
|
private static final Logger logger = Logger.getInstance(Template.class);
|
|
|
|
public static class TemplateContext
|
|
{
|
|
HashMap secs = null;
|
|
}
|
|
|
|
/**
|
|
* Saved context for the nested case
|
|
*/
|
|
private TemplateContext _savedContext = null;
|
|
|
|
private boolean _fatalError = false;
|
|
|
|
/**
|
|
* Inner class that is exposed to handle errors
|
|
*/
|
|
private org.apache.beehive.netui.tags.IErrorReporter _innerErrors;
|
|
|
|
/**
|
|
* Returns the name of the Tag. This is used to
|
|
* identify the type of tag reporting errors.
|
|
*/
|
|
public String getTagName() {
|
|
return "Template";
|
|
}
|
|
|
|
/**
|
|
* The URL of the template file...
|
|
*/
|
|
private String _templatePage;
|
|
|
|
/**
|
|
* boolean indicating the error reporting.
|
|
*/
|
|
private boolean _reportErrors = false;
|
|
|
|
/**
|
|
* Set the boolean indicating that the template should report errors. The
|
|
* errors will be reported through a HTML tag that must be found
|
|
* in the template JSP.
|
|
* @param reportErrors boolean indicating that errors should be reported
|
|
*
|
|
* @jsptagref.attributedescription
|
|
* Boolean. Determines if the template should report errors.
|
|
* The errors will be reported inline on the JSP page.
|
|
*
|
|
* @jsptagref.databindable false
|
|
*
|
|
* @jsptagref.attributesyntaxvalue <i>boolean_reportErrors</i>
|
|
*
|
|
* @netui:attribute required="false" rtexprvalue="true"
|
|
* description="Determines if the template should report errors.
|
|
* The errors will be reported inline on the JSP page."
|
|
*/
|
|
public void setReportErrors(boolean reportErrors) {
|
|
_reportErrors = reportErrors;
|
|
}
|
|
|
|
/**
|
|
* Set the URL of the template to use. The <code>templatePage</code>
|
|
* is an URL which
|
|
* identifies the JSP template page.
|
|
* @param templatePage - a URL pointing to a JSP page that represents the
|
|
* template.
|
|
*
|
|
* @jsptagref.attributedescription
|
|
* The URL of the template page to use.
|
|
*
|
|
* @jsptagref.databindable false
|
|
*
|
|
* @jsptagref.attributesyntaxvalue <i>string_urlToTemplatePage</i>
|
|
*
|
|
* @netui:attribute required="true" rtexprvalue="true"
|
|
* description="The URL of the template page to use."
|
|
*/
|
|
public void setTemplatePage(String templatePage) {
|
|
_templatePage = templatePage;
|
|
}
|
|
|
|
/**
|
|
* @netui:attribute required="false" rtexprvalue="true"
|
|
* description="Set the document type (html4-loose or xhtml1-transitional) of the document."
|
|
*/
|
|
public void setDocumentType(String docType)
|
|
{
|
|
int rendering = 0;
|
|
if (docType != null) {
|
|
if (docType.equals(Html.HTML_401))
|
|
rendering = TagRenderingBase.HTML_RENDERING;
|
|
else if (docType.equals(Html.HTML_401_QUIRKS))
|
|
rendering = TagRenderingBase.HTML_RENDERING_QUIRKS;
|
|
else if (docType.equals(Html.XHTML_10))
|
|
rendering = TagRenderingBase.XHTML_RENDERING;
|
|
else
|
|
rendering = TagRenderingBase.getDefaultDocType();
|
|
}
|
|
else {
|
|
rendering = TagRenderingBase.getDefaultDocType();
|
|
}
|
|
pageContext.getRequest().setAttribute(Html.DOC_TYPE_OVERRIDE, new Integer(rendering));
|
|
}
|
|
|
|
/**
|
|
* the tag extension lifecycle method called when the tag is first
|
|
* encountered. This will cause the body of
|
|
* the tag to be evaluated.
|
|
* @return int indicating that the body should be evaluated.
|
|
* @throws JspException on errors.
|
|
*/
|
|
public int doStartTag()
|
|
throws JspException {
|
|
|
|
Tag parent = getParent();
|
|
if (parent != null) {
|
|
String s = Bundle.getString("TempExcp_ContainedTemplate");
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
_fatalError = true;
|
|
return SKIP_BODY;
|
|
}
|
|
|
|
ServletRequest req = pageContext.getRequest();
|
|
_savedContext = (TemplateContext) req.getAttribute(TEMPLATE_SECTIONS);
|
|
TemplateContext ctxt = new TemplateContext();
|
|
req.setAttribute(TEMPLATE_SECTIONS,ctxt);
|
|
|
|
return EVAL_BODY_INCLUDE;
|
|
}
|
|
|
|
/**
|
|
* The tag extension lifecycle method called after the tag has
|
|
* processed the body. This method will include the template
|
|
* JSP page specified by the <code>templatePage</code> attribute. The
|
|
* contents of the template are made available to the template page.
|
|
* @return SKIP_PAGE to skip all processing after the template.
|
|
* @throws JspException on all errors. The most common error is
|
|
* an error indicating that the JSP page representing the Template
|
|
* isn't found.
|
|
*/
|
|
public int doEndTag()
|
|
throws JspException {
|
|
|
|
// if there was an error, exit
|
|
if (_fatalError) {
|
|
localRelease();
|
|
return EVAL_PAGE;
|
|
}
|
|
|
|
// get the request and response
|
|
ServletRequest req = pageContext.getRequest();
|
|
ServletResponse resp = pageContext.getResponse();
|
|
|
|
if (_innerErrors != null) {
|
|
req.setAttribute(CONTAINER_ERRORS,_innerErrors);
|
|
}
|
|
|
|
String realURI = getRealURI((HttpServletRequest) req,_templatePage);
|
|
if (!templateExists(realURI)) {
|
|
String s = Bundle.getString("TempExcp_MissingTemplate",
|
|
new Object[]{_templatePage});
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
localRelease();
|
|
return SKIP_BODY;
|
|
}
|
|
|
|
RequestDispatcher rd = req.getRequestDispatcher(realURI);
|
|
if (rd == null) {
|
|
String s = Bundle.getString("Temp_RequestDispatcherReturnNull",new Object[]{"realURI"});
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
localRelease();
|
|
return SKIP_PAGE;
|
|
}
|
|
try {
|
|
// dispatch to the template itself...
|
|
JspWriter out = pageContext.getOut();
|
|
out.flush();
|
|
// We have to make sure that the Page Flow framework doesn't use the Servlet Include path as the request
|
|
// URI while we're rendering a template page. This is so that rendered URLs are relative to the current
|
|
// page, not the included template page.
|
|
InternalUtils.setIgnoreIncludeServletPath( req, true );
|
|
try {
|
|
rd.include(req,resp);
|
|
}
|
|
finally {
|
|
InternalUtils.setIgnoreIncludeServletPath( req, false );
|
|
}
|
|
}
|
|
catch (IOException e) {
|
|
String s = Bundle.getString("TempExcp_ExceptIncludeTemplate",
|
|
new Object[]{"IOException",
|
|
_templatePage});
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
localRelease();
|
|
return SKIP_PAGE;
|
|
}
|
|
catch (ServletException se) {
|
|
|
|
System.err.println("Servlet Excepiton");
|
|
|
|
// Report the servlet exception
|
|
String s = Bundle.getString("TempLog_ServletException",
|
|
new Object[]{se.getMessage()});
|
|
registerTagError(s,null);
|
|
|
|
// walk the servlet hierarchy
|
|
Throwable t = se.getRootCause();
|
|
if (t == null) {
|
|
s = Bundle.getString("TempLog_ServletError",
|
|
new Object[]
|
|
{_templatePage,
|
|
se.getMessage()
|
|
});
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
localRelease();
|
|
return SKIP_PAGE;
|
|
}
|
|
|
|
// Walk all of the errors
|
|
while (t != null && (t instanceof ServletException ||
|
|
t instanceof JspException)) {
|
|
|
|
s = Bundle.getString("TempLog_Cause",
|
|
new Object[]
|
|
{t.getClass().getName(),
|
|
t.getMessage()
|
|
});
|
|
logger.error(s);
|
|
|
|
if (t.getMessage() == null) {
|
|
logger.error("Unwinding Servlet Exception",t);
|
|
t.printStackTrace();
|
|
}
|
|
if (t instanceof ServletException)
|
|
t = ((ServletException) t).getRootCause();
|
|
else
|
|
t = ((JspException) t).getRootCause();
|
|
}
|
|
if (t == null) {
|
|
s = Bundle.getString("TempLog_ServletError",
|
|
new Object[]
|
|
{_templatePage,
|
|
se.getMessage()
|
|
});
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
logger.error(s);
|
|
localRelease();
|
|
return SKIP_PAGE;
|
|
}
|
|
if (t instanceof AssertionError) {
|
|
s = Bundle.getString("TempLog_AssertCause",
|
|
new Object[]
|
|
{t.getStackTrace().toString(),
|
|
});
|
|
registerTagError(s,null);
|
|
}
|
|
else {
|
|
s = Bundle.getString("TempLog_Cause",
|
|
new Object[]
|
|
{t.getClass().getName(),
|
|
t.getMessage()
|
|
});
|
|
registerTagError(s,null);
|
|
}
|
|
s = Bundle.getString("TempExcp_ExceptIncludeTemplate",
|
|
new Object[]{"ServletException",
|
|
_templatePage});
|
|
registerTagError(s,null);
|
|
reportErrors();
|
|
localRelease();
|
|
return SKIP_PAGE;
|
|
}
|
|
|
|
// skip the page because on this pass we forwarded to the template
|
|
// for rendering...
|
|
req.setAttribute(TEMPLATE_SECTIONS,_savedContext);
|
|
localRelease();
|
|
return SKIP_PAGE;
|
|
}
|
|
|
|
private boolean templateExists(String realURI)
|
|
throws JspException
|
|
{
|
|
try {
|
|
URL uri = pageContext.getServletContext().getResource(realURI);
|
|
return (uri != null);
|
|
}
|
|
catch (MalformedURLException e) {
|
|
String s = Bundle.getString("TempExcp_ExceptIncludeDefault",
|
|
new Object[]{"MalformedURLException",
|
|
realURI});
|
|
logger.error(s,e);
|
|
JspException jspException = new JspException(s, e);
|
|
// todo: future cleanup
|
|
// The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
|
|
// this will cause an IllegalStateException on the following call.
|
|
if (jspException.getCause() == null) {
|
|
jspException.initCause(e);
|
|
}
|
|
throw jspException;
|
|
}
|
|
}
|
|
|
|
static String getRealURI(HttpServletRequest req,String uri) {
|
|
if (uri.charAt(0) == '/') {
|
|
return uri;
|
|
}
|
|
String path = req.getServletPath();
|
|
int pos = path.lastIndexOf('/');
|
|
if (pos != -1) {
|
|
path = path.substring(0,pos+1);
|
|
}
|
|
return path+uri;
|
|
}
|
|
|
|
/**
|
|
* Reset all of the fields of the tag.
|
|
*/
|
|
protected void localRelease() {
|
|
super.localRelease();
|
|
_fatalError = false;
|
|
_templatePage = null;
|
|
_innerErrors = null;
|
|
_reportErrors = false;
|
|
_savedContext = null;
|
|
}
|
|
|
|
/**
|
|
* Add an error to the errors being reported by this tag.
|
|
* @param ape - The AbstractPageError to add
|
|
*/
|
|
public void addError(AbstractPageError ape) {
|
|
if (_innerErrors == null) {
|
|
_innerErrors = new InnerErrorReporter();
|
|
}
|
|
_innerErrors.addError(ape);
|
|
}
|
|
|
|
/**
|
|
* Return an ArrayList of the errors
|
|
* @return an <code>ArrayList</code> of all errors.
|
|
*/
|
|
public ArrayList returnErrors() {
|
|
if (_innerErrors == null) {
|
|
_innerErrors = new InnerErrorReporter();
|
|
}
|
|
return _innerErrors.returnErrors();
|
|
}
|
|
|
|
/**
|
|
* This boolean indicates if an ErrorReporter is reporting errors
|
|
* or not. The caller should check this before calling addError
|
|
* because the ErrorReporter may be off for some reason.
|
|
* @return a boolean indicating if the tag is reporting errors or not.
|
|
*/
|
|
public boolean isReporting() {
|
|
return _reportErrors;
|
|
}
|
|
|
|
static class InnerErrorReporter implements org.apache.beehive.netui.tags.IErrorReporter
|
|
{
|
|
/**
|
|
* The errors reported by contained tags
|
|
*/
|
|
private ArrayList _errors;
|
|
|
|
/**
|
|
* Add an error to the errors being reported by this tag.
|
|
* @param ape - The AbstractPageError to add
|
|
*/
|
|
public void addError(AbstractPageError ape) {
|
|
assert (ape != null);
|
|
if (_errors == null) {
|
|
_errors = new ArrayList();
|
|
}
|
|
|
|
// add the error and update it
|
|
_errors.add(ape);
|
|
ape.errorNo = _errors.size();
|
|
}
|
|
|
|
/**
|
|
* This boolean indicates if an ErrorReporter is reporting errors
|
|
* or not. The caller should check this before calling addError
|
|
* because the ErrorReporter may be off for some reason.
|
|
* @return a boolean indicating if the tag is reporting errors or not.
|
|
*/
|
|
public boolean isReporting() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return an ArrayList of the errors
|
|
* @return an <code>ArrayList</code> of all errors.
|
|
*/
|
|
public ArrayList returnErrors() {
|
|
ArrayList ret = _errors;
|
|
_errors = null;
|
|
return ret;
|
|
}
|
|
}
|
|
}
|