JournaledFile.java revision 1afd1c90ebe789b8d3a137004127a50d2db7e3b5
1/*
2 * Copyright (C) 2009 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.internal.util;
18
19import java.io.File;
20import java.io.IOException;
21
22public class JournaledFile {
23    File mReal;
24    File mTemp;
25    boolean mWriting;
26
27    public JournaledFile(File real, File temp) {
28        mReal = real;
29        mTemp = temp;
30    }
31
32    /** Returns the file for you to read.
33     * @more
34     * Prefers the real file.  If it doesn't exist, uses the temp one, and then copies
35     * it to the real one.  If there is both a real file and a temp one, assumes that the
36     * temp one isn't fully written and deletes it.
37     */
38    public File chooseForRead() {
39        File result;
40        if (mReal.exists()) {
41            result = mReal;
42            if (mTemp.exists()) {
43                mTemp.delete();
44            }
45        } else if (mTemp.exists()) {
46            result = mTemp;
47            mTemp.renameTo(mReal);
48        } else {
49            return mReal;
50        }
51        return result;
52    }
53
54    /**
55     * Returns a file for you to write.
56     * @more
57     * If a write is already happening, throws.  In other words, you must provide your
58     * own locking.
59     * <p>
60     * Call {@link #commit} to commit the changes, or {@link #rollback} to forget the changes.
61     */
62    public File chooseForWrite() {
63        if (mWriting) {
64            throw new IllegalStateException("uncommitted write already in progress");
65        }
66        if (!mReal.exists()) {
67            // If the real one doesn't exist, it's either because this is the first time
68            // or because something went wrong while copying them.  In this case, we can't
69            // trust anything that's in temp.  In order to have the chooseForRead code not
70            // use the temporary one until it's fully written, create an empty file
71            // for real, which will we'll shortly delete.
72            try {
73                mReal.createNewFile();
74            } catch (IOException e) {
75                // Ignore
76            }
77        }
78
79        if (mTemp.exists()) {
80            mTemp.delete();
81        }
82        mWriting = true;
83        return mTemp;
84    }
85
86    /**
87     * Commit changes.
88     */
89    public void commit() {
90        if (!mWriting) {
91            throw new IllegalStateException("no file to commit");
92        }
93        mWriting = false;
94        mTemp.renameTo(mReal);
95    }
96
97    /**
98     * Roll back changes.
99     */
100    public void rollback() {
101        if (!mWriting) {
102            throw new IllegalStateException("no file to roll back");
103        }
104        mWriting = false;
105        mTemp.delete();
106    }
107}
108