OLE-DB Simple Provider for Java: A Data Binding API for MSHTML

MSHTML, introduced in Microsoft Internet Explorer 4.0, supports a very simple, easily implemented API for exposing data to HTML pages. The API supports access to string and variant data types and is built using array-like structures to expose rows and columns. Third parties can use the OLE-DB Simple Provider API to implement their custom data source objects.

  • Introduction
  • OLE-DB Simple Provider
    • Data References
    • Schema Functions
    • Variant-Oriented Functions
    • Deletion and Insertion
    • Searching
    • Asynchronous Data Population
    • Internationalization
    • Sinking Events
  • OLE-DB Simple Provider Listener
    • Notes
    • Notes
    • Notes
    • Notes
    • Notes
    • Notes
    • Notes for all events functions
  • Interface Summary
  • Exceptions

Introduction

OLE-DB Simple Provider (OSP) is a JavaBeans interface specification that hosts can implement to expose their tabular data to HTML elements in Internet Explorer 4.0/MSHTML. OSP specifies only simple, minimal functionality that requires only minimal effort on the part of applet authors to expose their data. OSP specifies methods for adding, deleting, and setting the value of columns, searching through the set of data, and receiving notifications on data events.

OLE-DB Simple Provider

OSP exposes a set of tabular data (data set) in an array-like structure. Elements of the data set are referenced by their row and column indexes.

Data References

Indexes begin with value 1 and increase. There are two special index values:

0 Reserved for label information in the case of a row, and header information in the case of a column.
-1 The wildcard value. Refers to an unknown value or all values. For example, Row=-1, Col=2 refers to the entire column 2.

OSP defines two distinct interfaces: OLEDBSimpleProvider and OLEDBSimpleProviderListener. As you would expect, OLEDBSimpleProvider implements the methods used for data access, and OLEDBSimpleProviderListener specifies the methods to be implemented by the host for receiving notification of data changes.

Schema Functions

Three functions are provided for obtaining the schema for the underlying rows or columns.

Function Description
public int getRowCount( ) throws OSPException Returns the number of rows in the data set.
public int getColumnCount( ) throws OSPException Returns the number of columns in the data set.
public int getRWStatus(int iRow, int iColumn) throws OSPException Returns the read/write status for the specified row and column. If one or both of iRow and iColumn have a value of -1, the read/write status is returned for the entire column, row, or complete data set.

OSPRW is an interface that defines the following return values.

public interface OSPRW
{
    public static final int OSPRW_DEFAULT = 1;
    public static final int OSPRW_READONLY = 0;
    public static final int OSPRW_READWRITE = 1;
    public static final int OSPRW_MIXED = 2;
}

The definitions for the values are:

OSPRW_READONLY Cell, row, column, or data set is read-only.
OSPRW_READWRITE Cell, row, column, or data set can be modified.
OSPRW_MIXED Cell, row, column, or data set read-write status unknown or row, column or data set mixed status.

Variant-Oriented Functions

Two functions are provided to allow retrieval and setting of variant values in the data set.

Function Description
public Object getVariant(int iRow, int iColumn, int fFormatted) throws OSPException Returns the value of the cell at the row and column indicated. Based on the value specified in fFormatted, the returned value will contain either a value with the type of the underlying column, a string that corresponds to the underlying type of the column, or an HTML fragment that corresponds to the underlying type of the column.
public void setVariant(int iRow, int iColumn, int fFormatted, Object objVal) throws OSPException Sets the value of the cell at the row and column indicated to the value of the Object supplied in objVal in the type requested by the value of fFormatted.

OSPFORMAT is an interface that defines the following values:

public interface OSPFORMAT
{
    public static final int OSPFORMAT_RAW = 0;
    public static final int OSPFORMAT_DEFAULT = 0;
    public static final int OSPFORMAT_FORMATTED = 1;
    public static final int OSPFORMAT_HTML = 2;
}

The definitions for the values are:

OSPFORMAT_RAW The underlying type of the column should be used to either set the value or get the value.
OSPFORMAT_FORMATTED The underlying type of the column should be converted to a string and the string returned as a BSTR within the variant.
OSPFORMAT_HTML The underlying type of the column should be converted to an HTML string.

Notes

  • Valid Java class types for these interfaces are Integer, Long, Float, Double, String, Boolean, Date, and null.
  • Types other than those listed immediately above will be represented as String by calling the toString function on the class.
  • For setVariant with any of the formatting options, it is the provider's responsibility to perform the appropriate coercion to the underlying type of the column. In cases where the coercion cannot be successfully completedd, an appropriate exception should be thrown.
  • For setVariant with OSPFORMAT_FORMATTED, the provider is free to interpret the input string however it sees fit. Alternatively, the provider may attempt to parse the input string and heuristically determine an appropriate data type and value to place into the cell—for example, when updating formatted spreadsheet cells.
  • When OSPFORMAT_HTML is requested, the provider can, at its option, return a plain string (unadorned with HTML tags and so on).
  • A database NULL is indicated by returning a null.
  • Wildcard values (-1) are not valid for the iRow and iColumn attributes in setVariant or getVariant. That is, only a single value can be returned/set from either call.

Deletion and Insertion

Functions are provided to insert and delete rows. For all functions, the row numbers provide the position within the data set to which the row will be inserted. That is, the row inserted will have the index specified.

Function Description
public int deleteRows(int iRow, int cRows) throws OSPException Deletes cRows from the data set starting at row iRow. The number of rows successfully deleted is returned.
public int insertRows(int iRow, int cRows) throws OSPException Inserts cRows into the data set starting at row iRow. The number of rows successfully inserted is returned.

Notes

  • In the case of an error, the provider can choose to insert or delete rows up to the point where the error occurred. In such a case, the return value specifies the number of rows actually inserted or deleted.
  • Values for the inserted rows are not specified. Use the setVariant function to set the value of the cells inside rows that were inserted.

Searching

A function is provided that supports some basic searching capability on the data set. The goal of this function is to be fairly easy for the provider to implement while providing the client with a meaningful set of functionality to perform complex searching.

Function Description
public int find(int iRowStart, int iColumn, Object objVal, int findFlags, int compType) throws OSPException Searches for the values specified in objVal in column iColumn starting at row iRowStart. Upon successful completion, the row number with the matching value is returned in piRowFound.

The OSPFIND interface defines the values for findFlags as follows:

public interface OSPFIND
{
    public static final int OSPFIND_DEFAULT = 0;
    public static final int OSPFIND_UP = 1;
    public static final int OSPFIND_CASESENSITIVE = 2;
}

The definitions for the values are:

OSPFIND_UP Specifies scan should be decreasing in row number.
OSPFIND_CASESENSITIVE Specifies that search should be case sensitive.

Similarly, the OSPCOMP interface defines the values for COMpType as follows:

public interface OSPCOMP
{
    public static final int OSPCOMP_EQ = 1;
    public static final int OSPCOMP_DEFAULT = 1;
    public static final int OSPCOMP_LT = 2;
    public static final int OSPCOMP_LE = 3;
    public static final int OSPCOMP_GE = 4;
    public static final int OSPCOMP_GT = 5;
    public static final int OSPCOMP_NE = 6;
}

The definitions for the values are:

OSPCOMP_LT Specifies the first value less than objVal is being searched for.
OSPCOMP_LE Specifies the first value less than or equal to objVal is being searched for.
OSPCOMP_GT Specifies the first value greater than objVal is being searched for.
OSPCOMP_GE Specifies the first value greater than or equal to objVal is being searched for.
OSPCOMP_EQ Specifies the first value equal to objVal is being searched for.
OSPCOMP_NE Specifies the first value not equal to objVal is being searched for.

Notes

  • Find does compares using the native type of the underlying column. Providers are encouraged to simply fail when the type of the objVal differs from the underlying type of the column.
  • Find supports the same object types for objVal as setVariant and getVariant.
  • comparing string values always compares values as they would be returned from a call to getVariant with OSPFORMAT_FORMATTED. Providers should not coerce values of objVal obtained from a previous call to getVariant with OSPFORMAT_HTML.
  • If no corresponding match is found, Find should return the constant -1.

Asynchronous Data Population

The main function of an OSP is to provide data to an HTML page. In many cases, the data will need to be transported over high-latency, low bandwidth networks, in most cases using a 28.8K modem. The OSP design accounts for making data available as expediently as possible while maintaining an architecture and interface specification that is easy to author.

All OSP authors are encouraged to implement support for populating (or retrieving) the data they expose asynchronously. This allows the host to continue processing during data transmission instead of blocking on a call to the OSP until the entire data set has been transmitted to the client.

The majority of the work required to support asynchronous data delivery does not involve the OSP interface specifically—it is a requirement on the mechanisms to retrieve data from their underlying storages. Accordingly, three methods are required in the OSP interface: one to indicate whether the provider supports asynchronous population; one to return the progress of the asynchronous population; and a third to stop the asynchronous transmission of data.

Two additional events are added to the OSP event notifications. One event indicates when additional data has beComponent Object Model (COM)e available for access through the interface, while the other signals that the asynchronous data population has completed. Both events are specified below in the OLE-DB Simple Provider Listener section.

Function Description
public boolean isAsync( ) throws OSPException Returns a Boolean value indicating whether data is being populated asynchronously.

Notes

The isAsynch event should never block; it should always return the appropriate state.

Function Description
public int getEstimatedRows( ) throws OSPException Returns the estimated total number of rows in the data set. Callers can use this in conjunction with getRowCount with an asynchronous provider to obtain an estimate of the percentage of data received.

Notes

  • When the number of rows in the data set cannot be estimated, the provider should return the special value -1.
  • Synchronous providers should not block on the call to getEstimatedRows. They should either return -1 to indicate that they cannot provide an estimate, or the actual row count (same as would be obtained through getRowCount).
  • Providers should not return the final row count from estimatedRows prior to firing the transfercomplete event (see below).
Function Description
public void stopTransfer( ) throws OSPException Requests that the OSP discontinue asynchronous transfer of data.

Notes

  • Providers should make every effort to expose the data transferred prior to receiving this request. The provider must expose a valid, consistent OSP interface for such data—appropriate column/row count, read/write status, and so on.
  • Providers that cannot expose the data transferred prior to this request should discontinue the transfer of data and fail all subsequent calls to the OSP interface.

Internationalization

Implementers might have to consider cases where the consumer is running in a different locale from the source of the data. The provider is responsible for ensuring that data conversions are done in an appropriate fashion. In particular, be careful in cases where the string representation of many data types differs according to locale. For example, the float or double number 3.14 would be "3.14" in the US, and "3,14" in Germany. Date representations differ even more. For example, number (especially float, date) to string conversions done by getVariant or setVariant with the OSP_FORMATTED option specified should usually be done in the locale of the browser.

The Find method has more complicated issues to deal with to implement its ordered comparisons correctly across locale boundaries. These comparisons should, where possible, also be done in the locale of the browser, although this issue is not as clear cut. (For example, if the data consists of strings containing accented characters, it is not clear that a browser in the English-United States (en-us), locale has any preferred sorting order for these strings. The sorting order of the provider locale might be more useful.)

It is anticipated that there will be cases where the consumer will need to know the locale of the data in order to perform proper operations on that data. A method is provided for the provider to return this information.

Function Description
public String getLocale( ) throws OSPException Returns a string that indicates the locale of the data. This is used to perform the appropriate type conversions.

Notes

  • The getLocale event should simply return the empty string ("") when the provider does not support localization. This will allow the consuming entity to use the default from the operating system.
  • For all return values the category LC_ALL is assumed by the consumer.

Sinking Events

Methods are provided to attach and detach a unicast event handler for all events.

Function Description
public void addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener objOSPL) throws OSPException Registers the event handler interface specified by objOSPL to receive notifications of changes to data.

Notes

  • If an event handler was specified previously, addOLEDBSimpleProviderListener replaces the previous event handler.
  • OSP events are unicast only. There can be only one listener.
Function Description
public void removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener objOSPL) throws OSPException Unregisters the event handler interface specified by objOSPL.

Notes

The removeOLEDBSimpleProviderListener event should always specify the objOSPL parameter previously registered. In the event that objOSPL differs from the event handler registered, the call should fail.

OLE-DB Simple Provider Listener

OSP implementers fire notifications of changes to the underlying data through a single event handler. There are pre- and post-notifications for each event.

The event handler, registered through addOLEDBSimpleProviderListener, should implement the following methods.

Function Description
public void aboutToChangeCell(int iRow, int iColumn) throws Exception Indicates that the cell indicated is about to change. This notification is fired when values are modified through setVariant or internally by a provider. The aboutToChangeCell event should fire before any data values are changed.

Notes

  • Calls to getVariant should return the original, unchanged value(s) prior to the return of control from firing the aboutToChangeCell event.
  • In aboutToChangeCell, the value -1 can be specified for iRow, iColumn, or both.
Function Description
public void cellChanged(int iRow, int iColumn) throws Exception Indicates that the cell indicated was changed. This notification is fired when values are modified through setVariant or internally by a provider.

Notes

  • The cellChanged event should be fired after the data value(s) has changed. All subsequent calls to getVariant should return the new value(s).
  • In cellChanged, the value -1 can be specified for iRow, iColumn, or both.
Function Description
public void aboutToDeleteRows(int iRow, int cRows) throws Exception Indicates that cRows beginning at row iRow are about to be deleted. This notification is fired just prior to the rows being deleted either from a call to deleteRows or internally by the provider.

Notes

Access to cells in the deleted rows should remain valid until control is returned from the aboutToDeleteRows function.

Function Description
public void deletedRows(int iRow, int cRows) throws Exception Indicates that cRows were deleted beginning at row iRow. This notification is fired when rows are deleted through deleteRows or internally by a provider.

Notes

The deletedRows event should be fired after the rows are deleted.

Function Description
public void aboutToInsertRows(int iRow, int cRows) throws Exception Indicates that cRows beginning at row iRow are about to be inserted. This notification is fired just prior to the rows being inserted either from a call to the insertRows event or internally by the provider.
public void insertedRows(int iRow, int cRows) throws Exception Indicates that cRows were inserted beginning at row iRow. This notification is fired when rows are inserted through the insertRows event or internally by a provider.
public void rowsAvailable(int iRow, int cRows) throws Exception Indicates that cRows beginning at iRow are now available. This is useful for providers that obtain and expose their data asynchronously. The event notifies consumers that additional data is available.

Notes

OSP consumers expect periodic notifications when data is available since they will likely be displaying or processing the data as it arrives. Providers should weigh the advantages of code simplification by firing the event once for each row against the performance impact of doing so.

Function Description
public void transferComplete(int doneReason) throws Exception All providers must fire this event to indicate that the data transfer is complete. The event should be fired after the entire data set exposed by the provider is available. The doneReason parameter indicates whether the population was completed successfully, aborted, or was stopped.

The OSPXFER interface defines values for the doneReason parameter as follows:

public interface OSPXFER
{
    public static final int OSPXFER_compLETE = 0;
    public static final int OSPXFER_ABORT = 1;
    public static final int OSPXFER_ERROR = 2;
}

The definitions for the values are:

OSPXFER_COMPLETE Indicates that data transmission is completed successfully.
OSPXFER_ABORT Indicates that the data transmission is completed due to a call to the Stop method.
OSPXFER_ERROR Indicates that the data transmission completed in an error state.

Notes

For providers that retrieve their data asynchronously, this event signifies that no additional rowsAvailable events will be called for this data set.

Notes for all events functions

If your provider does not require any additional processing, it is not necessary to catch exceptions from the event routines. The unhandled exceptions will return up through to the setVariant, or [insert|delete]Rows call from which it initiated. The exception should be handled there.

Interface Summary

All interfaces are part of the COM.ms.osp package. The interfaces are as follows:

public interface OLEDBSimpleProvider
{
    public int getRowCount( ) throws OSPException;
    public int getColumnCount( ) throws OSPException;
    public int getRWStatus(int iRow, int iColumn) throws OSPException;
    public Object getVariant(int iRow, int iColumn, int fFormatted) throws OSPException;
    public void setVariant(int iRow, int iColumn, int fFormatted, Object obj) throws OSPException;
    public int deleteRows(int iRow, int cRows) throws OSPException;
    public int insertRows(int iRow, int cRows) throws OSPException;
    public int find(int iRowStart, int iColumn, Object objVal, int findFlags, int compType)
        throws OSPException;
    public boolean isAsync( ) throws OSPException;
    public int getEstimatedRows( ) throws OSPException;
    public void stopTransfer( ) throws OSPException;
    public String getLocale( ) throws OSPException;
    public void addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener objOSPL)
        throws OSPException,
    java.util.TooManyListenersException;
    public void removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener objOSPL)
        throws OSPException;
}

abstract interface OLEDBSimpleProviderListener
{
    public void aboutToChangeCell(int iRow, int iColumn) throws Exception;
    public void cellChanged(int iRow, int iColumn) throws Exception;
    public void aboutToDeleteRows(int iRow, int cRows) throws Exception;
    public void deletedRows(int iRow, int cRows) throws Exception;
    public void aboutToInsertRows(int iRow, int cRows) throws Exception;
    public void insertedRows(int iRow, int cRows) throws Exception;
    public void rowsAvailable(int iRow, int cRows) throws Exception;
    public void transfercomplete(int doneReason) throws Exception;
}

public interface OSPRW
{
    public static final int OSPRW_DEFAULT = 1;
    public static final int OSPRW_READONLY = 0;
    public static final int OSPRW_READWRITE = 1;
    public static final int OSPRW_MIXED = 2;
}

public interface OSPFORMAT
{
    public static final int OSPFORMAT_RAW = 0;
    public static final int OSPFORMAT_DEFAULT = 0;
    public static final int OSPFORMAT_FORMATTED = 1;
    public static final int OSPFORMAT_HTML = 2;
}

public interface OSPFIND
{
    public static final int OSPFIND_DEFAULT = 0;
    public static final int OSPFIND_UP = 1;
    public static final int OSPFIND_CASESENSITIVE = 2;
}

public interface OSPCOMP
{
    public static final int OSPCOMP_EQ = 1;
    public static final int OSPCOMP_DEFAULT = 1;
    public static final int OSPCOMP_LT = 2;
    public static final int OSPCOMP_LE = 3;
    public static final int OSPCOMP_GE = 4;
    public static final int OSPCOMP_GT = 5;
    public static final int OSPCOMP_NE = 6;
}

public interface OSPXFER
{
    public static final int OSPXFER_COMPLETE = 0;
    public static final int OSPXFER_ABORT = 1;
    public static final int OSPXFER_ERROR = 2;
}

Exceptions

OSP supports a number of exceptions. They are defined as follows:

AccessDeniedException An inability to read/write the data has occurred.
ConversionException An error occurred attempting to coerce data types
IllegalArgumentException A method was passed an invalid or inappropriate argument or was invoked on an inappropriate object
NotImplementedException The functionality of a called method is not implemented
OSPException A failure has occurred calling an OSP method. This is used in all cases not covered by the previous exceptions