001: /*
002: * Title: ApplyDecoratorTag
003: * Description:
004: *
005: * This software is published under the terms of the OpenSymphony Software
006: * License version 1.1, of which a copy has been included with this
007: * distribution in the LICENSE.txt file.
008: */
009:
010: package com.opensymphony.module.sitemesh.taglib.page;
011:
012: import com.opensymphony.module.sitemesh.*;
013: import com.opensymphony.module.sitemesh.filter.PageRequestWrapper;
014: import com.opensymphony.module.sitemesh.filter.PageResponseWrapper;
015:
016: import javax.servlet.RequestDispatcher;
017: import javax.servlet.ServletException;
018: import javax.servlet.http.HttpServletRequest;
019: import javax.servlet.http.HttpServletResponse;
020: import javax.servlet.jsp.JspException;
021: import javax.servlet.jsp.tagext.BodyTagSupport;
022: import java.io.IOException;
023: import java.io.BufferedReader;
024: import java.io.InputStreamReader;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.net.URL;
029: import java.net.URLConnection;
030: import java.net.MalformedURLException;
031:
032: /**
033: * This tag inserts an external resource as a panel into the current Page.
034: *
035: * <p>The page attribute should point to the panel resource
036: * which should expose an entire page (e.g. another JSP file producing
037: * HTML). This attribute can be relative to the page it is being called
038: * from or an absolute path from the context-root.</p>
039: *
040: * <p><strong>OR</strong></p>
041: *
042: * <p>If the page attribute is not specified, the body content is parsed
043: * into the {@link com.opensymphony.module.sitemesh.Page} object and has
044: * the {@link com.opensymphony.module.sitemesh.Decorator} applied.</p>
045: *
046: * <p>The (optional) decorator attribute is the name of the
047: * {@link com.opensymphony.module.sitemesh.Decorator}
048: * to apply to the included page. Note that the implementation of
049: * {@link com.opensymphony.module.sitemesh.DecoratorMapper} can overide this.</p>
050: *
051: * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
052: * @version $Revision: 1.9 $
053: */
054: public class ApplyDecoratorTag extends BodyTagSupport implements
055: RequestConstants {
056: private String page = null;
057: private String decorator = null;
058:
059: private String contentType = null;
060: private String encoding = null;
061:
062: private Map params = new HashMap(6);
063: private Config config = null;
064: private DecoratorMapper decoratorMapper = null;
065: private Factory factory;
066:
067: /**
068: * Tag attribute: URI of page to include.
069: * Can be relative to page being called from, or absolute
070: * path from context-root of web-app.
071: */
072: public void setPage(String page) {
073: this .page = page;
074: }
075:
076: /**
077: * Add a parameter to the page. This has a package level
078: * access modifier so ParamTag can also call it.
079: */
080: void addParam(String name, String value) {
081: params.put(name, value);
082: }
083:
084: /**
085: * Tag attribute: If set, this value will override the 'title'
086: * property of the page. This is a convenience utility and is
087: * identical to specifing a 'page:param name=title' tag.
088: */
089: public void setTitle(String title) {
090: addParam("title", title);
091: }
092:
093: /**
094: * Tag attribute: If set, this value will override the 'id'
095: * property of the page. This is a convenience utility and is
096: * identical to specifing a 'page:param name=id' tag.
097: */
098: public void setId(String id) {
099: addParam("id", id);
100: }
101:
102: /**
103: * Tag attribute: Name of Decorator to apply to Page.
104: * This is passed to DecoratorMapper to retrieve appropriate
105: * Decorator. DecoratorMapper may override if needed.
106: *
107: * @see com.opensymphony.module.sitemesh.DecoratorMapper
108: */
109: public void setName(String decorator) {
110: if (decorator != null)
111: this .decorator = decorator;
112: }
113:
114: /** @deprecated Use setName() instead. */
115: public void setDecorator(String decorator) {
116: setName(decorator);
117: }
118:
119: public void setContentType(String contentType) {
120: this .contentType = contentType;
121: }
122:
123: public void setEncoding(String encoding) {
124: this .encoding = encoding;
125: }
126:
127: public int doStartTag() {
128: if (config == null) {
129: // set context if not already set
130: config = new Config(pageContext.getServletConfig());
131: factory = Factory.getInstance(config);
132: decoratorMapper = factory.getDecoratorMapper();
133: }
134: // return page == null ? EVAL_BODY_BUFFERED : SKIP_BODY;
135: return EVAL_BODY_BUFFERED;
136: }
137:
138: /** Ensure that external page contents are included in bodycontent. */
139: public int doAfterBody() throws JspException {
140: return SKIP_BODY;
141: }
142:
143: /** Standard taglib method: apply decorator to page. */
144: public int doEndTag() throws JspException {
145: try {
146: // if composite decorator, remember last page
147: Page oldPage = (Page) pageContext.getRequest()
148: .getAttribute(PAGE);
149:
150: // parse bodycontent into Page object
151: PageParser parser = getParserSelector().getPageParser(
152: contentType != null ? contentType : "text/html");
153: Page pageObj;
154:
155: if (page == null) {
156: // inline content
157: pageObj = parser.parse(bodyContent.getString()
158: .toCharArray());
159: } else if (page.startsWith("http://")
160: || page.startsWith("https://")) {
161: try {
162: URL url = new URL(page);
163: URLConnection urlConn = url.openConnection();
164: urlConn.setUseCaches(true);
165: BufferedReader in = new BufferedReader(
166: new InputStreamReader(urlConn
167: .getInputStream()));
168:
169: StringBuffer sbuf = new StringBuffer();
170: char[] buf = new char[1000];
171: for (;;) {
172: int moved = in.read(buf);
173: if (moved < 0)
174: break;
175: sbuf.append(buf, 0, moved);
176: }
177: in.close();
178: pageObj = parser.parse(sbuf.toString()
179: .toCharArray());
180: } catch (MalformedURLException e) {
181: throw new JspException(e);
182: } catch (IOException e) {
183: throw new JspException(e);
184: }
185: } else {
186: // external content
187: String fullPath = page;
188: if (fullPath.length() > 0 && fullPath.charAt(0) != '/') {
189: HttpServletRequest request = (HttpServletRequest) pageContext
190: .getRequest();
191:
192: // find absolute path if relative supplied
193: String this Path = request.getServletPath();
194:
195: // check if it did not return null (could occur when the servlet container
196: // does not use a servlet to serve the requested resouce)
197: if (this Path == null) {
198: String requestURI = request.getRequestURI();
199: if (request.getPathInfo() != null) {
200: // strip the pathInfo from the requestURI
201: this Path = requestURI.substring(0,
202: requestURI.indexOf(request
203: .getPathInfo()));
204: } else {
205: this Path = requestURI;
206: }
207: }
208:
209: fullPath = this Path.substring(0, this Path
210: .lastIndexOf('/') + 1)
211: + fullPath;
212: int dotdot;
213: while ((dotdot = fullPath.indexOf("..")) > -1) {
214: int prevSlash = fullPath.lastIndexOf('/',
215: dotdot - 2);
216: fullPath = fullPath.substring(0, prevSlash)
217: + fullPath.substring(dotdot + 2);
218: }
219: }
220:
221: // include page using filter response
222: RequestDispatcher rd = pageContext.getServletContext()
223: .getRequestDispatcher(fullPath);
224: PageRequestWrapper pageRequest = new PageRequestWrapper(
225: (HttpServletRequest) pageContext.getRequest());
226: PageResponseWrapper pageResponse = new PageResponseWrapper(
227: (HttpServletResponse) pageContext.getResponse(),
228: factory);
229:
230: StringBuffer sb = new StringBuffer(
231: contentType != null ? contentType : "text/html");
232: if (encoding != null) {
233: sb.append(";charset=").append(encoding);
234: }
235: pageResponse.setContentType(sb.toString());
236:
237: // if rd == null, then the panel was not found, but this isn't correct, so we need to spit out
238: // something appropriate. What this is, well...I don't know yet.
239: if (rd == null) {
240: throw new ApplyDecoratorException(
241: "The specified resource in applyDecorator tag ("
242: + fullPath + ") was not found.");
243: }
244: rd.include(pageRequest, pageResponse);
245: pageObj = pageResponse.getPage();
246: }
247:
248: // If pageObj == null, then the panel source had some weird error in
249: // it. Stop writing bugs like this. They're ugly and they make you smell funny.
250: if (pageObj == null) {
251: throw new ApplyDecoratorException(page
252: + " did not create a valid page to decorate.");
253: }
254:
255: // add extra params to Page
256: Iterator paramKeys = params.keySet().iterator();
257: while (paramKeys.hasNext()) {
258: String k = (String) paramKeys.next();
259: String v = (String) params.get(k);
260: pageObj.addProperty(k, v);
261: }
262:
263: // get decorator
264: if (decorator == null)
265: decorator = "";
266: pageObj.setRequest((HttpServletRequest) pageContext
267: .getRequest());
268: pageContext.getRequest().setAttribute(DECORATOR, decorator);
269: Decorator d = decoratorMapper.getDecorator(
270: ((HttpServletRequest) pageContext.getRequest()),
271: pageObj);
272: pageContext.getRequest().removeAttribute(DECORATOR);
273:
274: // apply decorator
275: if (d != null && d.getPage() != null) {
276: pageContext.getRequest().setAttribute(PAGE, pageObj);
277: pageContext.include(d.getPage());
278: } else {
279: throw new JspException(
280: "Cannot locate inline Decorator: " + decorator);
281: }
282:
283: // clean up
284: pageContext.getRequest().setAttribute(PAGE, oldPage);
285: params.clear(); // params need to be cleared between invocations - SIM-191
286: } catch (IOException e) {
287: throw new JspException(e);
288: } catch (ServletException e) {
289: throw new JspException(e);
290: } catch (ApplyDecoratorException e) {
291: try {
292: pageContext.getOut().println(e.getMessage());
293: } catch (IOException ioe) {
294: System.err
295: .println("IOException thrown in applyDecorator tag: "
296: + e.toString());
297: }
298: }
299: return EVAL_PAGE;
300: }
301:
302: private PageParserSelector getParserSelector() {
303: return Factory.getInstance(config);
304: }
305:
306: class ApplyDecoratorException extends Exception {
307: public ApplyDecoratorException(String s) {
308: super(s);
309: }
310: }
311: }
|