Important: The information in this document is obsolete and should not be used for new development.
Implementing Your Cyberdog Stream Class
Your Cyberdog stream class implements your service's protocol. Your subclass must override nearly all of the methods defined in theCyberStream
class. Listing 20 shows the SOM class definition for the stream associated with the Finger service.Listing 20 The SOM class definition of the Finger service stream
module AppleCyberdog { interface FingerStream : CyberStream { void FingerStream(in char* url); #ifdef __SOMIDL__ implementation { functionprefix = FingerStream__; override: somInit, somUninit, GetStreamStatus, GetTotalDataSize, GetStreamError, GetStatusString, GetLastModTime, IsTotalDataSizeAvailable, IsLastModTimeAvailable, Open, GetBuffer, ReleaseBuffer, Abort; releaseorder: IFingerStream; #ifdef __PRIVATE__ FingerStreamDatafData; #endif }; #endif }; };The following sections describe these methods.Initializing the Service's Stream Object
Your Cyberdog stream class maintains data needed to implement the service's protocol and data about the status of download operations. Listing 21 shows the data structure for the Finger service's stream. The structure includes accessor functions that are called by methods of the stream's class.Listing 21 The data structure of the Finger service stream
struct CFingerStreamData { CFingerStreamData(); virtual ~CFingerStreamData(); Boolean fAborting; StreamStatus fStatus; StringPtr fStatusString; OSErr fError; ThreadID fThreadID; Ptr fResultsBuffer; long fResultsBufferSize; tcp_endpoint* fTCP; char fHost[256]; char fUser[256]; short fPort; void SetStatusString(StringPtr str); void SetError(OSErr err); void SetResultsBuffer(Ptr buffer, long bufferLen); void GetResultsBuffer(Ptr* buffer, long* bufferLen); };Listing 22 shows theIFingerStream
method of the Finger service stream. This method creates the structure and initializes the structure's fields. You call this method immediately after you create a Finger service stream object.Listing 22 The
IFingerStream
method of the Finger service stream
SOM_Scope void SOMLINK FingerStream__IFingerStream (AppleCyberdog_FingerStream *somSelf, Environment *ev, char* url) { ... CFingerStreamData* data = new CFingerStreamData; if(data) ParseFingerURL(url, data->fHost, data->fUser, & data->fPort); somThis->fData = data; }Opening the Stream
You must override your Cyberdog stream'sOpen
method to initiate the download. Listing 23 shows theOpen
method of the Finger service stream. This method creates a cooperative Thread Manager thread to handle the download.Listing 23 The
Open
method of the Finger service stream
SOM_Scope void SOMLINK FingerStream__Open (AppleCyberdog_FingerStream *somSelf, Environment *ev) { ,,, CFingerStreamData* data = (CFingerStreamData*) somThis->fData; static ThreadEntryProcPtr gFingerThread = nil; const Size kThreadStackSize = 8 * 1024; if(gFingerThread == nil) gFingerThread = NewThreadEntryProc(FingerThread); OSErr err = NewThread(kCooperativeThread, gFingerThread, data, kThreadStackSize, kCreateIfNeeded, nil, & data->fThreadID); }The thread looks up the host's address and establishes a TCP connection with it. The thread then sends a Finger request for the user specified in the URL. When data is returned, it is placed in a buffer. The buffer is retrieved when the stream'sGetBuffer
method is called. See "Getting and Releasing Buffers" (page 33).The thread yields control whenever it waits for a response from the host. Listing 24 shows the loop in the thread that allocates a buffer and reads data into the buffer from the TCP stream.
Listing 24 The thread of the Finger service stream
pascal void* FingerThread(CFingerStreamData* data) { ... while(tcp->IsOpen() && !tcp->IsRemoteClosed() && !data->fAborting) { if(tcp->IsDataAvail() && (data->fResultsBuffer == nil)) { data->SetStatusString(gReadingStr); // Allocate a buffer to read the data into. amt = tcp->AmtDataAvailable(); buffer = (Ptr) ODNewPtr(amt); if(!buffer) {err = memFullErr; goto x_Exit;} // Read the data. err = tcp->Read(buffer, amt, kDefaultTimeout, TCP_Yielder); // Place the buffer into the streams data structure. data->SetResultsBuffer(buffer, amt); CHECK_FOR_ERR; } YieldToAnyThread(); } CHECK_FOR_ERR; data->fStatus |= kCDDownloadComplete; ... }The thread updates the error, status, and status string fields as it executes. When the download operation is complete, the thread applies a logical OR operation on the status field with thekCDDownloadComplete
constant.Listing 25 shows the
SetResultsBuffer
function, which sets the status to indicate that data is available.Listing 25 The
SetResultsBuffer
function of the Finger stream data structure
void CFingerStreamData::SetResultsBuffer(Ptr buffer, long bufferLen) { fResultsBuffer = buffer; fResultsBufferSize = bufferLen; fStatus |= kCDDataAvailable; }Getting and Releasing Buffers
You must override your Cyberdog stream'sGetBuffer
andReleaseBuffer
methods, respectively, to retrieve and release buffers. TheGetBuffer
method of the Finger service stream (not shown) obtains a reference to the stream's data structure and calls the structure's GetResultsBuffer function to update the buffer address and size. The GetResultsBuffer function also resets the fields and sets the status to indicate that data is no longer available after the data has been retrieved.Listing 26 The
GetResultsBuffer
function of the Finger stream data structure
void CFingerStreamData::GetResultsBuffer(Ptr* buffer, long* bufferLen) { *buffer = fResultsBuffer; *bufferLen = fResultsBufferSize; fResultsBuffer = nil; fResultsBufferSize = 0; fStatus &= ~kCDDataAvailable; }TheReleaseBuffer
method of the Finger service stream (not shown) releases the storage held by the buffer.Terminating a Stream
You must override your Cyberdog stream'sAbort
method to specify the actions to take when the download operation is terminated. Listing 27 shows theAbort
method of the Finger service stream. The method terminates the TCP connection.Listing 27 The
Abort
method of the Finger service stream
SOM_Scope void SOMLINK FingerStream__Abort (AppleCyberdog_FingerStream *somSelf, Environment *ev) { ... CFingerStreamData* data = (CFingerStreamData*) somThis->fData; if(!data->fAborting) { data->SetStatusString(gCancellingStr); data->fAborting = true; if(data->fTCP) data->fTCP->Abort(TCP_Yielder); } }Providing Stream Status Information
Your stream class must override methods that provide status information about the stream, as follows:
Listing 28 The
- Override the
IsLastModTimeAvailable
method to return whether or not the time the data was last placed in the stream is available. TheIsLastModTimeAvailable
method of the Finger service stream returnskODFalse
, indicating the time is not available.- Override the
GetLastModTime
method to return the last time that data was placed in the stream. TheGetLastModTime
method of the Finger service stream returnskCDLastModTimeUnknowable
, indicating that the time is not known.- Override the
IsTotalDataSizeAvailable
method to return whether or not the total size of the stream data is available. TheIsTotalDataSizeAvailable
method of the Finger service stream returnskODFalse
, indicating the size is not available.- Override the
GetTotalDataSize
method to return the number of bytes available for downloading. TheGetTotalDataSize
method of the Finger service stream returnskCDTotalDataSizeUnknowable
, indicating that the stream's size is unknown.- Override the
GetStreamError
method to return nontrivial errors during stream operations. TheGetStreamError
method of the Finger service stream returns thefError
field of the CFingerStreamData structure (see Listing 21), which is set by the stream's thread.- Override the
GetStreamStatus
method to return the current flags associated with the stream. TheGetStreamStatus
method of the Finger service stream returns thefStatus
field of the CFingerStreamData structure (see Listing 21), which is updated by the stream's thread.- Override the
GetStatusString
method to specify the current status of the stream. TheGetStatusString
method of the Finger service stream, shown in Listing 28, copies the fStatusString field of the CFingerStreamData structure (see Listing 21), which is updated by the stream's thread, to the message output parameter. The method also clears the kCDStatusStringChanged flag so that subsequent calls toGetStatusString
will not indicate a change. (The flag is set whenever the string is set.)
GetStatusString
method of the Finger service stream
SOM_Scope void SOMLINK FingerStream__GetStatusString (AppleCyberdog_FingerStream *somSelf, Environment *ev, Str255 message) { ... CFingerStreamData* data = (CFingerStreamData*) somThis->fData; if(data->fStatusString) pstrcpy(message, data->fStatusString); else message[0] = 0; data->fStatus &= ~kCDStatusStringChanged; }