Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/tripi/asp/Response.java


1   /**
2    * ArrowHead ASP Server 
3    * This is a source file for the ArrowHead ASP Server - an 100% Java
4    * VBScript interpreter and ASP server.
5    *
6    * For more information, see http://www.tripi.com/arrowhead
7    *
8    * Copyright (C) 2002  Terence Haddock
9    *
10   * This program is free software; you can redistribute it and/or modify
11   * it under the terms of the GNU General Public License as published by
12   * the Free Software Foundation; either version 2 of the License, or
13   * (at your option) any later version.
14   *
15   * This program is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   * GNU General Public License for more details.
19   *
20   * You should have received a copy of the GNU General Public License
21   * along with this program; if not, write to the Free Software
22   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23   *
24   */
25  package com.tripi.asp;
26  
27  import javax.servlet.http.HttpServletResponse;
28  import java.io.*;
29  import java.util.Calendar;
30  import java.util.Date;
31  import java.util.Enumeration;
32  import java.util.GregorianCalendar;
33  import java.util.Hashtable;
34  import java.util.TimeZone;
35  import java.util.Vector;
36  import java.text.DateFormat;
37  import java.text.SimpleDateFormat;
38  import org.apache.log4j.Category;
39  
40  /**
41   * ASP "Response" object. This object handles communication from ASP
42   * back to the client. Implementation state:
43   * <ul>
44   * <li><b>Cookies</b> - Implemented.
45   * <li><b>Buffer</b> - Implemented
46   * <li><b>CacheControl</b> - Implemented
47   * <li><b>Charset</b> - Implemented
48   * <li><b>ContentType</b> - Implemented
49   * <li><b>Expires</b> - Implementes, <b>NOTE</b> Expires is not updated
50   *                      properly when ExpiresAbsolute is used.
51   * <li><b>ExpiresAbsolute</b> - Fully implemented.
52   * <li><b>IsClientConnected</b> - <b>TODO</b> Not implemented.
53   * <li><b>PICS</b> - <b>TODO</b> Not implemented.
54   * <li><b>Status</b> - Fully implemented.
55   * <li><b>AddHeader</b> - Fully supported.
56   * <li><b>AppendToLog</b> - <b>TODO</b> Not implemented.
57   * <li><b>BinaryWrite</b> - Fully supported.
58   * <li><b>Clear</b> - Implemented.
59   * <li><b>End</b> - Implemented.
60   * <li><b>Flush</b> - Implemented.
61   * <li><b>Redirect</b> - Implemented.
62   * <li><b>Write</b> - Implemented.
63   * </ul>
64   *
65   * @author Terence Haddock
66   * @version 0.9
67   */
68  public class Response
69  {
70      /** Log4j debugging object */
71      Category DBG = Category.getInstance(Response.class);
72  
73      /** JSDK servlet response object */
74      HttpServletResponse response;
75  
76      /**
77       * Buffer for when Buffer = TRUE. When buffering is enabled,
78       * stringBuf != null, and when buffering is disabled, stringBuf == null.
79       */
80      StringBuffer    stringBuf = new StringBuffer();
81  
82      /**
83       * Have the headers been sent?
84       */
85      boolean            sentHeaders = false;
86  
87      /**
88       * Cookie jar to hold cookies to be sent to the client.
89       */
90      public CookieJar Cookies = new CookieJar();
91  
92      /**
93       * Cache-control setting
94       */
95      public String   cacheControl = "private";
96  
97      /**
98       * Content type setting
99       */
100     public String   contentType = "text/html";
101 
102     /**
103      * Charset setting
104      */
105     public String   charset = "";
106 
107     /**
108      * Current expiry string. Prepended with an underscore to keep it
109      * from being accessed within ASP.
110      */
111     AspDate    _expiryDate = null;
112 
113     /**
114      * Current expiry time, in minutes.
115      */
116     int    _expiryMinutes = 0;
117 
118     /**
119      * Constructor.
120      * @param response JSDK response object.
121      */
122     public Response(HttpServletResponse response)
123     {
124         this.response = response;
125     }
126 
127     /**
128      * This unsupported ASP-accessible function obtains the HTTP response
129      * object that this Response object points to.
130      * @return HttpServletResponse object
131      */
132     public HttpServletResponse getHttpServletResponse()
133     {
134         return response;
135     }
136 
137     /**
138      * ASP-accessible function to output text to the client.
139      * @param str Text to output to the client.
140      * @throws IOException on output error.
141      */
142     public void Write(String str) throws IOException, AspException
143     {
144         if (stringBuf != null) {
145             stringBuf.append(str);
146         } else {
147             if (!sentHeaders) sendHeaders();
148             OutputStream os = response.getOutputStream();
149             os.write(str.getBytes());
150             os.flush();
151         }
152     }
153 
154     /**
155      * ASP-accessible function to output any buffered text to the client.
156      * @throws IOException on output error.
157      * @throws AspException on ASP error.
158      */
159     public void Flush() throws IOException, AspException
160     {
161         if (stringBuf == null)
162             throw new AspException("Flush() called when " +
163                     "buffer = FALSE");
164         if (!sentHeaders) sendHeaders();
165         response.getOutputStream().write(stringBuf.toString().getBytes());
166         stringBuf = new StringBuffer();
167     }
168 
169     /**
170      * ASP-accessible function to redirect the user to a different URL.
171      * @param redir URL to redirect to, should be fully-qualified
172      * URL.
173      * @throws IOException on output error
174      * @throws AspException always throws AspExitScriptException
175      */
176     public void Redirect(String redir) throws IOException, AspException
177     {
178         if (!sentHeaders) sendHeaders();
179         response.setHeader("Location", redir);
180         response.sendError(302);
181         stringBuf = null;
182         throw new AspExitScriptException();
183     }
184 
185     /**
186      * This ASP-accessible function acts as the ASP field 'buffer', which
187      * enables or disables buffering.
188      * @param bBuffer enable, or disable buffer
189      * @throws IOException on error.
190      */
191     public void Buffer(boolean bBuffer) throws IOException, AspException
192     {
193         if (bBuffer)
194         {
195             if (sentHeaders)
196             {
197                 throw new AspException("Headers already sent, buffering " +
198                     "cannot be turned on after the headers have been sent.");
199             } else if (stringBuf == null) {
200                 stringBuf = new StringBuffer();
201             }
202         } else {
203             if (stringBuf != null) {
204                 if (stringBuf.length() == 0) stringBuf = null;
205                 else {
206                     throw new AspException("Buffering cannot be turned off " +
207                      "after it has been turned on.");
208                 }
209             }
210         }
211     }
212 
213     /**
214      * This ASP-accessible function obtains the buffer status
215      * @return <b>true</b> if buffering is on, <b>false</b> if not.
216      */
217     public boolean Buffer()
218     {
219         return stringBuf != null;
220     }
221 
222     /**
223      * The ASP-accessible Clear function clears any buffered text.
224      */
225     public void Clear() throws AspException
226     {
227         if (stringBuf == null)
228             throw new AspException("Flush() called when " +
229                     "buffer = FALSE");
230         stringBuf = new StringBuffer();
231     }
232 
233     /**
234      * The ASP-accessible End function ends the current ASP script
235      * immediately.
236      * @throws AspException always throws AspExitScriptException.
237      */
238     public void End() throws AspException
239     {
240         throw new AspExitScriptException();
241     }
242 
243     /**
244      * This ASP-accessible function sets the expiry time to the specified
245      * number of minutes from the current date/time.
246      * @param minutes Number of minutes
247      */
248     public void Expires(int minutes) throws AspException
249     {
250         if (sentHeaders) throw new AspException("Headers Sent");
251         Calendar cal = new GregorianCalendar();
252         Date date = new Date(System.currentTimeMillis());
253         cal.setTime(date);
254         cal.add(cal.MINUTE, minutes);
255         _expiryDate = new AspDate(cal.getTime());
256         _expiryMinutes = minutes;
257     }
258 
259     /**
260      * This ASP-accessible function obtains the number of minutes the
261      * expiry time is set to.
262      * @return Number of minutes
263      */
264     public int Expires()
265     {
266         return _expiryMinutes;
267     }
268 
269     /**
270      * This ASP-accessible function sets the expiry time to the specified
271      * absolute date.
272      * @param date Date to set expiry to
273      */
274     public void ExpiresAbsolute(AspDate date) throws AspException
275     {
276         if (sentHeaders) throw new AspException("Headers Sent");
277         _expiryDate = date;
278         /* Get the current date in seconds */
279         long currentTime = System.currentTimeMillis() / 1000;
280         /* Get the expiry date in seconds */
281         long expiryTime = date.toDate().getTime() / 1000;
282         /* Obtain the different in seconds between the two times */
283         long diffSec = (expiryTime - currentTime);
284         /* Set the difference in minutes to the expiry setting */
285         _expiryMinutes = (int)(diffSec / 60);
286     }
287 
288     /**
289      * This ASP-accessible function obtains the number of minutes the
290      * expiry time is set to.
291      * @return Number of minutes
292      */
293     public AspDate ExpiresAbsolute()
294     {
295         return _expiryDate;
296     }
297 
298     /**
299      * This ASP-accessible BinaryWrite function writes binary data to the
300      * output stream. This function assumes it's being called with
301      * an array of Chars.
302      * @param an ArrayNode object
303      */
304     public void BinaryWrite(byte arr[]) throws AspException, IOException
305     {
306         /* XXX This ignores Response.Buffer */
307         Flush();
308         OutputStream os = response.getOutputStream();
309         os.write(arr);
310         os.flush();
311     }
312 
313     /**
314      * Internal function to send the headers.
315      */
316     synchronized void sendHeaders() throws AspException
317     {
318         if (sentHeaders) return;
319         if (DBG.isDebugEnabled()) DBG.debug("Sending headers");
320         sentHeaders = true;
321         String contentType = this.contentType;
322         if (this.charset != null && this.charset.length() > 0) {
323             contentType = contentType + "; Charset=" + this.charset;
324         }
325         response.setContentType(contentType);
326         if (cacheControl == null) cacheControl = "private";
327         response.setHeader("Cache-control", cacheControl);
328         Cookies.sendCookies();
329         if (_expiryDate != null)
330         {
331             Calendar cal = _expiryDate.toCalendar();
332             if (!_expiryDate.hasTime())
333             {
334                 /* If the time is not set, assume the expiry is at midnight
335                    of the current date. */
336                 cal.set(cal.HOUR_OF_DAY, 24);
337                 cal.set(cal.MINUTE, 0);
338                 cal.set(cal.SECOND, 0);
339                 cal.set(cal.MILLISECOND, 0);
340             }
341             /* You can get the time in millisecond directly from cal 
342                in JDK 1.4 */
343             Date date = cal.getTime();
344             response.setDateHeader("Expires", date.getTime());
345         }
346     }
347 
348     /**
349      * ASP-accessible function to set the status for this request.
350      * @param status New status value
351      */
352     public void Status(int value)
353     {
354         response.setStatus(value);
355     }
356 
357     /**
358      * ASP-accessible function to add a header to the response stream.
359      * This function will not overwrite a header, it will add a new one
360      * to the response stream.
361      * @param name Header name
362      * @param value Header value
363      */
364     public void AddHeader(String name, String value)
365     {
366         response.addHeader(name, value);
367     }
368 
369     /**
370      * Cookie Jar class
371      */
372     public class CookieJar implements SimpleMap
373     {
374         /**
375          * AspCollection containing the cookie crumbs.
376          */
377         AspCollection cookieCrumbs = new AspCollection();
378 
379         /**
380           * Constructor, initally creates an empty cookie jar.
381          */
382         public CookieJar()
383         {
384         }
385 
386         /**
387          * Obtains a cookie from the jar. This class always returns
388          * a cookie crumb.
389          * @param key Key of cookie, will be converted to string and is
390          * case insensitive.
391          * @return CookieCrumb object representing a cookie.
392          */
393         public synchronized Object get(Object key) throws AspException
394         {
395             String strKey = Types.coerceToString(key);
396             CookieCrumb crumb;
397 
398             if (cookieCrumbs.containsKey(strKey))
399             {
400                 if (DBG.isDebugEnabled()) DBG.debug("existing(" + strKey + ")");
401                 crumb = (CookieCrumb)cookieCrumbs.get(strKey);
402             } else {
403                 if (DBG.isDebugEnabled())
404                 {
405                     DBG.debug("new(" + strKey + ")");
406                     DBG.debug("This: " + this);
407                 }
408                 crumb = new CookieCrumb(strKey);
409                 cookieCrumbs.put(strKey, crumb);
410             }
411             return crumb;
412         }
413     
414         /**
415          * Stores a cookie value in the jar. 
416          * @param key Key of cookie, will be converted to a string and is
417          * case insensitive.
418          * @param obj Value to store in the cookie, will be converted
419          * to a string.
420          */
421         public synchronized void put(Object key, Object value)
422             throws AspException
423         {
424             CookieCrumb crumb = (CookieCrumb)get(key);
425             crumb.setValue(value);
426         }
427 
428         /**
429          * Enumerates the elements in this jar.
430          * @return Enumeration of elements in this jar.
431          */
432         public Enumeration getKeys() throws AspException
433         {
434             return cookieCrumbs.getKeys();
435         }
436 
437         /**
438          * Send all of the cookies stored in the jar.
439          */
440         void sendCookies() throws AspException
441         {
442             if (DBG.isDebugEnabled())
443             {
444                 DBG.debug("Sending cookies");
445                 DBG.debug("cookieCrumbs: " + cookieCrumbs);
446                 DBG.debug("This: " + this);
447             }
448             Enumeration values = cookieCrumbs.elements();
449             while (values.hasMoreElements())
450             {
451                 CookieCrumb crumb = (CookieCrumb)values.nextElement();
452                 if (DBG.isDebugEnabled()) DBG.debug("Sending cookie:" + crumb);
453                 if (crumb.name != null) {
454                     StringBuffer cookieBuf = new StringBuffer();
455                     cookieBuf.append(crumb.name);
456                     if (crumb.fullValue != null)
457                     {
458                         cookieBuf.append('=');
459                         cookieBuf.append(crumb.fullValue);
460                     }
461                     if (crumb.expires != null)
462                     {
463                         DateFormat df = new SimpleDateFormat(
464                             "EEE, dd-MMM-yyyy hh:mm:ss z");
465                         final TimeZone gmtTime = TimeZone.getTimeZone("GMT");
466                         df.setTimeZone(gmtTime);
467                         String text = df.format(crumb.expires.toDate());
468                         cookieBuf.append("; expires=");
469                         cookieBuf.append(text);
470                     }
471                     cookieBuf.append("; path=");
472                     cookieBuf.append(crumb.path);
473                     if (crumb.secure)
474                     {
475                         cookieBuf.append("; secure");
476                     }
477                     response.addHeader("Set-Cookie", cookieBuf.toString());
478                 }
479             }
480         }
481 
482         /**
483          * CookieCrumb class to hold cookie information.
484          */
485         public class CookieCrumb implements SimpleReference, SimpleMap
486         {
487             /**
488              * Cookie name
489              */
490             String name = null;
491 
492             /**
493              * Cookie base value
494              */
495             String value = null;
496 
497             /**
498              * Values of the keys
499              */
500             AspCollection keys = new AspCollection();
501 
502             /**
503              * Expires value.
504              */
505             AspDate expires = null;
506 
507             /**
508              * Cookie path
509              */
510             String path = "/";
511 
512             /**
513              * Secure cookie?
514              */
515             boolean secure = false;
516 
517             /**
518              * Cookie full value
519              */
520             String fullValue = null;
521 
522             /**
523              * Constructor, setting a value.
524              * @param name cookie name
525              */
526             public CookieCrumb(String name)
527             {
528                 this.name = name;
529             }
530 
531             /**
532              * Sets the date this cookie expires.
533              * @param Expiry expiration date.
534              * @throws AspException
535              */
536             public void Expires(AspDate date) throws AspException
537             {
538                 this.expires = date;
539                 updateCookie();
540             }
541 
542             /**
543              * Sets the cookie path
544              * @param Path cookie path
545              * @throws AspException
546              */
547             public void Path(String path) throws AspException
548             {
549                 this.path = path;
550                 updateCookie();
551             }
552 
553             /**
554              * Set the secure flag
555              * @param secure The secure flag
556              * @throws AspException
557              */
558             public void Secure(boolean secure) throws AspException
559             {
560                 this.secure = secure;
561                 updateCookie();
562             }
563 
564             /**
565              * Reference write function. Overwrites the current cookie value.
566              * @param value new value
567              * @throws AspException on error
568              * @see SimpleReference#setValue
569              */
570             public void setValue(Object value) throws AspException
571             {
572                 if (sentHeaders) {
573                     throw new AspException("Headers Sent");
574                 }
575                 String str = Types.coerceToString(value);
576                 this.value = str;
577                 updateCookie();
578             }
579 
580             /**
581              * Reference read function.
582              * @return value current value
583              * @throws AspException on error
584              * @see SimpleReference#getValue
585              */
586             public Object getValue() throws AspException
587             {
588                 throw new AspException("Not implemented");
589             }
590 
591             /**
592              * Does this set of cookies have keys?
593              */
594             public boolean HasKeys() throws AspException
595             {
596                 return keys.size() > 0;
597             }
598 
599             /**
600              * Gets the value in a SimpleMap context.
601              * @param index Index of value to get
602              * @return value of cookie key
603              */
604             public Object get(Object index) throws AspException
605             {
606                 throw new AspNotImplementedException();
607             }
608             
609             /**
610              * Sets the value in a SimpleMap context.
611              * @param index Index of value to get
612              * @param value New value
613              */
614             public void put(Object index, Object value) throws AspException
615             {
616                 String indStr = Types.coerceToString(index);
617                 String valStr = Types.coerceToString(value);
618                 keys.put(indStr, valStr);
619                 updateCookie();
620             }
621 
622             /**
623              * Enumerates all of the keys in this cookie.
624              * @return enumeration of the keys in this cookie.
625              */
626             public Enumeration getKeys() throws AspException
627             {
628                 return keys.elements();
629             }
630 
631             /**
632              * Update the value of the cookie.
633              */
634             private void updateCookie() throws AspException
635             {
636                 boolean first = true;
637                 StringBuffer buf = new StringBuffer();
638                 if (value != null) {
639                     buf.append(Server.URLEncode(value));
640                     first = false;
641                 }
642                 for (Enumeration e = keys.keys(); e.hasMoreElements();)
643                 {
644                     String key = (String)e.nextElement();
645                     String val = (String)keys.get(key);
646 
647                     if (!first)
648                     {
649                         buf.insert(0, '&');
650                     } else {
651                         first = false;
652                     }
653                     buf.insert(0, Server.URLEncode(val));
654                     buf.insert(0, '=');
655                     buf.insert(0, Server.URLEncode(key));
656                 }
657                 fullValue = buf.toString();
658             }
659         }
660     }
661 }