| /**************************************************************** |
| * 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.james.mime4j.storage; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.apache.commons.io.FileUtils; |
| |
| /** |
| * A {@link StorageProvider} that stores the data in temporary files. The files |
| * are stored either in a user-specified directory or the default temporary-file |
| * directory (specified by system property <code>java.io.tmpdir</code>). |
| * <p> |
| * Example usage: |
| * |
| * <pre> |
| * File directory = new File("/tmp/mime4j"); |
| * StorageProvider provider = new TempFileStorageProvider(directory); |
| * DefaultStorageProvider.setInstance(provider); |
| * </pre> |
| */ |
| public class TempFileStorageProvider extends AbstractStorageProvider { |
| |
| private static final String DEFAULT_PREFIX = "m4j"; |
| |
| private final String prefix; |
| private final String suffix; |
| private final File directory; |
| |
| /** |
| * Equivalent to using constructor |
| * <code>TempFileStorageProvider("m4j", null, null)</code>. |
| */ |
| public TempFileStorageProvider() { |
| this(DEFAULT_PREFIX, null, null); |
| } |
| |
| /** |
| * Equivalent to using constructor |
| * <code>TempFileStorageProvider("m4j", null, directory)</code>. |
| */ |
| public TempFileStorageProvider(File directory) { |
| this(DEFAULT_PREFIX, null, directory); |
| } |
| |
| /** |
| * Creates a new <code>TempFileStorageProvider</code> using the given |
| * values. |
| * |
| * @param prefix |
| * prefix for generating the temporary file's name; must be at |
| * least three characters long. |
| * @param suffix |
| * suffix for generating the temporary file's name; may be |
| * <code>null</code> to use the suffix <code>".tmp"</code>. |
| * @param directory |
| * the directory in which the file is to be created, or |
| * <code>null</code> if the default temporary-file directory is |
| * to be used (specified by the system property |
| * <code>java.io.tmpdir</code>). |
| * @throws IllegalArgumentException |
| * if the given prefix is less than three characters long or the |
| * given directory does not exist and cannot be created (if it |
| * is not <code>null</code>). |
| */ |
| public TempFileStorageProvider(String prefix, String suffix, File directory) { |
| if (prefix == null || prefix.length() < 3) |
| throw new IllegalArgumentException("invalid prefix"); |
| |
| if (directory != null) { |
| try { |
| FileUtils.forceMkdir(directory); |
| } catch (IOException e) { |
| throw new IllegalArgumentException("invalid directory"); |
| } |
| } |
| |
| this.prefix = prefix; |
| this.suffix = suffix; |
| this.directory = directory; |
| } |
| |
| public StorageOutputStream createStorageOutputStream() throws IOException { |
| File file = File.createTempFile(prefix, suffix, directory); |
| file.deleteOnExit(); |
| |
| return new TempFileStorageOutputStream(file); |
| } |
| |
| private static final class TempFileStorageOutputStream extends |
| StorageOutputStream { |
| private final File file; |
| private OutputStream out; |
| |
| public TempFileStorageOutputStream(File file) throws IOException { |
| this.file = file; |
| this.out = new FileOutputStream(file); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| super.close(); |
| out.close(); |
| } |
| |
| @Override |
| protected void write0(byte[] buffer, int offset, int length) |
| throws IOException { |
| out.write(buffer, offset, length); |
| } |
| |
| @Override |
| protected Storage toStorage0() throws IOException { |
| // out has already been closed because toStorage calls close |
| return new TempFileStorage(file); |
| } |
| } |
| |
| private static final class TempFileStorage implements Storage { |
| |
| private File file; |
| |
| private static final Set<File> filesToDelete = new HashSet<File>(); |
| |
| public TempFileStorage(File file) { |
| this.file = file; |
| } |
| |
| public void delete() { |
| // deleting a file might not immediately succeed if there are still |
| // streams left open (especially under Windows). so we keep track of |
| // the files that have to be deleted and try to delete all these |
| // files each time this method gets invoked. |
| |
| // a better but more complicated solution would be to start a |
| // separate thread that tries to delete the files periodically. |
| |
| synchronized (filesToDelete) { |
| if (file != null) { |
| filesToDelete.add(file); |
| file = null; |
| } |
| |
| for (Iterator<File> iterator = filesToDelete.iterator(); iterator |
| .hasNext();) { |
| try { |
| FileUtils.forceDelete(iterator.next()); |
| iterator.remove(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| public InputStream getInputStream() throws IOException { |
| if (file == null) |
| throw new IllegalStateException("storage has been deleted"); |
| |
| return new BufferedInputStream(new FileInputStream(file)); |
| } |
| |
| } |
| |
| } |