KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > vfs > HttpPath


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.vfs;
31
32 import com.caucho.util.Alarm;
33 import com.caucho.util.CharBuffer;
34 import com.caucho.util.L10N;
35 import com.caucho.util.LruCache;
36 import com.caucho.util.QDate;
37
38 import org.xml.sax.Attributes JavaDoc;
39 import org.xml.sax.SAXException JavaDoc;
40
41 import java.io.IOException JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Map JavaDoc;
44
45 /**
46  * The HTTP scheme. Currently it supports GET and POST.
47  *
48  * <p>TODO: support WEBDAV, enabling the full Path API.
49  */

50 public class HttpPath extends FilesystemPath {
51   protected static L10N L = new L10N(HttpPath.class);
52
53   protected static LruCache<String JavaDoc,CacheEntry> _cache =
54     new LruCache<String JavaDoc,CacheEntry>(1024);
55   
56   protected String JavaDoc _host;
57   protected int _port;
58   protected String JavaDoc _query;
59
60   protected String JavaDoc _virtualHost;
61
62   protected CacheEntry _cacheEntry;
63
64   /**
65    * Creates a new HTTP root path with a host and a port.
66    *
67    * @param host the target host
68    * @param port the target port, if zero, uses port 80.
69    */

70   public HttpPath(String JavaDoc host, int port)
71   {
72     super(null, "/", "/");
73
74     _root = this;
75     _host = host;
76     _port = port == 0 ? 80 : port;
77   }
78
79   /**
80    * Creates a new HTTP sub path.
81    *
82    * @param root the HTTP filesystem root
83    * @param userPath the argument to the calling lookup()
84    * @param newAttributes any attributes passed to http
85    * @param path the full normalized path
86    * @param query any query string
87    */

88   HttpPath(FilesystemPath root,
89            String JavaDoc userPath, Map JavaDoc<String JavaDoc,Object JavaDoc> newAttributes,
90        String JavaDoc path, String JavaDoc query)
91   {
92     super(root, userPath, path);
93
94     _host = ((HttpPath) root)._host;
95     _port = ((HttpPath) root)._port;
96     _query = query;
97
98     if (newAttributes != null) {
99       _virtualHost = (String JavaDoc) newAttributes.get("host");
100     }
101   }
102
103   /**
104    * Overrides the default lookup to parse the host and port
105    * before parsing the path.
106    *
107    * @param userPath the path passed in by the user
108    * @param newAttributes attributes passed by the user
109    *
110    * @return the final path.
111    */

112   public Path lookupImpl(String JavaDoc userPath, Map JavaDoc<String JavaDoc,Object JavaDoc> newAttributes)
113   {
114     String JavaDoc newPath;
115
116     if (userPath == null)
117       return _root.fsWalk(getPath(), newAttributes, "/");
118
119     int length = userPath.length();
120     int colon = userPath.indexOf(':');
121     int slash = userPath.indexOf('/');
122
123     // parent handles scheme:xxx
124
if (colon != -1 && (colon < slash || slash == -1))
125       return super.lookupImpl(userPath, newAttributes);
126       
127       // //hostname
128
if (slash == 0 && length > 1 && userPath.charAt(1) == '/')
129       return schemeWalk(userPath, newAttributes, userPath, 0);
130
131       // /path
132
else if (slash == 0)
133       newPath = normalizePath("/", userPath, 0, '/');
134
135       // path
136
else
137       newPath = normalizePath(_pathname, userPath, 0, '/');
138
139     // XXX: does missing root here cause problems with restrictions?
140
return _root.fsWalk(userPath, newAttributes, newPath);
141   }
142
143   /**
144    * Walk down the path starting from the portion immediately following
145    * the scheme. i.e. schemeWalk is responsible for parsing the host and
146    * port from the URL.
147    *
148    * @param userPath the user's passed in path
149    * @param attributes the attributes for the new path
150    * @param uri the normalized full uri
151    * @param offset offset into the uri to start processing, i.e. after the
152    * scheme.
153    *
154    * @return the looked-up path.
155    */

156   protected Path schemeWalk(String JavaDoc userPath,
157                             Map JavaDoc<String JavaDoc,Object JavaDoc> attributes,
158                 String JavaDoc uri,
159                             int offset)
160   {
161     int length = uri.length();
162
163     if (length < 2 + offset ||
164         uri.charAt(offset) != '/' ||
165         uri.charAt(offset + 1) != '/')
166       throw new RuntimeException JavaDoc(L.l("bad scheme in `{0}'", uri));
167
168     CharBuffer buf = CharBuffer.allocate();
169     int i = 2 + offset;
170     int ch = 0;
171     for (; i < length && (ch = uri.charAt(i)) != ':' && ch != '/' && ch != '?';
172      i++) {
173       buf.append((char) ch);
174     }
175
176     String JavaDoc host = buf.close();
177     if (host.length() == 0)
178       throw new RuntimeException JavaDoc(L.l("bad host in `{0}'", uri));
179
180     int port = 0;
181     if (ch == ':') {
182       for (i++; i < length && (ch = uri.charAt(i)) >= '0' && ch <= '9'; i++) {
183     port = 10 * port + uri.charAt(i) - '0';
184       }
185     }
186
187     if (port == 0)
188       port = 80;
189
190     HttpPath root = create(host, port);
191
192     return root.fsWalk(userPath, attributes, uri.substring(i));
193   }
194
195   /**
196    * Scans the path portion of the URI, i.e. everything after the
197    * host and port.
198    *
199    * @param userPath the user's supplied path
200    * @param attributes the attributes for the new path
201    * @param uri the full uri for the new path.
202    *
203    * @return the found path.
204    */

205   public Path fsWalk(String JavaDoc userPath,
206              Map JavaDoc<String JavaDoc,Object JavaDoc> attributes,
207              String JavaDoc uri)
208   {
209     String JavaDoc path;
210     String JavaDoc query = null;
211     int queryIndex = uri.indexOf('?');
212     if (queryIndex >= 0) {
213       path = uri.substring(0, queryIndex);
214       query = uri.substring(queryIndex + 1);
215     } else
216       path = uri;
217
218     if (path.length() == 0)
219       path = "/";
220
221     return create(_root, userPath, attributes, path, query);
222   }
223
224   protected HttpPath create(String JavaDoc host, int port)
225   {
226     return new HttpPath(host, port);
227   }
228
229   protected HttpPath create(FilesystemPath root,
230                             String JavaDoc userPath,
231                 Map JavaDoc<String JavaDoc,Object JavaDoc> newAttributes,
232                             String JavaDoc path, String JavaDoc query)
233   {
234     return new HttpPath(root, userPath, newAttributes, path, query);
235   }
236
237   /**
238    * Returns the scheme, http.
239    */

240   public String JavaDoc getScheme()
241   {
242     return "http";
243   }
244
245   /**
246    * Returns a full URL for the path.
247    */

248   public String JavaDoc getURL()
249   {
250     int port = getPort();
251
252     return (getScheme() + "://" + getHost() +
253         (port == 80 ? "" : ":" + getPort()) +
254         getPath() +
255         (_query == null ? "" : "?" + _query));
256   }
257
258   /**
259    * Returns the host part of the url.
260    */

261   public String JavaDoc getHost()
262   {
263     return _host;
264   }
265
266   /**
267    * Returns the port part of the url.
268    */

269   public int getPort()
270   {
271     return _port;
272   }
273
274   /**
275    * Returns the user's path.
276    */

277   public String JavaDoc getUserPath()
278   {
279     return _userPath;
280   }
281
282   /**
283    * Returns the virtual host, if any.
284    */

285   public String JavaDoc getVirtualHost()
286   {
287     return _virtualHost;
288   }
289
290   /**
291    * Returns the query string.
292    */

293   public String JavaDoc getQuery()
294   {
295     return _query;
296   }
297
298   /**
299    * Returns the last modified time.
300    */

301   public long getLastModified()
302   {
303     return getCache().lastModified;
304   }
305
306   /**
307    * Returns the file's length
308    */

309   public long getLength()
310   {
311     return getCache().length;
312   }
313
314   /**
315    * Returns true if the file exists.
316    */

317   public boolean exists()
318   {
319     return getCache().lastModified >= 0;
320   }
321
322   /**
323    * Returns true if the file exists.
324    */

325   public boolean isFile()
326   {
327     return ! getPath().endsWith("/") && getCache().lastModified >= 0;
328   }
329
330   /**
331    * Returns true if the file is readable.
332    */

333   public boolean canRead()
334   {
335     return isFile();
336   }
337
338   /**
339    * Returns the last modified time.
340    */

341   public boolean isDirectory()
342   {
343     return getPath().endsWith("/") && getCache().lastModified >= 0;
344   }
345   
346   /**
347    * @return The contents of this directory or null if the path does not
348    * refer to a directory.
349    */

350   /*
351   public String []list() throws IOException
352   {
353     try {
354       HttpStream stream = (HttpStream) openReadWriteImpl();
355       stream.setMethod("PROPFIND");
356       stream.setAttribute("Depth", "1");
357
358       WriteStream os = new WriteStream(stream);
359       os.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
360       os.println("<propfind xmlns=\"DAV:\"><prop>");
361       os.println("<resourcetype/>");
362       os.println("</prop></propfind>");
363       os.flush();
364
365       ReadStream is = new ReadStream(stream);
366     
367       ListHandler handler = new ListHandler(getPath());
368       XmlParser parser = new XmlParser();
369       parser.setContentHandler(handler);
370
371       parser.parse(is);
372
373       is.close();
374       os.close();
375       stream.close();
376
377       ArrayList<String> names = handler.getNames();
378       String []list = new String[names.size()];
379       names.toArray(list);
380       
381       return list;
382     } catch (Exception e) {
383       throw new IOException(L.l("list() is not supported by this server"));
384     }
385   }
386   */

387
388   protected CacheEntry getCache()
389   {
390     if (_cacheEntry == null) {
391       synchronized (_cache) {
392         _cacheEntry = _cache.get(getPath());
393         if (_cacheEntry == null) {
394           _cacheEntry = new CacheEntry();
395           _cache.put(getPath(), _cacheEntry);
396         }
397       }
398     }
399     
400     long now = Alarm.getCurrentTime();
401     synchronized (_cacheEntry) {
402       try {
403         if (_cacheEntry.expires > now)
404           return _cacheEntry;
405       
406         HttpStreamWrapper stream = (HttpStreamWrapper) openReadImpl();
407         stream.setHead(true);
408         stream.setSocketTimeout(120000);
409
410         String JavaDoc status = (String JavaDoc) stream.getAttribute("status");
411         if (status.equals("200")) {
412           String JavaDoc lastModified = (String JavaDoc) stream.getAttribute("last-modified");
413
414           _cacheEntry.lastModified = 0;
415           if (lastModified != null) {
416             QDate date = QDate.getGlobalDate();
417             synchronized (date) {
418               _cacheEntry.lastModified = date.parseDate(lastModified);
419             }
420           }
421
422           String JavaDoc length = (String JavaDoc) stream.getAttribute("content-length");
423           _cacheEntry.length = 0;
424           if (length != null) {
425             _cacheEntry.length = Integer.parseInt(length);
426           }
427         }
428         else
429           _cacheEntry.lastModified = -1;
430         
431         _cacheEntry.expires = now + 5000;
432       
433         stream.close();
434         return _cacheEntry;
435       } catch (Exception JavaDoc e) {
436         _cacheEntry.lastModified = -1;
437         _cacheEntry.expires = now + 5000;
438
439         return _cacheEntry;
440       }
441     }
442   }
443   
444
445   /**
446    * Returns a read stream for a GET request.
447    */

448   public StreamImpl openReadImpl() throws IOException JavaDoc
449   {
450     return HttpStream.openRead(this);
451   }
452
453   /**
454    * Returns a read/write pair for a POST request.
455    */

456   public StreamImpl openReadWriteImpl() throws IOException JavaDoc
457   {
458     return HttpStream.openReadWrite(this);
459   }
460
461   @Override JavaDoc
462   protected Path cacheCopy()
463   {
464     return new HttpPath(getRoot(), getUserPath(),
465             null,
466             getPath(), _query);
467   }
468   
469   /**
470    * Returns the string form of the http path.
471    */

472   public String JavaDoc toString()
473   {
474     return getURL();
475   }
476
477   /**
478    * Returns a hashCode for the path.
479    */

480   public int hashCode()
481   {
482     return 65537 * super.hashCode() + 37 * _host.hashCode() + _port;
483   }
484
485   /**
486    * Overrides equals to test for equality with an HTTP path.
487    */

488   public boolean equals(Object JavaDoc o)
489   {
490     if (! (o instanceof HttpPath))
491       return false;
492
493     HttpPath test = (HttpPath) o;
494
495     if (! _host.equals(test._host))
496       return false;
497     else if (_port != test._port)
498       return false;
499     else if (_query != null && ! _query.equals(test._query))
500       return false;
501     else if (_query == null && test._query != null)
502       return false;
503     else
504       return true;
505   }
506
507   static class CacheEntry {
508     long lastModified;
509     long length;
510     boolean canRead;
511     long expires;
512   }
513
514   static class ListHandler extends org.xml.sax.helpers.DefaultHandler JavaDoc {
515     String JavaDoc _prefix;
516     ArrayList JavaDoc<String JavaDoc> _names = new ArrayList JavaDoc<String JavaDoc>();
517     boolean _inHref;
518
519     ListHandler(String JavaDoc prefix)
520     {
521       _prefix = prefix;
522     }
523     
524     ArrayList JavaDoc<String JavaDoc> getNames()
525     {
526       return _names;
527     }
528
529     public void startElement (String JavaDoc uri, String JavaDoc localName,
530                   String JavaDoc qName, Attributes JavaDoc attributes)
531     {
532       if (localName.equals("href"))
533         _inHref = true;
534     }
535
536     public void characters(char []data, int offset, int length)
537     throws SAXException JavaDoc
538     {
539       if (! _inHref)
540         return;
541
542       String JavaDoc href = new String JavaDoc(data, offset, length).trim();
543       if (! href.startsWith(_prefix))
544         return;
545
546       href = href.substring(_prefix.length());
547       if (href.startsWith("/"))
548         href = href.substring(1);
549
550       int p = href.indexOf('/');
551       if (href.equals("") || p == 0)
552         return;
553
554       if (p < 0)
555         _names.add(href);
556       else
557         _names.add(href.substring(0, p));
558     }
559
560     public void endElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
561     throws SAXException JavaDoc
562     {
563       if (localName.equals("href"))
564         _inHref = false;
565     }
566   }
567 }
568
Popular Tags