1 package servletunit;
2
3 // ServletUnit Library v1.2 - A java-based testing framework for servlets
4 // Copyright (C) June 1, 2001 Somik Raha
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 //
20 // For any questions or suggestions, you can write to me at :
21 // Email : somik@kizna.com
22 //
23 // Postal Address :
24 // Somik Raha
25 // R&D Team
26 // Kizna Corporation
27 // 2-1-17-6F, Sakamoto Bldg., Moto Azabu, Minato ku, Tokyo, 106 0046, JAPAN
28 //
29 // Additions by:
30 //
31 // Dane S. Foster
32 // Equity Technology Group, Inc
33 // http://www.equitytg.com.
34 // 954.360.9800
35 // dfoster@equitytg.com
36 //
37 // Additions by:
38 // Sean Pritchard
39 // smpritchard@yahoo.com
40 //
41 import junit.framework.AssertionFailedError;
42
43 import javax.servlet.ServletOutputStream;
44 import javax.servlet.http.Cookie;
45 import javax.servlet.http.HttpServletResponse;
46 import java.io.*;
47 import java.util.HashMap;
48 import java.util.Locale;
49 import java.util.Date;
50 import java.text.SimpleDateFormat;
51
52
53 // StrutsTestCase - a JUnit extension for testing Struts actions
54 // within the context of the ActionServlet.
55 // Copyright (C) 2002 Deryl Seale
56 //
57 // This library is free software; you can redistribute it and/or
58 // modify it under the terms of the Apache Software License as
59 // published by the Apache Software Foundation; either version 1.1
60 // of the License, or (at your option) any later version.
61 //
62 // This library is distributed in the hope that it will be useful,
63 // but WITHOUT ANY WARRANTY; without even the implied warranty of
64 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
65 // Apache Software Foundation Licens for more details.
66 //
67 // You may view the full text here: http://www.apache.org/LICENSE.txt
68
69 public class HttpServletResponseSimulator implements HttpServletResponse
70 {
71 private OutputStream servOStream; // The non-default javax.servlet.ServletOutputStream
72
73 private boolean calledGetWriter, calledGetOutputStream;
74 private StringWriter stringWriter=null;
75 private PrintWriter printWriter=null;
76 private Locale locale = null;
77 private int contentLength;
78 private String contentType = null;
79 private int status = 200;
80 private String message = null;
81 private HashMap headers = new HashMap();
82 private HashMap cookies = new HashMap();
83
84 String charEncoding;
85
86 private boolean isCommitted = false;
87
88 public static final int SC_CONTINUE = 100;
89
90
91 public static final int SC_SWITCHING_PROTOCOLS = 101;
92
93 public static final int SC_OK = 200;
94
95 public static final int SC_CREATED = 201;
96
97 public static final int SC_ACCEPTED = 202;
98
99 public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
100
101 public static final int SC_NO_CONTENT = 204;
102
103 public static final int SC_RESET_CONTENT = 205;
104
105 public static final int SC_PARTIAL_CONTENT = 206;
106
107 public static final int SC_MULTIPLE_CHOICES = 300;
108
109 public static final int SC_MOVED_PERMANENTLY = 301;
110
111 public static final int SC_MOVED_TEMPORARILY = 302;
112
113 public static final int SC_SEE_OTHER = 303;
114
115 public static final int SC_NOT_MODIFIED = 304;
116
117 public static final int SC_USE_PROXY = 305;
118
119 public static final int SC_BAD_REQUEST = 400;
120
121 public static final int SC_UNAUTHORIZED = 401;
122
123 public static final int SC_PAYMENT_REQUIRED = 402;
124
125 public static final int SC_FORBIDDEN = 403;
126
127 public static final int SC_NOT_FOUND = 404;
128
129 public static final int SC_METHOD_NOT_ALLOWED = 405;
130
131 public static final int SC_NOT_ACCEPTABLE = 406;
132
133 public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
134
135 public static final int SC_REQUEST_TIMEOUT = 408;
136
137 public static final int SC_CONFLICT = 409;
138
139 public static final int SC_GONE = 410;
140
141 public static final int SC_LENGTH_REQUIRED = 411;
142
143 public static final int SC_PRECONDITION_FAILED = 412;
144
145 public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
146
147 public static final int SC_REQUEST_URI_TOO_LONG = 414;
148
149 public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
150
151 public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
152
153 public static final int SC_EXPECTATION_FAILED = 417;
154
155 public static final int SC_INTERNAL_SERVER_ERROR = 500;
156
157 public static final int SC_NOT_IMPLEMENTED = 501;
158
159 public static final int SC_BAD_GATEWAY = 502;
160
161 public static final int SC_SERVICE_UNAVAILABLE = 503;
162
163 public static final int SC_GATEWAY_TIMEOUT = 504;
164
165 public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
166
167 /**
168 * Add a cookie to this response, which will then be stored in the browser.
169 */
170 public void addCookie(Cookie cookie)
171 {
172 cookies.put( cookie.getName(), cookie );
173 }
174
175 /**
176 * Returns a cookie with a given, or null if this cookie has
177 * not been added to the repsonse.
178 */
179 public Cookie findCookie( String name ) {
180 return (Cookie) cookies.get( name );
181 }
182
183 /**
184 * This method is not supported.
185 */
186 public void addDateHeader(String name, long date)
187 {
188 this.headers.put(name,new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z").format(new Date(date)));
189 }
190
191 /**
192 * Adds a response header with the given name and value.
193 */
194 public void addHeader(String name, String value)
195 {
196 this.setHeader(name,value);
197 }
198
199 /**
200 * Returns a given header field, or null if this header
201 * has not been set.
202 */
203 public String getHeader(String name) {
204 if (headers.containsKey(name))
205 return (String) headers.get(name);
206 else
207 return null;
208 }
209
210 /**
211 * Adds a response header with the given name and integer value.
212 */
213 public void addIntHeader(String name, int value)
214 {
215 this.setIntHeader(name,value);
216 }
217
218 /**
219 * returns true if a header with the given name
220 * has already been set
221 */
222 public boolean containsHeader(String name)
223 {
224 return headers.containsKey(name);
225 }
226
227 /**
228 * Returns the given URL unmodified
229 */
230 public String encodeRedirectUrl(String url)
231 {
232 return url;
233 }
234
235
236 /**
237 * Returns the given URL unmodified.
238 */
239 public String encodeRedirectURL(String url)
240 {
241 return url;
242 }
243
244 /**
245 * Returns the given URL unmodified.
246 */
247 public String encodeUrl(String url)
248 {
249 return url;
250 }
251
252 /**
253 * Returns the given URL unmodified
254 */
255 public String encodeURL(String url)
256 {
257 return url;
258 }
259
260 /**
261 * This method is not supported.
262 */
263 public void flushBuffer() throws IOException
264 {
265 throw new UnsupportedOperationException("flushBuffer operation is not supported!");
266 }
267
268
269 /**
270 * This method is not supported.
271 */
272 public int getBufferSize()
273 {
274 throw new UnsupportedOperationException("getBufferSize operation is not supported!");
275 }
276
277 /**
278 * This method is not supported.
279 */
280 public String getCharacterEncoding()
281 {
282 return charEncoding;
283 }
284
285 /**
286 * Returns the locale assigned to the response.
287 *
288 *
289 * @see #setLocale
290 *
291 */
292 public Locale getLocale()
293 {
294 if (locale == null)
295 return Locale.US;
296 else
297 return locale;
298 }
299
300
301
302 /**
303 * Returns a {@link ServletOutputStream} suitable for writing binary
304 * data in the response. The servlet container does not encode the
305 * binary data.
306
307 * <p> Calling flush() on the ServletOutputStream commits the response.
308
309 * Either this method or {@link #getWriter} may
310 * be called to write the body, not both.
311 *
312 * @return a {@link ServletOutputStream} for writing binary data
313 *
314 * @exception IllegalStateException if the <code>getWriter</code> method
315 * has been called on this response
316 *
317 * @exception IOException if an input or output exception occurred
318 *
319 * @see #getWriter
320 *
321 */
322 public ServletOutputStream getOutputStream() throws IOException
323 {
324 if( this.calledGetWriter )
325 throw new IllegalStateException( "The getWriter method has already been called" );
326
327 ServletOutputStream oStream = null;
328 if( null == this.servOStream )
329 oStream = new ServletOutputStreamSimulator();
330 else
331 oStream = new ServletOutputStreamSimulator( this.servOStream );
332 // resets the status of servOStream to prevent us from possible using a closed stream
333 this.servOStream = null;
334 this.calledGetOutputStream = true;
335 return oStream;
336 }
337
338
339 /**
340 * Returns a <code>PrintWriter</code> object that
341 * can send character text to the client.
342 * The character encoding used is the one specified
343 * in the <code>charset=</code> property of the
344 * {@link #setContentType} method, which must be called
345 * <i>before</i> calling this method for the charset to take effect.
346 *
347 * <p>If necessary, the MIME type of the response is
348 * modified to reflect the character encoding used.
349 *
350 * <p> Calling flush() on the PrintWriter commits the response.
351 *
352 * <p>Either this method or {@link #getOutputStream} may be called
353 * to write the body, not both.
354 *
355 *
356 * @return a <code>PrintWriter</code> object that
357 * can return character data to the client
358 *
359 * @exception UnsupportedEncodingException if the charset specified in
360 * <code>setContentType</code> cannot be
361 * used
362 *
363 * @exception IllegalStateException if the <code>getOutputStream</code>
364 * method has already been called for this
365 * response object
366 *
367 * @exception IOException if an input or output exception occurred
368 *
369 * @see #getOutputStream
370 * @see #setContentType
371 *
372 */
373 public PrintWriter getWriter() throws IOException
374 {
375 if( this.calledGetOutputStream )
376 throw new IllegalStateException( "The getOutputStream method has already been called" );
377
378 if( stringWriter == null )
379 stringWriter = new StringWriter();
380 if( printWriter == null )
381 printWriter = new PrintWriter( stringWriter );
382
383 this.calledGetWriter = true;
384 return printWriter;
385 }
386
387
388 /**
389 * Use this method to pick up the string buffer which will hold
390 * the contents of the string buffer. You can then
391 * write your test case to examine the contents of this
392 * buffer and match it against an expected output.
393 */
394 public StringBuffer getWriterBuffer()
395 {
396 if (stringWriter==null) return null;
397 return stringWriter.getBuffer();
398 }
399
400 //TODO: better documentation
401 public boolean isCommitted()
402 {
403 return isCommitted;
404 }
405
406 public void setIsCommitted(boolean isCommitted) {
407 this.isCommitted = isCommitted;
408 }
409
410 /**
411 * Reinitializes all local variables.
412 * Note, in most servlet containers, you may get an
413 * IllegalStateException if you call this method
414 * after committing the response.
415 * That behavior is not replicated here.
416 */
417 public void reset()
418 {
419 this.calledGetOutputStream = false;
420 this.calledGetWriter = false;
421 this.contentLength = 0;
422 this.contentType = null;
423 this.stringWriter = null;
424 this.printWriter = null;
425 headers = new HashMap();
426 }
427
428 /**
429 * This method is not supported.
430 */
431 public void resetBuffer()
432 {
433 throw new UnsupportedOperationException("resetBuffer operation is not supported.");
434 }
435
436 /**
437 * Sends an error response to the client using the specified
438 * status clearing the buffer. This method always throws
439 * an AssertionFailedError with the corresponding error
440 * number.
441 *
442 * @param sc the error status code
443 */
444 public void sendError(int sc) throws IOException
445 {
446 setStatus(sc);
447 throw new AssertionFailedError("received error: " + sc);
448 }
449
450
451 /**
452 * Sends an error response to the client using the specified
453 * status clearing the buffer. This method always throws
454 * an AssertionFailedError with the corresponding error
455 * number and descriptive text.
456 *
457 * @param sc the error status code
458 * @param msg the descriptive message
459 */
460 public void sendError(int sc, String msg) throws IOException
461 {
462 setStatus(sc,msg);
463 throw new AssertionFailedError("received error " + sc + " : " + msg);
464 }
465
466 /**
467 * Resets the response and sets the appropriate redirect headers.
468 */
469 public void sendRedirect(String location) throws IOException
470 {
471 reset();
472 setStatus(SC_MOVED_TEMPORARILY);
473 setHeader("Location", location);
474 }
475
476 /**
477 * This method is not supported.
478 */
479 public void setBufferSize(int size)
480 {
481 throw new UnsupportedOperationException("setBufferSize operation not supported.");
482 }
483
484
485 /**
486 * Sets the length of the content body in the response
487 * In HTTP servlets, this method sets the HTTP Content-Length header.
488 *
489 *
490 * @param len an integer specifying the length of the
491 * content being returned to the client; sets
492 * the Content-Length header
493 *
494 */
495 public void setContentLength(int len)
496 {
497 this.contentLength = len;
498 }
499
500 /**
501 * returns the content length previously set in setContentLength()
502 * @return the content length
503 */
504 public int getContentLength(){
505 return this.contentLength;
506 }
507
508 /**
509 * Sets the content type of the response being sent to
510 * the client. The content type may include the type of character
511 * encoding used, for example, <code>text/html; charset=ISO-8859-4</code>.
512 *
513 * <p>If obtaining a <code>PrintWriter</code>, this method should be
514 * called first.
515 *
516 *
517 * @param type a <code>String</code> specifying the MIME
518 * type of the content
519 *
520 * @see #getOutputStream
521 * @see #getWriter
522 *
523 */
524 public void setContentType(String type)
525 {
526 this.contentType = type;
527 }
528
529 /**
530 * returns the content type previously set in setContentType()
531 * @return the content type
532 */
533 public String getContentType(){
534 return this.contentType;
535 }
536
537
538 /**
539 * This method is not supported.
540 */
541 public void setDateHeader(String name, long date)
542 {
543 this.addDateHeader(name,date);
544 }
545
546 /**
547 * adds the name/value pair to the headers
548 */
549 public void setHeader(String name, String value)
550 {
551 if (name.equalsIgnoreCase("content-type")) {
552 setContentType(value);
553 return;
554 }
555 else if (name.equalsIgnoreCase("content-length")) {
556 this.setContentLength(Integer.parseInt(value));
557 return;
558 }
559
560 headers.put(name, value);
561 }
562
563 /**
564 * Removes a given header
565 */
566 public void removeHeader(String name) {
567 if (headers.containsKey(name))
568 headers.remove(name);
569 }
570
571 /**
572 * Adds the given name/value pair to the headers collection.
573 */
574 public void setIntHeader(String name, int value)
575 {
576 setHeader(name, String.valueOf(value));
577 }
578
579
580 /**
581 * Sets the locale of the response, setting the headers (including the
582 * Content-Type's charset) as appropriate. This method should be called
583 * before a call to {@link #getWriter}. By default, the response locale
584 * is the default locale for the server.
585 *
586 * @param loc the locale of the response
587 *
588 * @see #getLocale
589 *
590 */
591 public void setLocale(Locale loc)
592 {
593 this.locale = loc;
594 }
595
596 /**
597 * The default action of calling the <code>getOutputStream</code> method
598 * is to return a <code>javax.servlet.ServletOutputStream</code> object
599 * that sends the data to <code> System.out</code>. If you don't want
600 * the output sent to <code>System.out</code> you can use this method to
601 * set where the output will go. Please note, subsequent calls to
602 * <code>getOutputStream</code> will reset the output path to
603 * <code>System.out</code>. This prevents the OutputStream returned by
604 * calling getOutputStream from writing to a closed stream
605 *
606 * @param out The <code>java.io.OutputStream</code> that represents
607 * the real path of the output.
608 */
609 public void setOutputStream( OutputStream out )
610 {
611 this.servOStream = out;
612 }
613
614 /**
615 * Sets the given status code.
616 */
617 public void setStatus(int sc)
618 {
619 setStatus(sc, null);
620 }
621
622 /**
623 * Sets the given status and an associated message.
624 */
625 public void setStatus(int sc, String sm)
626 {
627 this.status = sc;
628 this.message = sm;
629 }
630
631 /**
632 * Returns the status code for this response, which is useful for testing expected errors.
633 * @return the status code for this response.
634 */
635 public int getStatusCode() {
636 return this.status;
637 }
638
639 public void setCharacterEncoding(String charEncoding) {
640 this.charEncoding = charEncoding;
641 }
642
643 public String getMessage() {
644 return message;
645 }
646
647
648 }
649