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