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