001: package com.opensymphony.webwork.components;
002:
003: import com.opensymphony.webwork.config.Configuration;
004: import com.opensymphony.webwork.util.FastByteArrayOutputStream;
005: import com.opensymphony.webwork.RequestUtils;
006: import com.opensymphony.webwork.WebWorkConstants;
007: import com.opensymphony.xwork.util.OgnlValueStack;
008:
009: import org.apache.commons.logging.Log;
010: import org.apache.commons.logging.LogFactory;
011:
012: import javax.servlet.RequestDispatcher;
013: import javax.servlet.ServletException;
014: import javax.servlet.ServletOutputStream;
015: import javax.servlet.ServletRequest;
016: import javax.servlet.http.HttpServletRequest;
017: import javax.servlet.http.HttpServletResponse;
018: import javax.servlet.http.HttpServletResponseWrapper;
019: import java.io.IOException;
020: import java.io.OutputStreamWriter;
021: import java.io.PrintWriter;
022: import java.io.Writer;
023: import java.net.URLEncoder;
024: import java.util.*;
025:
026: /**
027: * <!-- START SNIPPET: javadoc -->
028: * <p>Include a servlet's output (result of servlet or a JSP page).</p>
029: * <p>Note: Any additional params supplied to the included page are <b>not</b> accessible within the rendered page
030: * through the <ww:property...> tag!</p>
031: * <!-- END SNIPPET: javadoc -->
032: *
033: *
034: * <!-- START SNIPPET: params -->
035: * <ul>
036: * <li>value* (String) - jsp page to be included</li>
037: * </ul>
038: * <!-- END SNIPPET: params -->
039: *
040: *
041: * <p/> <b>Examples</b>
042: * <pre>
043: * <!-- START SNIPPET: example -->
044: * <-- One: -->
045: * <ww:include value="myJsp.jsp" />
046: *
047: * <-- Two: -->
048: * <ww:include value="myJsp.jsp">
049: * <ww:param name="param1" value="value2" />
050: * <ww:param name="param2" value="value2" />
051: * </ww:include>
052: *
053: * <-- Three: -->
054: * <ww:include value="myJsp.jsp">
055: * <ww:param name="param1">value1</ww:param>
056: * <ww:param name="param2">value2<ww:param>
057: * </ww:include>
058: * <!-- END SNIPPET: example -->
059: *
060: * <!-- START SNIPPET: exampledescription -->
061: * Example one - do an include myJsp.jsp page
062: * Example two - do an include to myJsp.jsp page with parameters param1=value1 and param2=value2
063: * Example three - do an include to myJsp.jsp page with parameters param1=value1 and param2=value2
064: * <!-- END SNIPPET: exampledescription -->
065: * </pre>
066: *
067: * @author Rickard Oberg (rickard@dreambean.com)
068: * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
069: * @author Rene Gielen
070: * @author tm_jee
071: * @author Rainer Hermanns
072: * @version $Revision: 2676 $
073: * @since 2.2
074: *
075: * @ww.tag name="include" tld-body-content="JSP" tld-tag-class="com.opensymphony.webwork.views.jsp.IncludeTag"
076: * description="Include a servlet's output (result of servlet or a JSP page)"
077: */
078: public class Include extends Component {
079:
080: private static final Log _log = LogFactory.getLog(Include.class);
081:
082: private static String encoding;
083: private static boolean encodingDefined = true;
084:
085: protected String value;
086: private HttpServletRequest req;
087: private HttpServletResponse res;
088:
089: public Include(OgnlValueStack stack, HttpServletRequest req,
090: HttpServletResponse res) {
091: super (stack);
092: this .req = req;
093: this .res = res;
094: }
095:
096: public boolean end(Writer writer, String body) {
097: String page = findString(value, "value",
098: "You must specify the URL to include. Example: /foo.jsp");
099: StringBuffer urlBuf = new StringBuffer();
100:
101: // Add URL
102: urlBuf.append(page);
103:
104: // Add request parameters
105: if (parameters.size() > 0) {
106: urlBuf.append('?');
107:
108: String concat = "";
109:
110: // Set parameters
111: Iterator iter = parameters.entrySet().iterator();
112:
113: while (iter.hasNext()) {
114: Map.Entry entry = (Map.Entry) iter.next();
115: Object name = entry.getKey();
116: List values = (List) entry.getValue();
117:
118: for (int i = 0; i < values.size(); i++) {
119: urlBuf.append(concat);
120: urlBuf.append(name);
121: urlBuf.append('=');
122:
123: try {
124: urlBuf.append(URLEncoder.encode(values.get(i)
125: .toString(), "UTF-8"));
126: } catch (Exception e) {
127: _log.warn("unable to url-encode "
128: + values.get(i).toString()
129: + ", it will be ignored");
130: }
131:
132: concat = "&";
133: }
134: }
135: }
136:
137: String result = urlBuf.toString();
138:
139: // Include
140: try {
141: include(result, writer, req, res);
142: } catch (Exception e) {
143: LogFactory.getLog(getClass()).warn(
144: "Exception thrown during include of " + result, e);
145: }
146:
147: return super .end(writer, body);
148: }
149:
150: /**
151: * The jsp/servlet output to include
152: * @ww.tagattribute required="true" type="String"
153: */
154: public void setValue(String value) {
155: this .value = value;
156: }
157:
158: public static String getContextRelativePath(ServletRequest request,
159: String relativePath) {
160: String returnValue;
161:
162: if (relativePath.startsWith("/")) {
163: returnValue = relativePath;
164: } else if (!(request instanceof HttpServletRequest)) {
165: returnValue = relativePath;
166: } else {
167: HttpServletRequest hrequest = (HttpServletRequest) request;
168: String uri = (String) request
169: .getAttribute("javax.servlet.include.servlet_path");
170:
171: if (uri == null) {
172: uri = RequestUtils.getServletPath(hrequest);
173: }
174:
175: returnValue = uri.substring(0, uri.lastIndexOf('/')) + '/'
176: + relativePath;
177: }
178:
179: // .. is illegal in an absolute path according to the Servlet Spec and will cause
180: // known problems on Orion application servers.
181: if (returnValue.indexOf("..") != -1) {
182: Stack stack = new Stack();
183: StringTokenizer pathParts = new StringTokenizer(returnValue
184: .replace('\\', '/'), "/");
185:
186: while (pathParts.hasMoreTokens()) {
187: String part = pathParts.nextToken();
188:
189: if (!part.equals(".")) {
190: if (part.equals("..")) {
191: stack.pop();
192: } else {
193: stack.push(part);
194: }
195: }
196: }
197:
198: StringBuffer flatPathBuffer = new StringBuffer();
199:
200: for (int i = 0; i < stack.size(); i++) {
201: flatPathBuffer.append("/").append(stack.elementAt(i));
202: }
203:
204: returnValue = flatPathBuffer.toString();
205: }
206:
207: return returnValue;
208: }
209:
210: public void addParameter(String key, Object value) {
211: // don't use the default implementation of addParameter,
212: // instead, include tag requires that each parameter be a list of objects,
213: // just like the HTTP servlet interfaces are (String[])
214: if (value != null) {
215: List currentValues = (List) parameters.get(key);
216:
217: if (currentValues == null) {
218: currentValues = new ArrayList();
219: parameters.put(key, currentValues);
220: }
221:
222: currentValues.add(value);
223: }
224: }
225:
226: public static void include(String aResult, Writer writer,
227: ServletRequest request, HttpServletResponse response)
228: throws ServletException, IOException {
229: String resourcePath = getContextRelativePath(request, aResult);
230: RequestDispatcher rd = request
231: .getRequestDispatcher(resourcePath);
232:
233: if (rd == null) {
234: throw new ServletException("Not a valid resource path:"
235: + resourcePath);
236: }
237:
238: PageResponse pageResponse = new PageResponse(response);
239:
240: // Include the resource
241: rd.include((HttpServletRequest) request, pageResponse);
242:
243: //write the response back to the JspWriter, using the correct encoding.
244: String encoding = getEncoding();
245:
246: if (encoding != null) {
247: //use the encoding specified in the property file
248: pageResponse.getContent().writeTo(writer, encoding);
249: } else {
250: //use the platform specific encoding
251: pageResponse.getContent().writeTo(writer, null);
252: }
253: }
254:
255: /**
256: * Get the encoding specified by the property 'webwork.i18n.encoding' in webwork.properties,
257: * or return the default platform encoding if not specified.
258: * <p/>
259: * Note that if the property is not initially defined, this will return the system default,
260: * even if the property is later defined. This is mainly for performance reasons. Undefined
261: * properties throw exceptions, which are a costly operation.
262: * <p/>
263: * If the property is initially defined, it is read every time, until is is undefined, and then
264: * the system default is used.
265: * <p/>
266: * Why not cache it completely? Some applications will wish to be able to dynamically set the
267: * encoding at runtime.
268: *
269: * @return The encoding to be used.
270: */
271: private static String getEncoding() {
272: if (encodingDefined) {
273: try {
274: encoding = Configuration
275: .getString(WebWorkConstants.WEBWORK_I18N_ENCODING);
276: } catch (IllegalArgumentException e) {
277: encoding = System.getProperty("file.encoding");
278: encodingDefined = false;
279: }
280: }
281:
282: return encoding;
283: }
284:
285: /**
286: * Implementation of ServletOutputStream that stores all data written
287: * to it in a temporary buffer accessible from {@link #getBuffer()} .
288: *
289: * @author <a href="joe@truemesh.com">Joe Walnes</a>
290: * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
291: */
292: static final class PageOutputStream extends ServletOutputStream {
293:
294: private FastByteArrayOutputStream buffer;
295:
296: public PageOutputStream() {
297: buffer = new FastByteArrayOutputStream();
298: }
299:
300: /**
301: * Return all data that has been written to this OutputStream.
302: */
303: public FastByteArrayOutputStream getBuffer() throws IOException {
304: flush();
305:
306: return buffer;
307: }
308:
309: public void close() throws IOException {
310: buffer.close();
311: }
312:
313: public void flush() throws IOException {
314: buffer.flush();
315: }
316:
317: public void write(byte[] b, int o, int l) throws IOException {
318: buffer.write(b, o, l);
319: }
320:
321: public void write(int i) throws IOException {
322: buffer.write(i);
323: }
324:
325: public void write(byte[] b) throws IOException {
326: buffer.write(b);
327: }
328: }
329:
330: /**
331: * Simple wrapper to HTTPServletResponse that will allow getWriter()
332: * and getResponse() to be called as many times as needed without
333: * causing conflicts.
334: * <p/>
335: * The underlying outputStream is a wrapper around
336: * {@link PageOutputStream} which will store
337: * the written content to a buffer.
338: * <p/>
339: * This buffer can later be retrieved by calling {@link #getContent}.
340: *
341: * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
342: * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
343: */
344: static final class PageResponse extends HttpServletResponseWrapper {
345:
346: protected PrintWriter pagePrintWriter;
347: protected ServletOutputStream outputStream;
348: private PageOutputStream pageOutputStream = null;
349:
350: /**
351: * Create PageResponse wrapped around an existing HttpServletResponse.
352: */
353: public PageResponse(HttpServletResponse response) {
354: super (response);
355: }
356:
357: /**
358: * Return the content buffered inside the {@link PageOutputStream}.
359: *
360: * @return
361: * @throws IOException
362: */
363: public FastByteArrayOutputStream getContent()
364: throws IOException {
365: //if we are using a writer, we need to flush the
366: //data to the underlying outputstream.
367: //most containers do this - but it seems Jetty 4.0.5 doesn't
368: if (pagePrintWriter != null) {
369: pagePrintWriter.flush();
370: }
371:
372: return ((PageOutputStream) getOutputStream()).getBuffer();
373: }
374:
375: /**
376: * Return instance of {@link PageOutputStream}
377: * allowing all data written to stream to be stored in temporary buffer.
378: */
379: public ServletOutputStream getOutputStream() throws IOException {
380: if (pageOutputStream == null) {
381: pageOutputStream = new PageOutputStream();
382: }
383:
384: return pageOutputStream;
385: }
386:
387: /**
388: * Return PrintWriter wrapper around PageOutputStream.
389: */
390: public PrintWriter getWriter() throws IOException {
391: if (pagePrintWriter == null) {
392: pagePrintWriter = new PrintWriter(
393: new OutputStreamWriter(getOutputStream(),
394: getCharacterEncoding()));
395: }
396:
397: return pagePrintWriter;
398: }
399: }
400: }
|