Method and system for distributing data events over an information bus6704803Abstract A group of protocols is described that establish an information bus. The protocols allow various applications and components to plug into the information bus. As a member of the bus, each application or component can exchange information with any other application or component in a structured way. The information bus is especially useful in interconnecting Java beans and applets in a Java virtual machine and in a distributive computer environment. An object oriented computer system having multiple participants is disclosed that utilizes a communications bus for sharing data between the multiple participants. The communications bus comprises membership generator logic, data event builder logic, data notification logic, and data control logic. The membership generator logic is configured to grant each requesting participant membership to the communications bus. The data event builder logic is configured to prepare data events occurring the computer system. The data notification logic is responsive to the data event builder logic and is configured to notify all participant members of the data events generated by the data event builder logic. The data control logic, which is coupled to the data notification logic, is configured to prioritize the notification of data events to the multiple participants of the communications bus. The data event can serve to announce the availability of a new data element, to request a given data element located in one of the member participants, or to announce a changed data element in one of the member participants. The data control logic can be configured to prioritize the notification of data events to selected groups of the multiple participants and it can be configured to provide selected grouping of the multiple participants. In addition, the data control logic may be configured to establish a virtual machine bridge between a first data controller and a first virtual machine and a second data controller and a second virtual machine. In addition, the membership generator logic generates a list of registered applications. Claims What is claimed is: Description FIELD OF THE INVENTION
class myMember implements InfoBusMember
{
private InfoBusMemberSupport m_memberSupport = new
InfoBusMemberSupport( this );
public void setInfoBus(String name) throws PropertyVetoException
{
m_memberSupport.setInfoBus(name);
}
// other wrapper methods go here
}
The public InfoBusMemberSupport(InfoBusMember member) method serves as a method constructor that sets the InfoBus reference member to null, and creates an instance of each of the VetoableChangeSupport and PropertyChangeSupport objects. The member parameter is a reference to the InfoBusMember instance that contains this InfoBusMemberSupport, and is used for property change notifications on the "InfoBus" property. The Membership method is public synchronized void joinInfoBus(String busName) throws InfoBusMembershipException, PropertyVetoException The method provides for an InfoBusMember to get an InfoBus and join it. Following a successful return from this method, the InfoBus property has a reference to the bus to which this class belongs. The application can then add listeners to begin receiving InfoBus events. If this method is called when the member has previously joined a bus but has not left it, InfoBusMembershipException is thrown, and the membership is unchanged. The public synchronized void leaveInfoBus( ) throws InfoBusMembershipException, PropertyVetoException method is called by an application after removing its event listeners when it is finished with a given InfoBus. It must be called before the application shuts down or before joining a different bus. The protocol also includes methods to manage the "InfoBus" property. The public synchronized void setInfoBus(InfoBus newInfoBus) throws PropertyVetoException method is called to set the InfoBus property for a given member. Setting this property results in changes to membership: any prior membership is terminated, and if newInfoBus is not null, the member joins that bus. Any vetoable or property change listeners are notified about the change according to the standard rules. This method is typically called by a container application, such as a Bean builder environment, to cause InfoBus members it contains to be members of a particular bus. PropertyVetoException is thrown when a VetoablePropertyListener on the member refuses to allow the change in membership. StaleInfoBusException is thrown when newInfoBus refers to InfoBus instance that is stale. The public InfoBus getInfoBus( ) method implementation in this class returns the current value of the "InfoBus" property. The public void addInfoBusVetoableListener(VetoableChangeListener vcl) and public void removeInfoBusVetoableListener(VetoableChangeListener vcl) are methods that call addVetoableChangeListener( ) or removeVetoableChangeListener( ) on a VetoableChangeSupport object in member data. The public void addInfoBusPropertyListener(PropertyChangeListener pcl) and public void removeInfoBusPropertyListener(PropertyChangeListener pcl) are methods that call addPropertyChangeListener or removePropertyChangeListener on a PropertyChangeSupport object in member data. For maximum flexibility, applets should accept a bus name as a parameter in the HTML tags and use it if found. If none is found, it should use the default InfoBus. InfoBus members should be prepared to have their "InfoBus" property changed by an outside class (such as a container). The InfoBusMemberSupport class provides this support. Rendezvous This section describes the event-based mechanism used by InfoBus components to announce data availability and request data among other components on the bus. The negotiation for data is also referred to as the `rendezvous.` FIG. 6 depicts a functional block diagram of the InfoBus 310 as shown previously in FIG. 3 where the producer 312 and a consumer 314 on bus 322 are ready to receive events. The same numbering used in FIG. 3 also applies to and is continued in FIG. 6. Events 343 are sent by the InfoBus 322 to listeners for each component on the bus. Three types of events are defined: InfoBusItemAvailableEvent--an event that is broadcast on behalf of a producer to let potential consumers know about the availability of a new data item through the InfoBus. InfoBusItemRevokedEvent--an event that is broadcast on behalf of a producer to let consumers know that a previously available data item is no longer available. InfoBusItemRequestedEvent--an event that is broadcast on behalf of a consumer to let producers know about the need for a particular data item that they may be able to supply. The three InfoBus events are subclasses of a common base class, each with methods needed for their particular task. The InfoBus class provides methods that create and broadcast these event objects on behalf of producers and consumers, including fireItemAvailable( ) and fireItemRevoked( ) for use by producers, and findDataItem( ) and findMultipleDataItems( ) for use by consumers. Once a class has joined an InfoBus, it needs to provide an event listener to the InfoBus in order to receive events from the InfoBus. InfoBus components are depicted as producer 312 or consumer 314 and listen for events to discover the availability or revocation of data items, or to hear requests for data items, or both. The InfoBus technology defines interfaces InfoBusDataProducer 336 and InfoBusDataConsumer 338 that extend InfoBusEventListener to indicate whether a component is a data producer, a data consumer, or both. The API details for InfoBusEventListener, InfoBusDataProducer, and InfoBusDataConsumer are described below. There are two event listeners lists provided. The first event list includes data producer event listeners 332 and the second event list includes data consumer event listeners 334. Data producer 312 is an InfoBus participant that implements InfoBusDataProducer 336 to listen for requests and announces data availability or revocation by firing events on the bus. Data producer 312 calls addDataProducer( ) to begin receiving events. Applets typically do this in their start( ) method so they begin to receive events when the page is activated and call removeDataProducer in the stop( ) method. Following this protocol reduces overhead when the page is not active. With some browsers, however, it is possible to use an instance of the InfoBus for communication between applications on different web pages. The browser must not "prune" Java applets as pages are changed for this to work, and the InfoBus applications must not remove their listener on stop( ) in order to receive or send InfoBus events. Producer events are generated by calling methods fireItemAvailable( ) and fireItemRevoked( ) on the InfoBus class, which send these events to registered consumers. The producer 312 handles request events via dataItemRequested( ). If the producer 312 can provide the requested data, it stores a reference to the data item by calling setDataItem( ) on the event, otherwise it simply returns. Data consumer 314 is an InfoBus participant that implements InfoBusDataConsumer 338 to listen for availability and revocation events, and requests data items by firing events on the bus. Similar to producer mechanism 312, it controls events by calling addDataConsumer( ) and removeDataConsumer( ). Data consumer 314 finds out about new data by handling dataItemAvailable( ) and revocation of announced data by handling dataItemRevoked( ). It decides whether it is interested in the data by inspecting the data item name or data flavors obtained from the event via getDataItemName( ) or getDataFlavors( ). If it wants the data, it can obtain it directly from the producer by calling requestDataItem( ) on the event. Data consumer 314 can request a data item by name even if it has not been announced by a producer 312. For example, findDataItem( ) can be called to find a data item by name. Such blind requests are often required when a consumer initializes, in case producer 312 announced the data item before consumer 314 had completed initialization. If the data is unavailable from any producer, null is returned to the caller. If more than one registered producer 312 is capable of producing the data, the first one that supplies the data item satisfies the request. A consumer 314 can call findMultipleDataItems( ) to get data items from all registered producers that are capable of supplying the data, and choose for itself which one it will take. A component can be both a producer 312 and a consumer 314 by implementing InfoBusDataProducer 336 and InfoBusDataConsumer 338. An applet might do this to filter data or translate it to a new form for a consumer 314. Producers 312 and consumers 314 cannot directly create and broadcast events to others on the bus because the constructors for events are not public and because producers 312 and consumers 314 do not have access to the other producers list 332 and consumers list 334. The InfoBus 310 intentionally prevents the use of custom events, since event traffic on the bus limits scalability. Data controllers 316 do have the ability to send events to producers 312 and consumers 314 if they choose to allow to mediate the flow of events among other components. Data controllers 316, however, use a different mechanism for handling events from consumers and producers, and are therefore not event listeners themselves. FIG. 6 shows a producer 312 and a consumer 314 just after they have provided event listeners 332, 334 to the InfoBus instance 322 to which they belong. The InfoBus instance 322 has a list of consumer listeners 334 and a list of producer listeners 332 that is separate from other InfoBus instances in order to control the scope of conversations between applets and to reduce traffic. Although the membership connections and change listeners of FIG. 3 are not shown in FIG. 6, they are still part of the overall bus structure and are eliminated for simplicity in FIG. 6. FIG. 6 shows applications that have only one InfoBusEventListener associated with each InfoBusMember. They are not combined into one interface as it is often convenient for an application to have more than one event listener, each specialized to a particular data item of interest. InfoBus components can register as many event listeners as they need. For example, consider a shared technology that supports the notion of the currently active selection by way of a dataitem called "CurrentSelection." The provider of this item is likely to be in a different class than, for example, the provider of a collection of cells, and the use of multiple event listeners makes structuring the classes more convenient. Security during the data exchange or rendezvous process can be approached from two granularities: security checks before permitting joining an InfoBus instance constitute a large-grained approach, while security checks upon delivery of an InfoBusEvent to a producer or consumer constitute a fine-grained approach. The fine-grained security approach occurs in the producers and consumers themselves. In one example, a producer creates a data access permission akin to the FilePermission class, with system security policy files that enumerate classes that have that access permission. When this producer receives an InfoBusItemRequested event, it can call the AccessController's checkPermission method to verify that all objects in the call stack--which will include the requesting consumer--have the necessary access permission before releasing data. Consumers that wish to implement this kind of fine-grained permission checking need to take the additional step of implementing the javax.infobus.SecureConsumer interface. Without the SecureConsumer interface the data that a consumer requests is returned by the InfoBus and the producer providing that data has no presence in the call stack. By implementing SecureConsumer, each producer that returns data is actually calling the SecureConsumer.setDataItem( ) method, allowing the SecureConsumer to perform an AccessController checkPermission( ) before accepting and processing the data. The InfoBusPolicyHelper provides the methods necessary to implement the large-grained approach: for each of the supported actions, there is a matching InfoBusPolicyHelper call that is made before executing the request. Performing the rendezvous checks by default produces unwanted overhead in code execution as well as overhead in management of system security policies. The InfoBusPolicyHelper checks done during membership processing, combined with fine-grained checks done by individual consumers and producers during rendezvous, are a sufficient and optimal means of creating a secure InfoBus application. Systems that wish the additional layer of security described here as large-grained rendezvous checks have the means of implementing it by providing a custom policy helper. An InfoBus participant should create classes that implement InfoBusDataProducer 336 and InfoBusDataConsumer 338 separately from one that defines other methods or implements other interfaces, especially InfoBusMember 328. This separation is provided because the listener interfaces are available from events and data items and introspection allows access to other methods available on these objects. In particular, if InfoBusMember 328 is in the same class, it would allow access to setInfoBus( ), which a malicious application could use to force a member onto a different bus. Data items can be announced as available by name and consumers can request data items by name. Data items can be named using the recommended naming conventions described in this section, based on Universal Resource. However, data items are not required to follow these conventions. The only requirements for data item names are: No data item name can begin with the `%` character, which defines a reserved space for data item names. If the data item name starts with "infobus:" or "/", the same as a URI does, it must follow all of the rules for the convention. It should be possible to name data items by way of applet parameters, the UI, or both. URI-Based InfoBus Naming Convention <infobus_uri>::=<abs_infobus_uri>.vertline.< rel_infobus_uri> <abs_infobus_uri>::=<infobus_scheme>`:`<rel infobus_uri> <infobus_scheme>::=`infobus` <rel_infobus_uri>::=`/`<infobus_name>{<producer_id}`/`< infobus_data_item_name> <infobus_name>::=<unreserved>* <producer_id>::=`/`<producer_class>{`/`< producer_discriminator>}* <producer_class>::=fully-qualified Java class name (with dot separators) <producer_discriminator>::=<unreserved>* <infobus_data_item_name>::=<unreserved>* <unreserved>=ALPHA.vertline.DIGIT.vertline.safe.vertline.extra <extra>=`!`.vertline.`'`.vertline.`(`.vertline.`)`.vertline.`,` <safe>=`$`.vertline.`-`.vertline.`_`.vertline.`.` These notes apply to item names, though they are not suggested by the BNF description above: The infobus_name is the one specified for a named InfoBus, or when using a default name for a DOCBASE, can be obtained by calling myMember.getInfoBus( ).getName( ). Note that infobus_name cannot begin with `-`, and the use of `%` as the leading character is reserved. The producer_class is a qualified Java class name with dot separators, like com.lotus.esuite.sheet. DataFlavors and the MIME type strings they expose can be used to describe data items provided in the InfoBus rendezvous. This is helpful to the consumer to determine before requesting data from an available event whether it can make use of it. This is helpful to the producer to determine whether it can supply the data in response to a request event in a useful form. In announcing the availability of a data item, a producer can supply an array of DataFlavors available for the item by way of a parameter on the InfoBus.fireItemAvailable( ) method. The value of this parameter, whether a reference to an array or just null, is available to the consumer by way of InfoBusItemAvailableEvent.getDataFlavors( ). The flavors are the union of the groups listed above. Similarly, a consumer can indicate its preferred format for a data item by providing an array of DataFlavors, ordered by preference, on the InfoBus.findDataItem( ) and findMultipleDataItems( ) methods. The value of this parameter, whether a reference to an array or just null, is available to the producer by way of InfoBusItemRequestedEvent.getDataFlavors( ). Because this is a hint to the producer, and the producer may supply an item that is not one of the preferred flavors, the consumer must code defensively to ensure that it has an item of a type it can use, for example by using the instance of operator. The InfoBus class is involved with membership and with rendezvous. The membership methods have already been presented. The InfoBus methods used to manage event listeners are now presented. public synchronized void addDataProducer(InfoBusDataProducer producer) public synchronized void removeDataProducer(InfoBusDataProducer producer) public synchronized void addDataConsumer(InfoBusDataConsumer consumer) public synchronized void removeDataConsumer(InfoBusDataConsumer consumer) These methods add or remove event listeners from the list of data producers or data consumers maintained by each InfoBus instance. The add methods should be called after the component has joined a bus. After adding an event listener, the class will begin to receive requests from data consumers on the same bus. If the add methods are called on a stale InfoBus instance, the StaleInfoBusException is thrown. The remove methods must be called to remove all listeners before shutting down the application to release references from the InfoBus to the application class and to allow the InfoBus instance to be released. InfoBus methods are used by producers, consumers, and data controllers to fire events. The first method in each group is one that is used by a producer or consumer to fire an event. This method actually defers the distribution of events to registered data controllers, or the default controller if no data controllers are registered; data controllers must not call this method. Each event group also has methods to fire events to a specific target or a Vector of targets. Events fired with these methods are delivered directly to the indicated targets, not to other controllers. These methods can be called by data controllers, and must not be called by data producers or consumers. Any method that fires events (including the `find` methods) can throw java.security.AccessControlException. Because this is a runtime exception, the use of `try . . . catch` clauses is optional. The InfoBus unites multiple components that work together as one application. Each InfoBus component must be aware that their code may execute in a multithreaded fashion, even when they do not spawn threads themselves, since they may be called from other components that do use multiple threads. The InfoBus requires that when an available event for a particular data item name and producer is fired, it must be received by all listeners before the corresponding revoked event (i.e., a revoked event from the same producer with the same data item name) is fired. To meet this requirement the producer must temporarily disable its ability to send the corresponding revoked event by using synchronization techniques appropriate for multithreaded operation before firing the available event. This can be accomplished using a synchronization block around code that fires available and revoked events. In implementing this, a component must not specify its InfoBus as the parameter to the Java synchronized keyword as this can cause a deadlock to occur. The public void fireItemAvailable(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataProducer producer) method is called by producers to create an instance of InfoBusItemAvailableEvent and send it to data consumers on the bus, indicating the dataItemName and its producer. Producers can specify the flavors of data they can supply, or use null if they don't care to describe the data. Consumers can examine the offered flavors and decide whether they can use the data, or decide after requesting the data. Data controllers must not call this method. The public void fireItemAvailable(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataProducer source, InfoBusDataConsumer target) and public void fireItemAvailable(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataProducer source, Vector targets) methods are designed for use by data controllers. The first method creates an InfoBusItemAvailableEvent and delivers it to target. The second method creates a single InfoBusItemAvailableEvent and delivers it to all consumers specified in the Vector targets. All elements specified in targets must implement InfoBusDataConsumer. The targets Vector is copied by the InfoBus before distribution begins. The public void fireItemRevoked(String dataItemName, InfoBusDataProducer producer) method is called by producers to create an instance of InfoBusItemRevokedEvent and send it to data consumers on the bus, indicating the dataItemName and its producer. Data controllers must not call this method. Producers should call this method when a data item that has been announced as available will no longer be available. The public void fireItemRevoked(String dataItemName, InfoBusDataProducer source, InfoBusDataConsumer target) and public void fireItemRevoked(String dataItemName, InfoBusDataProducer source, Vector targets) methods are designed for use by data controllers. The first method creates an InfoBusItemRevokedEvent and delivers it to target. The second method creates a single InfoBusItemRevokedEvent and delivers it to all consumers specified in the Vector targets. All elements specified in targets must implement InfoBusDataConsumer. The targets Vector is copied by the InfoBus before distribution begins. The public Object findDataItem(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataConsumer consumer) method is called by consumers to create an instance of InfoBusItemRequestedEvent and send it to data producers on the bus, indicating the dataItemName and consumer that wants it. The consumer can specify its preferred flavors or just say null. The event is sent to each registered producer until a producer supplies a data item, at which time the data item is returned to the caller. The order of polling the data producers for such requests is indeterminate. If no producer satisfies the request, the method returns null to indicate that the requested data item is not available. Because data controllers control the distribution of this type of event, they must not call this method. The public Object findDataItem(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataConsumer consumer, InfoBusDataProducer target) and public Object findDataItem(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataConsumer consumer, Vector targets) methods are designed for use by data controllers. The first method creates an InfoBusItemRequestedEvent and delivers it to target, then returns a response to the request or null if target does not fill the request. The second method creates a single InfoBusItemRequestedEvent and delivers it to the producers found in the targets Vector until one producer responds by filling the request or all producers have been queried. The method returns the response object if a producer filled the request, or null if no producer responded. All elements specified in targets must implement InfoBusDataProducer. The targets Vector is copied by the InfoBus before distribution begins. The public Object{character pullout} findMultipleDataItems(String dataItemName, DataFlavor{character pullout} flavors, InfoBusDataConsumer consumer) method creates an instance of InfoBusItemRequestedEvent and sends it to all data producers on the bus, indicating the dataItemName and the consumer that requested it. The consumer can specify its preferred flavors or just say null. Each data item supplied by a producer is stored in an array, which is returned to the caller. If no producers offer the requested data item, this method returns null. The InfoBusEvent class is the base class for InfoBus events used for a rendezvous to provide a data item from a producer to a consumer. Subclasses are defined for each event type for the purpose of determining the event type via the Java "instanceof" operator. The public String getDataItemName( ) method is an accessor that allows an event handler to look at the data item name to see if it can produce or use the named data item. The InfoBusItemAvailableEvent class is sent on behalf of a data producer to announce the availability of new data to all data consumers that have joined a given InfoBus instance. A producer creates and broadcasts the event by calling InfoBus.fireItemAvailable( ). Because the constructor is package access, the event cannot be created directly by an application. The public Object requestDataItem(InfoBusDataConsumer consumer, DataFlavor{character pullout} flavors) method can be called by a consumer to request a data item announced by way of the InfoBusItemAvailableEvent. The method creates an InfoBusItemRequestedEvent and sends it directly to the producer that announced the item. The producer returns a reference to the item. When flavors is not null, it specifies an array of flavors the consumer can use. The producer may decide not to return an item if it cannot provide it in one of these flavors. The public InfoBusDataProducer getSourceAsProducer( ) method returns a reference to the source of the event, i.e. the event handler of the producer that generated the InfoBusItemAvailableEvent. The source of available events is always an InfoBusDataProducer. Event.getSource returns an Object reference to the producer. The consumer can use the reference to the producer to uniquely identify the producer of an announced item. The public DataFlavor{character pullout} getDataFlavors( ) method allows a consumer to consider the type of information being announced as available before requesting a data item. It returns a reference to array of DataFlavor objects that describe the formats the producer can provide either in the data item itself, or by way of Transferable.getTransferData( ). If this method returns null, it means the producer did not specify the DataFlavors in announcing this data. The InfoBusItemRevokedEvent class is sent on behalf of a data producer to announce the revocation of a previously announced item. It is used by consumers, who should release their reference to the item if they hold it, and controllers, who may wish to update a list of currently available items. Additionally, all items announced as available should have a matching revoked announcement. In addition to sending an InfoBusRevokedEvent, the revoked change event should be sent to data item change listeners. The event is created and broadcast by calling InfoBus.fireItemRevoked( ). Because the constructor is package access, the event cannot be created directly by an application. The public InfoBusDataProducer getSourceAsProducer( ) method returns a reference to source of the event, i.e. the event handler of the producer that generated the InfoBusItemRevokedEvent. The source of revoked events is always an InfoBusDataProducer. The InfoBusItemRequestedEvent class is sent on behalf of a data consumer to find a named data item it would like to receive. For example, when an applet is starting, it cannot know whether a given data item has been announced, so it asks for the item by calling one of the find methods in InfoBus, which generate this event. Because the constructor is package access, the event cannot be created directly by an application. The public void setDataItem(Object item) is a method accessor that the data producer uses to set a data item it is supplying in response to the request event. If the source of this RequestedEvent is an InfoBusSecureConsumer, the call to setDataItem( ) will also call the SecureConsumer's setDataItem( ) method to permit the SecureConsumer to perform permission checks and determine trustworthiness of the responding producer. SetDataItem( ) is a write-once method: if the data item in the event is non-null, it cannot be overwritten. The field will be null [writable] when the RequestedEvent is first delivered to a producer. The public Object getDataItem( ) is an accessor that is used by the InfoBus or a data controller to get a reference to the data item previously set by a data producer via setDataItem( ). If no producer responded to the event, calling this method will return null. The method is also used in the implementation of InfoBus.findMultipleDataItems to get each data item available from the data producers on a given InfoBus instance. The public InfoBusDataConsumer getSourceAsConsumer( ) method returns a reference to the source of the event, i.e. the event handler of the consumer that generated the InfoBusItemRequestedEvent. The source of request events is always an InfoBusDataConsumer. The public DataFlavor{character pullout} getDataFlavors( ) method exposes the DataFlavors that the consumer prefers, as a hint to producers that can supply data in more than one format. If this method returns null, the consumer did not provide any DataFlavor preferences when it requested the event. The consumer may specify Mime types in the order it prefers, including InfoBus and standard Mime types. The Mime string application/x-java-infobus; class-javax.infobus.DataItem indicates that a consumer will accept any type of InfoBus access interface available for the item. Flavors are a hint to the producer, which is not required to consider the requested DataFlavors in supplying the data item The InfoBusEventListener interface extends java.util.EventListener 326 and java.beans.PropertyChangeListener 342 to serve as a base class for the data producer and data consumer interfaces. FIG. 7 shows the class hierarchy for these interfaces. Each event listener 336, 338 must be registered with the InfoBus instance 322 after joining it in order to receive events; this is accomplished by calling InfoBus.addDataProducer or addDataConsumer, as appropriate for the type of event listener interface. An object that serves as both consumer and producer would add itself via both add methods. The listener should be added during the applet's start( ) method (or its moral equivalent) and removed during the applet's stop( ) method in order to reduce event traffic when the browser switches to a different page. The public void propertyChange(PropertyChangeEvent event) method is called whenever the member associated with this producer or consumer has its "InfoBus" property changed by a means other than calling the leave method, for example when a builder calls InfoBusMember.setInfoBus( ) to force it to talk to a different bus. The method is inherited from PropertyChangeListener. The desired implementation includes: Check event.getPropertyName( ) is "InfoBus" and event.getSource( ) is your parent InfoBusMember. If the event.getOldValue( ) is not null, call event.getOldValue( ).removeDataProducer( ) to stop listening to the old InfoBus instance. If the event.getNewValue( ) is not null, call event.getNewValue( ).addDataProducer( ) to listen for events from the new InfoBus instance. An InfoBusDataProducer interface is provided that extends InfoBusEventListener and is implemented by classes that wish to serve as a data producer. A class that implements this interface should be registered with the InfoBus via addDataProducer( ) during the applet's start( ) method (or the moral equivalent if not an applet), and removed via during the applet's stop( ) method. The public void dataItemRequested(InfoBusItemRequestedEvent event) method is called by the InfoBus class on behalf of a data consumer that is requesting a data item by name. The desired implementation: check the data item name (obtained via event.getDataItemName( )) to see if it is an item that can be supplied by this producer. If not, return. optionally, call AccessController.checkPermission( ) to determine permissions to decide whether to provide the item to the caller. create an instance of the data item, or get a reference if it already exists, and set it via event.setData Item( ). A InfoBusDataConsumer interface is provided that extends InfoBusEventListener and is implemented by a class that wishes to serve as a data consumer. The class should be registered with the InfoBus via addDataConsumer( ) during the applet's start( ) method (or the functional equivalent if not an applet), and removed during the applet's stop( ) method. The public void dataItemAvailable(InfoBusItemAvailableEvent event) method is called by the InfoBus class on behalf of a data producer that is announcing the availability of a new data item by name. A consumer that obtains a data item from a producer should be prepared to release it when the producer announces that the item is being revoked via InfoBusDataConsumer.dataItemRevoked( ). The desired implementation: Optionally, call AccessController.checkPermission( ) to determine permissions in deciding whether to request the item from the producer. Check the data item name (obtained via event.getDataItemName( )) to see if the item is wanted. If not, return. Get a reference to the data item by calling the event.requestDataItem( ) method. If desired, and if a DataItemChangeManager is present, set a DataItemChangeListener on the data item. The public void dataItemRevoked(InfoBusItemRevokedEvent event) method is called by the InfoBus class on behalf of a data producer that is revoking the availability of a previously announced data item. A consumer that is using this data item should release it upon receiving this notification. The desired implementation: Check the data item name (obtained via event.getDataItemName( )) and the producer sending the event (obtained via event.getSourceAsProducer( )) to see if this is an item held by this consumer. If not, return. Remove any change listeners set on this data item. Release all references to the data itemheld by this consumer Data Items The next element with the InfoBus protocol is the Data items and how they are managed within the system. Data items are any Java Object passed by reference from a producer to a consumer by way of a request event, and any sub-items when collection interfaces are used. The InfoBus API defines a data item transfer object as an Object for maximum flexibility and compatibility with the JDK Collection classes. The InfoBus API defines several interfaces to add InfoBus-standard functionality to the data items: The DataItem interface provides descriptive and identifying information about the data item itself. The DataItemChangeManager interface manages DataItemChangeListeners from consumers. The DataItemView interface provides methods to manage a view associated with an item. The awt.data-transfer.Transferable interface provides an alternate access mechanism for data in another standard format. A variety of standard access interfaces can be implemented by a data item to provide application-independent access methods for consumers to navigate and retrieve data from the item. A consumer can examine a data item using the instanceof operator (or catch a cast exception) to discover whether a given interface is available on the item. For example, the consumer can find out whether change notifications are available on a given data item by testing for an instanceof DataItemChangeManager. Data items can be a single-valued Object wrapper using the ImmediateAccess interface. FIG. 8 illustrates the "CurrentTime" data item in TimeSource sample application. A data producer implements a data item 346 to identify and describe the data. The data producer implements a DataItemChangeListener 1214 to notify consumers of changes. A Double object, which represents the current time, is a member data 352. The consumer can access the member data item using methods provided by the ImmediateAccess 350 implementation. A data item class definition looks like this:
class CurrentTimeItem
implements DataItem, DataItemChageManager, ImmediateAccess
{
Double time = new Double(0);
// methods for DataItem
// methods for DataItemChangeManager
// methods for ImmediateAccess
};
To access the Double object contained in the member data 352 of the CurrentTimeItem instance, the ImmediateAccess.getValueAsObject( ) method can be called. The reference to the Double is returned, allowing calls to methods on that object, as described in java.lang.Double. The ImmediateAccess interface 350 also defines a method to return a string rendering of the Double. The Clock sample consumer uses this method for getting a formatted time string from the producer. For many data items, the data object could be a part of the inheritance hierarchy, in which the class declaration above would extend the data object. This is possible when the data object is not declared as a final class. In such cases, getValueAsObject( ) simply returns this. TimeSource and Clock are intended to be simple examples of a producer and consumer applet. In real-world examples, data items will often contain more interesting data structures, such as a collection of other data items, using various collection interfaces to provide rich structuring of a complex data set. FIG. 9 shows InfoBus objects in a spreadsheet producer 910, which provides access to a collection of cell objects 918 by way of the ArrayAccess interface 914 and is modeled after the producer 312 of FIG. 8. The getItemByCoordinates( ) method returns an ImmediateAccess item 916 to provide access to a given cell's data found in cell objects 918. Spreadsheet object 912 offers change notifications at both the sheet and cell level. A spreadsheet producer might also implement a Collection to provide access to various ranges of cells (not shown in the picture). Spreadsheet data structures generally contain a lot of information that are used for internal purposes and would not be provided to consumers, for example a formula used to calculate the value of a cell. They can also be large, and contain many cells and ranges. This has two important implications in implementing the model. First, applications of this size will generally not carry copies of their data in the various data items they expose, because it is time-consuming and wasteful of resources. Instead, an access interface serves as a proxy for accessing the data from the internal representation of the sheet, and carries a means of getting the data (e.g., a reference to the cell in the internal representation) rather than a copy of the data. Second, it is generally inefficient to create data items for all cells when the ArrayAccess data item is created. Instead, ImmediateAccess data items for cells should be created on demand and released when no longer needed. DataItem interface 346 provides identifying and descriptive information about a data item. Well-behaved InfoBus producers must implement this interface on the top-level data item being exchanged. For multi-level data items, such as a Collection of data items, implementing DataItem for items at every level is recommended. The public Object getProperty(String propertyName) method returns a property or metadata information about the item, as indicated by property Name. One property is recommended for DataItems at the top-level: the "Name" property must provide the data item name that was used for announcement or request in the rendezvous process. This does not apply to nameless DataItems, i.e. those below the rendezvous point of a hierarchical data item. Support for other properties is optional; null should be returned for unsupported properties. Property names should not contain the `*` character. The public InfoBusEventListener getSource( ) method returns a reference to the creator of this data item. This method should return a reference to the InfoBusEventListener (usually an InfoBusDataProducer) used for rendezvous of this item as their source. Data items can also be supplied by a consumer to the producer, for temporary use in changing mutable data items (to provide to the producer a means of accessing the new value). In this case the source of the temporary item is an InfoBusDataConsumer. null is not a permissible return value from this method. The public void release( ) method allows a producer to know when a consumer has finished using a data item. Consumers are required to call this method when they are about to drop their last reference to an item or anything obtained directly or indirectly from the item, including subitems and transfer data (from Transferable.getTransferData( )). Producers may optionally manage resources for data items in their implementation of this method. A DataItemView interface is provided for producers to implement to optimize the management of a view of the contents of a particular subset of records. The view represents the window of data that is currently visible to the consumer. For example, a consumer of an object that implements ScrollableRowsetAccess and DataItemView can paint the cells in a grid view of a subset of its rows. As the view is scrolled, the items in the view change as new rows appear. Without the use of this interface, the view can be populated by changing the current row of a ScrollableRowsetAccess to get values to be displayed for each row in the view, but this can be time-consuming. The ViewStart property indicates the number of the row in the row set that is seen as the first row of the view. There is no relationship between the current row of the rowset and the ViewStart; it is possible to scroll the view without affecting the current row, or change the current row without scrolling the view. It is possible for a view to contain fewer rows than specified in getView( ). Similarly, when scrolled to the end, the view may end up with fewer rows than were originally requested for viewSize. A similar situation can occur when rows are deleted from a rowset. In these cases, the ArrayAccess obtained from getView( ) must indicate the number of rows actually in the view from dimension[0] are returned by getDimensions( ). Attempts to access items beyond this dimension must cause IndexOutOfBoundsException to be thrown. This use of this interface is optional: producers can implement it or not as they choose; consumers may use it or not if it is present. The public int getViewStart( ) method returns the current value of the ViewStart property. The public void setViewStart(int absoluteRow) method sets ViewStart to absoluteRow. The absoluteRow should be greater than or equal to zero. The public void scrollView(int relativeAmount) method changes ViewStart by relativeAmount relative to its current position. relativeAmount is negative to move backwards, or positive to move forward. The public ArrayAccess getView(int viewSize) method returns a two-dimensional ArrayAccess object that represents a row and column view with viewSize rows. The array must be two-dimensional, corresponding to row and column numbers in the view, respectively. The ArrayAccess should be read-only; an attempt to set a new value must throw UnsupportedOperationException. Sub-items returned by this ArrayAccess must be ImmediateAccess items that correspond to the current view to provide standard access to the values in each sub-item. If the ViewStart property changes, the values returned by items in the array change so that the view maps to a different range of rows in the row set. For example, if DataItemView were implemented on a RowsetAccess object, and an ArrayAccess was obtained by calling this method, and ViewStart is 0, getting the item at coordinate [1,2] of the array returns the item at row 2, column 3 in the row set. If the consumer calls scrollView(5), ViewStart changes to 5, and the value of item [1,2] changes to be the value at row 7, column 3 in the row set. A Transferable interface can optionally be implemented on any data item at any level. The Transferable mechanism provides an alternative to the access interfaces for accessing one or more JDK-standard formats for a data item. Using this mechanism it is possible to achieve essentially a dynamic clipboard implementation. The Transferable object exposes the data items DataFlavors, as described in java.sun.com/products/JDK/1.1/docs/api/Packagejava.awt. datatransfer.html When the producer wishes to share a Transferable implementation that can work for more than one data item, it can carry a reference to the implementation, and delegate Transferable method calls directly to that object. When implementing Transferable.getTransferDataFlavors( ), the returned array must include only those MIME types for data that can be accessed via Transferable.getTransferData( ). When Transferable.getTransferData( ) is used to get data in a particular data flavor, the reference handed back counts as one of the references that must be released prior to calling DataItem.release( ) at any point above the Transferable object. InfoBus access interfaces do not provide an explicit locking API. Producers can lock a given class instance in the implementation of access methods using synchronized methods or blocks as needed. Some data items require a critical resource to be allocated by the producer. For example, a data access component may allocate a system resource on a database server that must be released when it is no longer needed. Such producers will typically track the consumers that have requested data items associated with the resource, for example by count or by handing out separate instances of the data item access object, and release the resources when the last consumer indicates that it has finished using the resource. The DataItem.release( ) method is designed to provide an indication to the producer when a given consumer has finished using a data item. Consumers are required to call the DataItem.release( ) when they are finished using any object obtained directly or indirectly from the data item, including objects returned by Transferable.getTransferData( ). After a consumer calls this method, it must not make any further calls on the DataItem or its sub-items, and should drop its reference (for example, if it's member data, set that member reference to null). When release( ) is called for any DataItem, it means that the consumer is finished with the item at that level and all subitems it may have. When a consumer passes around a data item references among various objects it manages, it must track its use of the references so that it knows when the last reference is dropped. The consumer may optionally look for sub-items that implement DataItem and release these when they are no longer needed. This is a good idea for large, multi-level collection data items. Requirements needed by the Producer are now described. DataItem is required for all top-level (rendezvous) data items, and is optional for all sub-items in a collection hierarchy. The producer is required to provide an implementation of release( ) for each object that implements DataItem. However, DataItem.release( ) is only a hint to the producer, and it can use any strategy it chooses for managing associated resources, or provide an empty implementation to ignore the hints. A producer may decide to manage resources at the top-level data item, or at all levels of the collection hierarchy, or not at all, according to its own requirements. In a multi-level collection hierarchy which contains a DataItem at more than one level, calling DataItem.release( ) at any level means that that node and all nodes below it are released; calling release( ) on the top-level DataItem releases the entire data item for the consumer that called it. When a producer supports the release of resources, it must do so in such a way that when one consumer calls release( ), it will not affect other consumers who hold a reference to any part of the same data item. Of course, because a producer manages the lifetime of a data item, it can revoke it at any time it chooses, but ideally it aims to minimize disruption to consumers. In any case, when a producer does release resources associated with a data item, it should always send a DataItemRevokedEvent to listeners of the item, and if it is a top-level item, it should also send an InfoBusItemRevokedEvent by calling InfoBus.fireRevoked( ). FIG. 10 is a block diagram illustrating the hierarchy or the release( ) rules. There's a data item called A that was provided to the consumer via a rendezvous. A implements a DataItem and a Collection interface. Collection A has two sub-items, Collection B1 and B2, which each implement DataItem. Collection B1 has one sub-item, an ImmediateAccess C, that implements Transferable but does not implement DataItem. Suppose the consumer has requested references to B1, B2, C, and a data transfer object T obtained from C.getTransferData( ).B1.release( ) should be called when all references to C and T have been dropped, and the consumer plans to drop B1 immediately after calling B1.release( ). A.release( ) should be called when all references to B1, B2, C, and T have been dropped, and the consumer will drop A immediately after calling A.release( ). Calling A.release( ) implies that all sub-items are also released, so when all references to sub-items have been dropped, B.release( ) need not be called before calling A.release( ). Above, it was presented that data items suitable for exchange on the InfoBus are those that might be useful to more than one consumer and might be available from more than one producer. Data may be represented by one of the standard access interfaces defined here. Although private access methods can be used with items exchanged on the InfoBus, it defeats the purpose of providing a standard means of exchange between arbitrary components. The InfoBus access interfaces, when implemented on a data item, allow the consumer to access the producer's data in a standard, application independent fashion, without knowing the details of the internal representation of the data the way the producer sees it. A producer should implement as many standard access interfaces as possible, to support the greatest variety of consumers, and richer function of those consumers. In general, the access interfaces are not mutually exclusive; depending on the nature of the data, it might make sense to implement all of them. Producers establish a policy of when, if ever, data can be changed by consumers. The policy may vary depending on the consumer's permissions. Most InfoBus access interfaces define methods for changing the data items. If a producer chooses not to support changes to a data item, whether for any consumer or on the basis of examining a particular consumer, it can throw an exception in place of making the change. The producer may call AccessController.checkPermission( ) to determine permissions during the handling of a method that can change a data item. The producer decides how permissions will be used in this case. It is preferred that producers accept an ImmediateAccess, if present in the argument to setValue-type methods, as the preferred source of a newValue, but also allow for producers to accept other types of Objects that make sense for the application. This applies to all access interfaces defined in this InfoBus Specification as well as the similar methods in interfaces defined for JDK Collections. ImmediateAccess provides the producer with methods needed to determine the new value. It can attempt to parse a string representation, or copy an Object if it is of a suitable type, or use the value from an Object. The mutable data item must implement all methods that change data items, including collections, in a way that is thread-safe, for example by using a synchronized method or block. If the data item supports DataItemChangeEvents, it must distribute change events due to changes before exiting the synchronized code region. This means a consumer that is changing a data item can rely on seeing the event for the change it initiated before its call to change a data item returns. A data item implements ImmediateAccess interface 350 of FIG. 8 to allow data values to be retrieved directly from calls to methods on this interface, returning an immediate rendering of the data as a String or Object. The interface is also convenient for wrapping final Objects, which cannot be passed with additional interfaces via a subclass, as in the example showing a data item housing a Double in FIG. 8. ImmediateAccess is preferred for data items that are not collections, because they provide common renderings as a String or Object, independent of the type of data the item represents. This makes it easier for data consumers, who can simply use these strings for representing the object to the user, without knowing any more about the nature of the Object. ImmediateAccess can also be used to supply a user-presentable string identifying the collection as a whole. This may be different from the advertised data item name, which is obtained by a method in the DataItem interface. The public String getValueAsString( ) method returns an unformatted representation of the data item. For example, if the item represents a dollar value, the string may return just the digits. There is no requirement that the returned string be the same as getValueAsObject( ).getString( ). The public String getPresentationString(java.util.Locale locale) method returns a formatted representation of the data item appropriate for the specified locale. For example, if the item represents a dollar value and the locale indicates the United States, the string may be formatted with a dollar sign, commas, and a decimal point. If locale is null, the consumer is asking for the producer's default locale. The method may throw UnsupportedOperationException to indicate that it does not support the specified locale. The public Object getValueAsObject( ) method returns a reference to the Object that is "wrapped" in this ImmediateAccess. The type of the Object is implementation dependent. A consumer that accesses the Object directly may interrogate it to discover its type, or may examine the MIME type (if available) for this purpose. A producer may choose not to expose an Object by returning null. The public Object setValue(Object newValue) throws InvalidDataException method sets a newValue for the immediate data in this object. It is recommended that all producers accept an ImmediateAccess, if present, as the preferred source of a newValue, but also allow producers to accept other types of Objects that make sense for the application. A producer should not change the type of Object housed in an ImmediateAccess, only its value. The producer's implementation must obtain the new value from the argument object before returning, rather than saving a reference to it, because the caller may change its value after the return. In obtaining the newValue from the argument, the provider's implementation may need to make a deep (recursive) copy of newValue to get all values changed by the consumer, for example when newValue is a collection of subitems. If the item supports change notifications, the producer should notify listeners after the value has changed, but before returning from setValue. Such change notifications look the same as if the producer had changed the value itself. UnsupportedOperationException should be thrown when the underlying data source does not support changes from any consumer (e.g., read-only). java.security.AccessExceptions should be thrown when a given consumer does not have permission to change the data. Java.lang.IllegalArgumentException should be thrown if newValue is a type that is not recognized by the producer for representing the new value. InvalidDataException should be thrown if the new value is invalid for this field. Data items that implement the ArrayAccess interface are collections of data items organized in an n-dimensional array. ArrayAccess objects are bounded in size; that is, their dimensions can be obtained at any time. Essential to the notion of an array is that you have random access (without significant performance penalty) to any element of the array. Almost all the other forms of data could be modeled as a (degenerate) form of an array, but often the notion of unpenalized access to any element does not hold true. Those methods where invalid coordinates are specified can throw ArrayIndexOutOfBoundsException. The method public int{character pullout} getdimensions( ), which throws the ArrayIndexOutOfBoundsException when the coordinates are invalid. The number of integers in the returned array indicates the number of dimensions in the object implementing the ArrayAccess, and the value of each integer indicates the number of elements in the ArrayAccess in that dimension--e.g., a return value of {5,6,7} indicates a 5.times.6.times.7 three-dimensional ArrayAccess. The public Object getItemByCoordinates(int{character pullout} coordinates) method retrieves an individual data item from an ArrayAccess by way of its index. Retrieval of a data item via getItemByCoordinates( ) should not affect the cursor for other access interfaces on the object that implements ArrayAccess. Indexing in each dimension is zero-based; that is, coordinates[i] range from 0 to (getDimensions( )[i]-1), to agree with Java arrays. For mutable ArrayAccess objects, it is preferred that this method return an item that implements ImmediateAccess to allow the item to be changed; or to establish an initial value if the location had no previous value set. The public void setItemByCoordinates(int{character pullout} coordinates, Object newValue) throws InvalidDataException method sets a new value for an item at the indicated coordinates in an ArrayAccess. Setting a data item via this method should not affect Iterators for other access interfaces on the object that implements ArrayAccess. Indexing in each dimension is zero-based; that is, coordinates[i] range from 0 to (getDimensions( )[i]-1), to agree with Java arrays. It is preferred that all producers accept an ImmediateAccess for setting the newvalue. UnsupportedOperationException must be thrown when the underlying data source does not support changes from any consumer. The java.security.AccessExceptions must be thrown when a given consumer does not have permission to change the data. The java.lang.IllegalArgumentException must be thrown if newValue is a type that is not recognized by the producer for representing the new value. InvalidDataException must be thrown if the new value is invalid for this field. The public ArrayAccess subdivide(int{character pullout} startCoordinates, int{character pullout} endCoordinates) method returns an ArrayAccess which is a subset of the ArrayAccess on which it was called, with coordinates in the new subset readjusted to start at zero. For example, a data set arranged as rows and columns can be divided into arrays representing individual columns or rows. The endCoordinates must be equal to or greater than the startCoordinates for all dimensions. The method throws an ArrayIndexOutOfBoundsException on an out-of-bounds access. InfoBus does not define a specific access interface for implementing a tree. Trees should be implemented by using one of the JDK Collection interfaces recursively, e.g. creating a Collection that contains Collection objects, and so on. When ArrayAccess and any of the JDK Collection access interfaces are implemented on the same data item, there may or may not be a relationship between the order of accessing elements using an Iterator or ListIterator and the indices of an ArrayAccess. When using the standard implementations of the collections, it may not be convenient to provide indexed access in an efficient manner. When using the JDK Collection interfaces as a public contract for a private implementation along with ArrayAccess, it is preferred that the right-most integer in the dimensions array be the index that changes most frequently when an object is iterated. For example, an ArrayAccess that returns {5, 4, 3} as its dimension array is a 5.times.4.times.3 array, and successive calls to Iterator.next( ) would return the following elements from ArrayAccess: 0, 0, 0 0, 0, 1 0, 0, 2 0, 1, 0 etc. The task of creating an InfoBus-compliant data item includes making a decision on which of the interfaces to implement. While the InfoBus API requires only that a data item be an Object, there are additional requirements for an InfoBus-compliant data item. Note that the `top-level` item refers to the one handed out via the rendezvous mechanism, which may have sub-items. Data items that are members of a collection data item are referred to here as "sub-items." It is required that top-level data items implement DataItem. It is also recommended to implement DataItem for sub-items whenever possible. DataItemChangeManager is recommended for all data items where it makes sense, including sub-items of multi-level data items. When present, the manager is required to manage listeners and send notifications to them for all changes. An InfoBus-compliant data item is required to provide at least one of the standard access interfaces for top-level data items. It is preferred to use these interfaces for all sub-items. It is preferred that methods used to set a value in a mutable data item accept an ImmediateAccess, if present, as the source of the new value; other Objects can be accepted at the discretion of the producer. Database Access Interfaces In many cases including Relational Database Management Systems, data is organized into (or can be returned as) tables containing rows and columns. Each row has the same number of columns, and each column is homogenous--within a column, the data is of a particular datatype, or null. A table may have no rows. Typically, a server program that controls all access to the data manages the data. To retrieve data from such a source, the client composes a query (typically in a dialect of SQL), submits it to the database server, and receives a result set of rows, or rowset, in return. It is then possible to determine the "shape" of the rowset (the number of columns returned and their names and datatypes). There may be no data rows in the rowset. To send data to such a source, or modify the data, INSERT, UPDATE and DELETE operations are supported. These return a success indicator and the number of rows affected (this may be zero), but not a result set. Other operations are also usually supported, including such things as creating and deleting tables, granting and revoking access, and storage management. These operations return a success indicator but not a result set or number of rows affected. While tables in a database and the rowsets returned from retrieval queries could be modeled as InfoBus ArrayAccess data items, this is not a natural match for the following reasons: The number of rows and columns is not known ahead of time and can be expensive to determine, so ArrayAccess.getDimensions( ) cannot always be supported; A very large number of rows may be returned The column names and datatypes may not be known ahead of time and it may be necessary to discover this at runtime. RowsetAccess interfaces are provided to solve these problems. This family of interfaces can be used for constructing a data item in addition to or instead of other access interfaces defined in the previous chapter. FIG. 11 illustrates the use of a DAC 1110 for serving data to local consumers 1114. When a remote server 1112 provides the data, a data access component (DAC) 1110 can be constructed as a producer that provides RowsetAccess data items. Remote server 1112 communicates with DAC 1110 via a network protocol 1116. DAC 1110 serves as a translator between the remote source 1112 and the local consumers 1114 of the data. The RowsetAccess interfaces represent a different model from the access interfaces previously described above in that the contents of a RowsetAccess item change as the cursor is moved. This difference reflects the orientation of an external, potentially huge data store. The data is not "in" the data access applet or bean, but in another data store, usually on another machine. The interfaces described above are more oriented toward internal data, i.e. data which is "in" an applet or bean running in the local environment such that the total size is known and all the data is immediately available. Also, the RowsetAccess interfaces extend the use of the DataItemChangeEvent in two ways. First, if there are any change listeners on a RowsetAccess item, a RowsetCursorMovedEvent is emitted when the row cursor changes. Second, data items are used to represent column values, and as the row cursor changes, these data items are modified by the data producer and change notifications sent to any listeners. While this is a standard InfoBus mechanism, in this case it is the data producer itself which is changing the values of the items representing columns. Even with these differences, it might make sense to implement both RowsetAccess and ArrayAccess for some data items. For example, if a query results in a set of a hundred rows, the data access component (producer) may choose to make it available via both interfaces. To process a retrieval query, a database server may do extensive work on behalf of the client, using available indexes wisely, constructing temporary tables when appropriate, obtaining and releasing physical locks on data and index pages, etc. The server typically maintains bookkeeping structures as it returns the result rows, and the current row is presented to the client via a "cursor." Servers free up resources as soon as possible in order to serve more clients more efficiently, so generally only one cursor is supported per result set. While some servers support backward scrolling cursors, only forward scrolling cursors are guaranteed. In support of the database notion of a cursor, RowsetAccess implements a slightly different model for data items compared to those described in the Data Items section above. Whereas it looks like a collection of records (rows), when the consumer obtains a row | ||||||
