In addition, the JCL offers other important features necessary to support Object-Relational DBMSs like PREDATOR. Specifically, the JCL supports:
ServerSite S = new ServerSite("turing.csuglab.cornell.edu", 9700);identifies a database server accepting connections on port 9700 of turing.csuglab.cornell.edu. If the client entity is an applet, it will only be able to make a connection to a database server running on the host from which it was downloaded; this is a restriction imposed by the Java security manager. In this case, the client should supply the following information to the constructor:
ServerSite S = new ServerSite(getCodeBase().getHost(), 9700);(Note that it is possible to run a session with a server which does not run on the machine from which the applet was downloaded. This is achieved by using a JCLReflector object, which acts like a proxy for the client.) Once the database server's site has been identified, a Connection object should be created. Then, open the connection, supplying the site constructed above:
Connection C = new Connection(); try { C.open(S); } catch (java.net.UnknownHostException X) { // The supplied host is unknown; did you spell the host's name correctly? } catch (CannotConnectException X) { // The connection could not be established; is this a PREDATOR server? } catch (ConnectionAlreadyOpenException X) { // The connection object is already open; close it first }
try { Query Q = new Query("SELECT R.pic, R.name FROM Renoir R"); } catch (BadQueryException X) { // The query is improperly formatted }A query is improperly formatted if a semicolon (;) appears anywhere in the query. Semicolons are disallowed because only one query can be associated with a Query object. (Since semicolons delimit queries, a concatenation of two of more queries includes at least one semicolon.)
Then, the query can be submitted via a call to processQuery(Query, boolean):
QueryResult R; try { R = C.processQuery(Q, false); } catch (NoOpenConnectionException X) { // The connection must first be opened } catch (UnterminatedQueryException X) { // The previous query was not terminated via a call to endQuery() }After the query has been processed, the query's transactional context at the server has not yet been deleted, i.e., the transaction has not yet been committed or aborted. This permits callbacks to be guaranteed the same ACID properties queries have. To end the query's transaction at the server, a call must be made to endQuery(). Until this call is made, no new queries can be processed, nor can the connection be closed. (If callback properties are unimportant, the boolean parameter can be set to true, and the query will be ended automatically, as soon as all of the results have been read completely.)
if (R == null) { // There was no result from the query } else if (R instanceof RecordStreamResult) { RecordStreamResult RSR = (RecordStreamResult)R; // Process the record stream } else if (R instanceof UnformattedResult) { UnformattedResult UR = (UnformattedResult)R; // Process the unformatted result }The two result types, RecordStreamResult and UnformattedResult, correspond to the two types of responses sent by a PREDATOR server. When the result is a relation, a RecordStreamResult is returned. When the result is textual data, an UnformattedResult is returned. For example, if the query "SELECT * FROM _STABLES" is submitted, the result is a relation. But is the query "list rels" is submitted, the result is textual.
Unformatted Results
Since unformatted results are textual, the only access method is via the function toString(). For example:
UnformattedResult UR = (UnformattedResult)R; String Text = UR.toString(); System.out.println("Result: " + Text);
It is important to note that the ADT classes are not values. That is to say, the ADT_int object maintained by the JCL is not an integer. Rather, it is a container for information about integers. ADT objects also provide methods to create new DataObjects, which are value objects. For example, the JCL uses the ADT_image object it maintains to create a new ImageObject, which represents an actual image. (A JCL client is never called upon to create new objects; all objects are returned via calls to RecordStreamResult.getNextRecord(SubclassSelector). However, it is important to understand the conceptual distinction between ADT classes and the objects they manage.)
As previously mentioned, the result tuples from queries are arrays of DataObjects. These objects are only minimally functional insofar as their true properties are hidden. The DataObject class defines a single method, toString(ADTMetaInfo), which prints a textual version of the object. For instance, an DoubleObject will print its value, e.g., "3.141592654". (The parameter ADTMetaInfo is mandatory, and is explained below. If this parameter is omitted, the method invoked will be the toString() method of the Java Core Object class, giving unexpected results.)
To exploit hidden functionality of DataObjects, the Java operator instanceof must be used. Although clients may check to see if a specific type has been returned, e.g., an ImageObject, this is not recommended. To permit extensibility, common functionality that data objects offer has been grouped into interfaces, which the data objects can implement. These interfaces include:
DataObject tuple[] = RSR.getNextRecord(null); for (int i = 0; i < tuple.length; i++) { ADTMetaInfo MetaInfo = RSR.getSchema().getAttribute(i).getMetaInfo(); ADT TypeADT = RSR.getSchema().getAttribute(i).getTypeADT(); if (tuple[i] instanceof DisplayObject) { ((DisplayObject)tuple[i]).display(MetaInfo); } else if ((tuple[i] instanceof MIMETypeObject) && (tuple[i] instanceof CallbackObject)) { try { getAppletContext(). showDocument(C.getCallbackObjectURL((CallbackObject)Object, TypeADT, MetaInfo), "Requested Object"); } catch (Exception X) { // Something went wrong } } else System.out.println(tuple[i].toString(MetaInfo)); }
DataObject tuple[] = RSR.getNextRecord(null); for (int i = 0; i < tuple.length; i++) { ADTMetaInfo MetaInfo = RSR.getSchema().getAttribute(i).getMetaInfo(); ADT TypeADT = RSR.getSchema().getAttribute(i).getTypeADT(); DataObject Object = tuple[i]; /* * Now MetaInfo is the meta-information object corresponding to * Object, which is an object of type TypeADT. */ }Meta-information is used in PREDATOR to allow ADTs to maintain domain information. For instance, an attribute of a result relation might contain an attribute of the Image type. The database server knows that objects in the column are images, but nothing more. (This is an artifact of the structure of PREDATOR; the database server knows nothing about images. All work with image objects is performed by the image ADT, which contains all the domain semantics.) Thus, if some semantic information is common to all the objects, it must be stored in every object. To prevent such a waste of space, ADTs are permitted to create meta-information for every attribute. In this object, which is stored with the attribute, the ADT can store information common to all the objects in that column of the relation. For instance, if a table contains a column of images which are known to have the same size and format, e.g., 100 KB JPEG images, this information is stored in the meta-info by the ADT. As a result, whenever one works with data objects, the meta-info of that object must also be present. (Otherwise, all the necessary information would not be available to the ADT when working with the object.)
When the JCL opens a connection, the database server sends over a list of type names which corresponds to the types supported by the server. Using these names, the JCL queries the web server from which it was loaded for the corresponding ADT classes. So, for example, when the database server sends over the type name "audio", the JCL attempts to load the class "PREDATOR.ADT.ADT_audio". If the load is successful, the client can now work with that type. If not, result values of that type are ignored by the JCL.
Error Handling
In working with the JCL, errors crop up from time to time. These errors can come from two different sources: the database server, and the JCL itself.
Server Errors
A database server error may arise if the queries sent are inappropriate.
Error handling in the
PREDATOR server is
asynchronous; as a result, errors can be reported at any time. To check
if the server has noted an error, use the isError() function.
If an error has occurred, the actual error (which provides messages and
stack information) can be obtained via a call to getError().
Applications should check for errors at the following times:
In the (hopefully, rare) instance that the JCL experiences an error not caused by faulty use, a InternalError is thrown. If an internal error occurs in your application, please contact PREDATOR support.
If a result DataObject implements the CallbackObject interface, it is a callback object. To access the object's full value, a callback request must be made. For instance, to access the actual image in a result ImageObject, the server must be contacted.
Callback requests occur via HTTP requests. (Interestingly, these requests are made not of a web server, but directly of the PREDATOR server. The database server recognizes HTTP requests as callbacks, decodes them, and serves the required objects.) To create a connection by which the object can be accessed, a URL is necessary. The JCL provides a way to obtain a URL for a CallbackObject through the getCallbackObjectURL(CallbackObject, ADT, ADTMetaInfo) method.
Once the URL is obtained, it can be used in many ways to fetch the "full value". If the object is a MIME type object and is to be displayed, the URL can be handed to the browser, which will display the object. If the object is to be fetched into memory, a java.net.URLConnection can be created, the the stream can be read into an internal array.
The JCL accomplishes interface extensibility by forcing each ADT's Java
class to export to the system the minimum functionality class (MFC)
for objects of that type, so that the data type will operate properly.
For instance, the polygon data type specifies that all objects it
deals with must at least provide the functionality of the
PolygonObject
class. Interfaces are free to use any subclass of this class they desire.
(The MFC is exported by the method getObjectClass(ADTMetaInfo).)
To implement interface extensibility, interfaces decide on the
extended functionality they require from the data types. For instance,
in the above example, the interface designer might define an interface
as below:
Performing Callbacks
When query results contain large objects, such as images or audio clips, it makes sense to suspend the delivery of these objects until they are requested explicitly by the client. For instance, if a result contains hundreds of large images and a client is likely to view a small fraction of them, the images need not be delivered until the client requests them. When a client requests an object whose delivery has been suspended, the request is termed a callback. In the current implementation, all data types which are "large" require callbacks. This includes images, audio clips, rasters, polygons, etc.
Maintaining Interface Extensibility
Interface extensibility
corresponds to dynamically adding functionality to a set of data types.
For example,
consider the traditional Geographic Information System (GIS) types: points,
polygons, and raster satellite images. In a generic interface, it is
perfectly acceptable for each of these types to display themselves in an
independent manner. Frequently, however, a new interface requires the
old data types to behave differently. For example, a GIS interface
which deals with California-specific data might wish to display the points,
polygons, and rasters properly situated on a map of California. How is
this to be accomplished if each data type describes how it is to be displayed
independently?
public interface CaliforniaObject {
public void DrawYourself(CaliforniaMap C);
}
The designer then procedes to subclass the MFC of each ADT from
which she desires the added functionality. Continuing
the example, the designer would implement a class as below:
public class CaliforniaPolygon extends PolygonObject implements CaliforniaObject {
public void DrawYourself(CaliforniaMap C)
{
/* ... */
}
}
for each of the data types which will support the new functionality.
These classes are called added functionality classes (AFC).
Finally, the designer must inform the JCL object that the
AFCs should be used as a substitutes for the MFCs they correspond to.
This is accomplished by providing a SubclassSelector to calls to getNextRecord(SubclassSelector)
that maps MFCs to their corresponding AFCs.
For instance, the mapping might be, "Given class foo, if the
class P(foo) exists, use it instead." The appropriate precautions
to insure the
mapping preserves each ADT's minimum functionality are taken, and then
the AFC objects are substituted for objects of the MFC.
Application Programmer's Interface
The API for the JCL has been automatically generated using javadoc. Is it available in HTML format. If you are interested in browsing the API to familiarize yourself with the JCL, we recommend that you begin with the PREDATOR.Connection object, which is the main interface to the JCL for any Java program.
Back to PREDATOR Home Page