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