ImapTempFileLiteral.java revision 977a7d206a866f07774d98aa2ffa2c51aa057de1
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.mail.store.imap;
18
19import com.android.email.Email;
20import com.android.email.FixedLengthInputStream;
21import com.android.email.Utility;
22
23import org.apache.commons.io.IOUtils;
24
25import android.util.Log;
26
27import java.io.ByteArrayInputStream;
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.FileNotFoundException;
31import java.io.FileOutputStream;
32import java.io.IOException;
33import java.io.InputStream;
34import java.io.OutputStream;
35
36/**
37 * Subclass of {@link ImapString} used for literals backed by a temp file.
38 */
39public class ImapTempFileLiteral extends ImapString {
40    private boolean mDestroyed = false;
41
42    /* package for test */ final File mFile;
43
44    /** Size is purely for toString() */
45    private final int mSize;
46
47    /* package */  ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
48        mSize = stream.getLength();
49        mFile = File.createTempFile("imap", ".tmp", Email.getTempDirectory());
50
51        // Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
52        // so it'd simply cause a memory leak.
53        // deleteOnExit() simply adds filenames to a static list and the list will never shrink.
54        // mFile.deleteOnExit();
55        OutputStream out = new FileOutputStream(mFile);
56        IOUtils.copy(stream, out);
57        out.close();
58    }
59
60    /**
61     * Make sure we delete the temp file.
62     *
63     * We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
64     */
65    @Override
66    protected void finalize() throws Throwable {
67        try {
68            destroy();
69        } finally {
70            super.finalize();
71        }
72    }
73
74    private void checkNotDestroyed() {
75        if (mDestroyed) {
76            throw new RuntimeException("Already destroyed");
77        }
78    }
79
80    @Override
81    public InputStream getAsStream() {
82        checkNotDestroyed();
83        try {
84            return new FileInputStream(mFile);
85        } catch (FileNotFoundException e) {
86            // It's probably possible if we're low on storage and the system clears the cache dir.
87            Log.w(Email.LOG_TAG, "ImapTempFileLiteral: Temp file not found");
88
89            // Return 0 byte stream as a dummy...
90            return new ByteArrayInputStream(new byte[0]);
91        }
92    }
93
94    @Override
95    public String getString() {
96        checkNotDestroyed();
97        try {
98            return Utility.fromAscii(IOUtils.toByteArray(getAsStream()));
99        } catch (IOException e) {
100            Log.w(Email.LOG_TAG, "ImapTempFileLiteral: Error while reading temp file");
101            return "";
102        }
103    }
104
105    @Override
106    public void destroy() {
107        try {
108            if (!mDestroyed && mFile.exists()) {
109                mFile.delete();
110            }
111        } finally {
112            mDestroyed = true;
113        }
114    }
115
116    @Override
117    public String toString() {
118        return String.format("{%d byte literal(file)}", mSize);
119    }
120
121    public boolean tempFileExistsForTest() {
122        return mFile.exists();
123    }
124}
125