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.vfs2.provider.http;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.HttpURLConnection;
22  
23  import org.apache.commons.httpclient.Header;
24  import org.apache.commons.httpclient.HttpClient;
25  import org.apache.commons.httpclient.HttpMethod;
26  import org.apache.commons.httpclient.URIException;
27  import org.apache.commons.httpclient.methods.GetMethod;
28  import org.apache.commons.httpclient.methods.HeadMethod;
29  import org.apache.commons.httpclient.util.DateUtil;
30  import org.apache.commons.httpclient.util.URIUtil;
31  import org.apache.commons.vfs2.FileContentInfoFactory;
32  import org.apache.commons.vfs2.FileNotFoundException;
33  import org.apache.commons.vfs2.FileSystemException;
34  import org.apache.commons.vfs2.FileSystemOptions;
35  import org.apache.commons.vfs2.FileType;
36  import org.apache.commons.vfs2.RandomAccessContent;
37  import org.apache.commons.vfs2.provider.AbstractFileName;
38  import org.apache.commons.vfs2.provider.AbstractFileObject;
39  import org.apache.commons.vfs2.provider.URLFileName;
40  import org.apache.commons.vfs2.util.MonitorInputStream;
41  import org.apache.commons.vfs2.util.RandomAccessMode;
42  
43  /**
44   * A file object backed by Apache Commons HttpClient.
45   * <p>
46   * TODO - status codes.
47   * </p>
48   *
49   * @param <FS> An {@link HttpFileSystem} subclass
50   * @deprecated Use {@link org.apache.commons.vfs2.provider.http5}.
51   */
52  @Deprecated
53  public class HttpFileObject<FS extends HttpFileSystem> extends AbstractFileObject<FS> {
54  
55      /**
56       * An InputStream that cleans up the HTTP connection on close.
57       */
58      static class HttpInputStream extends MonitorInputStream {
59          private final GetMethod method;
60  
61          HttpInputStream(final GetMethod method) throws IOException {
62              super(method.getResponseBodyAsStream());
63              this.method = method;
64          }
65  
66          HttpInputStream(final GetMethod method, final int bufferSize) throws IOException {
67              super(method.getResponseBodyAsStream(), bufferSize);
68              this.method = method;
69          }
70  
71          /**
72           * Called after the stream has been closed.
73           */
74          @Override
75          protected void onClose() throws IOException {
76              method.releaseConnection();
77          }
78      }
79  
80      private final String urlCharset;
81      private final String userAgent;
82      private final boolean followRedirect;
83      private HeadMethod method;
84  
85      /**
86       * Constructs a new instance.
87       *
88       * @param fileName the file name.
89       * @param fileSystem the file system.
90       */
91      protected HttpFileObject(final AbstractFileName fileName, final FS fileSystem) {
92          this(fileName, fileSystem, HttpFileSystemConfigBuilder.getInstance());
93      }
94  
95      /**
96       * Constructs a new instance.
97       *
98       * @param fileName the file name.
99       * @param fileSystem the file system.
100      * @param builder Configuration options for HTTP.
101      */
102     protected HttpFileObject(final AbstractFileName fileName, final FS fileSystem,
103             final HttpFileSystemConfigBuilder builder) {
104         super(fileName, fileSystem);
105         final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions();
106         urlCharset = builder.getUrlCharset(fileSystemOptions);
107         userAgent = builder.getUserAgent(fileSystemOptions);
108         followRedirect = builder.getFollowRedirect(fileSystemOptions);
109     }
110 
111     /**
112      * Detaches this file object from its file resource.
113      */
114     @Override
115     protected void doDetach() throws Exception {
116         synchronized (getFileSystem()) {
117             method = null;
118         }
119     }
120 
121     /**
122      * Returns the size of the file content (in bytes).
123      */
124     @Override
125     protected long doGetContentSize() throws Exception {
126         final Header header = getHeadMethod().getResponseHeader("content-length");
127         if (header == null) {
128             // Assume 0 content-length
129             return 0;
130         }
131         return Long.parseLong(header.getValue());
132     }
133 
134     /**
135      * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
136      * {@link FileType#FILE}.
137      * <p>
138      * It is guaranteed that there are no open output streams for this file when this method is called.
139      * </p>
140      * <p>
141      * The returned stream does not have to be buffered.
142      * </p>
143      */
144     @Override
145     protected InputStream doGetInputStream(final int bufferSize) throws Exception {
146         final GetMethod getMethod = new GetMethod();
147         setupMethod(getMethod);
148         final int status = getAbstractFileSystem().getClient().executeMethod(getMethod);
149         if (status == HttpURLConnection.HTTP_NOT_FOUND) {
150             throw new FileNotFoundException(getName());
151         }
152         if (status != HttpURLConnection.HTTP_OK) {
153             throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status));
154         }
155 
156         return new HttpInputStream(getMethod, bufferSize);
157     }
158 
159     /**
160      * Returns the last modified time of this file.
161      * <p>
162      * This implementation throws an exception.
163      * </p>
164      */
165     @Override
166     protected long doGetLastModifiedTime() throws Exception {
167         final Header header = getHeadMethod().getResponseHeader("last-modified");
168         FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName());
169         return DateUtil.parseDate(header.getValue()).getTime();
170     }
171 
172     @Override
173     protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
174         return new HttpRandomAccessContent<>(this, mode);
175     }
176 
177     /**
178      * Determines the type of this file. Must not return null. The return value of this method is cached, so the
179      * implementation can be expensive.
180      */
181     @Override
182     protected FileType doGetType() throws Exception {
183         // Use the HEAD method to probe the file.
184         final int status = this.getHeadMethod().getStatusCode();
185         if (status == HttpURLConnection.HTTP_OK
186                 || status == HttpURLConnection.HTTP_BAD_METHOD /* method is bad, but resource exist */) {
187             return FileType.FILE;
188         }
189         if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) {
190             return FileType.IMAGINARY;
191         }
192         throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status));
193     }
194 
195     @Override
196     protected boolean doIsWriteable() throws Exception {
197         return false;
198     }
199 
200     /**
201      * Throws UnsupportedOperationException.
202      *
203      * @throws UnsupportedOperationException always thrown.
204      */
205     @Override
206     protected String[] doListChildren() throws Exception {
207         throw new UnsupportedOperationException("Not implemented.");
208     }
209 
210     /**
211      * Encodes the given path.
212      *
213      * @param unescaped An unescaped path.
214      * @return the encoded path.
215      * @throws URIException if the default protocol charset is not supported
216      */
217     protected String encodePath(final String unescaped) throws URIException {
218         return URIUtil.encodePath(unescaped);
219     }
220 
221     /**
222      * Gets a new FileContentInfoFactory.
223      *
224      * @return a new FileContentInfoFactory.
225      */
226     @Override
227     protected FileContentInfoFactory getFileContentInfoFactory() {
228         return new HttpFileContentInfoFactory();
229     }
230 
231     /**
232      * Gets whether to follow redirects.
233      *
234      * @return whether to follow redirects.
235      */
236     protected boolean getFollowRedirect() {
237         return followRedirect;
238     }
239 
240     /**
241      * Gets a new HeadMethod.
242      *
243      * @return a new HeadMethod.
244      * @throws IOException if an IO error occurs.
245      */
246     HeadMethod getHeadMethod() throws IOException {
247         // need to synchronize on the file system as the detach method will clear out "method"
248         synchronized (getFileSystem()) {
249             if (method != null) {
250                 return method;
251             }
252             method = new HeadMethod();
253             try {
254                 setupMethod(method);
255                 final HttpClient client = getAbstractFileSystem().getClient();
256                 client.executeMethod(method);
257             } finally {
258                 method.releaseConnection();
259             }
260             return method;
261         }
262     }
263 
264     /**
265      * Gets the URL charset name.
266      *
267      * @return the URL charset name.
268      */
269     protected String getUrlCharset() {
270         return urlCharset;
271     }
272 
273     /**
274      * Gets the user agent.
275      *
276      * @return the user agent.
277      */
278     protected String getUserAgent() {
279         return userAgent;
280     }
281 
282     /**
283      * Prepares a HttpMethod object.
284      *
285      * @param method The object which gets prepared to access the file object.
286      * @throws FileSystemException if an error occurs.
287      * @throws URIException if path cannot be represented.
288      * @since 2.0 (was package)
289      */
290     protected void setupMethod(final HttpMethod method) throws FileSystemException, URIException {
291         final String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(this.getUrlCharset());
292         method.setPath(pathEncoded);
293         method.setFollowRedirects(this.getFollowRedirect());
294         method.setRequestHeader("User-Agent", this.getUserAgent());
295     }
296 
297     /*
298      * protected Map doGetAttributes() throws Exception { TreeMap map = new TreeMap();
299      *
300      * Header contentType = method.getResponseHeader("content-type"); if (contentType != null) { HeaderElement[] element
301      * = contentType.getValues(); if (element != null && element.length > 0) { map.put("content-type",
302      * element[0].getName()); } }
303      *
304      * map.put("content-encoding", method.getResponseCharSet()); return map; }
305      */
306 }