1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.store

File FileDeleteTransactionRunnable.java

 

Coverage histogram

../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

18
29
6
1
187
77
19
0.66
4.83
6
3.17

Classes

Class Line # Actions
FileDeleteTransactionRunnable 34 29 0% 19 12
0.773584977.4%
 

Contributing tests

This file is covered by 8 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.store;
21   
22    import java.io.File;
23    import java.io.IOException;
24    import java.util.concurrent.locks.ReadWriteLock;
25   
26    /**
27    * A TransactionRunnable for deleting a file safely.
28    * The operation can be rolled back even after the onCommit() function is called.
29    * It is only final when the onComplete function is called.
30    *
31    * @version $Id: 9422ea4462baa5bdb4fff5fdbc600f0b68be42c0 $
32    * @since 3.0M2
33    */
 
34    public class FileDeleteTransactionRunnable extends StartableTransactionRunnable<TransactionRunnable>
35    {
36    /**
37    * The location of the file to sdelete.
38    */
39    private final File toDelete;
40   
41    /**
42    * The location of the backup file.
43    */
44    private final File backupFile;
45   
46    /**
47    * A lock to hold while running this TransactionRunnable.
48    */
49    private final ReadWriteLock lock;
50   
51    /**
52    * False until preRun() has complete. If false then we know there is nothing to rollback and
53    * more importantly, we do not know if files in the temporary and backup locations are not
54    * from a previous (catastrophically failed) delete or save operation.
55    */
56    private boolean preRunComplete;
57   
58    /**
59    * The Constructor.
60    *
61    * @param toDelete the file to delete.
62    * @param backupFile a temporary file, this should not contain anything important as it will be deleted
63    * and must not be altered while the operation is running. This will contain whatever
64    * was in the toDelete file prior, just in case onRollback must be called.
65    * @param lock a ReadWriteLock whose writeLock will be locked as the beginning of the process and
66    * unlocked when complete.
67    */
 
68  11 toggle public FileDeleteTransactionRunnable(final File toDelete,
69    final File backupFile,
70    final ReadWriteLock lock)
71    {
72  11 this.toDelete = toDelete;
73  11 this.backupFile = backupFile;
74  11 this.lock = lock;
75    }
76   
77    /**
78    * {@inheritDoc}
79    * <p>
80    * Obtain the lock and make sure the temporary and backup files are deleted.
81    * </p>
82    *
83    * @see StartableTransactionRunnable#onPreRun()
84    */
 
85  11 toggle protected void onPreRun() throws IOException
86    {
87  11 this.lock.writeLock().lock();
88  11 this.clearBackup();
89  11 this.preRunComplete = true;
90    }
91   
 
92  10 toggle protected void onRun() throws IOException
93    {
94  10 if (this.toDelete.exists()) {
95  8 this.toDelete.renameTo(this.backupFile);
96    }
97    }
98   
99    /**
100    * {@inheritDoc}
101    * <p>
102    * There are a few possibilities. If preRun() has not completed then there may be an old backup from a previous
103    * delete, anyway if preRun() has not completed then we know there is nothing to rollback. Otherwise:
104    * </p>
105    * <ol>
106    * <li>There is a backup file but no main file, it has been renamed, rename it back to the main location.</li>
107    * <li>There is a main file and no backup. Nothing has probably happened, do nothing to rollback.</li>
108    * <li>There are neither backup nor main files, this means we tried to delete a file which didn't exist to begin
109    * with.</li>
110    * <li>There are both main and backup files. AAAAAaaa what do we do?! Throw an exception which will be reported.
111    * </li>
112    * </ol>
113    *
114    * @see StartableTransactionRunnable#onRollback()
115    */
 
116  2 toggle protected void onRollback()
117    {
118    // If this is false then we know run() has not yet happened and we know there is nothing to do.
119  2 if (this.preRunComplete) {
120  2 boolean isBackupFile = this.backupFile.exists();
121  2 boolean isMainFile = this.toDelete.exists();
122   
123    // 1.
124  2 if (isBackupFile && !isMainFile) {
125  1 this.backupFile.renameTo(this.toDelete);
126  1 return;
127    }
128   
129    // 2.
130  1 if (!isBackupFile && isMainFile) {
131  0 return;
132    }
133   
134    // 3.
135  1 if (!isBackupFile && !isMainFile) {
136  1 return;
137    }
138   
139    // 4.
140  0 if (isBackupFile && isMainFile) {
141  0 throw new IllegalStateException("Tried to rollback the deletion of file "
142    + this.toDelete.getAbsolutePath() + " and encountered a "
143    + "backup and a main file. Since the main file is renamed "
144    + "to a backup location before deleting, this should never "
145    + "happen.");
146    }
147    }
148    }
149   
150    /**
151    * {@inheritDoc}
152    * <p>
153    * Once this is called, there is no going back.
154    * Remove backup file and unlock the lock.
155    * </p>
156    *
157    * @see StartableTransactionRunnable#onComplete()
158    */
 
159  11 toggle protected void onComplete() throws IOException
160    {
161  11 if (!this.preRunComplete) {
162  0 throw new IllegalStateException("Deleting file: " + this.toDelete.getAbsolutePath()
163    + " onPreRun has not been called, maybe the class was extended "
164    + "and it was overridden?");
165    }
166  11 try {
167  11 this.clearBackup();
168    } finally {
169  11 this.lock.writeLock().unlock();
170    }
171    }
172   
173    /**
174    * Remove backup file.
175    *
176    * @throws IOException if removing file fails or file still exists after delete() is called.
177    */
 
178  22 toggle private void clearBackup() throws IOException
179    {
180  22 if (this.backupFile.exists()) {
181  12 this.backupFile.delete();
182    }
183  22 if (this.backupFile.exists()) {
184  0 throw new IOException("Could not remove backup file, cannot proceed.");
185    }
186    }
187    }