3. File Objects

Once you have a repository, you need to create a file. This is a two step process. The first step is to register the file. To do this, you define the file's name and pass it to Repository::createFile(), which returns the fileid of the created file. At this point, the file is registered in the system, but there's nothing actually in the file system.

You operate on files in the repository using JW::DMS::File objects. These are standard Ruby File objects which are extended to incorporate the necessary locking, transaction, and path management facilities used in the system. You open a DMS file like you do a normal Ruby file, but rather than specifying a file name, you provide the fileid. The whole process of creating and opening a file is as follows:


  fileid = dms.createFile('README.TXT')
  file = dms.openFile(fileid, 'w')

The second argument to openFile() is the file mode, just like the standard Ruby File::open() call, where you specify read/write access. Depending on the mode, the respository will obtain the requisite locks. The locking implementation simply ensures that there can only be one File object open with write access to a file at a time. There are two basic locks: shared and exclusive. File objects open in read obtain a shared lock. A file object opening for write obtains an exclusive lock. Thus, there can be multiple readers, but only one writer.

Note

The locking system is implemented using PostgreSQL advisory locks. Rather than using actual filesystem locks, all file access is coordinated from the database. The advantage here is that if an exception is encountered, any locks can be easily cleared by a single call to the database, whereas it might be more difficult to track down any particular file locks which may have been issued. All that is required for DMS is to call Repository.unlockAll(), which releases all locks acquired by a given Repository object. The other advantage is that since the locking implementation is in PostgreSQL, you can host the file store anywhere (including distributed file systems like NFS and Samba) and locking will work just as reliably.

Once you obtain the file object, you operate on it exactly as you would a Ruby file. The only difference is that when you call close(), the File object will update the system metadata fields (e.g. size, hash, mtime, etc.). before it closes the actual file system handle.