Using the JXplorer Threading Model
How to Make your Application Unusable
Directory operations occur over networks, which can be slow, and may require processing on a busy directory server, which may also be slow. If your application is waiting on the results of a directory operation, it can 'freeze up'. The GUI doesn't respond to mouse clicks, or may not repaint properly. The user will assume the program has crashed, and may kill it manually. The worst that can occur is when the directory server crashes or your network dies, and the browser freezes until the connection times out.
Making your editor multi-threaded
Fortunately it is relatively easy to make a Java application multi-threaded, so, as an editor writer, it would not be much work to put your directory code in a different thread. However this isn't necessary using JXplorer's pluggable editors, since JX is already multi-threaded. In fact, pluggable editors written using the techniques of the last chapter are already multi-threaded, and will not hang.
Problems arise when you need to write code that is conditional on the results of a directory operation. For example, you may want to create a particular entry if it doesn't exist, or modify it if it does. The way to do this in a pluggable entry editor is to either use the 'DataListener' interface (for simple operations), or to pass an extended 'DataQuery' object (for more complex tasks). This chapter will show you how to use the simpler DataListener interface, while the next chapter explains how to use the general purpose 'DataQuery' method to run arbitrary code.
Using the DataListener interface
The displayEntry() method of DataSink contains the entry to display,
and a 'DataSource'. The DataSource can be used to carry out various
directory operations, such as getEntry()
or copyTree()
.
However, since these operations occur in another thread, rather than
returning data or a success code immediately, they return a 'DataQuery'
object.
The DataQuery object is used to communicate with the connection thread.
If you need to know what happens to the operation (and you may not -
for example the results of a 'copyTree()
' operation, or any errors, will
be displayed by the browser without any intervention) you can use the
DataQuery object. This is done by registering a 'DataListener' with the
DataQuery object, in the same way as an 'ActionListener' might be
registered with a button. When the DataQuery has completed (either
successfully, or with an error) your DataListener will be called with
the result.
The DataListener only has one method returning one object - the original
DataQuery! However in this method you are guaranteed that the DataQuery
has finished, and is ready for reading. So within the DataListener method
you can use all the DataQuery methods such as hasException()
or getResult()
.
This may seem a bit of effort to go to, but is in fact fairly straightforward. Attempting to read the DataQuery object immediately can be very dangerous - in the best possible case it will block the current thread until the data is ready, in the worst case it will attempt to block the thread making the directory connection and will throw an exception in order to avoid thread 'deadlock'.
The following code snippet (using an anonymous inner class) is a quick example of how to use a DataListener:
public class MyPluggableEditor extends BasicPluggableEditor
{
...
...
public void displayEntry(DXEntry entry, DataSource myDataSource)
{
...
...
DataQuery readQuery = myDataSource.getEntry(
new DN("cn=fred,ou=R&D,o=CA"));
...
readQuery.addDataListener(new DataListener()
{
public void dataReady(DataQuery query)
{
if (query.hasException())
{
System.err.println("couldn't read entry " +
query.getDN()+"\nexception= "+query.getException());
// prevent the browser also displaying the error.
query.squelch();
}
else
{
System.out.println("read entry " + query.getEntry());
}
}
});
...
}
...
}
Other DataListeners
Your pluggable editor isn't the only Data Listener. The JXplorer browser tree is another, and it will respond to any data operations that occur, showing error messages or changing the tree as appropriate.
Most of the time this is what you want. However, if you'd prefer to keep your
operations private (maybe you're handling your own exceptions, or you're hiding a
sub-level of the directory from the user) you can 'squelch()
' the
query, preventing any other listeners from processing the query.
Complex Directory Interactions
Sometimes the above still isn't enough - you need to make a directory request in your pluggable editor, and then, depending on the result, you need to make further requests.
There are a number of ways of doing this. One method
is to use the getBroker()
DataQuery method. This gives you raw access
to the directory connection methods.
This method falls down if the initial directory action is non-standard, and may
also be a little clumsy if you have a single unit of work to do, that could be nicely
executed in one place. In this case, the best thing to do may be to extend the
DataQuery class itself, and pass an 'extended DataQuery' to the DataSource, using
DataSource's 'extendedRequest()
' method. How to do this is covered
in the next chapter.