29.3.10. Sending signals (to plug-ins)

BASE has a simple system for sending signals between different parts of a system. This signalling system was initially developed to be able to kill plug-ins that a user for some reason wanted to abort. The signalling system as such is not limited to this and it can be used for other purposes as well. Signals can of course be handled internally in a single JVM but also sent externally to other JVM:s running on the same or a different computer. The transport mechanism for signals is decoupled from the actual handling of them. If you want to, you could implement a signal transporter that sends signal as emails and the target plug-in would never know.

The remainder of this section will focus mainly on the sending and transportation of signals. For more information about handling signals on the receiving end, see Section 26.2.3, “Abort a running a plug-in”.

Diagram of classes and methods

Figure 29.21. The signalling system

The signalling system

The signalling system is rather simple. An object that wish to receieve signals must implement the SignalTarget. It's only method is getSignalHandler(). A SignalHandler is an object that knows what to do when a signal is delivered to it. The target object may implement the SignalHandler itself or use one of the existing handlers.

The difficult part here is to be aware that a signal is usually delivered by a separate thread. The target object must be aware of this and know how to handle multiple threads. As an example we can use the ThreadSignalHandler which simply calls Thread.interrupt() to deliver a signal. The target object that uses this signal handler must know that it should check Thread.interrupted() at regular intervals from the worker thread. If that method returns true, it means that the ABORT signal has been delivered and the main thread should clean up and exit as soon as possible.

Even if a signal handler could be given directly to the party that may be interested in sending a signal to the target this is not recommended. This would only work when sending signals within the same virtual machine. The signalling system includes SignalTransporter and SignalReceiver objects that are used to decouple the sending of signals with the handling of signals. The implementation usually comes in pairs, for example SocketSignalTransporters and SocketSignalReceiver.

Setting up the transport mechanism is usually a system responsibility. Only the system know what kind of transport that is appropriate for it's current setup. Ie. should signals be delievered by TCP/IP sockets, only internally, or should a delivery mechanism based on web services be implemented? If a system wants to receive signals it must create an appropriate SignalReceiver object. Within BASE the internal job queue set up it's own signalling system that can be used to send signals (eg. kill) running jobs. The job agents do the same but uses a different implementation. See the section called “Internal job queue section” for more information about how to configure the internal job queue's signal receiver. In both cases, there is only one signal receiver instance active in the system.

Let's take the internal job queue as an example. Here is how it works:

  • When the internal job queue is started, it will also create a signal receiver instance according to the settings in base.config. The default is to create LocalSignalReceiver which can only be used inside the same JVM. If needed, this can be changed to a SocketSignalReceiver or any other user-provided implementation.

  • When the job queue has found a plug-in to execute it will check if it also implements the SignalTarget interface. If it does, a signal handler is created and registered with the signal receiver. This is actually done by the BASE core by calling PluginExecutionRequest.registerSignalReceiver() which also makes sure that the the ID returned from the registration is stored in the database together with the job item representing the plug-in to execute.

  • Now, when the web client see's a running job which has a non-empty signal transporter property, the Abort button is activated. If the user clicks this button the BASE core uses the information in the database to create SignalTransporter object. This is simply done by calling Job.getSignalTransporter(). The created signal transporter knows how to send a signal to the signal receiver it was first registered with. When the signal arrives at the receiver it will find the handler for it and call SignalHandler.handleSignal(). This will in it's turn trigger some action in the signal target which soon will abort what it is doing and exit.