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.util.internal.InternalStringBuilder;
022:
023: import org.apache.beehive.netui.tags.AbstractClassicTag;
024: import org.apache.beehive.netui.util.Bundle;
025: import org.apache.beehive.netui.util.logging.Logger;
026:
027: import javax.servlet.RequestDispatcher;
028: import javax.servlet.ServletException;
029: import javax.servlet.ServletRequest;
030: import javax.servlet.http.HttpServletRequest;
031: import javax.servlet.http.HttpServletResponse;
032: import javax.servlet.jsp.JspException;
033: import javax.servlet.jsp.JspWriter;
034: import java.io.IOException;
035: import java.io.Writer;
036: import java.net.MalformedURLException;
037: import java.net.URL;
038:
039: /**
040: * Used within a template JSP page to define a placeholder for section content.
041: * Within a template, one or more sections are defined within the overall
042: * structure of the page. Each section has a unique name identifying it.
043: * The content page, through the <code>Section</code> tag, provides content
044: * that is included into the defined sections.
045: * <p>
046: * All content found within the body of the <code>IncludeSection</code>
047: * is ignored.
048:
049: * @jsptagref.tagdescription
050: * Defines a content placeholder within a template.
051: * Each placeholder must have a unique name identifying it.
052: * Different content pages adopt the template page, set properties
053: * on its placeholders (using the {@link Section} tag), and render the
054: * completed HTML in the browser.
055: *
056: * <p>For example, a template page can use the <netui-template:includeSection> tag to
057: * define a content placeholder.
058: *
059: * <p><b>In the template JSP page...</b>
060: *
061: * <pre> <table>
062: * <tr>
063: * <td colspan="3">
064: * <netui-template:includeSection name="tableHeader"/>
065: * </td>
066: * </tr></pre>
067: *
068: * <p>Then a content page can set HTML content in the placeholder using the {@link Section} tag.
069: *
070: * <p><b>In a content JSP page...</b>
071: *
072: * <pre> <netui-template:section name="tableHeader">
073: * <h1>HEADER TEXT</h1>
074: * </netui-template:section></pre>
075: *
076: * <p>The HTML rendered in the browser will appear as follows.
077: *
078: * <pre> <table>
079: * <tr>
080: * <td colspan="3">
081: * <h1>HEADER TEXT</h1>
082: * </td>
083: * </tr></pre>
084: *
085: * <p>If the content page does not define content to be placed in the placeholder, then
086: * the <code>defaultPage</code> attribute will be used. The
087: * <code>defaultPage</code> attribute points at a stand-alone JSP page. The entire contents of the page
088: * will be placed in the placeholder, after any Java elements, such as scriptlets have been resolved.
089: *
090: * @example
091: * In this sample a <netui-template:includeSection> tag defines a place holder for a
092: * table row
093: *
094: * <pre> <tr>
095: * <netui-template:includeSection name="rowPlaceholder" defaultPage="defaultPage.jsp"/>
096: * </tr></pre>
097: *
098: * <p>If there is no content page that sets content into this placeholder using a <netui-template:section>
099: * tag, then the entire contents of the defaultPage.jsp will be used.
100: * Assume that the defaultPage.jsp appears as follows.
101: *
102: * <pre> <p><%= 1 + 1 %></p></pre>
103: *
104: * Then the HTML rendered in the browser will appear as follows. Note that the Java scriptlet
105: * <code><%= 1 + 1 %></code> has been resolved to the value <code>2</code>.
106: *
107: * <pre> <tr>
108: * <p>2</p>
109: * </tr></pre>
110: *
111: * @netui:tag name="includeSection"
112: * description="Include this tag in a template file to mark out content that will be used in another JSP page."
113: */
114: public class IncludeSection extends AbstractClassicTag implements
115: TemplateConstants {
116: private static final Logger logger = Logger
117: .getInstance(IncludeSection.class);
118:
119: /**
120: * The name of the section.
121: */
122: private String _name;
123:
124: /**
125: * The name of a JSP that will act as a default page
126: */
127: private String _default;
128:
129: /**
130: * Returns the name of the Tag. This is used to
131: * identify the type of tag reporting errors.
132: */
133: public String getTagName() {
134: return "IncludeSection";
135: }
136:
137: /**
138: * Sets the name of the section. This name must be unique within
139: * the template page.
140: * @param name The name of the defined section within the template.
141: * This name must be unique within the template.
142: *
143: * @jsptagref.attributedescription
144: * The name of the section. This name must be unique within the template page.
145: *
146: * @jsptagref.databindable false
147: *
148: * @jsptagref.attributesyntaxvalue <i>string_name</i>
149: *
150: * @netui:attribute required="true" rtexprvalue="true"
151: * description="The name of the section. This name must be unique within the template page."
152: */
153: public void setName(String name) {
154: _name = name;
155: }
156:
157: /**
158: * Sets a default JSP page to provide content for the section if
159: * the content page does not define the content.
160: * @param defaultPage a URL identifying a JSP or HTML page
161: * providing default content to the defined section.
162: *
163: * @jsptagref.attributedescription
164: * A default JSP page to provide content for the placeholder if
165: * the content page fails to define the content.
166: *
167: * @jsptagref.databindable false
168: *
169: * @jsptagref.attributesyntaxvalue <i>string_defaultPage</i>
170: *
171: * @netui:attribute required="false" rtexprvalue="true"
172: * description="A default JSP page to provide content for the placeholder if
173: * the content page fails to define the content."
174: */
175: public void setDefaultPage(String defaultPage) {
176: _default = defaultPage;
177: }
178:
179: /**
180: * Renders the content of the section into the template. Errors
181: * are reported inline within the template in development
182: * mode. If no sections are defined an error is reported. If
183: * a section is not defined and no default URL is provided an
184: * error is reported.
185: * @return SKIP_BODY to skip any content found in the tag.
186: * @throws JspException on Errors.
187: */
188: public int doStartTag() throws JspException {
189: ServletRequest req = pageContext.getRequest();
190: Template.TemplateContext tc = (Template.TemplateContext) req
191: .getAttribute(TEMPLATE_SECTIONS);
192: if (tc == null) {
193:
194: String s = Bundle.getString("Tags_TemplateContextMissing");
195: // report the error. If this returns a value then we throw an
196: // exception
197: logger.warn(stripBold(s));
198: registerTagError(s, null);
199: reportErrors();
200: localRelease();
201: return SKIP_BODY;
202: }
203: if (tc.secs == null) {
204: if (_default != null) {
205: return callDefault(req);
206: }
207: String s = Bundle.getString("Tags_TemplateSectionMissing",
208: new Object[] { _name });
209: // report the error. If this returns a value then we throw an
210: // exception
211: logger.warn(stripBold(s));
212: registerTagError(s, null);
213: reportErrors();
214: localRelease();
215: return SKIP_BODY;
216: }
217:
218: String val = (String) tc.secs.get(_name);
219: if (val == null) {
220:
221: if (_default == null) {
222: String s = Bundle.getString(
223: "Tags_TemplateSectionMissing",
224: new Object[] { _name });
225: logger.warn(stripBold(s));
226:
227: // report the error. If this returns a value then we throw an
228: // exception
229: registerTagError(s, null);
230: reportErrors();
231: localRelease();
232: return SKIP_BODY;
233: }
234: return callDefault(req);
235:
236: }
237:
238: try {
239: Writer out = pageContext.getOut();
240: out.write(val);
241: } catch (IOException e) {
242: String reason = Bundle.getString("TempExcp_WritingContent");
243: String s = Bundle.getString("TempExcp_Except",
244: new Object[] { "IOException", reason });
245: logger.error(s);
246:
247: JspException jspException = new JspException(s, e);
248: // todo: future cleanup
249: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
250: // this will cause an IllegalStateException on the following call.
251: if (jspException.getCause() == null) {
252: jspException.initCause(e);
253: }
254: throw jspException;
255: }
256: // continue to evalue the page...(This should be a template)
257: localRelease();
258: return SKIP_BODY;
259: }
260:
261: private int callDefault(ServletRequest req) throws JspException {
262: if (!defaultExists()) {
263: String s = Bundle.getString("TempExcp_MissingDefaultPage",
264: new Object[] { _default });
265: logger.error(s);
266: registerTagError(s, null);
267: reportErrors();
268: localRelease();
269: return SKIP_BODY;
270: }
271:
272: try {
273: HttpServletResponse resp = (HttpServletResponse) pageContext
274: .getResponse();
275:
276: RequestDispatcher rd = req.getRequestDispatcher(_default);
277: // This is now causing an IOException in Weblogic. This code was added because Tomcat didn't work
278: // without it. I'm going to catch and ignore the IOException because this really should affect
279: // things.
280: try {
281: JspWriter out = pageContext.getOut();
282: out.flush();
283: } catch (IOException ignore) {
284: }
285: rd.include(req, resp);
286: localRelease();
287: return SKIP_BODY;
288: } catch (IOException e) {
289: String s = Bundle.getString(
290: "TempExcp_ExceptIncludeDefault", new Object[] {
291: "IOException", _default });
292: logger.error(s, e);
293: JspException jspException = new JspException(s, e);
294: // todo: future cleanup
295: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
296: // this will cause an IllegalStateException on the following call.
297: if (jspException.getCause() == null) {
298: jspException.initCause(e);
299: }
300: throw jspException;
301: } catch (ServletException se) {
302: String s = Bundle.getString(
303: "TempExcp_ExceptIncludeDefault", new Object[] {
304: "ServletException", _default });
305: logger.error(s, se);
306: JspException jspException = new JspException(s, se);
307: // todo: future cleanup
308: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
309: // this will cause an IllegalStateException on the following call.
310: if (jspException.getCause() == null) {
311: jspException.initCause(se);
312: }
313: throw jspException;
314: }
315: }
316:
317: private boolean defaultExists() throws JspException {
318: HttpServletRequest req = (HttpServletRequest) pageContext
319: .getRequest();
320: String realURI = Template.getRealURI(req, _default);
321: try {
322: URL uri = pageContext.getServletContext().getResource(
323: realURI);
324: return (uri != null);
325: } catch (MalformedURLException e) {
326: String s = Bundle.getString(
327: "TempExcp_ExceptIncludeDefault", new Object[] {
328: "MalformedURLException", _default });
329: logger.error(s, e);
330: JspException jspException = new JspException(s, e);
331: // todo: future cleanup
332: // The 2.5 Servlet api will set the initCause in the Throwable superclass during construction,
333: // this will cause an IllegalStateException on the following call.
334: if (jspException.getCause() == null) {
335: jspException.initCause(e);
336: }
337: throw jspException;
338: }
339: }
340:
341: /**
342: * Resets all of the fields of the tag.
343: */
344: protected void localRelease() {
345: super .localRelease();
346: _name = null;
347: _default = null;
348: }
349:
350: /**
351: * This will strip any html out of a warning
352: */
353: static String stripBold(String in) {
354: String boldStart = "<b>";
355: String boldEnd = "</b>";
356: int pos = in.indexOf(boldStart);
357: if (pos == -1)
358: return in;
359: InternalStringBuilder sb = new InternalStringBuilder(in
360: .substring(0, pos));
361: int fill = pos + boldStart.length();
362: pos = in.indexOf(boldEnd, fill);
363: if (pos == -1)
364: return in;
365: sb.append(in.substring(fill, pos));
366: pos += boldEnd.length();
367: sb.append(in.substring(pos));
368: return sb.toString();
369: }
370: }
|