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.http4;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  
24  import org.apache.commons.vfs2.FileContentInfoFactory;
25  import org.apache.commons.vfs2.FileNotFoundException;
26  import org.apache.commons.vfs2.FileSystemException;
27  import org.apache.commons.vfs2.FileSystemOptions;
28  import org.apache.commons.vfs2.FileType;
29  import org.apache.commons.vfs2.RandomAccessContent;
30  import org.apache.commons.vfs2.provider.AbstractFileName;
31  import org.apache.commons.vfs2.provider.AbstractFileObject;
32  import org.apache.commons.vfs2.provider.GenericURLFileName;
33  import org.apache.commons.vfs2.util.RandomAccessMode;
34  import org.apache.http.Header;
35  import org.apache.http.HttpResponse;
36  import org.apache.http.HttpStatus;
37  import org.apache.http.client.HttpClient;
38  import org.apache.http.client.methods.HttpGet;
39  import org.apache.http.client.methods.HttpHead;
40  import org.apache.http.client.methods.HttpUriRequest;
41  import org.apache.http.client.protocol.HttpClientContext;
42  import org.apache.http.client.utils.DateUtils;
43  import org.apache.http.client.utils.URIUtils;
44  import org.apache.http.protocol.HTTP;
45  
46  /**
47   * A file object backed by Apache HttpComponents HttpClient.
48   *
49   * @param <FS> An {@link Http4FileSystem} subclass
50   */
51  public class Http4FileObject<FS extends Http4FileSystem> extends AbstractFileObject<FS> {
52  
53      /**
54       * URL charset string.
55       */
56      private final String urlCharset;
57  
58      /**
59       * Internal URI mapped to this <code>FileObject</code>.
60       * For example, the internal URI of <code>http4://example.com/a.txt</code> is <code>http://example.com/a.txt</code>.
61       */
62      private final URI internalURI;
63  
64      /**
65       * The last executed HEAD <code>HttpResponse</code> object.
66       */
67      private HttpResponse lastHeadResponse;
68  
69      /**
70       * Construct <code>Http4FileObject</code>.
71       *
72       * @param name file name
73       * @param fileSystem file system
74       * @throws FileSystemException if any error occurs
75       * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error
76       */
77      protected Http4FileObject(final AbstractFileName name, final FS fileSystem)
78              throws FileSystemException, URISyntaxException {
79          this(name, fileSystem, Http4FileSystemConfigBuilder.getInstance());
80      }
81  
82      /**
83       * Construct <code>Http4FileObject</code>.
84       *
85       * @param name file name
86       * @param fileSystem file system
87       * @param builder <code>Http4FileSystemConfigBuilder</code> object
88       * @throws FileSystemException if any error occurs
89       * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error
90       */
91      protected Http4FileObject(final AbstractFileName name, final FS fileSystem,
92              final Http4FileSystemConfigBuilder builder) throws FileSystemException, URISyntaxException {
93          super(name, fileSystem);
94          final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions();
95          urlCharset = builder.getUrlCharset(fileSystemOptions);
96          final String pathEncoded = ((GenericURLFileName) name).getPathQueryEncoded(getUrlCharset());
97          internalURI = URIUtils.resolve(fileSystem.getInternalBaseURI(), pathEncoded);
98      }
99  
100     @Override
101     protected FileType doGetType() throws Exception {
102         lastHeadResponse = executeHttpUriRequest(new HttpHead(getInternalURI()));
103         final int status = lastHeadResponse.getStatusLine().getStatusCode();
104 
105         if (status == HttpStatus.SC_OK
106                 || status == HttpStatus.SC_METHOD_NOT_ALLOWED /* method is not allowed, but resource exist */) {
107             return FileType.FILE;
108         } else if (status == HttpStatus.SC_NOT_FOUND || status == HttpStatus.SC_GONE) {
109             return FileType.IMAGINARY;
110         } else {
111             throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status));
112         }
113     }
114 
115     @Override
116     protected long doGetContentSize() throws Exception {
117         if (lastHeadResponse == null) {
118             return 0L;
119         }
120 
121         final Header header = lastHeadResponse.getFirstHeader(HTTP.CONTENT_LEN);
122 
123         if (header == null) {
124             // Assume 0 content-length
125             return 0;
126         }
127 
128         return Long.parseLong(header.getValue());
129     }
130 
131     @Override
132     protected long doGetLastModifiedTime() throws Exception {
133         FileSystemException.requireNonNull(lastHeadResponse, "vfs.provider.http/last-modified.error", getName());
134 
135         final Header header = lastHeadResponse.getFirstHeader("Last-Modified");
136 
137         FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName());
138 
139         return DateUtils.parseDate(header.getValue()).getTime();
140     }
141 
142 
143     @Override
144     protected InputStream doGetInputStream() throws Exception {
145         final HttpGet getRequest = new HttpGet(getInternalURI());
146         final HttpResponse httpResponse = executeHttpUriRequest(getRequest);
147         final int status = httpResponse.getStatusLine().getStatusCode();
148 
149         if (status == HttpStatus.SC_NOT_FOUND) {
150             throw new FileNotFoundException(getName());
151         }
152 
153         if (status != HttpStatus.SC_OK) {
154             throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status));
155         }
156 
157         return new MonitoredHttpResponseContentInputStream(httpResponse);
158     }
159 
160     @Override
161     protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
162         return new Http4RandomAccessContent<>(this, mode);
163     }
164 
165     @Override
166     protected String[] doListChildren() throws Exception {
167         throw new UnsupportedOperationException("Not implemented.");
168     }
169 
170     @Override
171     protected boolean doIsWriteable() throws Exception {
172         return false;
173     }
174 
175     @Override
176     protected FileContentInfoFactory getFileContentInfoFactory() {
177         return new Http4FileContentInfoFactory();
178     }
179 
180     @Override
181     protected void doDetach() throws Exception {
182         lastHeadResponse = null;
183     }
184 
185     /**
186      * Return URL charset string.
187      * @return URL charset string
188      */
189     protected String getUrlCharset() {
190         return urlCharset;
191     }
192 
193     /**
194      * Return the internal <code>URI</code> object mapped to this file object.
195      *
196      * @return the internal <code>URI</code> object mapped to this file object
197      * @throws FileSystemException if any error occurs
198      */
199     protected URI getInternalURI() throws FileSystemException {
200         return internalURI;
201     }
202 
203     /**
204      * Return the last executed HEAD <code>HttpResponse</code> object.
205      *
206      * @return the last executed HEAD <code>HttpResponse</code> object
207      * @throws IOException if IO error occurs
208      */
209     HttpResponse getLastHeadResponse() throws IOException {
210         if (lastHeadResponse != null) {
211             return lastHeadResponse;
212         }
213 
214         return executeHttpUriRequest(new HttpHead(getInternalURI()));
215     }
216 
217     /**
218      * Execute the request using the given {@code httpRequest} and return a <code>HttpResponse</code> from the execution.
219      *
220      * @param httpRequest <code>HttpUriRequest</code> object
221      * @return <code>HttpResponse</code> from the execution
222      * @throws IOException if IO error occurs
223      */
224     HttpResponse executeHttpUriRequest(final HttpUriRequest httpRequest) throws IOException {
225         final HttpClient httpClient = getAbstractFileSystem().getHttpClient();
226         final HttpClientContext httpClientContext = getAbstractFileSystem().getHttpClientContext();
227         return httpClient.execute(httpRequest, httpClientContext);
228     }
229 
230 }