# jEdit 2.6 User's Guide

Next, Prev

## Chapter 11. Developing Plugins

# Developing Virtual File Systems

This chapter first explains the workings of jEdit's I/O system in detail. Then, it overviews the APIs allowing plugins to extend it.

## Input/Output Basics

jEdit performs all buffer loading and saving in a separate thread, known as the work thread. It is an instance of the `org.gjt.sp.util.WorkThread` class, and at its simplest, all it does is execute queued requests. A requests can specify if it is to be run in the work thread, or the AWT event dispatch thread. In the former case, the work thread simply executes the request. In the latter, passes it to the `SwingUtilities.invokeLater()` method, which queues it up for execution in the event dispatch thread. The ability to execute requests in the AWT thread may seem redudant, but in fact it is very useful; for example, it is used to update jEdit's GUI after a file load is complete.[1] The work thread is not limited to performing I/O; in fact, plugins can create their own work thread instances and use them for other purposes. However, in this section, we will only cover I/O-related uses of the work thread.

## Buffer Loading and Saving

The `Buffer.load()` method is called when a buffer load is requested by the user; either when a file is opened, or the File>Reload command is invoked. The main purpose of this method is to queue two requests with the I/O work thread; the first loads the buffer, the second performs various post-load "cleanups", such as updating the GUI, setting up a new undo queue, and so on. Because the latter is of little relevance to the plugin VFS developer, it will not be discussed further.

The buffer loading request is an instance of the `org.gjt.sp.jedit.io.IORequest` class. This class first obtains the virtual filesystem used by that buffer (more on how this is determined later). It then calls the `_createInputStream()` method of the filesystem. This method does whatever is necessary to obtain a stream to read from; for example, the "file" VFS simply creates a `FileInputStream`. The "ftp" VFS opens a network connection, logs in, and begins downloading the specified file. `IORequest` then procceeds to read the buffer from this input stream. Although in reality the buffer load process is a bit more complicated than this, but the above should still give you a good idea of how it works.

The `Buffer.save()` method behaves in a similar manner. It also queues up an `IORequest`, which calls `VFS._createOutputStream()` instead of `VFS._createInputStream()`.

Now wait a minute, you say. Why bother with all this `IORequest` stuff? Why doesn't `Buffer.load()` just call the VFS directly? To understand, consider the above more carefully. `Buffer.load()` queues two requests, and returns to its caller. The requests are then run asynchronously, in a different thread. This allows them to be aborted if the user wishes to do so. Views also continue to be repainted.

## The VFSManager Class

    public class VFSManager
    {
            public static void registerVFS(String protocol, VFS vfs);
            public static void waitForRequests();
            public static void runInAWTThread(Runnable run);
            public static void error(Component comp, String message, Object[] args);
    }

TODO

#### VFSManager.registerVFS() method

    import org.gjt.sp.jedit.io.VFS;

``public static void registerVFS`(String protocol, VFS vfs);`

This method registers the specified virtual filesystem instance as being able to handle buffers whose URL protocol is `protocol`. In most cases, you would call this method from your plugin's `start()` method. The following example registers a new instance of the (hypothetical) `ZipVFS` class as being able to handle URLs with protocol "zip:":

    VFSManager.registerVFS("zip",new ZipVFS());

#### VFSManager.waitForRequests() method

``public static void waitForRequests`(void);`

This method blocks the current thread until the work thread finishes executing all pending requests. Note that if you need to be notified when a buffer finishes loading, it is a much better idea to listen for the `BufferUpdate.LOADED` EditBus message than to use this method.

#### VFSManager.runInAWTThread() method

``public static void runInAWTThread`(Runnable run);`

This method adds the specified `Runnable` to the work thread queue. When the request moves to the front of the queue, it will be run in the AWT event dispatch thread. TODO: give examples of this method's usage.

#### VFSManager.error() method

``public static void runInAWTThread`(Component comp, String message, Object[] args);`

This method is exactly the same as `GUIUtilities.error()`, except that it is safe to call it from the work thread.

## The VFS Class

TODO

## Advanced Topics

### Reusing network connections

TODO: FtpVFS reuses the same FtpClient throughout a load or save... describe how this is done with buffer-local properties, and the VFS.load/saveComplete() methods.

### Storing login information

TODO: Again, buffer-local properties are you friend...

### Pre-load sanity checks

TODO: describe why opening an unreadable file doesn't create an empty buffer; the return value of VFS.load() method

### Notes

[1] | Most Swing are not thread safe and can only be called from the AWT thread; attempting to call them from the work thread can lead to race conditions.

Home, Up
Prev: The EditBus
Next: The Activity Log