KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > io > GeneralArchiveController


1 /*
2  * GeneralArchiveController.java
3  *
4  * Created on 20. November 2006, 02:11
5  */

6
7 package de.schlichtherle.io;
8
9 import de.schlichtherle.io.archive.spi.*;
10
11 import java.io.*;
12
13 /**
14  * This class implements the behaviour required by all archive controllers,
15  * independent of their update strategy.
16  * At current, this is the handling of the archive file system, but should
17  * also contain the input archive, output archive and archive entries in
18  * future.
19  * It is up to the sub class to implement the actual update strategy
20  * (full update, append, etc.).
21  *
22  * @author Christian Schlichtherle
23  * @version @version@
24  * @since TrueZIP 6.4
25  */

26 abstract class GeneralArchiveController
27         extends ArchiveController
28         implements ArchiveEntryFactory {
29
30     /**
31      * The state of the virtual archive file system.
32      */

33     private FileSystemState fileSystemState = new ResetFileSystem();
34
35     /** Creates a new instance of GeneralArchiveController */
36     protected GeneralArchiveController(
37             java.io.File JavaDoc target,
38             ArchiveController enclController,
39             String JavaDoc enclEntryName,
40             ArchiveDriver driver) {
41         super(target, enclController, enclEntryName, driver);
42     }
43
44     protected final ArchiveFileSystem getFileSystem(boolean autoCreate)
45     throws IOException {
46         assert readLock().isLocked() || writeLock().isLocked();
47         return fileSystemState.getFileSystem(autoCreate);
48     }
49
50     protected final boolean isTouched() {
51         ArchiveFileSystem fileSystem = getFileSystem();
52         return fileSystem != null && fileSystem.isTouched();
53     }
54
55     /**
56      * Called by this controller's {@link ArchiveFileSystem} to notify it
57      * that the file system has been touched.
58      * A file system is touched if an operation has been performed on it
59      * which modifies it.
60      * <p>
61      * <b>Warning:</b> The write lock of this controller must
62      * be acquired while this method is called!
63      */

64     protected void touch() throws IOException {
65         assert writeLock().isLocked();
66         setScheduled(true);
67     }
68
69     protected final ArchiveFileSystem getFileSystem() {
70         return fileSystemState.getFileSystem();
71     }
72
73     protected final void setFileSystem(ArchiveFileSystem fileSystem) {
74         fileSystemState.setFileSystem(fileSystem);
75     }
76
77     private static abstract class FileSystemState {
78         public abstract ArchiveFileSystem getFileSystem(boolean autoCreate)
79         throws IOException;
80
81         public ArchiveFileSystem getFileSystem() {
82             return null;
83         }
84
85         public abstract void setFileSystem(ArchiveFileSystem fileSystem);
86     } // interface FileSystemState
87

88     private class ResetFileSystem extends FileSystemState {
89         public ArchiveFileSystem getFileSystem(final boolean autoCreate)
90         throws IOException {
91             try {
92                 class Mounter implements IORunnable {
93                     public void run() throws IOException {
94                         // Check state again: Another thread may have changed
95
// it while we released all read locks in order to
96
// acquire the write lock!
97
if (fileSystemState == ResetFileSystem.this) {
98                             mount(autoCreate);
99                             assert fileSystemState instanceof MountedFileSystem;
100                         } else {
101                             assert fileSystemState != null;
102                             assert !(fileSystemState instanceof ResetFileSystem);
103                         }
104                     }
105                 } // class Mounter
106

107                 runWriteLocked(new Mounter());
108             } catch (FalsePositiveException falsePositive) {
109                 // Catch and cache exceptions for uncacheable false positives.
110
// The state is reset when File.delete() is called on the false
111
// positive archive file or File.update() or File.umount().
112
// This is an important optimization: When hitting a false
113
// positive archive file, a client application might perform
114
// a lot of tests on it (isDirectory(), isFile(), exists(),
115
// length(), etc). If the exception were not cached, each call
116
// would run the file system initialization again, only to
117
// result in another instance of the same exception type again.
118
// Note that it is important to cache the exceptions for
119
// cacheable false positives only: Otherwise, side effects
120
// of the archive driver may not be accounted for.
121
if (falsePositive.isCacheable())
122                     fileSystemState = new FalsePositiveFileSystem(falsePositive);
123                 throw falsePositive;
124             }
125
126             assert fileSystemState != this;
127             // DON'T just call fileSystemState.getFileSystem()!
128
// This would return null if fileSystemState is an instance of
129
// FalsePositiveFileSystem.
130
return fileSystemState.getFileSystem(autoCreate);
131         }
132
133         public void setFileSystem(ArchiveFileSystem fileSystem) {
134             // Passing in null may happen by reset().
135
if (fileSystem != null)
136                 fileSystemState = new MountedFileSystem(fileSystem);
137         }
138     } // class ResetFileSystem
139

140     private class MountedFileSystem extends FileSystemState {
141         private final ArchiveFileSystem fileSystem;
142
143         private MountedFileSystem(final ArchiveFileSystem fileSystem) {
144             assert fileSystem != null : "It's illegal to use this state with null as the file system!";
145             this.fileSystem = fileSystem;
146         }
147
148         public ArchiveFileSystem getFileSystem(boolean autoCreate)
149         throws IOException {
150             return fileSystem;
151         }
152
153         public ArchiveFileSystem getFileSystem() {
154             return fileSystem;
155         }
156
157         public void setFileSystem(ArchiveFileSystem fileSystem) {
158             assert fileSystem == null : "It's illegal to assign a file system to an archive controller which already has its file system mounted!";
159             fileSystemState = new ResetFileSystem();
160         }
161     } // class MountedFileSystem
162

163     private class FalsePositiveFileSystem extends FileSystemState {
164         private final IOException exception;
165
166         private FalsePositiveFileSystem(final IOException exception) {
167             assert exception != null : "It's illegal to use this state with null as the IOException!";
168             this.exception = exception;
169         }
170
171         public ArchiveFileSystem getFileSystem(boolean autoCreate)
172         throws IOException {
173             throw exception;
174         }
175
176         public void setFileSystem(ArchiveFileSystem fileSystem) {
177             assert fileSystem == null : "It's illegal to assign a file system to an archive controller for a false positive archive file!";
178             fileSystemState = new ResetFileSystem();
179         }
180     } // class FalsePositiveFileSystem
181

182     /**
183      * Mounts the virtual file system from the target file.
184      * This method is called <em>while</em> the write lock to mount the file
185      * system for this controller is acquired.
186      * <p>
187      * Upon normal termination, this method is expected to have called
188      * {@link setFileSystem} to assign the fully initialized file system
189      * to this controller.
190      * Other than this, the method must not have any side effects on the
191      * state of this class or its super class.
192      * It may, however, have side effects on the state of the sub class.
193      *
194      * @param autoCreate If the archive file does not exist and this
195      * parameter is <code>true</code>, a new file system with only a
196      * root directory is created with its last modification time set
197      * to the system's current time.
198      * @return A valid archive file system - <code>null</code> is not permitted.
199      * @throws FalsePositiveException
200      * @throws IOException On any other I/O related issue with the target file
201      * or the target file of any enclosing archive file's controller.
202      */

203     protected abstract void mount(boolean autoCreate)
204     throws IOException;
205
206     protected void reset() throws IOException {
207         setFileSystem(null);
208     }
209
210     public final ArchiveEntry createArchiveEntry(String JavaDoc name, ArchiveEntry blueprint)
211     throws CharConversionException {
212         return getDriver().createArchiveEntry(this, name, blueprint);
213     }
214 }
215
Popular Tags