Writing Callback Objects
Introduction
Callback objects provide a means to keep resource properties in synch with the corresponding state in the backend managed resource. You write callback objects and register them on any non-static resource properties. The registration of callbacks is typically done upon initialization of the Resource class, in its init method. An example of this can be seen in the FileSystemResource.
There are two interfaces which can be implemented when writing a Callback object. Which one you choose will depend on how you would like each ResourceProperty to behave:
- org.apache.ws.resource.properties.ResourcePropertyCallback - Implement this interface if the resource property will change (not static i.e. "name"), but is not modifiable externally. It provides the ability to refresh the front-end with the current state of your backend.
- org.apache.ws.resource.properties.SetResourcePropertyCallback - Implement this interface if the resource property is modifiable, thus methods like insert, update and delete may be appropriate. This interface extends the ResourcePropertyCallback.
Implementing a ResourcePropertyCallback
The ResourcePropertyCallback is a way for you to synchronize the front-end resource properties with the backend. The interface defines one method:
ResourceProperty refreshProperty( ResourceProperty prop );
Notice the operation is passed a ResourceProperty. The ResourceProperty contains methods for pulling the values out of the property. You can get an iterator, in the case of a list, or can retrieve via an index: ResourceProperty.get(i)
The returned property will be an XmlBean type. What "type" will depend on your method description in your wsdl. If the type is a schema-defined type (string, int etc.), XmlBeans provides objects specific to those types. In the BackupFrequencyCallback we see that BackupFrequency was an xsd:int and so the XmlBean type is XmlInt:
public ResourceProperty refreshProperty(ResourceProperty prop) { XmlInt xInt = (XmlInt) prop.get( 0 ); xInt.setIntValue( m_fileSystem.getBackupFrequency() ); return prop; }
In the case of complex types, XmlBeans will generate a type and that is what will be passed to the operation. We can see an example of this in OptionsCallback:
public ResourceProperty refreshProperty( ResourceProperty prop ) { Iterator iterator = prop.iterator( ); while ( iterator.hasNext( ) ) { OptionsDocument.Options o = (OptionsDocument.Options) iterator.next( ); clearOptionsFromProperty( o ); //add current options... List options = m_fileSystem.getOptions( ); for ( int i = 0; i < options.size( ); i++ ) { o.addOption( options.get( i ).toString( ) ); } } return prop; }
In both cases the objects (XmlInt and Options) all extend XmlObject as a common class, however it is useful to utilize the defined class' operations directly.
Once you have a handle on the passed-in object, you will need to set the equivalent value, from the current state of your backend, on the passed-in object.
The refreshProperty method is called by Apollo before servicing GetResourceProperty, GetMultipleResourceProperties and QueryResourceProperties requests.
Implementing the SetResourcePropertyCallback
The SetResourcePropertyCallback is a way for the backend to be changed via requests to the front-end resource properties. The SetResourcePropertyCallback interface defines three methods:
void deleteProperty( QName propQName ); void insertProperty( Object[] prop ); void updateProperty( Object[] prop );
The operations will follow the same semantics as are defined in the specification and will be restricted to the bounds of you schema. If you define an element with a maxOccurs="1", and attempt to add (insertProperty) another item to it, Apollo will generate a fault.
The CommentCallback illustrates how these methods may be implemented:
public void deleteProperty( QName propQName ) { return; // no need to implement - Apollo will never call delete for a prop whose minOccurs != 0 } public void insertProperty( Object[] propElems ) { // Comment prop has cardinality of 1, so passed array will always have exactly one element XmlString xString = (XmlString) propElems[0]; m_fileSystem.setComment( xString.getStringValue() ); } public void updateProperty( Object[] prop ) { insertProperty( prop ); }
Notice that the deleteProperty method does nothing since the implementor knew that the method would never be called since the schema forbids it. The updateProperty simply hands-off to the insertProperty since the implementor knew that an update on the backend is the same as an insert. The insertProperty is the workhorse method and takes the Object[], obtains the value from it and sets it on the backend.
Registering a Callback
Once you've written your callback objects you will need to register them with the resource properties. This is done in the init method of your Resource implementation class. See the FileSystemResource for an example.