001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.vfs.provider.http;
018:
019: import org.apache.commons.httpclient.Header;
020: import org.apache.commons.httpclient.HttpClient;
021: import org.apache.commons.httpclient.HttpMethod;
022: import org.apache.commons.httpclient.URIException;
023: import org.apache.commons.httpclient.methods.GetMethod;
024: import org.apache.commons.httpclient.methods.HeadMethod;
025: import org.apache.commons.httpclient.util.DateParser;
026: import org.apache.commons.httpclient.util.URIUtil;
027: import org.apache.commons.vfs.FileContentInfoFactory;
028: import org.apache.commons.vfs.FileName;
029: import org.apache.commons.vfs.FileSystemException;
030: import org.apache.commons.vfs.FileType;
031: import org.apache.commons.vfs.RandomAccessContent;
032: import org.apache.commons.vfs.provider.AbstractFileObject;
033: import org.apache.commons.vfs.provider.URLFileName;
034: import org.apache.commons.vfs.util.MonitorInputStream;
035: import org.apache.commons.vfs.util.RandomAccessMode;
036:
037: import java.io.IOException;
038: import java.io.InputStream;
039: import java.net.HttpURLConnection;
040:
041: /**
042: * A file object backed by commons httpclient.
043: *
044: * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
045: * @version $Revision: 480428 $ $Date: 2006-11-28 22:15:24 -0800 (Tue, 28 Nov 2006) $
046: * @todo status codes
047: */
048: public class HttpFileObject extends AbstractFileObject {
049: private final HttpFileSystem fileSystem;
050: private final String urlCharset;
051: private HeadMethod method;
052:
053: protected HttpFileObject(final FileName name,
054: final HttpFileSystem fileSystem) {
055: super (name, fileSystem);
056: this .fileSystem = fileSystem;
057: urlCharset = HttpFileSystemConfigBuilder.getInstance()
058: .getUrlCharset(getFileSystem().getFileSystemOptions());
059: }
060:
061: /**
062: * Detaches this file object from its file resource.
063: */
064: protected void doDetach() throws Exception {
065: method = null;
066: }
067:
068: /**
069: * Determines the type of this file. Must not return null. The return
070: * value of this method is cached, so the implementation can be expensive.
071: */
072: protected FileType doGetType() throws Exception {
073: // Use the HEAD method to probe the file.
074: method = new HeadMethod();
075: setupMethod(method);
076: final HttpClient client = fileSystem.getClient();
077: final int status = client.executeMethod(method);
078: method.releaseConnection();
079: if (status == HttpURLConnection.HTTP_OK) {
080: return FileType.FILE;
081: } else if (status == HttpURLConnection.HTTP_NOT_FOUND
082: || status == HttpURLConnection.HTTP_GONE) {
083: return FileType.IMAGINARY;
084: } else {
085: throw new FileSystemException(
086: "vfs.provider.http/head.error", getName());
087: }
088: }
089:
090: /**
091: * Lists the children of this file.
092: */
093: protected String[] doListChildren() throws Exception {
094: throw new Exception("Not implemented.");
095: }
096:
097: /**
098: * Returns the size of the file content (in bytes).
099: */
100: protected long doGetContentSize() throws Exception {
101: final Header header = method
102: .getResponseHeader("content-length");
103: if (header == null) {
104: // Assume 0 content-length
105: return 0;
106: }
107: return Integer.parseInt(header.getValue());
108: }
109:
110: /**
111: * Returns the last modified time of this file.
112: * <p/>
113: * This implementation throws an exception.
114: */
115: protected long doGetLastModifiedTime() throws Exception {
116: final Header header = method.getResponseHeader("last-modified");
117: if (header == null) {
118: throw new FileSystemException(
119: "vfs.provider.http/last-modified.error", getName());
120: }
121: return DateParser.parseDate(header.getValue()).getTime();
122: }
123:
124: /**
125: * Creates an input stream to read the file content from. Is only called
126: * if {@link #doGetType} returns {@link FileType#FILE}.
127: * <p/>
128: * <p>It is guaranteed that there are no open output streams for this file
129: * when this method is called.
130: * <p/>
131: * <p>The returned stream does not have to be buffered.
132: */
133: protected InputStream doGetInputStream() throws Exception {
134: final GetMethod getMethod = new GetMethod();
135: setupMethod(getMethod);
136: final int status = fileSystem.getClient().executeMethod(
137: getMethod);
138: if (status != HttpURLConnection.HTTP_OK) {
139: throw new FileSystemException(
140: "vfs.provider.http/get.error", getName());
141: }
142:
143: return new HttpInputStream(getMethod);
144: }
145:
146: protected RandomAccessContent doGetRandomAccessContent(
147: final RandomAccessMode mode) throws Exception {
148: return new HttpRandomAccesContent(this , mode);
149: }
150:
151: /**
152: * Prepares a Method object.
153: */
154: void setupMethod(final HttpMethod method)
155: throws FileSystemException, URIException {
156: String pathEncoded = ((URLFileName) getName())
157: .getPathQueryEncoded(urlCharset);
158: method.setPath(pathEncoded);
159: method.setFollowRedirects(true);
160: method.setRequestHeader("User-Agent", "Jakarta-Commons-VFS");
161: }
162:
163: protected String encodePath(final String decodedPath)
164: throws URIException {
165: String pathEncoded = URIUtil.encodePath(decodedPath);
166: return pathEncoded;
167: }
168:
169: /**
170: * An InputStream that cleans up the HTTP connection on close.
171: */
172: static class HttpInputStream extends MonitorInputStream {
173: private final GetMethod method;
174:
175: public HttpInputStream(final GetMethod method)
176: throws IOException {
177: super (method.getResponseBodyAsStream());
178: this .method = method;
179: }
180:
181: /**
182: * Called after the stream has been closed.
183: */
184: protected void onClose() throws IOException {
185: method.releaseConnection();
186: }
187: }
188:
189: protected FileContentInfoFactory getFileContentInfoFactory() {
190: return new HttpFileContentInfoFactory();
191: }
192:
193: HeadMethod getHeadMethod() {
194: return method;
195: }
196:
197: /*
198: protected Map doGetAttributes() throws Exception
199: {
200: TreeMap map = new TreeMap();
201:
202: Header contentType = method.getResponseHeader("content-type");
203: if (contentType != null)
204: {
205: HeaderElement[] element = contentType.getValues();
206: if (element != null && element.length > 0)
207: {
208: map.put("content-type", element[0].getName());
209: }
210: }
211:
212: map.put("content-encoding", method.getResponseCharSet());
213: return map;
214: }
215: */
216: }
|