/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.vfs2.provider.http;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.util.DateUtil;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.vfs2.FileContentInfoFactory;
import org.apache.commons.vfs2.FileNotFoundException;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.RandomAccessContent;
import org.apache.commons.vfs2.provider.AbstractFileName;
import org.apache.commons.vfs2.provider.AbstractFileObject;
import org.apache.commons.vfs2.provider.URLFileName;
import org.apache.commons.vfs2.util.MonitorInputStream;
import org.apache.commons.vfs2.util.RandomAccessMode;

A file object backed by Apache Commons HttpClient.

TODO - status codes.

Type parameters:
/** * A file object backed by Apache Commons HttpClient. * <p> * TODO - status codes. * </p> * * @param <FS> An {@link HttpFileSystem} subclass */
public class HttpFileObject<FS extends HttpFileSystem> extends AbstractFileObject<FS> {
An InputStream that cleans up the HTTP connection on close.
/** * An InputStream that cleans up the HTTP connection on close. */
static class HttpInputStream extends MonitorInputStream { private final GetMethod method; public HttpInputStream(final GetMethod method) throws IOException { super(method.getResponseBodyAsStream()); this.method = method; }
Called after the stream has been closed.
/** * Called after the stream has been closed. */
@Override protected void onClose() throws IOException { method.releaseConnection(); } } private final String urlCharset; private final String userAgent; private final boolean followRedirect; private HeadMethod method; protected HttpFileObject(final AbstractFileName name, final FS fileSystem) { this(name, fileSystem, HttpFileSystemConfigBuilder.getInstance()); } protected HttpFileObject(final AbstractFileName name, final FS fileSystem, final HttpFileSystemConfigBuilder builder) { super(name, fileSystem); final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions(); urlCharset = builder.getUrlCharset(fileSystemOptions); userAgent = builder.getUserAgent(fileSystemOptions); followRedirect = builder.getFollowRedirect(fileSystemOptions); }
Detaches this file object from its file resource.
/** * Detaches this file object from its file resource. */
@Override protected void doDetach() throws Exception { method = null; }
Returns the size of the file content (in bytes).
/** * Returns the size of the file content (in bytes). */
@Override protected long doGetContentSize() throws Exception { final Header header = method.getResponseHeader("content-length"); if (header == null) { // Assume 0 content-length return 0; } return Long.parseLong(header.getValue()); }
Creates an input stream to read the file content from. Is only called if doGetType returns FileType.FILE.

It is guaranteed that there are no open output streams for this file when this method is called.

The returned stream does not have to be buffered.

/** * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns * {@link FileType#FILE}. * <p> * It is guaranteed that there are no open output streams for this file when this method is called. * </p> * <p> * The returned stream does not have to be buffered. * </p> */
@Override protected InputStream doGetInputStream() throws Exception { final GetMethod getMethod = new GetMethod(); setupMethod(getMethod); final int status = getAbstractFileSystem().getClient().executeMethod(getMethod); if (status == HttpURLConnection.HTTP_NOT_FOUND) { throw new FileNotFoundException(getName()); } if (status != HttpURLConnection.HTTP_OK) { throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status)); } return new HttpInputStream(getMethod); }
Returns the last modified time of this file.

This implementation throws an exception.

/** * Returns the last modified time of this file. * <p> * This implementation throws an exception. * </p> */
@Override protected long doGetLastModifiedTime() throws Exception { final Header header = method.getResponseHeader("last-modified"); FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName()); return DateUtil.parseDate(header.getValue()).getTime(); } @Override protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { return new HttpRandomAccessContent<>(this, mode); }
Determines the type of this file. Must not return null. The return value of this method is cached, so the implementation can be expensive.
/** * Determines the type of this file. Must not return null. The return value of this method is cached, so the * implementation can be expensive. */
@Override protected FileType doGetType() throws Exception { // Use the HEAD method to probe the file. final int status = this.getHeadMethod().getStatusCode(); if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_BAD_METHOD /* method is bad, but resource exist */) { return FileType.FILE; } else if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) { return FileType.IMAGINARY; } else { throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status)); } } @Override protected boolean doIsWriteable() throws Exception { return false; }
Lists the children of this file.
/** * Lists the children of this file. */
@Override protected String[] doListChildren() throws Exception { throw new Exception("Not implemented."); } protected String encodePath(final String decodedPath) throws URIException { return URIUtil.encodePath(decodedPath); } @Override protected FileContentInfoFactory getFileContentInfoFactory() { return new HttpFileContentInfoFactory(); } protected boolean getFollowRedirect() { return followRedirect; } protected String getUserAgent() { return userAgent; } HeadMethod getHeadMethod() throws IOException { if (method != null) { return method; } method = new HeadMethod(); try { setupMethod(method); final HttpClient client = getAbstractFileSystem().getClient(); client.executeMethod(method); } finally { method.releaseConnection(); } return method; } protected String getUrlCharset() { return urlCharset; }
Prepares a HttpMethod object.
Params:
  • method – The object which gets prepared to access the file object.
Throws:
Since:2.0 (was package)
/** * Prepares a HttpMethod object. * * @param method The object which gets prepared to access the file object. * @throws FileSystemException if an error occurs. * @throws URIException if path cannot be represented. * @since 2.0 (was package) */
protected void setupMethod(final HttpMethod method) throws FileSystemException, URIException { final String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(this.getUrlCharset()); method.setPath(pathEncoded); method.setFollowRedirects(this.getFollowRedirect()); method.setRequestHeader("User-Agent", this.getUserAgent()); } /* * protected Map doGetAttributes() throws Exception { TreeMap map = new TreeMap(); * * Header contentType = method.getResponseHeader("content-type"); if (contentType != null) { HeaderElement[] element * = contentType.getValues(); if (element != null && element.length > 0) { map.put("content-type", * element[0].getName()); } } * * map.put("content-encoding", method.getResponseCharSet()); return map; } */ }