KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > storagemanager > StorageManager


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.osgi.storagemanager;
12
13 import java.io.*;
14 import java.security.AccessController JavaDoc;
15 import java.util.*;
16 import org.eclipse.core.runtime.internal.adaptor.*;
17 import org.eclipse.osgi.framework.internal.reliablefile.*;
18 import org.eclipse.osgi.framework.util.SecureAction;
19
20 /**
21  * Storage managers provide a facility for tracking the state of a group of files having
22  * relationship with each others and being updated by several entities at the same time.
23  * The typical usecase is in shared configuration data areas.
24  * <p>
25  * The facilities provided here are cooperative. That is, all participants must
26  * agree to the conventions and to calling the given API. There is no capacity
27  * to enforce these conventions or prohibit corruption.
28  * </p>
29  * <p>
30  * Clients can not extend this class
31  * </p>
32  * <p>
33  * Example
34  * <pre>
35  * //Open the storage manager
36  * org.eclipse.osgi.storagemanager.StorageManager cacheStorageManager = new StorageManager("d:/sharedFolder/bar/", false); //$NON-NLS-1$
37  * try {
38  * cacheStorageManager.open(true);
39  * } catch (IOException e) {
40  * // Ignore the exception. The registry will be rebuilt from source.
41  * }
42  *
43  * //To read from a file
44  * java.io.File fileA = cacheStorageManager.lookup("fileA", false));
45  * java.io.File fileB = cacheStorageManager.lookup("fileB", false));
46  * //Do the reading code
47  * new java.io.FileOutputStream(fileA);
48  *
49  * //To write in files
50  * cacheStorageManager.add("fileC"); //add the file to the filemanager (in this case we assume it is not already here)
51  * cacheStorageManager.add("fileD");
52  *
53  * // The file is never written directly into the file name, so we create some temporary file
54  * java.io.File fileC = cacheStorageManager.createTempFile("fileC");
55  * java.io.File fileD = cacheStorageManager.createTempFile("fileD");
56  *
57  * //Do the actual writing here...
58  *
59  * //Finally update the storagemanager with the actual file to manage.
60  * cacheStorageManager.update(new String[] {"fileC", "fileD"}, new String[] {fileC.getName(), fileD.getName()};
61  *
62  * //Close the file manager at the end
63  * cacheStorageManager.close();
64  * </pre>
65  * </p>
66  * <p>
67  * Implementation details <br>
68  * The following implementation details are provided to help with understanding the
69  * behavior of this class.
70  * The general principle is to maintain a table which maps user-level file names
71  * onto an actual disk files. If a file needs to be modified,
72  * it is stored into a new file. The old content is not removed from disk until all entities
73  * have closed there instance of the storage manager.
74  * Once the instance has been created, open() must be called before performing any other operation.
75  * On open the storage manager obtains a snapshot of the current managed files contents. If an
76  * entity updates a managed file, the storage manager will save the content for that instance of the
77  * storage manager, all other storage manager instances will still have access to that managed file's
78  * content as it was when the instance was first opened.
79  * </p>
80  * @since 3.2
81  */

82
83 // Note the implementation of this class originated from the following deprecated classes:
84
// /org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/FileManager.java
85
// /org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/StreamManager.java
86
public final class StorageManager {
87     private static final int FILETYPE_STANDARD = 0;
88     private static final int FILETYPE_RELIABLEFILE = 1;
89     private static final SecureAction secure = (SecureAction) AccessController.doPrivileged(SecureAction.createSecureAction());
90     private static boolean tempCleanup = Boolean.valueOf(secure.getProperty("osgi.embedded.cleanTempFiles")).booleanValue(); //$NON-NLS-1$
91
private static boolean openCleanup = Boolean.valueOf(secure.getProperty("osgi.embedded.cleanupOnOpen")).booleanValue(); //$NON-NLS-1$
92
private static final String JavaDoc MANAGER_FOLDER = ".manager"; //$NON-NLS-1$
93
private static final String JavaDoc TABLE_FILE = ".fileTable"; //$NON-NLS-1$
94
private static final String JavaDoc LOCK_FILE = ".fileTableLock"; //$NON-NLS-1$
95
private static final int MAX_LOCK_WAIT = 5000; // 5 seconds
96

97     private class Entry {
98         int readId;
99         int writeId;
100         int fileType;
101
102         Entry(int readId, int writeId, int type) {
103             this.readId = readId;
104             this.writeId = writeId;
105             this.fileType = type;
106         }
107
108         int getReadId() {
109             return readId;
110         }
111
112         int getWriteId() {
113             return writeId;
114         }
115
116         int getFileType() {
117             return fileType;
118         }
119
120         void setReadId(int value) {
121             readId = value;
122         }
123
124         void setWriteId(int value) {
125             writeId = value;
126         }
127
128         void setFileType(int type) {
129             fileType = type;
130         }
131     }
132
133     private File base; //The folder managed
134
private File managerRoot; //The folder that will contain all the file related to the functionning of the manager (typically a subdir of base)
135

136     private String JavaDoc lockMode = null;
137     private File tableFile = null;
138     private File lockFile; // The lock file for the table (this file is the same for all the instances)
139
private Locker locker; // The locker for the lock
140

141     private File instanceFile = null; //The file reprensenting the running instance. It is created when the table file is read.
142
private Locker instanceLocker = null; //The locker for the instance file.
143
private boolean readOnly; // Whether this storage manager is in read-only mode
144
private boolean open; // Whether this storage manager is open for use
145

146     // locking related fields
147
private int tableStamp = -1;
148
149     private Properties table = new Properties();
150     private boolean useReliableFiles = Boolean.valueOf(secure.getProperty("osgi.useReliableFiles")).booleanValue(); //$NON-NLS-1$
151

152     /**
153      * Returns a new storage manager for the area identified by the given base
154      * directory.
155      *
156      * @param base the directory holding the files to be managed
157      * @param lockMode the lockMode to use for the storage manager. It can have one the 3 values: none, java.io, java.nio
158      * and also supports null in which case the lock strategy will be the global one.
159      */

160     public StorageManager(File base, String JavaDoc lockMode) {
161         this(base, lockMode, false);
162     }
163
164     /**
165      * Returns a new storage manager for the area identified by the given base
166      * directory.
167      *
168      * @param base the directory holding the files to be managed
169      * @param lockMode the lockMode to use for the storage manager. It can have one the 3 values: none, java.io, java.nio
170      * and also supports null in which case the lock strategy will be the global one.
171      * @param readOnly true if the managed files are read-only
172      */

173     public StorageManager(File base, String JavaDoc lockMode, boolean readOnly) {
174         this.base = base;
175         this.lockMode = lockMode;
176         this.managerRoot = new File(base, MANAGER_FOLDER);
177         if (!readOnly)
178             this.managerRoot.mkdirs();
179         this.tableFile = new File(managerRoot, TABLE_FILE);
180         this.lockFile = new File(managerRoot, LOCK_FILE);
181         this.readOnly = readOnly;
182         open = false;
183     }
184
185     private void initializeInstanceFile() throws IOException {
186         if (instanceFile != null || readOnly)
187             return;
188         this.instanceFile = File.createTempFile(".tmp", ".instance", managerRoot); //$NON-NLS-1$//$NON-NLS-2$
189
this.instanceFile.deleteOnExit();
190         instanceLocker = BasicLocation.createLocker(instanceFile, lockMode);
191         instanceLocker.lock();
192     }
193
194     private String JavaDoc getAbsolutePath(String JavaDoc file) {
195         return new File(base, file).getAbsolutePath();
196     }
197
198     /**
199      * Add the given managed file name to the list of files managed by this manager.
200      *
201      * @param managedFile name of the file to manage
202      * @throws IOException if there are any problems adding the given file name to the manager
203      */

204     public void add(String JavaDoc managedFile) throws IOException {
205         add(managedFile, FILETYPE_STANDARD);
206     }
207
208     /* (non-Javadoc
209      * Add the given file name to the list of files managed by this manager.
210      *
211      * @param managedFile name of the file to manage.
212      * @param fileType the file type.
213      * @throws IOException if there are any problems adding the given file to the manager
214      */

215     private void add(String JavaDoc managedFile, int fileType) throws IOException {
216         if (!open)
217             throw new IOException(EclipseAdaptorMsg.fileManager_notOpen);
218         if (readOnly)
219             throw new IOException(EclipseAdaptorMsg.fileManager_illegalInReadOnlyMode);
220         if (!lock(true))
221             throw new IOException(EclipseAdaptorMsg.fileManager_cannotLock);
222         try {
223             updateTable();
224             Entry entry = (Entry) table.get(managedFile);
225             if (entry == null) {
226                 entry = new Entry(0, 1, fileType);
227                 table.put(managedFile, entry);
228                 // if this managed file existed before, ensure there is not an old
229
// version on the disk to avoid name collisions. If version found,
230
// us the oldest generation+1 for the write ID.
231
int oldestGeneration = findOldestGeneration(managedFile);
232                 if (oldestGeneration != 0)
233                     entry.setWriteId(oldestGeneration + 1);
234                 save();
235             } else {
236                 if (entry.getFileType() != fileType) {
237                     entry.setFileType(fileType);
238                     updateTable();
239                     save();
240                 }
241             }
242         } finally {
243             release();
244         }
245     }
246
247     /* (non-Javadoc)
248      * Find the oldest generation of a file still available on disk
249      * @param file the file from which to obtain the oldest generation.
250      * @return the oldest generation of the file or 0 if the file does
251      * not exist.
252      */

253     private int findOldestGeneration(String JavaDoc managedFile) {
254         String JavaDoc[] files = base.list();
255         int oldestGeneration = 0;
256         if (files != null) {
257             String JavaDoc name = managedFile + '.';
258             int len = name.length();
259             for (int i = 0; i < files.length; i++) {
260                 if (!files[i].startsWith(name))
261                     continue;
262                 try {
263                     int generation = Integer.parseInt(files[i].substring(len));
264                     if (generation > oldestGeneration)
265                         oldestGeneration = generation;
266                 } catch (NumberFormatException JavaDoc e) {
267                     continue;
268                 }
269             }
270         }
271         return oldestGeneration;
272     }
273
274     /**
275      * Update the given managed files with the content in the given source files.
276      * The managedFiles is a list of managed file names which are currently managed.
277      * If a managed file name is not currently managed it will be added as a
278      * managed file for this storage manager.
279      * The sources are absolute (or relative to the current working directory)
280      * file paths containing the new content for the corresponding managed files.
281      *
282      * @param managedFiles the managed files to update
283      * @param sources the new content for the managed files
284      * @throws IOException if there are any problems updating the given managed files
285      */

286     public void update(String JavaDoc[] managedFiles, String JavaDoc[] sources) throws IOException {
287         if (!open)
288             throw new IOException(EclipseAdaptorMsg.fileManager_notOpen);
289         if (readOnly)
290             throw new IOException(EclipseAdaptorMsg.fileManager_illegalInReadOnlyMode);
291         if (!lock(true))
292             throw new IOException(EclipseAdaptorMsg.fileManager_cannotLock);
293         try {
294             updateTable();
295             int[] originalReadIDs = new int[managedFiles.length];
296             boolean error = false;
297             for (int i = 0; i < managedFiles.length; i++) {
298                 originalReadIDs[i] = getId(managedFiles[i]);
299                 if (!update(managedFiles[i], sources[i]))
300                     error = true;
301             }
302             if (error) {
303                 // restore the original readIDs to avoid inconsistency for this group
304
for (int i = 0; i < managedFiles.length; i++) {
305                     Entry entry = (Entry) table.get(managedFiles[i]);
306                     entry.setReadId(originalReadIDs[i]);
307                 }
308                 throw new IOException(EclipseAdaptorMsg.fileManager_updateFailed);
309             }
310             save(); //save only if no errors
311
} finally {
312             release();
313         }
314     }
315
316     /**
317      * Returns a list of all the managed files currently being managed.
318      *
319      * @return the names of the managed files
320      */

321     public String JavaDoc[] getManagedFiles() {
322         if (!open)
323             return null;
324         Set set = table.keySet();
325         String JavaDoc[] keys = (String JavaDoc[]) set.toArray(new String JavaDoc[set.size()]);
326         String JavaDoc[] result = new String JavaDoc[keys.length];
327         for (int i = 0; i < keys.length; i++)
328             result[i] = new String JavaDoc(keys[i]);
329         return result;
330     }
331
332     /**
333      * Returns the directory containing the files being managed by this storage
334      * manager.
335      *
336      * @return the directory containing the managed files
337      */

338     public File getBase() {
339         return base;
340     }
341
342     /**
343      * Returns the current numeric id (appendage) of the given managed file.
344      * <code>managedFile + "." + getId(target)</code>. A value of -1 is returned
345      * if the given name is not managed.
346      *
347      * @param managedFile the name of the managed file
348      * @return the id of the managed file
349      */

350     public int getId(String JavaDoc managedFile) {
351         if (!open)
352             return -1;
353         Entry entry = (Entry) table.get(managedFile);
354         if (entry == null)
355             return -1;
356         return entry.getReadId();
357     }
358
359     /**
360      * Returns if readOnly state this storage manager is using.
361      *
362      * @return if this storage manager update state is read-only.
363      */

364     public boolean isReadOnly() {
365         return readOnly;
366     }
367
368     /* (non-Javadoc)
369      * Attempts to lock the state of this manager and returns <code>true</code>
370      * if the lock could be acquired.
371      * <p>
372      * Locking a manager is advisory only. That is, it does not prevent other
373      * applications from modifying the files managed by this manager.
374      * </p>
375      *
376      * @exception IOException
377      * if there was an unexpected problem while acquiring the
378      * lock.
379      */

380     private boolean lock(boolean wait) throws IOException {
381         if (readOnly)
382             return false;
383         if (locker == null) {
384             locker = BasicLocation.createLocker(lockFile, lockMode);
385             if (locker == null)
386                 throw new IOException(EclipseAdaptorMsg.fileManager_cannotLock);
387         }
388         boolean locked = locker.lock();
389         if (locked || !wait)
390             return locked;
391         //Someone else must have the directory locked, but they should release it quickly
392
long start = System.currentTimeMillis();
393         while (true) {
394             try {
395                 Thread.sleep(200); // 5x per second
396
} catch (InterruptedException JavaDoc e) {/*ignore*/
397             }
398             locked = locker.lock();
399             if (locked)
400                 return true;
401             // never wait longer than 5 seconds
402
long time = System.currentTimeMillis() - start;
403             if (time > MAX_LOCK_WAIT)
404                 return false;
405         }
406     }
407
408     /**
409      * Returns the actual file location to use when reading the given managed file.
410      * A value of <code>null</code> can be returned if the given managed file name is not
411      * managed and add is set to false.
412      * <p>
413      * The returned file should be considered read-only. Any updates to the content of this
414      * file should be done using {@link #update(String[], String[])}.
415      *
416      * @param managedFile the managed file to lookup
417      * @param add indicate whether the managed file name should be added to the manager if
418      * it is not already managed.
419      * @throws IOException if the add flag is set to true and the addition of the managed file failed
420      * @return the absolute file location to use for the given managed file or
421      * <code>null</code> if the given managed file is not managed
422      */

423     public File lookup(String JavaDoc managedFile, boolean add) throws IOException {
424         if (!open)
425             throw new IOException(EclipseAdaptorMsg.fileManager_notOpen);
426         Entry entry = (Entry) table.get(managedFile);
427         if (entry == null) {
428             if (add) {
429                 add(managedFile);
430                 entry = (Entry) table.get(managedFile);
431             } else {
432                 return null;
433             }
434         }
435         return new File(getAbsolutePath(managedFile + '.' + entry.getReadId()));
436     }
437
438     private boolean move(String JavaDoc source, String JavaDoc managedFile) {
439         File original = new File(source);
440         File targetFile = new File(managedFile);
441         // its ok if the original does not exist. The table entry will capture
442
// that fact. There is no need to put something in the filesystem.
443
if (!original.exists() || targetFile.exists())
444             return false;
445         return original.renameTo(targetFile);
446     }
447
448     /**
449      * Saves the state of the storage manager and releases any locks held.
450      */

451     private void release() {
452         if (locker == null)
453             return;
454         locker.release();
455     }
456
457     /**
458      * Removes the given managed file from management by this storage manager.
459      *
460      * @param managedFile the managed file to remove
461      * @throws IOException if an error occured removing the managed file
462      */

463     public void remove(String JavaDoc managedFile) throws IOException {
464         if (!open)
465             throw new IOException(EclipseAdaptorMsg.fileManager_notOpen);
466         if (readOnly)
467             throw new IOException(EclipseAdaptorMsg.fileManager_illegalInReadOnlyMode);
468         // The removal needs to be done eagerly, so the value is effectively removed from the disktable.
469
// Otherwise, an updateTable() caused by an update(,) could cause the file to readded to the local table.
470
if (!lock(true))
471             throw new IOException(EclipseAdaptorMsg.fileManager_cannotLock);
472         try {
473             updateTable();
474             table.remove(managedFile);
475             save();
476         } finally {
477             release();
478         }
479     }
480
481     private void updateTable() throws IOException {
482         int stamp;
483         stamp = ReliableFile.lastModifiedVersion(tableFile);
484         if (stamp == tableStamp || stamp == -1)
485             return;
486         Properties diskTable = new Properties();
487         try {
488             InputStream input;
489             input = new ReliableFileInputStream(tableFile);
490             try {
491                 diskTable.load(input);
492             } finally {
493                 input.close();
494             }
495         } catch (IOException e) {
496             throw e; // rethrow the exception, we have nothing to add here
497
}
498         tableStamp = stamp;
499         for (Enumeration e = diskTable.keys(); e.hasMoreElements();) {
500             String JavaDoc file = (String JavaDoc) e.nextElement();
501             String JavaDoc value = diskTable.getProperty(file);
502             if (value != null) {
503                 Entry entry = (Entry) table.get(file);
504                 // check front of value for ReliableFile
505
int id;
506                 int fileType;
507                 int idx = value.indexOf(',');
508                 if (idx != -1) {
509                     id = Integer.parseInt(value.substring(0, idx));
510                     fileType = Integer.parseInt(value.substring(idx + 1));
511                 } else {
512                     id = Integer.parseInt(value);
513                     fileType = FILETYPE_STANDARD;
514                 }
515                 if (entry == null) {
516                     table.put(file, new Entry(id, id + 1, fileType));
517                 } else {
518                     entry.setWriteId(id + 1);
519                     //don't change type
520
}
521             }
522         }
523     }
524
525     /*
526      * This method should be called while the manager is locked.
527      */

528     private void save() throws IOException {
529         if (readOnly)
530             return;
531         // if the table file has change on disk, update our data structures then
532
// rewrite the file.
533
updateTable();
534
535         Properties props = new Properties();
536         for (Enumeration e = table.keys(); e.hasMoreElements();) {
537             String JavaDoc file = (String JavaDoc) e.nextElement();
538             Entry entry = (Entry) table.get(file);
539             String JavaDoc value;
540             if (entry.getFileType() != FILETYPE_STANDARD) {
541                 value = Integer.toString(entry.getWriteId() - 1) + ',' + //In the table we save the write number - 1, because the read number can be totally different.
542
Integer.toString(entry.getFileType());
543             } else {
544                 value = Integer.toString(entry.getWriteId() - 1); //In the table we save the write number - 1, because the read number can be totally different.
545
}
546             props.put(file, value);
547         }
548         ReliableFileOutputStream fileStream = new ReliableFileOutputStream(tableFile);
549         try {
550             boolean error = true;
551             try {
552                 props.store(fileStream, "safe table"); //$NON-NLS-1$
553
fileStream.close();
554                 error = false;
555             } finally {
556                 if (error)
557                     fileStream.abort();
558             }
559         } catch (IOException e) {
560             throw new IOException(EclipseAdaptorMsg.fileManager_couldNotSave);
561         }
562         tableStamp = ReliableFile.lastModifiedVersion(tableFile);
563     }
564
565     private boolean update(String JavaDoc managedFile, String JavaDoc source) throws IOException {
566         Entry entry = (Entry) table.get(managedFile);
567         if (entry == null)
568             add(managedFile);
569         int newId = entry.getWriteId();
570         // attempt to rename the file to the next generation
571
boolean success = move(getAbsolutePath(source), getAbsolutePath(managedFile) + '.' + newId);
572         if (!success) {
573             //possible the next write generation file exists? Lets determine the largest
574
//generation number, then use that + 1.
575
newId = findOldestGeneration(managedFile) + 1;
576             success = move(getAbsolutePath(source), getAbsolutePath(managedFile) + '.' + newId);
577         }
578         if (!success)
579             return false;
580         // update the entry. read and write ids should be the same since
581
// everything is in sync
582
entry.setReadId(newId);
583         entry.setWriteId(newId + 1);
584         return true;
585     }
586
587     /**
588      * This methods remove all the temporary files that have been created by the storage manager.
589      * This removal is only done if the instance of eclipse calling this method is the last instance using this storage manager.
590      * @throws IOException
591      */

592     private void cleanup() throws IOException {
593         if (readOnly)
594             return;
595         //Lock first, so someone else can not start while we're in the middle of cleanup
596
if (!lock(true))
597             throw new IOException(EclipseAdaptorMsg.fileManager_cannotLock);
598         try {
599             //Iterate through the temp files and delete them all, except the one representing this storage manager.
600
String JavaDoc[] files = managerRoot.list();
601             if (files != null) {
602                 for (int i = 0; i < files.length; i++) {
603                     if (files[i].endsWith(".instance") && instanceFile != null && !files[i].equalsIgnoreCase(instanceFile.getName())) { //$NON-NLS-1$
604
Locker tmpLocker = BasicLocation.createLocker(new File(managerRoot, files[i]), lockMode);
605                         if (tmpLocker.lock()) {
606                             //If I can lock it is a file that has been left behind by a crash
607
tmpLocker.release();
608                             new File(managerRoot, files[i]).delete();
609                         } else {
610                             tmpLocker.release();
611                             return; //The file is still being locked by somebody else
612
}
613                     }
614                 }
615             }
616
617             //If we are here it is because we are the last instance running. After locking the table and getting its latest content, remove all the backup files and change the table
618
updateTable();
619             Collection managedFiles = table.entrySet();
620             for (Iterator iter = managedFiles.iterator(); iter.hasNext();) {
621                 Map.Entry fileEntry = (Map.Entry) iter.next();
622                 String JavaDoc fileName = (String JavaDoc) fileEntry.getKey();
623                 Entry info = (Entry) fileEntry.getValue();
624                 if (info.getFileType() == FILETYPE_RELIABLEFILE) {
625                     ReliableFile.cleanupGenerations(new File(base, fileName));
626                 } else {
627                     //Because we are cleaning up, we are giving up the values from our table, and we must delete all the files that are not referenced by the table
628
String JavaDoc readId = Integer.toString(info.getWriteId() - 1);
629                     deleteCopies(fileName, readId);
630                 }
631             }
632
633             if (tempCleanup) {
634                 files = base.list();
635                 if (files != null) {
636                     for (int i = 0; i < files.length; i++) {
637                         if (files[i].endsWith(ReliableFile.tmpExt)) {
638                             new File(base, files[i]).delete();
639                         }
640                     }
641                 }
642             }
643         } catch (IOException e) {
644             //If the exception comes from the updateTable(), there has been a problem in reading the file.
645
//If an exception occured in the save, then the table won't be up to date!
646
throw e;
647         } finally {
648             release();
649         }
650     }
651
652     private void deleteCopies(String JavaDoc fileName, String JavaDoc exceptionNumber) {
653         String JavaDoc notToDelete = fileName + '.' + exceptionNumber;
654         String JavaDoc[] files = base.list();
655         if (files == null)
656             return;
657         for (int i = 0; i < files.length; i++) {
658             if (files[i].startsWith(fileName + '.') && !files[i].equals(notToDelete))
659                 new File(base, files[i]).delete();
660         }
661     }
662
663     /**
664      * This method declares the storage manager as closed. From thereon, the instance can no longer be used.
665      * It is important to close the manager as it also cleans up old copies of the managed files.
666      */

667     public void close() {
668         if (!open)
669             return;
670         open = false;
671         if (readOnly)
672             return;
673         try {
674             cleanup();
675         } catch (IOException e) {
676             //Ignore and close.
677
}
678         if (instanceLocker != null)
679             instanceLocker.release();
680
681         if (instanceFile != null)
682             instanceFile.delete();
683     }
684
685     /**
686      * This methods opens the storage manager.
687      * This method must be called before any operation on the storage manager.
688      * @param wait indicates if the open operation must wait in case of contention on the lock file.
689      * @throws IOException if an error occured opening the storage manager
690      */

691     public void open(boolean wait) throws IOException {
692         if (openCleanup)
693             cleanup();
694         if (!readOnly) {
695             boolean locked = lock(wait);
696             if (!locked && wait)
697                 throw new IOException(EclipseAdaptorMsg.fileManager_cannotLock);
698         }
699
700         try {
701             initializeInstanceFile();
702             updateTable();
703             open = true;
704         } finally {
705             release();
706         }
707     }
708
709     /**
710      * Creates a new unique empty temporary-file in the storage manager base directory. The file name
711      * must be at least 3 characters. This file can later be used to update a managed file.
712      *
713      * @param file the file name to create temporary file from.
714      * @return the newly-created empty file.
715      * @throws IOException if the file can not be created.
716      * @see #update(String[], String[])
717      */

718     public File createTempFile(String JavaDoc file) throws IOException {
719         if (readOnly)
720             throw new IOException(EclipseAdaptorMsg.fileManager_illegalInReadOnlyMode);
721         File tmpFile = File.createTempFile(file, ReliableFile.tmpExt, base);
722         tmpFile.deleteOnExit();
723         return tmpFile;
724     }
725
726     /**
727      * Returns a managed <code>InputStream</code> for a managed file.
728      * <code>null</code> can be returned if the given name is not managed.
729      *
730      * @param managedFile the name of the managed file to open.
731      * @return an input stream to the managed file or
732      * <code>null</code> if the given name is not managed.
733      * @throws IOException if the content is missing, corrupt or an error occurs.
734      */

735     public InputStream getInputStream(String JavaDoc managedFile) throws IOException {
736         return getInputStream(managedFile, ReliableFile.OPEN_BEST_AVAILABLE);
737     }
738
739     /**
740      * Returns a managed input stream set for the managed file names.
741      * Elements of the returned set may be <code>null</code> if a given name is not managed.
742      * This method should be used for managed file sets which use the output streams returned
743      * by the {@link #getOutputStreamSet(String[])} to save data.
744      *
745      * @param managedFiles the names of the managed files to open.
746      * @return a set input streams to the given managed files.
747      * @throws IOException if the content of one of the managed files is missing, corrupt or an error occurs.
748      */

749     public InputStream[] getInputStreamSet(String JavaDoc[] managedFiles) throws IOException {
750         InputStream[] streams = new InputStream[managedFiles.length];
751         for (int i = 0; i < streams.length; i++)
752             streams[i] = getInputStream(managedFiles[i], ReliableFile.OPEN_FAIL_ON_PRIMARY);
753         return streams;
754     }
755
756     private InputStream getInputStream(String JavaDoc managedFiles, int openMask) throws IOException {
757         if (useReliableFiles) {
758             int id = getId(managedFiles);
759             if (id == -1)
760                 return null;
761             return new ReliableFileInputStream(new File(getBase(), managedFiles), id, openMask);
762         }
763         File lookup = lookup(managedFiles, false);
764         if (lookup == null)
765             return null;
766         return new FileInputStream(lookup);
767     }
768
769     /**
770      * Returns a <code>ManagedOutputStream</code> for a managed file.
771      * Closing the ouput stream will update the storage manager with the
772      * new content of the managed file.
773      *
774      * @param managedFile the name of the managed file to write.
775      * @return a managed output stream for the managed file.
776      * @throws IOException if an error occurs opening the managed file.
777      */

778     public ManagedOutputStream getOutputStream(String JavaDoc managedFile) throws IOException {
779         if (useReliableFiles) {
780             ReliableFileOutputStream out = new ReliableFileOutputStream(new File(getBase(), managedFile));
781             return new ManagedOutputStream(out, this, managedFile, null);
782         }
783         File tmpFile = createTempFile(managedFile);
784         return new ManagedOutputStream(new FileOutputStream(tmpFile), this, managedFile, tmpFile);
785     }
786
787     /**
788      * Returns an array of <code>ManagedOutputStream</code> for a set of managed files.
789      * When all managed output streams in the set have been closed, the storage manager
790      * will be updated with the new content of the managed files.
791      * Aborting any one of the streams will cause the entire content of the set to abort
792      * and be discarded.
793      *
794      * @param managedFiles list of names of the managed file to write.
795      * @return an array of managed output streams respectively of managed files.
796      * @throws IOException if an error occurs opening the managed files.
797      */

798     public ManagedOutputStream[] getOutputStreamSet(String JavaDoc[] managedFiles) throws IOException {
799         int count = managedFiles.length;
800         ManagedOutputStream[] streams = new ManagedOutputStream[count];
801         int idx = 0;
802         try {
803             for (; idx < count; idx++) {
804                 ManagedOutputStream newStream = getOutputStream(managedFiles[idx]);
805                 newStream.setStreamSet(streams);
806                 streams[idx] = newStream;
807             }
808         } catch (IOException e) {
809             // cleanup
810
for (int jdx = 0; jdx < idx; jdx++)
811                 streams[jdx].abort();
812             throw e;
813         }
814         return streams;
815     }
816
817     /* (non-Javadoc)
818      * Instructs this manager to abort and discard a managed output stream.
819      * This method should be used if any errors occur after opening a managed
820      * output stream where the contents should not be saved.
821      * If this output stream is part of a set, all other managed output streams in this set
822      * will also be closed and aborted.
823      * @param out the managed output stream
824      * @see #getOutputStream(String)
825      * @see #getOutputStreamSet(String[])
826      */

827     void abortOutputStream(ManagedOutputStream out) {
828         ManagedOutputStream[] set = out.getStreamSet();
829         if (set == null) {
830             set = new ManagedOutputStream[] {out};
831         }
832         synchronized (set) {
833             for (int idx = 0; idx < set.length; idx++) {
834                 out = set[idx];
835                 if (out.getOutputFile() == null) {
836                     // this is a ReliableFileOutpuStream
837
ReliableFileOutputStream rfos = (ReliableFileOutputStream) out.getOutputStream();
838                     rfos.abort();
839                 } else {
840                     // plain FileOutputStream();
841
if (out.getState() == ManagedOutputStream.ST_OPEN) {
842                         try {
843                             out.getOutputStream().close();
844                         } catch (IOException e) {/*do nothing*/
845                         }
846                     }
847                     out.getOutputFile().delete();
848                 }
849                 out.setState(ManagedOutputStream.ST_CLOSED);
850             }
851         }
852     }
853
854     /* (non-Javadoc)
855      * Close the managed output stream and update the new content to
856      * this manager. If this managed output stream is part of a set, only after closing
857      * all managed output streams in the set will storage manager be updated.
858      *
859      * @param smos the output stream.
860      * @throws IOException if an errors occur.
861      * @see #getOutputStream(String)
862      * @see #getOutputStreamSet(String[])
863      */

864     void closeOutputStream(ManagedOutputStream smos) throws IOException {
865         if (smos.getState() != ManagedOutputStream.ST_OPEN)
866             return;
867         ManagedOutputStream[] streamSet = smos.getStreamSet();
868         if (smos.getOutputFile() == null) {
869             // this is a ReliableFileOutputStream
870
ReliableFileOutputStream rfos = (ReliableFileOutputStream) smos.getOutputStream();
871             // manage file deletes
872
File file = rfos.closeIntermediateFile();
873             smos.setState(ManagedOutputStream.ST_CLOSED);
874             String JavaDoc target = smos.getTarget();
875             if (streamSet == null) {
876                 add(target, StorageManager.FILETYPE_RELIABLEFILE);
877                 update(new String JavaDoc[] {smos.getTarget()}, new String JavaDoc[] {file.getName()});
878                 ReliableFile.fileUpdated(new File(getBase(), smos.getTarget()));
879             }
880         } else {
881             // this is a plain old file output steam
882
OutputStream out = smos.getOutputStream();
883             out.flush();
884             try {
885                 ((FileOutputStream) out).getFD().sync();
886             } catch (SyncFailedException e) {/*ignore*/
887             }
888             out.close();
889             smos.setState(ManagedOutputStream.ST_CLOSED);
890             String JavaDoc target = smos.getTarget();
891             if (streamSet == null) {
892                 add(target, StorageManager.FILETYPE_STANDARD);
893                 update(new String JavaDoc[] {target}, new String JavaDoc[] {smos.getOutputFile().getName()});
894             }
895         }
896
897         if (streamSet != null) {
898             synchronized (streamSet) {
899                 //check all the streams to see if there are any left open....
900
for (int idx = 0; idx < streamSet.length; idx++) {
901                     if (streamSet[idx].getState() == ManagedOutputStream.ST_OPEN)
902                         return; //done
903
}
904                 //all streams are closed, we need to update storage manager
905
String JavaDoc[] targets = new String JavaDoc[streamSet.length];
906                 String JavaDoc[] sources = new String JavaDoc[streamSet.length];
907                 for (int idx = 0; idx < streamSet.length; idx++) {
908                     smos = streamSet[idx];
909                     targets[idx] = smos.getTarget();
910                     File outputFile = smos.getOutputFile();
911                     if (outputFile == null) {
912                         // this is a ReliableFile
913
add(smos.getTarget(), StorageManager.FILETYPE_RELIABLEFILE);
914                         ReliableFileOutputStream rfos = (ReliableFileOutputStream) smos.getOutputStream();
915                         File file = rfos.closeIntermediateFile(); //multiple calls to close() ok
916
sources[idx] = file.getName();
917                         ReliableFile.fileUpdated(new File(getBase(), smos.getTarget()));
918                     } else {
919                         add(smos.getTarget(), StorageManager.FILETYPE_STANDARD);
920                         sources[idx] = outputFile.getName();
921                     }
922                 }
923                 update(targets, sources);
924             }
925         }
926     }
927 }
Popular Tags