KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > tools > view > ImportSupport


1 /*
2  * Copyright 2003 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.velocity.tools.view;
18
19 import java.util.Locale JavaDoc;
20 import javax.servlet.http.HttpServletRequest JavaDoc;
21 import javax.servlet.http.HttpServletResponse JavaDoc;
22 import javax.servlet.http.HttpServletResponseWrapper JavaDoc;
23 import javax.servlet.ServletContext JavaDoc;
24 import javax.servlet.RequestDispatcher JavaDoc;
25 import javax.servlet.ServletOutputStream JavaDoc;
26 import javax.servlet.ServletException JavaDoc;
27
28 import java.io.InputStream JavaDoc;
29 import java.io.Reader JavaDoc;
30 import java.io.BufferedReader JavaDoc;
31 import java.io.StringReader JavaDoc;
32 import java.io.InputStreamReader JavaDoc;
33 import java.io.StringWriter JavaDoc;
34 import java.io.PrintWriter JavaDoc;
35 import java.io.ByteArrayOutputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.UnsupportedEncodingException JavaDoc;
38 import java.net.URL JavaDoc;
39 import java.net.URLConnection JavaDoc;
40 import java.net.HttpURLConnection JavaDoc;
41
42 /**
43  * <p>Provides methods to import arbitrary local or remote resources as strings.</p>
44  * <p>Based on ImportSupport from the JSTL taglib by Shawn Bayern</p>
45  *
46  * @author <a HREF="mailto:marinoj@centrum.is">Marino A. Jonsson</a>
47  * @since VelocityTools 1.1
48  * @version $Revision: 1.9 $ $Date: 2004/02/18 20:08:29 $
49  */

50 public abstract class ImportSupport {
51
52     protected ServletContext JavaDoc application;
53     protected HttpServletRequest JavaDoc request;
54     protected HttpServletResponse JavaDoc response;
55
56     protected boolean isAbsoluteUrl; // is our URL absolute?
57

58     protected static final String JavaDoc VALID_SCHEME_CHARS =
59         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
60
61     /** Default character encoding for response. */
62     protected static final String JavaDoc DEFAULT_ENCODING = "ISO-8859-1";
63
64     //*********************************************************************
65
// URL importation logic
66

67     /*
68      * Overall strategy: we have two entry points, acquireString() and
69      * acquireReader(). The latter passes data through unbuffered if
70      * possible (but note that it is not always possible -- specifically
71      * for cases where we must use the RequestDispatcher. The remaining
72      * methods handle the common.core logic of loading either a URL or a local
73      * resource.
74      *
75      * We consider the 'natural' form of absolute URLs to be Readers and
76      * relative URLs to be Strings. Thus, to avoid doing extra work,
77      * acquireString() and acquireReader() delegate to one another as
78      * appropriate. (Perhaps I could have spelled things out more clearly,
79      * but I thought this implementation was instructive, not to mention
80      * somewhat cute...)
81      */

82
83     /**
84      *
85      * @param url the URL resource to return as string
86      * @return the URL resource as string
87      * @throws IOException
88      * @throws java.lang.Exception
89      */

90     protected String JavaDoc acquireString(String JavaDoc url) throws IOException JavaDoc, Exception JavaDoc {
91         // Record whether our URL is absolute or relative
92
this.isAbsoluteUrl = isAbsoluteUrl(url);
93         if (this.isAbsoluteUrl)
94         {
95             // for absolute URLs, delegate to our peer
96
BufferedReader JavaDoc r = new BufferedReader JavaDoc(acquireReader(url));
97             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
98             int i;
99             // under JIT, testing seems to show this simple loop is as fast
100
// as any of the alternatives
101
while ((i = r.read()) != -1)
102             {
103                 sb.append((char)i);
104             }
105             return sb.toString();
106         }
107         else // handle relative URLs ourselves
108
{
109             // URL is relative, so we must be an HTTP request
110
if (!(request instanceof HttpServletRequest JavaDoc
111                   && response instanceof HttpServletResponse JavaDoc))
112             {
113                 throw new Exception JavaDoc("Relative import from non-HTTP request not allowed");
114             }
115
116             // retrieve an appropriate ServletContext
117
// normalize the URL if we have an HttpServletRequest
118
if (!url.startsWith("/"))
119             {
120                 String JavaDoc sp = ((HttpServletRequest JavaDoc)request).getServletPath();
121                 url = sp.substring(0, sp.lastIndexOf('/')) + '/' + url;
122             }
123             
124             // strip the session id from the url
125
url = stripSession(url);
126
127             // from this context, get a dispatcher
128
RequestDispatcher JavaDoc rd = application.getRequestDispatcher(url);
129             if (rd == null)
130             {
131                 throw new Exception JavaDoc("Couldn't get a RequestDispatcher for \""
132                                     + url + "\"");
133             }
134
135             // include the resource, using our custom wrapper
136
ImportResponseWrapper irw =
137                 new ImportResponseWrapper((HttpServletResponse JavaDoc)response);
138             try
139             {
140                 rd.include(request, irw);
141             }
142             catch (IOException JavaDoc ex)
143             {
144                 throw new Exception JavaDoc("Problem importing the relative URL \""
145                                     + url + "\". " + ex);
146             }
147             catch (RuntimeException JavaDoc ex)
148             {
149                 throw new Exception JavaDoc("Problem importing the relative URL \""
150                                     + url + "\". " + ex);
151             }
152
153             // disallow inappropriate response codes per JSTL spec
154
if (irw.getStatus() < 200 || irw.getStatus() > 299)
155             {
156                 throw new Exception JavaDoc("Invalid response code '" + irw.getStatus()
157                                     + "' for \"" + url + "\"");
158             }
159
160             // recover the response String from our wrapper
161
return irw.getString();
162         }
163     }
164
165     /**
166      *
167      * @param url the URL to read
168      * @return a Reader for the InputStream created from the supplied URL
169      * @throws IOException
170      * @throws java.lang.Exception
171      */

172     protected Reader JavaDoc acquireReader(String JavaDoc url) throws IOException JavaDoc, Exception JavaDoc
173     {
174         if (!this.isAbsoluteUrl)
175         {
176             // for relative URLs, delegate to our peer
177
return new StringReader JavaDoc(acquireString(url));
178         }
179         else
180         {
181             // absolute URL
182
try
183             {
184                 // handle absolute URLs ourselves, using java.net.URL
185
URL JavaDoc u = new URL JavaDoc(url);
186                 //URL u = new URL("http", "proxy.hi.is", 8080, target);
187
URLConnection JavaDoc uc = u.openConnection();
188                 InputStream JavaDoc i = uc.getInputStream();
189
190                 // okay, we've got a stream; encode it appropriately
191
Reader JavaDoc r = null;
192                 String JavaDoc charSet;
193
194                 // charSet extracted according to RFC 2045, section 5.1
195
String JavaDoc contentType = uc.getContentType();
196                 if (contentType != null)
197                 {
198                     charSet = this.getContentTypeAttribute(contentType, "charset");
199                     if (charSet == null)
200                     {
201                         charSet = DEFAULT_ENCODING;
202                     }
203                 }
204                 else
205                 {
206                     charSet = DEFAULT_ENCODING;
207                 }
208
209                 try
210                 {
211                     r = new InputStreamReader JavaDoc(i, charSet);
212                 }
213                 catch (Exception JavaDoc ex)
214                 {
215                     r = new InputStreamReader JavaDoc(i, DEFAULT_ENCODING);
216                 }
217
218                 // check response code for HTTP URLs before returning, per spec,
219
// before returning
220
if (uc instanceof HttpURLConnection JavaDoc)
221                 {
222                     int status = ((HttpURLConnection JavaDoc)uc).getResponseCode();
223                     if (status < 200 || status > 299)
224                     {
225                         throw new Exception JavaDoc(status + " " + url);
226                     }
227                 }
228                 return r;
229             }
230             catch (IOException JavaDoc ex)
231             {
232                 throw new Exception JavaDoc("Problem accessing the absolute URL \""
233                                     + url + "\". " + ex);
234             }
235             catch (RuntimeException JavaDoc ex)
236             {
237                 // because the spec makes us
238
throw new Exception JavaDoc("Problem accessing the absolute URL \""
239                                     + url + "\". " + ex);
240             }
241         }
242     }
243
244     /** Wraps responses to allow us to retrieve results as Strings. */
245     protected class ImportResponseWrapper extends HttpServletResponseWrapper JavaDoc
246     {
247         /*
248          * We provide either a Writer or an OutputStream as requested.
249          * We actually have a true Writer and an OutputStream backing
250          * both, since we don't want to use a character encoding both
251          * ways (Writer -> OutputStream -> Writer). So we use no
252          * encoding at all (as none is relevant) when the target resource
253          * uses a Writer. And we decode the OutputStream's bytes
254          * using OUR tag's 'charEncoding' attribute, or ISO-8859-1
255          * as the default. We thus ignore setLocale() and setContentType()
256          * in this wrapper.
257          *
258          * In other words, the target's asserted encoding is used
259          * to convert from a Writer to an OutputStream, which is typically
260          * the medium through with the target will communicate its
261          * ultimate response. Since we short-circuit that mechanism
262          * and read the target's characters directly if they're offered
263          * as such, we simply ignore the target's encoding assertion.
264          */

265
266         /** The Writer we convey. */
267         private StringWriter JavaDoc sw;
268
269         /** A buffer, alternatively, to accumulate bytes. */
270         private ByteArrayOutputStream JavaDoc bos;
271
272         /** 'True' if getWriter() was called; false otherwise. */
273         private boolean isWriterUsed;
274
275         /** 'True if getOutputStream() was called; false otherwise. */
276         private boolean isStreamUsed;
277
278         /** The HTTP status set by the target. */
279         private int status = 200;
280
281         //************************************************************
282
// Constructor and methods
283

284         /**
285          * Constructs a new ImportResponseWrapper.
286          * @param response the response to wrap
287          */

288         public ImportResponseWrapper(HttpServletResponse JavaDoc response)
289         {
290             super(response);
291         }
292
293         /**
294          * @return a Writer designed to buffer the output.
295          */

296         public PrintWriter JavaDoc getWriter()
297         {
298             if (isStreamUsed)
299             {
300                 throw new IllegalStateException JavaDoc("Unexpected internal error during import: "
301                                                 + "Target servlet called getWriter(), then getOutputStream()");
302             }
303             isWriterUsed = true;
304             sw = new StringWriter JavaDoc();
305             return new PrintWriter JavaDoc(sw);
306         }
307
308         /**
309          * @return a ServletOutputStream designed to buffer the output.
310          */

311         public ServletOutputStream JavaDoc getOutputStream()
312         {
313             if (isWriterUsed)
314             {
315                 throw new IllegalStateException JavaDoc("Unexpected internal error during import: "
316                                                 + "Target servlet called getOutputStream(), then getWriter()");
317             }
318             isStreamUsed = true;
319             bos = new ByteArrayOutputStream JavaDoc();
320             ServletOutputStream JavaDoc sos = new ServletOutputStream JavaDoc()
321                 {
322                     public void write(int b) throws IOException JavaDoc
323                     {
324                         bos.write(b);
325                     }
326                 };
327             return sos;
328         }
329
330         /** Has no effect. */
331         public void setContentType(String JavaDoc x)
332         {
333             // ignore
334
}
335
336         /** Has no effect. */
337         public void setLocale(Locale JavaDoc x)
338         {
339             // ignore
340
}
341
342         /**
343          * Sets the status of the response
344          * @param status the status code
345          */

346         public void setStatus(int status)
347         {
348             this.status = status;
349         }
350
351         /**
352          * @return the status of the response
353          */

354         public int getStatus()
355         {
356             return status;
357         }
358
359         /**
360          * Retrieves the buffered output, using the containing tag's
361          * 'charEncoding' attribute, or the tag's default encoding,
362          * <b>if necessary</b>.
363          * @return the buffered output
364          * @throws UnsupportedEncodingException if the encoding is not supported
365          */

366         public String JavaDoc getString() throws UnsupportedEncodingException JavaDoc
367         {
368             if (isWriterUsed)
369             {
370                 return sw.toString();
371             }
372             else if (isStreamUsed)
373             {
374                 return bos.toString(DEFAULT_ENCODING);
375             }
376             else
377             {
378                 return ""; // target didn't write anything
379
}
380         }
381     }
382
383     //*********************************************************************
384
// Public utility methods
385

386     /**
387      * Returns <tt>true</tt> if our current URL is absolute,
388      * <tt>false</tt> otherwise.
389      *
390      * @param url the url to check out
391      * @return true if the url is absolute
392      */

393     public static boolean isAbsoluteUrl(String JavaDoc url) {
394         // a null URL is not absolute, by our definition
395
if (url == null)
396         {
397             return false;
398         }
399
400         // do a fast, simple check first
401
int colonPos;
402         if ((colonPos = url.indexOf(":")) == -1)
403         {
404             return false;
405         }
406
407         // if we DO have a colon, make sure that every character
408
// leading up to it is a valid scheme character
409
for (int i = 0; i < colonPos; i++)
410         {
411             if (VALID_SCHEME_CHARS.indexOf(url.charAt(i)) == -1)
412             {
413                 return false;
414             }
415         }
416         // if so, we've got an absolute url
417
return true;
418     }
419
420     /**
421      * Strips a servlet session ID from <tt>url</tt>. The session ID
422      * is encoded as a URL "path parameter" beginning with "jsessionid=".
423      * We thus remove anything we find between ";jsessionid=" (inclusive)
424      * and either EOS or a subsequent ';' (exclusive).
425      *
426      * @param url the url to strip the session id from
427      * @return the stripped url
428      */

429     public static String JavaDoc stripSession(String JavaDoc url)
430     {
431         StringBuffer JavaDoc u = new StringBuffer JavaDoc(url);
432         int sessionStart;
433         while ((sessionStart = u.toString().indexOf(";jsessionid=")) != -1)
434         {
435             int sessionEnd = u.toString().indexOf(";", sessionStart + 1);
436             if (sessionEnd == -1)
437             {
438                 sessionEnd = u.toString().indexOf("?", sessionStart + 1);
439             }
440             if (sessionEnd == -1)
441             {
442                 // still
443
sessionEnd = u.length();
444             }
445             u.delete(sessionStart, sessionEnd);
446         }
447         return u.toString();
448     }
449
450     /**
451      * Get the value associated with a content-type attribute.
452      * Syntax defined in RFC 2045, section 5.1.
453      *
454      * @param input the string containing the attributes
455      * @param name the name of the content-type attribute
456      * @return the value associated with a content-type attribute
457      */

458     public static String JavaDoc getContentTypeAttribute(String JavaDoc input, String JavaDoc name)
459     {
460         int begin;
461         int end;
462         int index = input.toUpperCase().indexOf(name.toUpperCase());
463         if (index == -1)
464         {
465             return null;
466         }
467         index = index + name.length(); // positioned after the attribute name
468
index = input.indexOf('=', index); // positioned at the '='
469
if (index == -1)
470         {
471             return null;
472         }
473         index += 1; // positioned after the '='
474
input = input.substring(index).trim();
475
476         if (input.charAt(0) == '"')
477         {
478             // attribute value is a quoted string
479
begin = 1;
480             end = input.indexOf('"', begin);
481             if (end == -1)
482             {
483                 return null;
484             }
485         }
486         else
487         {
488             begin = 0;
489             end = input.indexOf(';');
490             if (end == -1)
491             {
492                 end = input.indexOf(' ');
493             }
494             if (end == -1)
495             {
496                 end = input.length();
497             }
498         }
499         return input.substring(begin, end).trim();
500     }
501
502 }
503
Popular Tags