Switch back to standard commons file-upload layout - simpler to merge updates. Part 2.
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Sun, 1 Aug 2010 09:28:02 +0000 (09:28 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Sun, 1 Aug 2010 09:28:02 +0000 (09:28 +0000)
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@981187 13f79535-47bb-0310-9956-ffa450edef68

18 files changed:
java/org/apache/tomcat/util/http/fileupload/Closeable.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/DiskFileItem.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/DiskFileItemFactory.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/FileItemHeadersImpl.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
java/org/apache/tomcat/util/http/fileupload/LimitedInputStream.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
java/org/apache/tomcat/util/http/fileupload/ServletFileUpload.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/ServletRequestContext.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/Streams.java [deleted file]
java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/servlet/ServletFileUpload.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/util/Closeable.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java [new file with mode: 0644]
java/org/apache/tomcat/util/http/fileupload/util/Streams.java [new file with mode: 0644]

diff --git a/java/org/apache/tomcat/util/http/fileupload/Closeable.java b/java/org/apache/tomcat/util/http/fileupload/Closeable.java
deleted file mode 100644 (file)
index 04d877b..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.IOException;
-
-
-/**
- * Interface of an object, which may be closed.
- */
-public interface Closeable {
-    /**
-     * Closes the object.
-     * @throws IOException An I/O error occurred.
-     */
-    void close() throws IOException;
-
-    /**
-     * Returns, whether the object is already closed.
-     * @return True, if the object is closed, otherwise false.
-     * @throws IOException An I/O error occurred.
-     */
-    boolean isClosed() throws IOException;
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/DiskFileItem.java b/java/org/apache/tomcat/util/http/fileupload/DiskFileItem.java
deleted file mode 100644 (file)
index 3d48a0d..0000000
+++ /dev/null
@@ -1,728 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-
-
-/**
- * <p> The default implementation of the
- * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
- *
- * <p> After retrieving an instance of this class from a {@link
- * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
- * {@link org.apache.commons.fileupload.DiskFileUpload
- * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may
- * either request all contents of file at once using {@link #get()} or
- * request an {@link java.io.InputStream InputStream} with
- * {@link #getInputStream()} and process the file without attempting to load
- * it into memory, which may come handy with large files.
- *
- * <p>When using the <code>DiskFileItemFactory</code>, then you should
- * consider the following: Temporary files are automatically deleted as
- * soon as they are no longer needed. (More precisely, when the
- * corresponding instance of {@link java.io.File} is garbage collected.)
- * This is done by the so-called reaper thread, which is started
- * automatically when the class {@link org.apache.commons.io.FileCleaner}
- * is loaded.
- * It might make sense to terminate that thread, for example, if
- * your web application ends. See the section on "Resource cleanup"
- * in the users guide of commons-fileupload.</p>
- *
- * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
- * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
- * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
- * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- * @author Sean C. Sullivan
- *
- * @since FileUpload 1.1
- *
- * @version $Id$
- */
-public class DiskFileItem
-    implements FileItem, FileItemHeadersSupport {
-
-    // ----------------------------------------------------- Manifest constants
-
-    /**
-     * The UID to use when serializing this instance.
-     */
-    private static final long serialVersionUID = 2237570099615271025L;
-
-
-    /**
-     * Default content charset to be used when no explicit charset
-     * parameter is provided by the sender. Media subtypes of the
-     * "text" type are defined to have a default charset value of
-     * "ISO-8859-1" when received via HTTP.
-     */
-    public static final String DEFAULT_CHARSET = "ISO-8859-1";
-
-
-    // ----------------------------------------------------------- Data members
-
-
-    /**
-     * UID used in unique file name generation.
-     */
-    private static final String UID =
-            new java.rmi.server.UID().toString()
-                .replace(':', '_').replace('-', '_');
-
-    /**
-     * Counter used in unique identifier generation.
-     */
-    private static int counter = 0;
-
-
-    /**
-     * The name of the form field as provided by the browser.
-     */
-    private String fieldName;
-
-
-    /**
-     * The content type passed by the browser, or <code>null</code> if
-     * not defined.
-     */
-    private String contentType;
-
-
-    /**
-     * Whether or not this item is a simple form field.
-     */
-    private boolean isFormField;
-
-
-    /**
-     * The original filename in the user's filesystem.
-     */
-    private String fileName;
-
-
-    /**
-     * The size of the item, in bytes. This is used to cache the size when a
-     * file item is moved from its original location.
-     */
-    private long size = -1;
-
-
-    /**
-     * The threshold above which uploads will be stored on disk.
-     */
-    private int sizeThreshold;
-
-
-    /**
-     * The directory in which uploaded files will be stored, if stored on disk.
-     */
-    private File repository;
-
-
-    /**
-     * Cached contents of the file.
-     */
-    private byte[] cachedContent;
-
-
-    /**
-     * Output stream for this item.
-     */
-    private transient DeferredFileOutputStream dfos;
-
-    /**
-     * The temporary file to use.
-     */
-    private transient File tempFile;
-
-    /**
-     * File to allow for serialization of the content of this item.
-     */
-    private File dfosFile;
-
-    /**
-     * The file items headers.
-     */
-    private FileItemHeaders headers;
-
-    // ----------------------------------------------------------- Constructors
-
-
-    /**
-     * Constructs a new <code>DiskFileItem</code> instance.
-     *
-     * @param fieldName     The name of the form field.
-     * @param contentType   The content type passed by the browser or
-     *                      <code>null</code> if not specified.
-     * @param isFormField   Whether or not this item is a plain form field, as
-     *                      opposed to a file upload.
-     * @param fileName      The original filename in the user's filesystem, or
-     *                      <code>null</code> if not specified.
-     * @param sizeThreshold The threshold, in bytes, below which items will be
-     *                      retained in memory and above which they will be
-     *                      stored as a file.
-     * @param repository    The data repository, which is the directory in
-     *                      which files will be created, should the item size
-     *                      exceed the threshold.
-     */
-    public DiskFileItem(String fieldName,
-            String contentType, boolean isFormField, String fileName,
-            int sizeThreshold, File repository) {
-        this.fieldName = fieldName;
-        this.contentType = contentType;
-        this.isFormField = isFormField;
-        this.fileName = fileName;
-        this.sizeThreshold = sizeThreshold;
-        this.repository = repository;
-    }
-
-
-    // ------------------------------- Methods from javax.activation.DataSource
-
-
-    /**
-     * Returns an {@link java.io.InputStream InputStream} that can be
-     * used to retrieve the contents of the file.
-     *
-     * @return An {@link java.io.InputStream InputStream} that can be
-     *         used to retrieve the contents of the file.
-     *
-     * @throws IOException if an error occurs.
-     */
-    public InputStream getInputStream()
-        throws IOException {
-        if (!isInMemory()) {
-            return new FileInputStream(dfos.getFile());
-        }
-
-        if (cachedContent == null) {
-            cachedContent = dfos.getData();
-        }
-        return new ByteArrayInputStream(cachedContent);
-    }
-
-
-    /**
-     * Returns the content type passed by the agent or <code>null</code> if
-     * not defined.
-     *
-     * @return The content type passed by the agent or <code>null</code> if
-     *         not defined.
-     */
-    public String getContentType() {
-        return contentType;
-    }
-
-
-    /**
-     * Returns the content charset passed by the agent or <code>null</code> if
-     * not defined.
-     *
-     * @return The content charset passed by the agent or <code>null</code> if
-     *         not defined.
-     */
-    public String getCharSet() {
-        ParameterParser parser = new ParameterParser();
-        parser.setLowerCaseNames(true);
-        // Parameter parser can handle null input
-        Map<String,String> params = parser.parse(getContentType(), ';');
-        return params.get("charset");
-    }
-
-
-    /**
-     * Returns the original filename in the client's filesystem.
-     *
-     * @return The original filename in the client's filesystem.
-     */
-    public String getName() {
-        return fileName;
-    }
-
-
-    // ------------------------------------------------------- FileItem methods
-
-
-    /**
-     * Provides a hint as to whether or not the file contents will be read
-     * from memory.
-     *
-     * @return <code>true</code> if the file contents will be read
-     *         from memory; <code>false</code> otherwise.
-     */
-    public boolean isInMemory() {
-        if (cachedContent != null) {
-            return true;
-        }
-        return dfos.isInMemory();
-    }
-
-
-    /**
-     * Returns the size of the file.
-     *
-     * @return The size of the file, in bytes.
-     */
-    public long getSize() {
-        if (size >= 0) {
-            return size;
-        } else if (cachedContent != null) {
-            return cachedContent.length;
-        } else if (dfos.isInMemory()) {
-            return dfos.getData().length;
-        } else {
-            return dfos.getFile().length();
-        }
-    }
-
-
-    /**
-     * Returns the contents of the file as an array of bytes.  If the
-     * contents of the file were not yet cached in memory, they will be
-     * loaded from the disk storage and cached.
-     *
-     * @return The contents of the file as an array of bytes.
-     */
-    public byte[] get() {
-        if (isInMemory()) {
-            if (cachedContent == null) {
-                cachedContent = dfos.getData();
-            }
-            return cachedContent;
-        }
-
-        byte[] fileData = new byte[(int) getSize()];
-        FileInputStream fis = null;
-
-        try {
-            fis = new FileInputStream(dfos.getFile());
-            fis.read(fileData);
-        } catch (IOException e) {
-            fileData = null;
-        } finally {
-            if (fis != null) {
-                try {
-                    fis.close();
-                } catch (IOException e) {
-                    // ignore
-                }
-            }
-        }
-
-        return fileData;
-    }
-
-
-    /**
-     * Returns the contents of the file as a String, using the specified
-     * encoding.  This method uses {@link #get()} to retrieve the
-     * contents of the file.
-     *
-     * @param charset The charset to use.
-     *
-     * @return The contents of the file, as a string.
-     *
-     * @throws UnsupportedEncodingException if the requested character
-     *                                      encoding is not available.
-     */
-    public String getString(final String charset)
-        throws UnsupportedEncodingException {
-        return new String(get(), charset);
-    }
-
-
-    /**
-     * Returns the contents of the file as a String, using the default
-     * character encoding.  This method uses {@link #get()} to retrieve the
-     * contents of the file.
-     *
-     * @return The contents of the file, as a string.
-     *
-     * @todo Consider making this method throw UnsupportedEncodingException.
-     */
-    public String getString() {
-        byte[] rawdata = get();
-        String charset = getCharSet();
-        if (charset == null) {
-            charset = DEFAULT_CHARSET;
-        }
-        try {
-            return new String(rawdata, charset);
-        } catch (UnsupportedEncodingException e) {
-            return new String(rawdata);
-        }
-    }
-
-
-    /**
-     * A convenience method to write an uploaded item to disk. The client code
-     * is not concerned with whether or not the item is stored in memory, or on
-     * disk in a temporary location. They just want to write the uploaded item
-     * to a file.
-     * <p>
-     * This implementation first attempts to rename the uploaded item to the
-     * specified destination file, if the item was originally written to disk.
-     * Otherwise, the data will be copied to the specified file.
-     * <p>
-     * This method is only guaranteed to work <em>once</em>, the first time it
-     * is invoked for a particular item. This is because, in the event that the
-     * method renames a temporary file, that file will no longer be available
-     * to copy or rename again at a later time.
-     *
-     * @param file The <code>File</code> into which the uploaded item should
-     *             be stored.
-     *
-     * @throws Exception if an error occurs.
-     */
-    public void write(File file) throws Exception {
-        if (isInMemory()) {
-            FileOutputStream fout = null;
-            try {
-                fout = new FileOutputStream(file);
-                fout.write(get());
-            } finally {
-                if (fout != null) {
-                    fout.close();
-                }
-            }
-        } else {
-            File outputFile = getStoreLocation();
-            if (outputFile != null) {
-                // Save the length of the file
-                size = outputFile.length();
-                /*
-                 * The uploaded file is being stored on disk
-                 * in a temporary location so move it to the
-                 * desired file.
-                 */
-                if (!outputFile.renameTo(file)) {
-                    BufferedInputStream in = null;
-                    BufferedOutputStream out = null;
-                    try {
-                        in = new BufferedInputStream(
-                            new FileInputStream(outputFile));
-                        out = new BufferedOutputStream(
-                                new FileOutputStream(file));
-                        IOUtils.copy(in, out);
-                    } finally {
-                        if (in != null) {
-                            try {
-                                in.close();
-                            } catch (IOException e) {
-                                // ignore
-                            }
-                        }
-                        if (out != null) {
-                            try {
-                                out.close();
-                            } catch (IOException e) {
-                                // ignore
-                            }
-                        }
-                    }
-                }
-            } else {
-                /*
-                 * For whatever reason we cannot write the
-                 * file to disk.
-                 */
-                throw new FileUploadException(
-                    "Cannot write uploaded file to disk!");
-            }
-        }
-    }
-
-
-    /**
-     * Deletes the underlying storage for a file item, including deleting any
-     * associated temporary disk file. Although this storage will be deleted
-     * automatically when the <code>FileItem</code> instance is garbage
-     * collected, this method can be used to ensure that this is done at an
-     * earlier time, thus preserving system resources.
-     */
-    public void delete() {
-        cachedContent = null;
-        File outputFile = getStoreLocation();
-        if (outputFile != null && outputFile.exists()) {
-            outputFile.delete();
-        }
-    }
-
-
-    /**
-     * Returns the name of the field in the multipart form corresponding to
-     * this file item.
-     *
-     * @return The name of the form field.
-     *
-     * @see #setFieldName(java.lang.String)
-     *
-     */
-    public String getFieldName() {
-        return fieldName;
-    }
-
-
-    /**
-     * Sets the field name used to reference this file item.
-     *
-     * @param fieldName The name of the form field.
-     *
-     * @see #getFieldName()
-     *
-     */
-    public void setFieldName(String fieldName) {
-        this.fieldName = fieldName;
-    }
-
-
-    /**
-     * Determines whether or not a <code>FileItem</code> instance represents
-     * a simple form field.
-     *
-     * @return <code>true</code> if the instance represents a simple form
-     *         field; <code>false</code> if it represents an uploaded file.
-     *
-     * @see #setFormField(boolean)
-     *
-     */
-    public boolean isFormField() {
-        return isFormField;
-    }
-
-
-    /**
-     * Specifies whether or not a <code>FileItem</code> instance represents
-     * a simple form field.
-     *
-     * @param state <code>true</code> if the instance represents a simple form
-     *              field; <code>false</code> if it represents an uploaded file.
-     *
-     * @see #isFormField()
-     *
-     */
-    public void setFormField(boolean state) {
-        isFormField = state;
-    }
-
-
-    /**
-     * Returns an {@link java.io.OutputStream OutputStream} that can
-     * be used for storing the contents of the file.
-     *
-     * @return An {@link java.io.OutputStream OutputStream} that can be used
-     *         for storing the contensts of the file.
-     *
-     * @throws IOException if an error occurs.
-     */
-    public OutputStream getOutputStream()
-        throws IOException {
-        if (dfos == null) {
-            File outputFile = getTempFile();
-            dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
-        }
-        return dfos;
-    }
-
-
-    // --------------------------------------------------------- Public methods
-
-
-    /**
-     * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
-     * data's temporary location on the disk. Note that for
-     * <code>FileItem</code>s that have their data stored in memory,
-     * this method will return <code>null</code>. When handling large
-     * files, you can use {@link java.io.File#renameTo(java.io.File)} to
-     * move the file to new location without copying the data, if the
-     * source and destination locations reside within the same logical
-     * volume.
-     *
-     * @return The data file, or <code>null</code> if the data is stored in
-     *         memory.
-     */
-    public File getStoreLocation() {
-        return dfos == null ? null : dfos.getFile();
-    }
-
-
-    // ------------------------------------------------------ Protected methods
-
-
-    /**
-     * Removes the file contents from the temporary storage.
-     */
-    @Override
-    protected void finalize() {
-        File outputFile = dfos.getFile();
-
-        if (outputFile != null && outputFile.exists()) {
-            outputFile.delete();
-        }
-    }
-
-
-    /**
-     * Creates and returns a {@link java.io.File File} representing a uniquely
-     * named temporary file in the configured repository path. The lifetime of
-     * the file is tied to the lifetime of the <code>FileItem</code> instance;
-     * the file will be deleted when the instance is garbage collected.
-     *
-     * @return The {@link java.io.File File} to be used for temporary storage.
-     */
-    protected File getTempFile() {
-        if (tempFile == null) {
-            File tempDir = repository;
-            if (tempDir == null) {
-                tempDir = new File(System.getProperty("java.io.tmpdir"));
-            }
-
-            String tempFileName =
-                "upload_" + UID + "_" + getUniqueId() + ".tmp";
-
-            tempFile = new File(tempDir, tempFileName);
-        }
-        return tempFile;
-    }
-
-
-    // -------------------------------------------------------- Private methods
-
-
-    /**
-     * Returns an identifier that is unique within the class loader used to
-     * load this class, but does not have random-like apearance.
-     *
-     * @return A String with the non-random looking instance identifier.
-     */
-    private static String getUniqueId() {
-        final int limit = 100000000;
-        int current;
-        synchronized (DiskFileItem.class) {
-            current = counter++;
-        }
-        String id = Integer.toString(current);
-
-        // If you manage to get more than 100 million of ids, you'll
-        // start getting ids longer than 8 characters.
-        if (current < limit) {
-            id = ("00000000" + id).substring(id.length());
-        }
-        return id;
-    }
-
-
-
-
-    /**
-     * Returns a string representation of this object.
-     *
-     * @return a string representation of this object.
-     */
-    @Override
-    public String toString() {
-        return "name=" + this.getName()
-            + ", StoreLocation="
-            + String.valueOf(this.getStoreLocation())
-            + ", size="
-            + this.getSize()
-            + "bytes, "
-            + "isFormField=" + isFormField()
-            + ", FieldName="
-            + this.getFieldName();
-    }
-
-
-    // -------------------------------------------------- Serialization methods
-
-
-    /**
-     * Writes the state of this object during serialization.
-     *
-     * @param out The stream to which the state should be written.
-     *
-     * @throws IOException if an error occurs.
-     */
-    private void writeObject(ObjectOutputStream out) throws IOException {
-        // Read the data
-        if (dfos.isInMemory()) {
-            cachedContent = get();
-        } else {
-            cachedContent = null;
-            dfosFile = dfos.getFile();
-        }
-
-        // write out values
-        out.defaultWriteObject();
-    }
-
-    /**
-     * Reads the state of this object during deserialization.
-     *
-     * @param in The stream from which the state should be read.
-     *
-     * @throws IOException if an error occurs.
-     * @throws ClassNotFoundException if class cannot be found.
-     */
-    private void readObject(ObjectInputStream in)
-            throws IOException, ClassNotFoundException {
-        // read values
-        in.defaultReadObject();
-
-        OutputStream output = getOutputStream();
-        if (cachedContent != null) {
-            output.write(cachedContent);
-        } else {
-            FileInputStream input = new FileInputStream(dfosFile);
-            IOUtils.copy(input, output);
-            dfosFile.delete();
-            dfosFile = null;
-        }
-        output.close();
-
-        cachedContent = null;
-    }
-
-    /**
-     * Returns the file item headers.
-     * @return The file items headers.
-     */
-    public FileItemHeaders getHeaders() {
-        return headers;
-    }
-
-    /**
-     * Sets the file item headers.
-     * @param pHeaders The file items headers.
-     */
-    public void setHeaders(FileItemHeaders pHeaders) {
-        headers = pHeaders;
-    }
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/DiskFileItemFactory.java b/java/org/apache/tomcat/util/http/fileupload/DiskFileItemFactory.java
deleted file mode 100644 (file)
index 1dc8f2b..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.File;
-
-
-/**
- * <p>The default {@link org.apache.commons.fileupload.FileItemFactory}
- * implementation. This implementation creates
- * {@link org.apache.commons.fileupload.FileItem} instances which keep their
- * content either in memory, for smaller items, or in a temporary file on disk,
- * for larger items. The size threshold, above which content will be stored on
- * disk, is configurable, as is the directory in which temporary files will be
- * created.</p>
- *
- * <p>If not otherwise configured, the default configuration values are as
- * follows:
- * <ul>
- *   <li>Size threshold is 10KB.</li>
- *   <li>Repository is the system default temp directory, as returned by
- *       <code>System.getProperty("java.io.tmpdir")</code>.</li>
- * </ul>
- * </p>
- *
- * <p>When using the <code>DiskFileItemFactory</code>, then you should
- * consider the following: Temporary files are automatically deleted as
- * soon as they are no longer needed. (More precisely, when the
- * corresponding instance of {@link java.io.File} is garbage collected.)
- * Cleaning up those files is done by an instance of
- * {@link FileCleaningTracker}, and an associated thread. In a complex
- * environment, for example in a web application, you should consider
- * terminating this thread, for example, when your web application
- * ends. See the section on "Resource cleanup"
- * in the users guide of commons-fileupload.</p>
- *
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- *
- * @since FileUpload 1.1
- *
- * @version $Id$
- */
-public class DiskFileItemFactory implements FileItemFactory {
-
-    // ----------------------------------------------------- Manifest constants
-
-
-    /**
-     * The default threshold above which uploads will be stored on disk.
-     */
-    public static final int DEFAULT_SIZE_THRESHOLD = 10240;
-
-
-    // ----------------------------------------------------- Instance Variables
-
-
-    /**
-     * The directory in which uploaded files will be stored, if stored on disk.
-     */
-    private File repository;
-
-
-    /**
-     * The threshold above which uploads will be stored on disk.
-     */
-    private int sizeThreshold = DEFAULT_SIZE_THRESHOLD;
-
-
-    /**
-     * <p>The instance of {@link FileCleaningTracker}, which is responsible
-     * for deleting temporary files.</p>
-     * <p>May be null, if tracking files is not required.</p>
-     */
-    private FileCleaningTracker fileCleaningTracker;
-
-    // ----------------------------------------------------------- Constructors
-
-
-    /**
-     * Constructs an unconfigured instance of this class. The resulting factory
-     * may be configured by calling the appropriate setter methods.
-     */
-    public DiskFileItemFactory() {
-        this(DEFAULT_SIZE_THRESHOLD, null);
-    }
-
-
-    /**
-     * Constructs a preconfigured instance of this class.
-     *
-     * @param sizeThreshold The threshold, in bytes, below which items will be
-     *                      retained in memory and above which they will be
-     *                      stored as a file.
-     * @param repository    The data repository, which is the directory in
-     *                      which files will be created, should the item size
-     *                      exceed the threshold.
-     */
-    public DiskFileItemFactory(int sizeThreshold, File repository) {
-        this.sizeThreshold = sizeThreshold;
-        this.repository = repository;
-    }
-
-    // ------------------------------------------------------------- Properties
-
-
-    /**
-     * Returns the directory used to temporarily store files that are larger
-     * than the configured size threshold.
-     *
-     * @return The directory in which temporary files will be located.
-     *
-     * @see #setRepository(java.io.File)
-     *
-     */
-    public File getRepository() {
-        return repository;
-    }
-
-
-    /**
-     * Sets the directory used to temporarily store files that are larger
-     * than the configured size threshold.
-     *
-     * @param repository The directory in which temporary files will be located.
-     *
-     * @see #getRepository()
-     *
-     */
-    public void setRepository(File repository) {
-        this.repository = repository;
-    }
-
-
-    /**
-     * Returns the size threshold beyond which files are written directly to
-     * disk. The default value is 10240 bytes.
-     *
-     * @return The size threshold, in bytes.
-     *
-     * @see #setSizeThreshold(int)
-     */
-    public int getSizeThreshold() {
-        return sizeThreshold;
-    }
-
-
-    /**
-     * Sets the size threshold beyond which files are written directly to disk.
-     *
-     * @param sizeThreshold The size threshold, in bytes.
-     *
-     * @see #getSizeThreshold()
-     *
-     */
-    public void setSizeThreshold(int sizeThreshold) {
-        this.sizeThreshold = sizeThreshold;
-    }
-
-
-    // --------------------------------------------------------- Public Methods
-
-    /**
-     * Create a new {@link org.apache.commons.fileupload.disk.DiskFileItem}
-     * instance from the supplied parameters and the local factory
-     * configuration.
-     *
-     * @param fieldName   The name of the form field.
-     * @param contentType The content type of the form field.
-     * @param isFormField <code>true</code> if this is a plain form field;
-     *                    <code>false</code> otherwise.
-     * @param fileName    The name of the uploaded file, if any, as supplied
-     *                    by the browser or other client.
-     *
-     * @return The newly created file item.
-     */
-    public FileItem createItem(String fieldName, String contentType,
-            boolean isFormField, String fileName) {
-        DiskFileItem result = new DiskFileItem(fieldName, contentType,
-                isFormField, fileName, sizeThreshold, repository);
-        FileCleaningTracker tracker = getFileCleaningTracker();
-        if (tracker != null) {
-            tracker.track(result.getTempFile(), this);
-        }
-        return result;
-    }
-
-
-    /**
-     * Returns the tracker, which is responsible for deleting temporary
-     * files.
-     * @return An instance of {@link FileCleaningTracker}, defaults to
-     *   {@link org.apache.commons.io.FileCleaner#getInstance()}. Null,
-     *   if temporary files aren't tracked.
-     */
-    public FileCleaningTracker getFileCleaningTracker() {
-        return fileCleaningTracker;
-    }
-
-    /**
-     * Returns the tracker, which is responsible for deleting temporary
-     * files.
-     * @param pTracker An instance of {@link FileCleaningTracker},
-     *   which will from now on track the created files. May be null
-     *   to disable tracking.
-     */
-    public void setFileCleaningTracker(FileCleaningTracker pTracker) {
-        fileCleaningTracker = pTracker;
-    }
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileItemHeadersImpl.java b/java/org/apache/tomcat/util/http/fileupload/FileItemHeadersImpl.java
deleted file mode 100644 (file)
index efbdf8e..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-
-/**
- * Default implementation of the {@link FileItemHeaders} interface.
- *
- * @author Michael C. Macaluso
- * @since 1.3
- */
-public class FileItemHeadersImpl implements FileItemHeaders, Serializable {
-    private static final long serialVersionUID = -4455695752627032559L;
-
-    /**
-     * Map of <code>String</code> keys to a <code>List</code> of
-     * <code>String</code> instances.
-     */
-    private final Map<String,List<String>> headerNameToValueListMap =
-        new HashMap<String,List<String>>();
-
-    /**
-     * List to preserve order of headers as added.  This would not be
-     * needed if a <code>LinkedHashMap</code> could be used, but don't
-     * want to depend on 1.4.
-     */
-    private final List<String> headerNameList = new ArrayList<String>();
-
-    public String getHeader(String name) {
-        String nameLower = name.toLowerCase(Locale.ENGLISH);
-        List<String> headerValueList = headerNameToValueListMap.get(nameLower);
-        if (null == headerValueList) {
-            return null;
-        }
-        return headerValueList.get(0);
-    }
-
-    public Iterator<String> getHeaderNames() {
-        return headerNameList.iterator();
-    }
-
-    public Iterator<String> getHeaders(String name) {
-        String nameLower = name.toLowerCase(Locale.ENGLISH);
-        List<String> headerValueList = headerNameToValueListMap.get(nameLower);
-        if (null == headerValueList) {
-            return Collections.<String>emptyList().iterator();
-        }
-        return headerValueList.iterator();
-    }
-
-    /**
-     * Method to add header values to this instance.
-     *
-     * @param name name of this header
-     * @param value value of this header
-     */
-    public synchronized void addHeader(String name, String value) {
-        String nameLower = name.toLowerCase();
-        List<String> headerValueList = headerNameToValueListMap.get(nameLower);
-        if (null == headerValueList) {
-            headerValueList = new ArrayList<String>();
-            headerNameToValueListMap.put(nameLower, headerValueList);
-            headerNameList.add(nameLower);
-        }
-        headerValueList.add(value);
-    }
-}
index 49985c6..f985257 100644 (file)
@@ -28,6 +28,10 @@ import java.util.NoSuchElementException;
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.util.FileItemHeadersImpl;
+import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Streams;
 
 
 /**
diff --git a/java/org/apache/tomcat/util/http/fileupload/LimitedInputStream.java b/java/org/apache/tomcat/util/http/fileupload/LimitedInputStream.java
deleted file mode 100644 (file)
index bbef674..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-
-/**
- * An input stream, which limits its data size. This stream is
- * used, if the content length is unknown.
- */
-public abstract class LimitedInputStream
-        extends FilterInputStream implements Closeable {
-    /**
-     * The maximum size of an item, in bytes.
-     */
-    private long sizeMax;
-    /**
-     * The current number of bytes.
-     */
-    private long count;
-    /**
-     * Whether this stream is already closed.
-     */
-    private boolean closed;
-
-    /**
-     * Creates a new instance.
-     * @param pIn The input stream, which shall be limited.
-     * @param pSizeMax The limit; no more than this number of bytes
-     *   shall be returned by the source stream.
-     */
-    public LimitedInputStream(InputStream pIn, long pSizeMax) {
-        super(pIn);
-        sizeMax = pSizeMax;
-    }
-
-    /**
-     * Called to indicate, that the input streams limit has
-     * been exceeded.
-     * @param pSizeMax The input streams limit, in bytes.
-     * @param pCount The actual number of bytes.
-     * @throws IOException The called method is expected
-     *   to raise an IOException.
-     */
-    protected abstract void raiseError(long pSizeMax, long pCount)
-            throws IOException;
-
-    /** Called to check, whether the input streams
-     * limit is reached.
-     * @throws IOException The given limit is exceeded.
-     */
-    private void checkLimit() throws IOException {
-        if (count > sizeMax) {
-            raiseError(sizeMax, count);
-        }
-    }
-
-    /**
-     * Reads the next byte of data from this input stream. The value
-     * byte is returned as an <code>int</code> in the range
-     * <code>0</code> to <code>255</code>. If no byte is available
-     * because the end of the stream has been reached, the value
-     * <code>-1</code> is returned. This method blocks until input data
-     * is available, the end of the stream is detected, or an exception
-     * is thrown.
-     * <p>
-     * This method
-     * simply performs <code>in.read()</code> and returns the result.
-     *
-     * @return     the next byte of data, or <code>-1</code> if the end of the
-     *             stream is reached.
-     * @exception  IOException  if an I/O error occurs.
-     * @see        java.io.FilterInputStream#in
-     */
-    @Override
-    public int read() throws IOException {
-        int res = super.read();
-        if (res != -1) {
-            count++;
-            checkLimit();
-        }
-        return res;
-    }
-
-    /**
-     * Reads up to <code>len</code> bytes of data from this input stream
-     * into an array of bytes. If <code>len</code> is not zero, the method
-     * blocks until some input is available; otherwise, no
-     * bytes are read and <code>0</code> is returned.
-     * <p>
-     * This method simply performs <code>in.read(b, off, len)</code>
-     * and returns the result.
-     *
-     * @param      b     the buffer into which the data is read.
-     * @param      off   The start offset in the destination array
-     *                   <code>b</code>.
-     * @param      len   the maximum number of bytes read.
-     * @return     the total number of bytes read into the buffer, or
-     *             <code>-1</code> if there is no more data because the end of
-     *             the stream has been reached.
-     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
-     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
-     * <code>len</code> is negative, or <code>len</code> is greater than
-     * <code>b.length - off</code>
-     * @exception  IOException  if an I/O error occurs.
-     * @see        java.io.FilterInputStream#in
-     */
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        int res = super.read(b, off, len);
-        if (res > 0) {
-            count += res;
-            checkLimit();
-        }
-        return res;
-    }
-
-    /**
-     * Returns, whether this stream is already closed.
-     * @return True, if the stream is closed, otherwise false.
-     * @throws IOException An I/O error occurred.
-     */
-    public boolean isClosed() throws IOException {
-        return closed;
-    }
-
-    /**
-     * Closes this input stream and releases any system resources
-     * associated with the stream.
-     * This
-     * method simply performs <code>in.close()</code>.
-     *
-     * @exception  IOException  if an I/O error occurs.
-     * @see        java.io.FilterInputStream#in
-     */
-    @Override
-    public void close() throws IOException {
-        closed = true;
-        super.close();
-    }
-}
index 9c64773..4a8873a 100644 (file)
@@ -22,6 +22,9 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 
+import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.util.Streams;
+
 
 /**
  * <p> Low level API for processing file uploads.
diff --git a/java/org/apache/tomcat/util/http/fileupload/ServletFileUpload.java b/java/org/apache/tomcat/util/http/fileupload/ServletFileUpload.java
deleted file mode 100644 (file)
index 49a9885..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-
-import javax.servlet.http.HttpServletRequest;
-
-
-/**
- * <p>High level API for processing file uploads.</p>
- *
- * <p>This class handles multiple files per single HTML widget, sent using
- * <code>multipart/mixed</code> encoding type, as specified by
- * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.  Use {@link
- * #parseRequest(HttpServletRequest)} to acquire a list of {@link
- * org.apache.commons.fileupload.FileItem}s associated with a given HTML
- * widget.</p>
- *
- * <p>How the data for individual parts is stored is determined by the factory
- * used to create them; a given part may be in memory, on disk, or somewhere
- * else.</p>
- *
- * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
- * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
- * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
- * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- * @author Sean C. Sullivan
- *
- * @version $Id$
- */
-public class ServletFileUpload extends FileUpload {
-
-    // ---------------------------------------------------------- Class methods
-
-
-    /**
-     * Utility method that determines whether the request contains multipart
-     * content.
-     *
-     * @param request The servlet request to be evaluated. Must be non-null.
-     *
-     * @return <code>true</code> if the request is multipart;
-     *         <code>false</code> otherwise.
-     */
-    public static final boolean isMultipartContent(
-            HttpServletRequest request) {
-        if (!"post".equals(request.getMethod().toLowerCase(Locale.ENGLISH))) {
-            return false;
-        }
-        String contentType = request.getContentType();
-        if (contentType == null) {
-            return false;
-        }
-        if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
-            return true;
-        }
-        return false;
-    }
-
-
-    // ----------------------------------------------------------- Constructors
-
-
-    /**
-     * Constructs an uninitialised instance of this class. A factory must be
-     * configured, using <code>setFileItemFactory()</code>, before attempting
-     * to parse requests.
-     *
-     * @see FileUpload#FileUpload(FileItemFactory)
-     */
-    public ServletFileUpload() {
-        super();
-    }
-
-
-    /**
-     * Constructs an instance of this class which uses the supplied factory to
-     * create <code>FileItem</code> instances.
-     *
-     * @see FileUpload#FileUpload()
-     * @param fileItemFactory The factory to use for creating file items.
-     */
-    public ServletFileUpload(FileItemFactory fileItemFactory) {
-        super(fileItemFactory);
-    }
-
-
-    // --------------------------------------------------------- Public methods
-
-
-    /**
-     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
-     * compliant <code>multipart/form-data</code> stream.
-     *
-     * @param request The servlet request to be parsed.
-     *
-     * @return A list of <code>FileItem</code> instances parsed from the
-     *         request, in the order that they were transmitted.
-     *
-     * @throws FileUploadException if there are problems reading/parsing
-     *                             the request or storing files.
-     */
-    public List<FileItem> parseRequest(HttpServletRequest request)
-    throws FileUploadException {
-        return parseRequest(new ServletRequestContext(request));
-    }
-
-
-    /**
-     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
-     * compliant <code>multipart/form-data</code> stream.
-     *
-     * @param request The servlet request to be parsed.
-     *
-     * @return An iterator to instances of <code>FileItemStream</code>
-     *         parsed from the request, in the order that they were
-     *         transmitted.
-     *
-     * @throws FileUploadException if there are problems reading/parsing
-     *                             the request or storing files.
-     * @throws IOException An I/O error occurred. This may be a network
-     *   error while communicating with the client or a problem while
-     *   storing the uploaded content.
-     */
-    public FileItemIterator getItemIterator(HttpServletRequest request)
-    throws FileUploadException, IOException {
-        return super.getItemIterator(new ServletRequestContext(request));
-    }
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/ServletRequestContext.java b/java/org/apache/tomcat/util/http/fileupload/ServletRequestContext.java
deleted file mode 100644 (file)
index bb5460a..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.InputStream;
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-
-
-/**
- * <p>Provides access to the request information needed for a request made to
- * an HTTP servlet.</p>
- *
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- *
- * @since FileUpload 1.1
- *
- * @version $Id$
- */
-public class ServletRequestContext implements RequestContext {
-
-    // ----------------------------------------------------- Instance Variables
-
-    /**
-     * The request for which the context is being provided.
-     */
-    private HttpServletRequest request;
-
-
-    // ----------------------------------------------------------- Constructors
-
-    /**
-     * Construct a context for this request.
-     *
-     * @param request The request to which this context applies.
-     */
-    public ServletRequestContext(HttpServletRequest request) {
-        this.request = request;
-    }
-
-
-    // --------------------------------------------------------- Public Methods
-
-    /**
-     * Retrieve the character encoding for the request.
-     *
-     * @return The character encoding for the request.
-     */
-    public String getCharacterEncoding() {
-        return request.getCharacterEncoding();
-    }
-
-    /**
-     * Retrieve the content type of the request.
-     *
-     * @return The content type of the request.
-     */
-    public String getContentType() {
-        return request.getContentType();
-    }
-
-    /**
-     * Retrieve the content length of the request.
-     *
-     * @return The content length of the request.
-     */
-    public int getContentLength() {
-        return request.getContentLength();
-    }
-
-    /**
-     * Retrieve the input stream for the request.
-     *
-     * @return The input stream for the request.
-     *
-     * @throws IOException if a problem occurs.
-     */
-    public InputStream getInputStream() throws IOException {
-        return request.getInputStream();
-    }
-
-    /**
-     * Returns a string representation of this object.
-     *
-     * @return a string representation of this object.
-     */
-    @Override
-    public String toString() {
-        return "ContentLength="
-            + this.getContentLength()
-            + ", ContentType="
-            + this.getContentType();
-    }
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/Streams.java b/java/org/apache/tomcat/util/http/fileupload/Streams.java
deleted file mode 100644 (file)
index d89f937..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.tomcat.util.http.fileupload;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-
-/** Utility class for working with streams.
- */
-public final class Streams {
-    /**
-     * Private constructor, to prevent instantiation.
-     * This class has only static methods.
-     */
-    private Streams() {
-        // Does nothing
-    }
-
-    /**
-     * Default buffer size for use in
-     * {@link #copy(InputStream, OutputStream, boolean)}.
-     */
-    private static final int DEFAULT_BUFFER_SIZE = 8192;
-
-    /**
-     * Copies the contents of the given {@link InputStream}
-     * to the given {@link OutputStream}. Shortcut for
-     * <pre>
-     *   copy(pInputStream, pOutputStream, new byte[8192]);
-     * </pre>
-     * @param pInputStream The input stream, which is being read.
-     * It is guaranteed, that {@link InputStream#close()} is called
-     * on the stream.
-     * @param pOutputStream The output stream, to which data should
-     * be written. May be null, in which case the input streams
-     * contents are simply discarded.
-     * @param pClose True guarantees, that {@link OutputStream#close()}
-     * is called on the stream. False indicates, that only
-     * {@link OutputStream#flush()} should be called finally.
-     *
-     * @return Number of bytes, which have been copied.
-     * @throws IOException An I/O error occurred.
-     */
-    public static long copy(InputStream pInputStream,
-            OutputStream pOutputStream, boolean pClose)
-            throws IOException {
-        return copy(pInputStream, pOutputStream, pClose,
-                new byte[DEFAULT_BUFFER_SIZE]);
-    }
-
-    /**
-     * Copies the contents of the given {@link InputStream}
-     * to the given {@link OutputStream}.
-     * @param pIn The input stream, which is being read.
-     *   It is guaranteed, that {@link InputStream#close()} is called
-     *   on the stream.
-     * @param pOut The output stream, to which data should
-     *   be written. May be null, in which case the input streams
-     *   contents are simply discarded.
-     * @param pClose True guarantees, that {@link OutputStream#close()}
-     *   is called on the stream. False indicates, that only
-     *   {@link OutputStream#flush()} should be called finally.
-     * @param pBuffer Temporary buffer, which is to be used for
-     *   copying data.
-     * @return Number of bytes, which have been copied.
-     * @throws IOException An I/O error occurred.
-     */
-    public static long copy(InputStream pIn,
-            OutputStream pOut, boolean pClose,
-            byte[] pBuffer)
-    throws IOException {
-        OutputStream out = pOut;
-        InputStream in = pIn;
-        try {
-            long total = 0;
-            for (;;) {
-                int res = in.read(pBuffer);
-                if (res == -1) {
-                    break;
-                }
-                if (res > 0) {
-                    total += res;
-                    if (out != null) {
-                        out.write(pBuffer, 0, res);
-                    }
-                }
-            }
-            if (out != null) {
-                if (pClose) {
-                    out.close();
-                } else {
-                    out.flush();
-                }
-                out = null;
-            }
-            in.close();
-            in = null;
-            return total;
-        } finally {
-            if (in != null) {
-                try {
-                    in.close();
-                } catch (Throwable t) {
-                    /* Ignore me */
-                }
-            }
-            if (pClose  &&  out != null) {
-                try {
-                    out.close();
-                } catch (Throwable t) {
-                    /* Ignore me */
-                }
-            }
-        }
-    }
-
-    /**
-     * This convenience method allows to read a
-     * {@link org.apache.commons.fileupload.FileItemStream}'s
-     * content into a string. The platform's default character encoding
-     * is used for converting bytes into characters.
-     * @param pStream The input stream to read.
-     * @see #asString(InputStream, String)
-     * @return The streams contents, as a string.
-     * @throws IOException An I/O error occurred.
-     */
-    public static String asString(InputStream pStream) throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        copy(pStream, baos, true);
-        return baos.toString();
-    }
-
-    /**
-     * This convenience method allows to read a
-     * {@link org.apache.commons.fileupload.FileItemStream}'s
-     * content into a string, using the given character encoding.
-     * @param pStream The input stream to read.
-     * @param pEncoding The character encoding, typically "UTF-8".
-     * @see #asString(InputStream)
-     * @return The streams contents, as a string.
-     * @throws IOException An I/O error occurred.
-     */
-    public static String asString(InputStream pStream, String pEncoding)
-            throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        copy(pStream, baos, true);
-        return baos.toString(pEncoding);
-    }
-}
diff --git a/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java b/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
new file mode 100644 (file)
index 0000000..3c5babe
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ * 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.tomcat.util.http.fileupload.disk;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+import org.apache.tomcat.util.http.fileupload.DeferredFileOutputStream;
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+import org.apache.tomcat.util.http.fileupload.FileItemHeadersSupport;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
+import org.apache.tomcat.util.http.fileupload.ParameterParser;
+
+
+/**
+ * <p> The default implementation of the
+ * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
+ *
+ * <p> After retrieving an instance of this class from a {@link
+ * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
+ * {@link org.apache.commons.fileupload.DiskFileUpload
+ * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may
+ * either request all contents of file at once using {@link #get()} or
+ * request an {@link java.io.InputStream InputStream} with
+ * {@link #getInputStream()} and process the file without attempting to load
+ * it into memory, which may come handy with large files.
+ *
+ * <p>When using the <code>DiskFileItemFactory</code>, then you should
+ * consider the following: Temporary files are automatically deleted as
+ * soon as they are no longer needed. (More precisely, when the
+ * corresponding instance of {@link java.io.File} is garbage collected.)
+ * This is done by the so-called reaper thread, which is started
+ * automatically when the class {@link org.apache.commons.io.FileCleaner}
+ * is loaded.
+ * It might make sense to terminate that thread, for example, if
+ * your web application ends. See the section on "Resource cleanup"
+ * in the users guide of commons-fileupload.</p>
+ *
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * @since FileUpload 1.1
+ *
+ * @version $Id$
+ */
+public class DiskFileItem
+    implements FileItem, FileItemHeadersSupport {
+
+    // ----------------------------------------------------- Manifest constants
+
+    /**
+     * The UID to use when serializing this instance.
+     */
+    private static final long serialVersionUID = 2237570099615271025L;
+
+
+    /**
+     * Default content charset to be used when no explicit charset
+     * parameter is provided by the sender. Media subtypes of the
+     * "text" type are defined to have a default charset value of
+     * "ISO-8859-1" when received via HTTP.
+     */
+    public static final String DEFAULT_CHARSET = "ISO-8859-1";
+
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * UID used in unique file name generation.
+     */
+    private static final String UID =
+            new java.rmi.server.UID().toString()
+                .replace(':', '_').replace('-', '_');
+
+    /**
+     * Counter used in unique identifier generation.
+     */
+    private static int counter = 0;
+
+
+    /**
+     * The name of the form field as provided by the browser.
+     */
+    private String fieldName;
+
+
+    /**
+     * The content type passed by the browser, or <code>null</code> if
+     * not defined.
+     */
+    private String contentType;
+
+
+    /**
+     * Whether or not this item is a simple form field.
+     */
+    private boolean isFormField;
+
+
+    /**
+     * The original filename in the user's filesystem.
+     */
+    private String fileName;
+
+
+    /**
+     * The size of the item, in bytes. This is used to cache the size when a
+     * file item is moved from its original location.
+     */
+    private long size = -1;
+
+
+    /**
+     * The threshold above which uploads will be stored on disk.
+     */
+    private int sizeThreshold;
+
+
+    /**
+     * The directory in which uploaded files will be stored, if stored on disk.
+     */
+    private File repository;
+
+
+    /**
+     * Cached contents of the file.
+     */
+    private byte[] cachedContent;
+
+
+    /**
+     * Output stream for this item.
+     */
+    private transient DeferredFileOutputStream dfos;
+
+    /**
+     * The temporary file to use.
+     */
+    private transient File tempFile;
+
+    /**
+     * File to allow for serialization of the content of this item.
+     */
+    private File dfosFile;
+
+    /**
+     * The file items headers.
+     */
+    private FileItemHeaders headers;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs a new <code>DiskFileItem</code> instance.
+     *
+     * @param fieldName     The name of the form field.
+     * @param contentType   The content type passed by the browser or
+     *                      <code>null</code> if not specified.
+     * @param isFormField   Whether or not this item is a plain form field, as
+     *                      opposed to a file upload.
+     * @param fileName      The original filename in the user's filesystem, or
+     *                      <code>null</code> if not specified.
+     * @param sizeThreshold The threshold, in bytes, below which items will be
+     *                      retained in memory and above which they will be
+     *                      stored as a file.
+     * @param repository    The data repository, which is the directory in
+     *                      which files will be created, should the item size
+     *                      exceed the threshold.
+     */
+    public DiskFileItem(String fieldName,
+            String contentType, boolean isFormField, String fileName,
+            int sizeThreshold, File repository) {
+        this.fieldName = fieldName;
+        this.contentType = contentType;
+        this.isFormField = isFormField;
+        this.fileName = fileName;
+        this.sizeThreshold = sizeThreshold;
+        this.repository = repository;
+    }
+
+
+    // ------------------------------- Methods from javax.activation.DataSource
+
+
+    /**
+     * Returns an {@link java.io.InputStream InputStream} that can be
+     * used to retrieve the contents of the file.
+     *
+     * @return An {@link java.io.InputStream InputStream} that can be
+     *         used to retrieve the contents of the file.
+     *
+     * @throws IOException if an error occurs.
+     */
+    public InputStream getInputStream()
+        throws IOException {
+        if (!isInMemory()) {
+            return new FileInputStream(dfos.getFile());
+        }
+
+        if (cachedContent == null) {
+            cachedContent = dfos.getData();
+        }
+        return new ByteArrayInputStream(cachedContent);
+    }
+
+
+    /**
+     * Returns the content type passed by the agent or <code>null</code> if
+     * not defined.
+     *
+     * @return The content type passed by the agent or <code>null</code> if
+     *         not defined.
+     */
+    public String getContentType() {
+        return contentType;
+    }
+
+
+    /**
+     * Returns the content charset passed by the agent or <code>null</code> if
+     * not defined.
+     *
+     * @return The content charset passed by the agent or <code>null</code> if
+     *         not defined.
+     */
+    public String getCharSet() {
+        ParameterParser parser = new ParameterParser();
+        parser.setLowerCaseNames(true);
+        // Parameter parser can handle null input
+        Map<String,String> params = parser.parse(getContentType(), ';');
+        return params.get("charset");
+    }
+
+
+    /**
+     * Returns the original filename in the client's filesystem.
+     *
+     * @return The original filename in the client's filesystem.
+     */
+    public String getName() {
+        return fileName;
+    }
+
+
+    // ------------------------------------------------------- FileItem methods
+
+
+    /**
+     * Provides a hint as to whether or not the file contents will be read
+     * from memory.
+     *
+     * @return <code>true</code> if the file contents will be read
+     *         from memory; <code>false</code> otherwise.
+     */
+    public boolean isInMemory() {
+        if (cachedContent != null) {
+            return true;
+        }
+        return dfos.isInMemory();
+    }
+
+
+    /**
+     * Returns the size of the file.
+     *
+     * @return The size of the file, in bytes.
+     */
+    public long getSize() {
+        if (size >= 0) {
+            return size;
+        } else if (cachedContent != null) {
+            return cachedContent.length;
+        } else if (dfos.isInMemory()) {
+            return dfos.getData().length;
+        } else {
+            return dfos.getFile().length();
+        }
+    }
+
+
+    /**
+     * Returns the contents of the file as an array of bytes.  If the
+     * contents of the file were not yet cached in memory, they will be
+     * loaded from the disk storage and cached.
+     *
+     * @return The contents of the file as an array of bytes.
+     */
+    public byte[] get() {
+        if (isInMemory()) {
+            if (cachedContent == null) {
+                cachedContent = dfos.getData();
+            }
+            return cachedContent;
+        }
+
+        byte[] fileData = new byte[(int) getSize()];
+        FileInputStream fis = null;
+
+        try {
+            fis = new FileInputStream(dfos.getFile());
+            fis.read(fileData);
+        } catch (IOException e) {
+            fileData = null;
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+
+        return fileData;
+    }
+
+
+    /**
+     * Returns the contents of the file as a String, using the specified
+     * encoding.  This method uses {@link #get()} to retrieve the
+     * contents of the file.
+     *
+     * @param charset The charset to use.
+     *
+     * @return The contents of the file, as a string.
+     *
+     * @throws UnsupportedEncodingException if the requested character
+     *                                      encoding is not available.
+     */
+    public String getString(final String charset)
+        throws UnsupportedEncodingException {
+        return new String(get(), charset);
+    }
+
+
+    /**
+     * Returns the contents of the file as a String, using the default
+     * character encoding.  This method uses {@link #get()} to retrieve the
+     * contents of the file.
+     *
+     * @return The contents of the file, as a string.
+     *
+     * @todo Consider making this method throw UnsupportedEncodingException.
+     */
+    public String getString() {
+        byte[] rawdata = get();
+        String charset = getCharSet();
+        if (charset == null) {
+            charset = DEFAULT_CHARSET;
+        }
+        try {
+            return new String(rawdata, charset);
+        } catch (UnsupportedEncodingException e) {
+            return new String(rawdata);
+        }
+    }
+
+
+    /**
+     * A convenience method to write an uploaded item to disk. The client code
+     * is not concerned with whether or not the item is stored in memory, or on
+     * disk in a temporary location. They just want to write the uploaded item
+     * to a file.
+     * <p>
+     * This implementation first attempts to rename the uploaded item to the
+     * specified destination file, if the item was originally written to disk.
+     * Otherwise, the data will be copied to the specified file.
+     * <p>
+     * This method is only guaranteed to work <em>once</em>, the first time it
+     * is invoked for a particular item. This is because, in the event that the
+     * method renames a temporary file, that file will no longer be available
+     * to copy or rename again at a later time.
+     *
+     * @param file The <code>File</code> into which the uploaded item should
+     *             be stored.
+     *
+     * @throws Exception if an error occurs.
+     */
+    public void write(File file) throws Exception {
+        if (isInMemory()) {
+            FileOutputStream fout = null;
+            try {
+                fout = new FileOutputStream(file);
+                fout.write(get());
+            } finally {
+                if (fout != null) {
+                    fout.close();
+                }
+            }
+        } else {
+            File outputFile = getStoreLocation();
+            if (outputFile != null) {
+                // Save the length of the file
+                size = outputFile.length();
+                /*
+                 * The uploaded file is being stored on disk
+                 * in a temporary location so move it to the
+                 * desired file.
+                 */
+                if (!outputFile.renameTo(file)) {
+                    BufferedInputStream in = null;
+                    BufferedOutputStream out = null;
+                    try {
+                        in = new BufferedInputStream(
+                            new FileInputStream(outputFile));
+                        out = new BufferedOutputStream(
+                                new FileOutputStream(file));
+                        IOUtils.copy(in, out);
+                    } finally {
+                        if (in != null) {
+                            try {
+                                in.close();
+                            } catch (IOException e) {
+                                // ignore
+                            }
+                        }
+                        if (out != null) {
+                            try {
+                                out.close();
+                            } catch (IOException e) {
+                                // ignore
+                            }
+                        }
+                    }
+                }
+            } else {
+                /*
+                 * For whatever reason we cannot write the
+                 * file to disk.
+                 */
+                throw new FileUploadException(
+                    "Cannot write uploaded file to disk!");
+            }
+        }
+    }
+
+
+    /**
+     * Deletes the underlying storage for a file item, including deleting any
+     * associated temporary disk file. Although this storage will be deleted
+     * automatically when the <code>FileItem</code> instance is garbage
+     * collected, this method can be used to ensure that this is done at an
+     * earlier time, thus preserving system resources.
+     */
+    public void delete() {
+        cachedContent = null;
+        File outputFile = getStoreLocation();
+        if (outputFile != null && outputFile.exists()) {
+            outputFile.delete();
+        }
+    }
+
+
+    /**
+     * Returns the name of the field in the multipart form corresponding to
+     * this file item.
+     *
+     * @return The name of the form field.
+     *
+     * @see #setFieldName(java.lang.String)
+     *
+     */
+    public String getFieldName() {
+        return fieldName;
+    }
+
+
+    /**
+     * Sets the field name used to reference this file item.
+     *
+     * @param fieldName The name of the form field.
+     *
+     * @see #getFieldName()
+     *
+     */
+    public void setFieldName(String fieldName) {
+        this.fieldName = fieldName;
+    }
+
+
+    /**
+     * Determines whether or not a <code>FileItem</code> instance represents
+     * a simple form field.
+     *
+     * @return <code>true</code> if the instance represents a simple form
+     *         field; <code>false</code> if it represents an uploaded file.
+     *
+     * @see #setFormField(boolean)
+     *
+     */
+    public boolean isFormField() {
+        return isFormField;
+    }
+
+
+    /**
+     * Specifies whether or not a <code>FileItem</code> instance represents
+     * a simple form field.
+     *
+     * @param state <code>true</code> if the instance represents a simple form
+     *              field; <code>false</code> if it represents an uploaded file.
+     *
+     * @see #isFormField()
+     *
+     */
+    public void setFormField(boolean state) {
+        isFormField = state;
+    }
+
+
+    /**
+     * Returns an {@link java.io.OutputStream OutputStream} that can
+     * be used for storing the contents of the file.
+     *
+     * @return An {@link java.io.OutputStream OutputStream} that can be used
+     *         for storing the contensts of the file.
+     *
+     * @throws IOException if an error occurs.
+     */
+    public OutputStream getOutputStream()
+        throws IOException {
+        if (dfos == null) {
+            File outputFile = getTempFile();
+            dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
+        }
+        return dfos;
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
+     * data's temporary location on the disk. Note that for
+     * <code>FileItem</code>s that have their data stored in memory,
+     * this method will return <code>null</code>. When handling large
+     * files, you can use {@link java.io.File#renameTo(java.io.File)} to
+     * move the file to new location without copying the data, if the
+     * source and destination locations reside within the same logical
+     * volume.
+     *
+     * @return The data file, or <code>null</code> if the data is stored in
+     *         memory.
+     */
+    public File getStoreLocation() {
+        return dfos == null ? null : dfos.getFile();
+    }
+
+
+    // ------------------------------------------------------ Protected methods
+
+
+    /**
+     * Removes the file contents from the temporary storage.
+     */
+    @Override
+    protected void finalize() {
+        File outputFile = dfos.getFile();
+
+        if (outputFile != null && outputFile.exists()) {
+            outputFile.delete();
+        }
+    }
+
+
+    /**
+     * Creates and returns a {@link java.io.File File} representing a uniquely
+     * named temporary file in the configured repository path. The lifetime of
+     * the file is tied to the lifetime of the <code>FileItem</code> instance;
+     * the file will be deleted when the instance is garbage collected.
+     *
+     * @return The {@link java.io.File File} to be used for temporary storage.
+     */
+    protected File getTempFile() {
+        if (tempFile == null) {
+            File tempDir = repository;
+            if (tempDir == null) {
+                tempDir = new File(System.getProperty("java.io.tmpdir"));
+            }
+
+            String tempFileName =
+                "upload_" + UID + "_" + getUniqueId() + ".tmp";
+
+            tempFile = new File(tempDir, tempFileName);
+        }
+        return tempFile;
+    }
+
+
+    // -------------------------------------------------------- Private methods
+
+
+    /**
+     * Returns an identifier that is unique within the class loader used to
+     * load this class, but does not have random-like apearance.
+     *
+     * @return A String with the non-random looking instance identifier.
+     */
+    private static String getUniqueId() {
+        final int limit = 100000000;
+        int current;
+        synchronized (DiskFileItem.class) {
+            current = counter++;
+        }
+        String id = Integer.toString(current);
+
+        // If you manage to get more than 100 million of ids, you'll
+        // start getting ids longer than 8 characters.
+        if (current < limit) {
+            id = ("00000000" + id).substring(id.length());
+        }
+        return id;
+    }
+
+
+
+
+    /**
+     * Returns a string representation of this object.
+     *
+     * @return a string representation of this object.
+     */
+    @Override
+    public String toString() {
+        return "name=" + this.getName()
+            + ", StoreLocation="
+            + String.valueOf(this.getStoreLocation())
+            + ", size="
+            + this.getSize()
+            + "bytes, "
+            + "isFormField=" + isFormField()
+            + ", FieldName="
+            + this.getFieldName();
+    }
+
+
+    // -------------------------------------------------- Serialization methods
+
+
+    /**
+     * Writes the state of this object during serialization.
+     *
+     * @param out The stream to which the state should be written.
+     *
+     * @throws IOException if an error occurs.
+     */
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        // Read the data
+        if (dfos.isInMemory()) {
+            cachedContent = get();
+        } else {
+            cachedContent = null;
+            dfosFile = dfos.getFile();
+        }
+
+        // write out values
+        out.defaultWriteObject();
+    }
+
+    /**
+     * Reads the state of this object during deserialization.
+     *
+     * @param in The stream from which the state should be read.
+     *
+     * @throws IOException if an error occurs.
+     * @throws ClassNotFoundException if class cannot be found.
+     */
+    private void readObject(ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        // read values
+        in.defaultReadObject();
+
+        OutputStream output = getOutputStream();
+        if (cachedContent != null) {
+            output.write(cachedContent);
+        } else {
+            FileInputStream input = new FileInputStream(dfosFile);
+            IOUtils.copy(input, output);
+            dfosFile.delete();
+            dfosFile = null;
+        }
+        output.close();
+
+        cachedContent = null;
+    }
+
+    /**
+     * Returns the file item headers.
+     * @return The file items headers.
+     */
+    public FileItemHeaders getHeaders() {
+        return headers;
+    }
+
+    /**
+     * Sets the file item headers.
+     * @param pHeaders The file items headers.
+     */
+    public void setHeaders(FileItemHeaders pHeaders) {
+        headers = pHeaders;
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java b/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java
new file mode 100644 (file)
index 0000000..09c8ded
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * 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.tomcat.util.http.fileupload.disk;
+
+import java.io.File;
+
+import org.apache.tomcat.util.http.fileupload.FileCleaningTracker;
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemFactory;
+
+
+/**
+ * <p>The default {@link org.apache.commons.fileupload.FileItemFactory}
+ * implementation. This implementation creates
+ * {@link org.apache.commons.fileupload.FileItem} instances which keep their
+ * content either in memory, for smaller items, or in a temporary file on disk,
+ * for larger items. The size threshold, above which content will be stored on
+ * disk, is configurable, as is the directory in which temporary files will be
+ * created.</p>
+ *
+ * <p>If not otherwise configured, the default configuration values are as
+ * follows:
+ * <ul>
+ *   <li>Size threshold is 10KB.</li>
+ *   <li>Repository is the system default temp directory, as returned by
+ *       <code>System.getProperty("java.io.tmpdir")</code>.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>When using the <code>DiskFileItemFactory</code>, then you should
+ * consider the following: Temporary files are automatically deleted as
+ * soon as they are no longer needed. (More precisely, when the
+ * corresponding instance of {@link java.io.File} is garbage collected.)
+ * Cleaning up those files is done by an instance of
+ * {@link FileCleaningTracker}, and an associated thread. In a complex
+ * environment, for example in a web application, you should consider
+ * terminating this thread, for example, when your web application
+ * ends. See the section on "Resource cleanup"
+ * in the users guide of commons-fileupload.</p>
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * @since FileUpload 1.1
+ *
+ * @version $Id$
+ */
+public class DiskFileItemFactory implements FileItemFactory {
+
+    // ----------------------------------------------------- Manifest constants
+
+
+    /**
+     * The default threshold above which uploads will be stored on disk.
+     */
+    public static final int DEFAULT_SIZE_THRESHOLD = 10240;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The directory in which uploaded files will be stored, if stored on disk.
+     */
+    private File repository;
+
+
+    /**
+     * The threshold above which uploads will be stored on disk.
+     */
+    private int sizeThreshold = DEFAULT_SIZE_THRESHOLD;
+
+
+    /**
+     * <p>The instance of {@link FileCleaningTracker}, which is responsible
+     * for deleting temporary files.</p>
+     * <p>May be null, if tracking files is not required.</p>
+     */
+    private FileCleaningTracker fileCleaningTracker;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an unconfigured instance of this class. The resulting factory
+     * may be configured by calling the appropriate setter methods.
+     */
+    public DiskFileItemFactory() {
+        this(DEFAULT_SIZE_THRESHOLD, null);
+    }
+
+
+    /**
+     * Constructs a preconfigured instance of this class.
+     *
+     * @param sizeThreshold The threshold, in bytes, below which items will be
+     *                      retained in memory and above which they will be
+     *                      stored as a file.
+     * @param repository    The data repository, which is the directory in
+     *                      which files will be created, should the item size
+     *                      exceed the threshold.
+     */
+    public DiskFileItemFactory(int sizeThreshold, File repository) {
+        this.sizeThreshold = sizeThreshold;
+        this.repository = repository;
+    }
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Returns the directory used to temporarily store files that are larger
+     * than the configured size threshold.
+     *
+     * @return The directory in which temporary files will be located.
+     *
+     * @see #setRepository(java.io.File)
+     *
+     */
+    public File getRepository() {
+        return repository;
+    }
+
+
+    /**
+     * Sets the directory used to temporarily store files that are larger
+     * than the configured size threshold.
+     *
+     * @param repository The directory in which temporary files will be located.
+     *
+     * @see #getRepository()
+     *
+     */
+    public void setRepository(File repository) {
+        this.repository = repository;
+    }
+
+
+    /**
+     * Returns the size threshold beyond which files are written directly to
+     * disk. The default value is 10240 bytes.
+     *
+     * @return The size threshold, in bytes.
+     *
+     * @see #setSizeThreshold(int)
+     */
+    public int getSizeThreshold() {
+        return sizeThreshold;
+    }
+
+
+    /**
+     * Sets the size threshold beyond which files are written directly to disk.
+     *
+     * @param sizeThreshold The size threshold, in bytes.
+     *
+     * @see #getSizeThreshold()
+     *
+     */
+    public void setSizeThreshold(int sizeThreshold) {
+        this.sizeThreshold = sizeThreshold;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Create a new {@link org.apache.tomcat.util.http.fileupload.disk.commons.fileupload.disk.DiskFileItem}
+     * instance from the supplied parameters and the local factory
+     * configuration.
+     *
+     * @param fieldName   The name of the form field.
+     * @param contentType The content type of the form field.
+     * @param isFormField <code>true</code> if this is a plain form field;
+     *                    <code>false</code> otherwise.
+     * @param fileName    The name of the uploaded file, if any, as supplied
+     *                    by the browser or other client.
+     *
+     * @return The newly created file item.
+     */
+    public FileItem createItem(String fieldName, String contentType,
+            boolean isFormField, String fileName) {
+        DiskFileItem result = new DiskFileItem(fieldName, contentType,
+                isFormField, fileName, sizeThreshold, repository);
+        FileCleaningTracker tracker = getFileCleaningTracker();
+        if (tracker != null) {
+            tracker.track(result.getTempFile(), this);
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns the tracker, which is responsible for deleting temporary
+     * files.
+     * @return An instance of {@link FileCleaningTracker}, defaults to
+     *   {@link org.apache.commons.io.FileCleaner#getInstance()}. Null,
+     *   if temporary files aren't tracked.
+     */
+    public FileCleaningTracker getFileCleaningTracker() {
+        return fileCleaningTracker;
+    }
+
+    /**
+     * Returns the tracker, which is responsible for deleting temporary
+     * files.
+     * @param pTracker An instance of {@link FileCleaningTracker},
+     *   which will from now on track the created files. May be null
+     *   to disable tracking.
+     */
+    public void setFileCleaningTracker(FileCleaningTracker pTracker) {
+        fileCleaningTracker = pTracker;
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/servlet/ServletFileUpload.java b/java/org/apache/tomcat/util/http/fileupload/servlet/ServletFileUpload.java
new file mode 100644 (file)
index 0000000..a95cc75
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.tomcat.util.http.fileupload.servlet;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemFactory;
+import org.apache.tomcat.util.http.fileupload.FileItemIterator;
+import org.apache.tomcat.util.http.fileupload.FileUpload;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+
+/**
+ * <p>High level API for processing file uploads.</p>
+ *
+ * <p>This class handles multiple files per single HTML widget, sent using
+ * <code>multipart/mixed</code> encoding type, as specified by
+ * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.  Use {@link
+ * #parseRequest(HttpServletRequest)} to acquire a list of {@link
+ * org.apache.commons.fileupload.FileItem}s associated with a given HTML
+ * widget.</p>
+ *
+ * <p>How the data for individual parts is stored is determined by the factory
+ * used to create them; a given part may be in memory, on disk, or somewhere
+ * else.</p>
+ *
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * @version $Id$
+ */
+public class ServletFileUpload extends FileUpload {
+
+    // ---------------------------------------------------------- Class methods
+
+
+    /**
+     * Utility method that determines whether the request contains multipart
+     * content.
+     *
+     * @param request The servlet request to be evaluated. Must be non-null.
+     *
+     * @return <code>true</code> if the request is multipart;
+     *         <code>false</code> otherwise.
+     */
+    public static final boolean isMultipartContent(
+            HttpServletRequest request) {
+        if (!"post".equals(request.getMethod().toLowerCase(Locale.ENGLISH))) {
+            return false;
+        }
+        String contentType = request.getContentType();
+        if (contentType == null) {
+            return false;
+        }
+        if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
+            return true;
+        }
+        return false;
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an uninitialised instance of this class. A factory must be
+     * configured, using <code>setFileItemFactory()</code>, before attempting
+     * to parse requests.
+     *
+     * @see FileUpload#FileUpload(FileItemFactory)
+     */
+    public ServletFileUpload() {
+        super();
+    }
+
+
+    /**
+     * Constructs an instance of this class which uses the supplied factory to
+     * create <code>FileItem</code> instances.
+     *
+     * @see FileUpload#FileUpload()
+     * @param fileItemFactory The factory to use for creating file items.
+     */
+    public ServletFileUpload(FileItemFactory fileItemFactory) {
+        super(fileItemFactory);
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
+     * compliant <code>multipart/form-data</code> stream.
+     *
+     * @param request The servlet request to be parsed.
+     *
+     * @return A list of <code>FileItem</code> instances parsed from the
+     *         request, in the order that they were transmitted.
+     *
+     * @throws FileUploadException if there are problems reading/parsing
+     *                             the request or storing files.
+     */
+    public List<FileItem> parseRequest(HttpServletRequest request)
+    throws FileUploadException {
+        return parseRequest(new ServletRequestContext(request));
+    }
+
+
+    /**
+     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
+     * compliant <code>multipart/form-data</code> stream.
+     *
+     * @param request The servlet request to be parsed.
+     *
+     * @return An iterator to instances of <code>FileItemStream</code>
+     *         parsed from the request, in the order that they were
+     *         transmitted.
+     *
+     * @throws FileUploadException if there are problems reading/parsing
+     *                             the request or storing files.
+     * @throws IOException An I/O error occurred. This may be a network
+     *   error while communicating with the client or a problem while
+     *   storing the uploaded content.
+     */
+    public FileItemIterator getItemIterator(HttpServletRequest request)
+    throws FileUploadException, IOException {
+        return super.getItemIterator(new ServletRequestContext(request));
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java b/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java
new file mode 100644 (file)
index 0000000..6db1d8e
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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.tomcat.util.http.fileupload.servlet;
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tomcat.util.http.fileupload.RequestContext;
+
+
+/**
+ * <p>Provides access to the request information needed for a request made to
+ * an HTTP servlet.</p>
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * @since FileUpload 1.1
+ *
+ * @version $Id$
+ */
+public class ServletRequestContext implements RequestContext {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The request for which the context is being provided.
+     */
+    private HttpServletRequest request;
+
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Construct a context for this request.
+     *
+     * @param request The request to which this context applies.
+     */
+    public ServletRequestContext(HttpServletRequest request) {
+        this.request = request;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Retrieve the character encoding for the request.
+     *
+     * @return The character encoding for the request.
+     */
+    public String getCharacterEncoding() {
+        return request.getCharacterEncoding();
+    }
+
+    /**
+     * Retrieve the content type of the request.
+     *
+     * @return The content type of the request.
+     */
+    public String getContentType() {
+        return request.getContentType();
+    }
+
+    /**
+     * Retrieve the content length of the request.
+     *
+     * @return The content length of the request.
+     */
+    public int getContentLength() {
+        return request.getContentLength();
+    }
+
+    /**
+     * Retrieve the input stream for the request.
+     *
+     * @return The input stream for the request.
+     *
+     * @throws IOException if a problem occurs.
+     */
+    public InputStream getInputStream() throws IOException {
+        return request.getInputStream();
+    }
+
+    /**
+     * Returns a string representation of this object.
+     *
+     * @return a string representation of this object.
+     */
+    @Override
+    public String toString() {
+        return "ContentLength="
+            + this.getContentLength()
+            + ", ContentType="
+            + this.getContentType();
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/util/Closeable.java b/java/org/apache/tomcat/util/http/fileupload/util/Closeable.java
new file mode 100644 (file)
index 0000000..2c18c47
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.tomcat.util.http.fileupload.util;
+
+import java.io.IOException;
+
+
+/**
+ * Interface of an object, which may be closed.
+ */
+public interface Closeable {
+    /**
+     * Closes the object.
+     * @throws IOException An I/O error occurred.
+     */
+    void close() throws IOException;
+
+    /**
+     * Returns, whether the object is already closed.
+     * @return True, if the object is closed, otherwise false.
+     * @throws IOException An I/O error occurred.
+     */
+    boolean isClosed() throws IOException;
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java b/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java
new file mode 100644 (file)
index 0000000..72c8563
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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.tomcat.util.http.fileupload.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+
+
+/**
+ * Default implementation of the {@link FileItemHeaders} interface.
+ *
+ * @author Michael C. Macaluso
+ * @since 1.3
+ */
+public class FileItemHeadersImpl implements FileItemHeaders, Serializable {
+    private static final long serialVersionUID = -4455695752627032559L;
+
+    /**
+     * Map of <code>String</code> keys to a <code>List</code> of
+     * <code>String</code> instances.
+     */
+    private final Map<String,List<String>> headerNameToValueListMap =
+        new HashMap<String,List<String>>();
+
+    /**
+     * List to preserve order of headers as added.  This would not be
+     * needed if a <code>LinkedHashMap</code> could be used, but don't
+     * want to depend on 1.4.
+     */
+    private final List<String> headerNameList = new ArrayList<String>();
+
+    public String getHeader(String name) {
+        String nameLower = name.toLowerCase(Locale.ENGLISH);
+        List<String> headerValueList = headerNameToValueListMap.get(nameLower);
+        if (null == headerValueList) {
+            return null;
+        }
+        return headerValueList.get(0);
+    }
+
+    public Iterator<String> getHeaderNames() {
+        return headerNameList.iterator();
+    }
+
+    public Iterator<String> getHeaders(String name) {
+        String nameLower = name.toLowerCase(Locale.ENGLISH);
+        List<String> headerValueList = headerNameToValueListMap.get(nameLower);
+        if (null == headerValueList) {
+            return Collections.<String>emptyList().iterator();
+        }
+        return headerValueList.iterator();
+    }
+
+    /**
+     * Method to add header values to this instance.
+     *
+     * @param name name of this header
+     * @param value value of this header
+     */
+    public synchronized void addHeader(String name, String value) {
+        String nameLower = name.toLowerCase();
+        List<String> headerValueList = headerNameToValueListMap.get(nameLower);
+        if (null == headerValueList) {
+            headerValueList = new ArrayList<String>();
+            headerNameToValueListMap.put(nameLower, headerValueList);
+            headerNameList.add(nameLower);
+        }
+        headerValueList.add(value);
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java b/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java
new file mode 100644 (file)
index 0000000..0f4a40e
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.tomcat.util.http.fileupload.util;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * An input stream, which limits its data size. This stream is
+ * used, if the content length is unknown.
+ */
+public abstract class LimitedInputStream
+        extends FilterInputStream implements Closeable {
+    /**
+     * The maximum size of an item, in bytes.
+     */
+    private long sizeMax;
+    /**
+     * The current number of bytes.
+     */
+    private long count;
+    /**
+     * Whether this stream is already closed.
+     */
+    private boolean closed;
+
+    /**
+     * Creates a new instance.
+     * @param pIn The input stream, which shall be limited.
+     * @param pSizeMax The limit; no more than this number of bytes
+     *   shall be returned by the source stream.
+     */
+    public LimitedInputStream(InputStream pIn, long pSizeMax) {
+        super(pIn);
+        sizeMax = pSizeMax;
+    }
+
+    /**
+     * Called to indicate, that the input streams limit has
+     * been exceeded.
+     * @param pSizeMax The input streams limit, in bytes.
+     * @param pCount The actual number of bytes.
+     * @throws IOException The called method is expected
+     *   to raise an IOException.
+     */
+    protected abstract void raiseError(long pSizeMax, long pCount)
+            throws IOException;
+
+    /** Called to check, whether the input streams
+     * limit is reached.
+     * @throws IOException The given limit is exceeded.
+     */
+    private void checkLimit() throws IOException {
+        if (count > sizeMax) {
+            raiseError(sizeMax, count);
+        }
+    }
+
+    /**
+     * Reads the next byte of data from this input stream. The value
+     * byte is returned as an <code>int</code> in the range
+     * <code>0</code> to <code>255</code>. If no byte is available
+     * because the end of the stream has been reached, the value
+     * <code>-1</code> is returned. This method blocks until input data
+     * is available, the end of the stream is detected, or an exception
+     * is thrown.
+     * <p>
+     * This method
+     * simply performs <code>in.read()</code> and returns the result.
+     *
+     * @return     the next byte of data, or <code>-1</code> if the end of the
+     *             stream is reached.
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterInputStream#in
+     */
+    @Override
+    public int read() throws IOException {
+        int res = super.read();
+        if (res != -1) {
+            count++;
+            checkLimit();
+        }
+        return res;
+    }
+
+    /**
+     * Reads up to <code>len</code> bytes of data from this input stream
+     * into an array of bytes. If <code>len</code> is not zero, the method
+     * blocks until some input is available; otherwise, no
+     * bytes are read and <code>0</code> is returned.
+     * <p>
+     * This method simply performs <code>in.read(b, off, len)</code>
+     * and returns the result.
+     *
+     * @param      b     the buffer into which the data is read.
+     * @param      off   The start offset in the destination array
+     *                   <code>b</code>.
+     * @param      len   the maximum number of bytes read.
+     * @return     the total number of bytes read into the buffer, or
+     *             <code>-1</code> if there is no more data because the end of
+     *             the stream has been reached.
+     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
+     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
+     * <code>len</code> is negative, or <code>len</code> is greater than
+     * <code>b.length - off</code>
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterInputStream#in
+     */
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        int res = super.read(b, off, len);
+        if (res > 0) {
+            count += res;
+            checkLimit();
+        }
+        return res;
+    }
+
+    /**
+     * Returns, whether this stream is already closed.
+     * @return True, if the stream is closed, otherwise false.
+     * @throws IOException An I/O error occurred.
+     */
+    public boolean isClosed() throws IOException {
+        return closed;
+    }
+
+    /**
+     * Closes this input stream and releases any system resources
+     * associated with the stream.
+     * This
+     * method simply performs <code>in.close()</code>.
+     *
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterInputStream#in
+     */
+    @Override
+    public void close() throws IOException {
+        closed = true;
+        super.close();
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/util/Streams.java b/java/org/apache/tomcat/util/http/fileupload/util/Streams.java
new file mode 100644 (file)
index 0000000..7f177ca
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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.tomcat.util.http.fileupload.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+/** Utility class for working with streams.
+ */
+public final class Streams {
+    /**
+     * Private constructor, to prevent instantiation.
+     * This class has only static methods.
+     */
+    private Streams() {
+        // Does nothing
+    }
+
+    /**
+     * Default buffer size for use in
+     * {@link #copy(InputStream, OutputStream, boolean)}.
+     */
+    private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+    /**
+     * Copies the contents of the given {@link InputStream}
+     * to the given {@link OutputStream}. Shortcut for
+     * <pre>
+     *   copy(pInputStream, pOutputStream, new byte[8192]);
+     * </pre>
+     * @param pInputStream The input stream, which is being read.
+     * It is guaranteed, that {@link InputStream#close()} is called
+     * on the stream.
+     * @param pOutputStream The output stream, to which data should
+     * be written. May be null, in which case the input streams
+     * contents are simply discarded.
+     * @param pClose True guarantees, that {@link OutputStream#close()}
+     * is called on the stream. False indicates, that only
+     * {@link OutputStream#flush()} should be called finally.
+     *
+     * @return Number of bytes, which have been copied.
+     * @throws IOException An I/O error occurred.
+     */
+    public static long copy(InputStream pInputStream,
+            OutputStream pOutputStream, boolean pClose)
+            throws IOException {
+        return copy(pInputStream, pOutputStream, pClose,
+                new byte[DEFAULT_BUFFER_SIZE]);
+    }
+
+    /**
+     * Copies the contents of the given {@link InputStream}
+     * to the given {@link OutputStream}.
+     * @param pIn The input stream, which is being read.
+     *   It is guaranteed, that {@link InputStream#close()} is called
+     *   on the stream.
+     * @param pOut The output stream, to which data should
+     *   be written. May be null, in which case the input streams
+     *   contents are simply discarded.
+     * @param pClose True guarantees, that {@link OutputStream#close()}
+     *   is called on the stream. False indicates, that only
+     *   {@link OutputStream#flush()} should be called finally.
+     * @param pBuffer Temporary buffer, which is to be used for
+     *   copying data.
+     * @return Number of bytes, which have been copied.
+     * @throws IOException An I/O error occurred.
+     */
+    public static long copy(InputStream pIn,
+            OutputStream pOut, boolean pClose,
+            byte[] pBuffer)
+    throws IOException {
+        OutputStream out = pOut;
+        InputStream in = pIn;
+        try {
+            long total = 0;
+            for (;;) {
+                int res = in.read(pBuffer);
+                if (res == -1) {
+                    break;
+                }
+                if (res > 0) {
+                    total += res;
+                    if (out != null) {
+                        out.write(pBuffer, 0, res);
+                    }
+                }
+            }
+            if (out != null) {
+                if (pClose) {
+                    out.close();
+                } else {
+                    out.flush();
+                }
+                out = null;
+            }
+            in.close();
+            in = null;
+            return total;
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Throwable t) {
+                    /* Ignore me */
+                }
+            }
+            if (pClose  &&  out != null) {
+                try {
+                    out.close();
+                } catch (Throwable t) {
+                    /* Ignore me */
+                }
+            }
+        }
+    }
+
+    /**
+     * This convenience method allows to read a
+     * {@link org.apache.commons.fileupload.FileItemStream}'s
+     * content into a string. The platform's default character encoding
+     * is used for converting bytes into characters.
+     * @param pStream The input stream to read.
+     * @see #asString(InputStream, String)
+     * @return The streams contents, as a string.
+     * @throws IOException An I/O error occurred.
+     */
+    public static String asString(InputStream pStream) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        copy(pStream, baos, true);
+        return baos.toString();
+    }
+
+    /**
+     * This convenience method allows to read a
+     * {@link org.apache.commons.fileupload.FileItemStream}'s
+     * content into a string, using the given character encoding.
+     * @param pStream The input stream to read.
+     * @param pEncoding The character encoding, typically "UTF-8".
+     * @see #asString(InputStream)
+     * @return The streams contents, as a string.
+     * @throws IOException An I/O error occurred.
+     */
+    public static String asString(InputStream pStream, String pEncoding)
+            throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        copy(pStream, baos, true);
+        return baos.toString(pEncoding);
+    }
+}