1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.components;
19
20 import java.io.IOException;
21 import java.io.OutputStreamWriter;
22 import java.io.PrintWriter;
23 import java.io.Writer;
24 import java.net.URLEncoder;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Stack;
30 import java.util.StringTokenizer;
31
32 import javax.servlet.RequestDispatcher;
33 import javax.servlet.ServletException;
34 import javax.servlet.ServletOutputStream;
35 import javax.servlet.ServletRequest;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38 import javax.servlet.http.HttpServletResponseWrapper;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.struts2.RequestUtils;
43 import org.apache.struts2.StrutsConstants;
44 import org.apache.struts2.config.Settings;
45 import org.apache.struts2.util.FastByteArrayOutputStream;
46
47 import com.opensymphony.xwork2.util.ValueStack;
48
49 /***
50 * <!-- START SNIPPET: javadoc -->
51 * <p>Include a servlet's output (result of servlet or a JSP page).</p>
52 * <p>Note: Any additional params supplied to the included page are <b>not</b> accessible within the rendered page
53 * through the <s:property...> tag!</p>
54 * <!-- END SNIPPET: javadoc -->
55 *
56 *
57 * <!-- START SNIPPET: params -->
58 * <ul>
59 * <li>value* (String) - jsp page to be included</li>
60 * </ul>
61 * <!-- END SNIPPET: params -->
62 *
63 *
64 * <p/> <b>Examples</b>
65 * <pre>
66 * <!-- START SNIPPET: example -->
67 * <-- One: -->
68 * <s:include value="myJsp.jsp" />
69 *
70 * <-- Two: -->
71 * <s:include value="myJsp.jsp">
72 * <s:param name="param1" value="value2" />
73 * <s:param name="param2" value="value2" />
74 * </s:include>
75 *
76 * <-- Three: -->
77 * <s:include value="myJsp.jsp">
78 * <s:param name="param1">value1</s:param>
79 * <s:param name="param2">value2<s:param>
80 * </s:include>
81 * <!-- END SNIPPET: example -->
82 *
83 * <!-- START SNIPPET: exampledescription -->
84 * Example one - do an include myJsp.jsp page
85 * Example two - do an include to myJsp.jsp page with parameters param1=value1 and param2=value2
86 * Example three - do an include to myJsp.jsp page with parameters param1=value1 and param2=value2
87 * <!-- END SNIPPET: exampledescription -->
88 * </pre>
89 *
90 * @s.tag name="include" tld-body-content="JSP" tld-tag-class="org.apache.struts2.views.jsp.IncludeTag"
91 * description="Include a servlet's output (result of servlet or a JSP page)"
92 */
93 public class Include extends Component {
94
95 private static final Log _log = LogFactory.getLog(Include.class);
96
97 private static String encoding;
98 private static boolean encodingDefined = true;
99
100 protected String value;
101 private HttpServletRequest req;
102 private HttpServletResponse res;
103
104 public Include(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
105 super(stack);
106 this.req = req;
107 this.res = res;
108 }
109
110 public boolean end(Writer writer, String body) {
111 String page = findString(value, "value", "You must specify the URL to include. Example: /foo.jsp");
112 StringBuffer urlBuf = new StringBuffer();
113
114
115 urlBuf.append(page);
116
117
118 if (parameters.size() > 0) {
119 urlBuf.append('?');
120
121 String concat = "";
122
123
124 Iterator iter = parameters.entrySet().iterator();
125
126 while (iter.hasNext()) {
127 Map.Entry entry = (Map.Entry) iter.next();
128 Object name = entry.getKey();
129 List values = (List) entry.getValue();
130
131 for (int i = 0; i < values.size(); i++) {
132 urlBuf.append(concat);
133 urlBuf.append(name);
134 urlBuf.append('=');
135
136 try {
137 urlBuf.append(URLEncoder.encode(values.get(i).toString(), "UTF-8"));
138 } catch (Exception e) {
139 _log.warn("unable to url-encode "+values.get(i).toString()+", it will be ignored");
140 }
141
142 concat = "&";
143 }
144 }
145 }
146
147 String result = urlBuf.toString();
148
149
150 try {
151 include(result, writer, req, res);
152 } catch (Exception e) {
153 LogFactory.getLog(getClass()).warn("Exception thrown during include of " + result, e);
154 }
155
156 return super.end(writer, body);
157 }
158
159 /***
160 * The jsp/servlet output to include
161 * @s.tagattribute required="true" type="String"
162 */
163 public void setValue(String value) {
164 this.value = value;
165 }
166
167 public static String getContextRelativePath(ServletRequest request, String relativePath) {
168 String returnValue;
169
170 if (relativePath.startsWith("/")) {
171 returnValue = relativePath;
172 } else if (!(request instanceof HttpServletRequest)) {
173 returnValue = relativePath;
174 } else {
175 HttpServletRequest hrequest = (HttpServletRequest) request;
176 String uri = (String) request.getAttribute("javax.servlet.include.servlet_path");
177
178 if (uri == null) {
179 uri = RequestUtils.getServletPath(hrequest);
180 }
181
182 returnValue = uri.substring(0, uri.lastIndexOf('/')) + '/' + relativePath;
183 }
184
185
186
187 if (returnValue.indexOf("..") != -1) {
188 Stack stack = new Stack();
189 StringTokenizer pathParts = new StringTokenizer(returnValue.replace('//', '/'), "/");
190
191 while (pathParts.hasMoreTokens()) {
192 String part = pathParts.nextToken();
193
194 if (!part.equals(".")) {
195 if (part.equals("..")) {
196 stack.pop();
197 } else {
198 stack.push(part);
199 }
200 }
201 }
202
203 StringBuffer flatPathBuffer = new StringBuffer();
204
205 for (int i = 0; i < stack.size(); i++) {
206 flatPathBuffer.append("/").append(stack.elementAt(i));
207 }
208
209 returnValue = flatPathBuffer.toString();
210 }
211
212 return returnValue;
213 }
214
215 public void addParameter(String key, Object value) {
216
217
218
219 if (value != null) {
220 List currentValues = (List) parameters.get(key);
221
222 if (currentValues == null) {
223 currentValues = new ArrayList();
224 parameters.put(key, currentValues);
225 }
226
227 currentValues.add(value);
228 }
229 }
230
231 public static void include(String aResult, Writer writer, ServletRequest request, HttpServletResponse response) throws ServletException, IOException {
232 String resourcePath = getContextRelativePath(request, aResult);
233 RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
234
235 if (rd == null) {
236 throw new ServletException("Not a valid resource path:" + resourcePath);
237 }
238
239 PageResponse pageResponse = new PageResponse(response);
240
241
242 rd.include((HttpServletRequest) request, pageResponse);
243
244
245 String encoding = getEncoding();
246
247 if (encoding != null) {
248
249 pageResponse.getContent().writeTo(writer, encoding);
250 } else {
251
252 pageResponse.getContent().writeTo(writer, null);
253 }
254 }
255
256 /***
257 * Get the encoding specified by the property 'struts.i18n.encoding' in struts.properties,
258 * or return the default platform encoding if not specified.
259 * <p/>
260 * Note that if the property is not initially defined, this will return the system default,
261 * even if the property is later defined. This is mainly for performance reasons. Undefined
262 * properties throw exceptions, which are a costly operation.
263 * <p/>
264 * If the property is initially defined, it is read every time, until is is undefined, and then
265 * the system default is used.
266 * <p/>
267 * Why not cache it completely? Some applications will wish to be able to dynamically set the
268 * encoding at runtime.
269 *
270 * @return The encoding to be used.
271 */
272 private static String getEncoding() {
273 if (encodingDefined) {
274 try {
275 encoding = Settings.get(StrutsConstants.STRUTS_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 /***
287 * Implementation of ServletOutputStream that stores all data written
288 * to it in a temporary buffer accessible from {@link #getBuffer()} .
289 *
290 * @author <a href="joe@truemesh.com">Joe Walnes</a>
291 * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
292 */
293 static final class PageOutputStream extends ServletOutputStream {
294
295 private FastByteArrayOutputStream buffer;
296
297
298 public PageOutputStream() {
299 buffer = new FastByteArrayOutputStream();
300 }
301
302
303 /***
304 * Return all data that has been written to this OutputStream.
305 */
306 public FastByteArrayOutputStream getBuffer() throws IOException {
307 flush();
308
309 return buffer;
310 }
311
312 public void close() throws IOException {
313 buffer.close();
314 }
315
316 public void flush() throws IOException {
317 buffer.flush();
318 }
319
320 public void write(byte[] b, int o, int l) throws IOException {
321 buffer.write(b, o, l);
322 }
323
324 public void write(int i) throws IOException {
325 buffer.write(i);
326 }
327
328 public void write(byte[] b) throws IOException {
329 buffer.write(b);
330 }
331 }
332
333
334 /***
335 * Simple wrapper to HTTPServletResponse that will allow getWriter()
336 * and getResponse() to be called as many times as needed without
337 * causing conflicts.
338 * <p/>
339 * The underlying outputStream is a wrapper around
340 * {@link PageOutputStream} which will store
341 * the written content to a buffer.
342 * <p/>
343 * This buffer can later be retrieved by calling {@link #getContent}.
344 *
345 * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
346 * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
347 */
348 static final class PageResponse extends HttpServletResponseWrapper {
349
350 protected PrintWriter pagePrintWriter;
351 protected ServletOutputStream outputStream;
352 private PageOutputStream pageOutputStream = null;
353
354
355 /***
356 * Create PageResponse wrapped around an existing HttpServletResponse.
357 */
358 public PageResponse(HttpServletResponse response) {
359 super(response);
360 }
361
362
363 /***
364 * Return the content buffered inside the {@link PageOutputStream}.
365 *
366 * @return
367 * @throws IOException
368 */
369 public FastByteArrayOutputStream getContent() throws IOException {
370
371
372
373 if (pagePrintWriter != null) {
374 pagePrintWriter.flush();
375 }
376
377 return ((PageOutputStream) getOutputStream()).getBuffer();
378 }
379
380 /***
381 * Return instance of {@link PageOutputStream}
382 * allowing all data written to stream to be stored in temporary buffer.
383 */
384 public ServletOutputStream getOutputStream() throws IOException {
385 if (pageOutputStream == null) {
386 pageOutputStream = new PageOutputStream();
387 }
388
389 return pageOutputStream;
390 }
391
392 /***
393 * Return PrintWriter wrapper around PageOutputStream.
394 */
395 public PrintWriter getWriter() throws IOException {
396 if (pagePrintWriter == null) {
397 pagePrintWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), getCharacterEncoding()));
398 }
399
400 return pagePrintWriter;
401 }
402 }
403 }