Objective Html - Signals and Slots
Author Keith Wong, keithwong@optushome.com.au
Last updated: 19th Jan 2002
Click here to see the signals available on each HtmlWidget.
The signals and slots mechanism is essentially a communication system between objects, otherwise known as the Objective Html Messaging System (OMS). There are two major components to OMS, the signal and the slot. A signal is emitted by objects that wish to inform other objects (the slots) of an interesting event. The HtmlTextBox object, for instance, emits the valueChanged signal when its text value is changed, and the HtmlPushButton object emits the clicked signal when the object is clicked. Objects (the slots) that are interested in these events can connect to these signals and the slot method will execute when the signal is emitted.
A signal is simply a Java instance method. Once a signal is emitted all slots that are connected to it will receive the signal regardless of whether the other slots successfully process the signal. The connected slot methods will be received the signal in the same order they were connected to the signal method. The only syntatical requirement of a signal method is that it is public and returns a Signal object. However the signal will not work unless the signal method is correctly implemented.
A slot is simply a Java instance method. When a signal is received by a listening slot object, the slot method will execute. Generally its desirable for a slot method not to throw any exceptions and have itself handle any exceptions that it might encounter. If you wish a slot method to throw an exception, then you can provide an ErrorHandler object. A ErrorHandler object is simply a class that implements the ErrorHandler interface. If this is provided then the handleError(SlotId, Signal, Throwable) will be called when an exception is thrown by the slot method. If no error handler object is provided then any exceptions thrown by the slots simply have their stack-traces printed to the standard out. Any public method is a validate candidate for being a slot. Even a signal method can act as a slot.
The signal can carry additional information in the form of method arguments, the slot can also receive these method arguments. A signal and a slot signature's must match for them to be connected. i.e. The parameter types of the signal must match the slot and in the correct order too. A slot can accept less arguments that the signal can give, in this case the extra arguments are lost. The opposite is not true however, a slot method cannot have more arguments that a signal method it is connecting to.
A signal can be connected to many slots and a slot can be connected to many signals. As well a signal doesn't need know anything about the slots that are connected to it and likewise a slot doesn't need to know anything about the signals that are connected to it. This is important as it provides your objects with a high-level of self encapsulation. Say for example, you have an operation that you wish to be executed when a button is clicked, but then at a later stage you change your mind and wish to have the operation to be executed when a selection list is changed. Without the signals and slots system your event processing may be tighly coupled with the operation, thus making it harder for you to change your code. Using signals and slots however, all you do is simply change your slot from being connected to a signal of the HtmlPushButton object to the appropriate signal emitted by the HtmlListBox object.
Connecting a Slot to a Signal
Below we will show 5 typical scenarios of how a slot can be connected to a signal.
connect(slot("saveForm()"), m_btnSave.signal("clicked()"));
To connect a slot to a signal you use the connect(SlotId, SignalId) method of the OMSObject class. Here our object m_btnSave is a HtmlPushButton and it emits the "clicked()" signal. When the user clicks on the button this signal will be emitted by the object and the slot "saveForm()" will receive the signal. i.e. the saveForm() method of this object will be executed. Note this object must extend the OMSObject class to participate in OMS. The slot(String) method identifies a slot connection given its method signature and the signal(String) method also identifies a signal connection given its method signature. The format for a method signature is given below.
connect(slot("questionToggled(boolean)"), m_cbxQuestion.signal("selectionChanged(boolean)"));
The object m_cbxQuestion is a HtmlCheckBox and it emits the "selectionChanged(boolean)" signal whenever its checked or unchecked. When the checked state is changed, the object will emit the signal and the slot method questionToggled(boolean) will be executed. Notice how the argument types of the signal method and slot method are the same, this is a necessary requirement as otherwise the signal wouldn't be able to pass on the additional information to the slot. The argument types are matched at connection time, if the argument types are not compatible then an exception is thrown by the connect method.
connect(slot("nameChanged(String)"), m_txtName.signal("valueChanged(String,String)"));
The object m_txtName is a HtmlTextBox and it emits the "valueChanged(String,String)" signal whenever the text value is changed. The first String argument is the new text value and the second String argument contains the old text value. When the signal is emitted the nameChanged(String) method of this object will be executed. Notice this time our slot method has one less argument than the signal method. This simply means that the slot method will lose some of the information provided by the signal. The argument types that are accepted by the slot must still match the signal.
connect(slot("nameChanged(String)", this), m_txtName.signal("valueChanged(String,String)"));
This time we have the same slot and signal as the previous example but we add an ErrorHandler object to the slot. Each slot can have one ErrorHandler object assigned to it. If an exception is thrown by the slot method then the handleError(SlotId, Signal, Throwable) method is invoked. If no ErrorHandler object is set then an exception stack-trace is printed to standard out and the exception is simply discarded.
connect(m_radYes.slot("selectionChanged(boolean)"), m_cbxQuestion.signal("selectionChanged(boolean)"));
The object m_cbxQuestion is a HtmlCheckBox and the object m_radYes is a HtmlRadioButton. Both objects emit the signal "selectionChanged(boolean)", so what we are doing here is connecting two signals together where one is acting as a slot. What results is a chaining of signals, i.e. when the "selectionChanged(boolean)" signal is emitted by object m_cbxQuestion, this will cause the "selectionChanged(boolean)" of object m_radYes to also be emitted. Any slot methods connected to the "selectionChanged(boolean)" signal of m_radYes would then be executed.
The Method Signature
The method signature is used to identify a slot or signal of an object. A method signature has the following format (this applies to slots and signals):
method-name(argtype1, argtype2, ...)
Some points:
The Update Order
When client data is submitted by the user the OHtml toolkit will sychronously update each form object with the submitted data. This means some objects will be updated before others and hence some objects will emit signals before others too. To give the developers control over which objects are updated first each control widget is given an update order. The update order is used by the HtmlForm object when it refreshes the state of the form objects with the updateData(HttpServletRequest) method. The control widgets with lowest update order values are updated first (0 being the lowest). If two or more control widgets have the same update order then they are updated accordingly to their natural order. The natural order is simply starting from the top-left of the form, going right first, then downwards. The default update order is 0 for all control widgets.
Implementing a Signal
There may be situations where you need to create your own signals. The most likely situation would be where you have two forms that need to communicate with each other. Below is a guideline of what you need to do to create your own signals.
This is the implementation of the valueChanged(String,String) signal of HtmlInput:
public Signal valueChanged(String sNewValue, String sOldValue) { ParameterList objParamList = new ParameterList(); objParamList.addParam(sNewValue); objParamList.addParam(sOldValue); Signal objSignal = null; try { objSignal = new Signal(signal("valueChanged(String, String)"), objParamList); } catch (Exception e) { e.printStackTrace(); // shouldn't happen } return objSignal; } // end valueChanged
To implement a signal method you need to do the following:
Limitations
All signal and slot methods must be public. Though it would been nice to have protected and private signals and slots the implementation would of been much more complicated due to Java's reflection restrictions and its usage would have been also more complicated. To keep the implementation light and simple it was decided to only offer public signals and slots. These concepts may be revisited later down the track.