View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs.provider.http;
18  
19  import org.apache.commons.httpclient.Header;
20  import org.apache.commons.httpclient.HttpClient;
21  import org.apache.commons.httpclient.HttpMethod;
22  import org.apache.commons.httpclient.URIException;
23  import org.apache.commons.httpclient.methods.GetMethod;
24  import org.apache.commons.httpclient.methods.HeadMethod;
25  import org.apache.commons.httpclient.util.DateParser;
26  import org.apache.commons.httpclient.util.URIUtil;
27  import org.apache.commons.vfs.FileContentInfoFactory;
28  import org.apache.commons.vfs.FileName;
29  import org.apache.commons.vfs.FileSystemException;
30  import org.apache.commons.vfs.FileType;
31  import org.apache.commons.vfs.RandomAccessContent;
32  import org.apache.commons.vfs.provider.AbstractFileObject;
33  import org.apache.commons.vfs.provider.URLFileName;
34  import org.apache.commons.vfs.util.MonitorInputStream;
35  import org.apache.commons.vfs.util.RandomAccessMode;
36  
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.net.HttpURLConnection;
40  
41  /***
42   * A file object backed by commons httpclient.
43   *
44   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
45   * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
46   * @todo status codes
47   */
48  public class HttpFileObject
49      extends AbstractFileObject
50  {
51      private final HttpFileSystem fileSystem;
52      private final String urlCharset;
53      private HeadMethod method;
54  
55      protected HttpFileObject(final FileName name,
56                               final HttpFileSystem fileSystem)
57      {
58          super(name, fileSystem);
59          this.fileSystem = fileSystem;
60          urlCharset = HttpFileSystemConfigBuilder.getInstance().getUrlCharset(getFileSystem().getFileSystemOptions());
61      }
62  
63      /***
64       * Detaches this file object from its file resource.
65       */
66      protected void doDetach()
67          throws Exception
68      {
69          method = null;
70      }
71  
72      /***
73       * Determines the type of this file.  Must not return null.  The return
74       * value of this method is cached, so the implementation can be expensive.
75       */
76      protected FileType doGetType()
77          throws Exception
78      {
79          // Use the HEAD method to probe the file.
80          method = new HeadMethod();
81          setupMethod(method);
82          final HttpClient client = fileSystem.getClient();
83          final int status = client.executeMethod(method);
84          method.releaseConnection();
85          if (status == HttpURLConnection.HTTP_OK)
86          {
87              return FileType.FILE;
88          }
89          else if (status == HttpURLConnection.HTTP_NOT_FOUND
90              || status == HttpURLConnection.HTTP_GONE)
91          {
92              return FileType.IMAGINARY;
93          }
94          else
95          {
96              throw new FileSystemException("vfs.provider.http/head.error", getName());
97          }
98      }
99  
100     /***
101      * Lists the children of this file.
102      */
103     protected String[] doListChildren()
104         throws Exception
105     {
106         throw new Exception("Not implemented.");
107     }
108 
109     /***
110      * Returns the size of the file content (in bytes).
111      */
112     protected long doGetContentSize()
113         throws Exception
114     {
115         final Header header = method.getResponseHeader("content-length");
116         if (header == null)
117         {
118             // Assume 0 content-length
119             return 0;
120         }
121         return Integer.parseInt(header.getValue());
122     }
123 
124     /***
125      * Returns the last modified time of this file.
126      * <p/>
127      * This implementation throws an exception.
128      */
129     protected long doGetLastModifiedTime()
130         throws Exception
131     {
132         final Header header = method.getResponseHeader("last-modified");
133         if (header == null)
134         {
135             throw new FileSystemException("vfs.provider.http/last-modified.error", getName());
136         }
137         return DateParser.parseDate(header.getValue()).getTime();
138     }
139 
140     /***
141      * Creates an input stream to read the file content from.  Is only called
142      * if {@link #doGetType} returns {@link FileType#FILE}.
143      * <p/>
144      * <p>It is guaranteed that there are no open output streams for this file
145      * when this method is called.
146      * <p/>
147      * <p>The returned stream does not have to be buffered.
148      */
149     protected InputStream doGetInputStream()
150         throws Exception
151     {
152         final GetMethod getMethod = new GetMethod();
153         setupMethod(getMethod);
154         final int status = fileSystem.getClient().executeMethod(getMethod);
155         if (status != HttpURLConnection.HTTP_OK)
156         {
157             throw new FileSystemException("vfs.provider.http/get.error", getName());
158         }
159 
160         return new HttpInputStream(getMethod);
161     }
162 
163     protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception
164     {
165         return new HttpRandomAccesContent(this, mode);
166     }
167 
168     /***
169      * Prepares a Method object.
170      */
171     void setupMethod(final HttpMethod method) throws FileSystemException, URIException
172     {
173         String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(urlCharset);
174         method.setPath(pathEncoded);
175         method.setFollowRedirects(true);
176         method.setRequestHeader("User-Agent", "Jakarta-Commons-VFS");
177     }
178 
179     protected String encodePath(final String decodedPath) throws URIException
180     {
181         String pathEncoded = URIUtil.encodePath(decodedPath);
182         return pathEncoded;
183     }
184 
185     /***
186      * An InputStream that cleans up the HTTP connection on close.
187      */
188     static class HttpInputStream
189         extends MonitorInputStream
190     {
191         private final GetMethod method;
192 
193         public HttpInputStream(final GetMethod method)
194             throws IOException
195         {
196             super(method.getResponseBodyAsStream());
197             this.method = method;
198         }
199 
200         /***
201          * Called after the stream has been closed.
202          */
203         protected void onClose()
204             throws IOException
205         {
206             method.releaseConnection();
207         }
208     }
209 
210 
211     protected FileContentInfoFactory getFileContentInfoFactory()
212     {
213         return new HttpFileContentInfoFactory();
214     }
215 
216     HeadMethod getHeadMethod()
217     {
218         return method;
219     }
220 
221     /*
222     protected Map doGetAttributes() throws Exception
223     {
224         TreeMap map = new TreeMap();
225 
226         Header contentType = method.getResponseHeader("content-type");
227         if (contentType != null)
228         {
229             HeaderElement[] element = contentType.getValues();
230             if (element != null && element.length > 0)
231             {
232                 map.put("content-type", element[0].getName());
233             }
234         }
235 
236         map.put("content-encoding", method.getResponseCharSet());
237         return map;
238     }
239     */
240 }