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 theCyberStreamclass. 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 theIFingerStreammethod 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
IFingerStreammethod 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'sOpenmethod to initiate the download. Listing 23 shows theOpenmethod of the Finger service stream. This method creates a cooperative Thread Manager thread to handle the download.Listing 23 The
Openmethod 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'sGetBuffermethod 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 thekCDDownloadCompleteconstant.Listing 25 shows the
SetResultsBufferfunction, which sets the status to indicate that data is available.Listing 25 The
SetResultsBufferfunction 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'sGetBufferandReleaseBuffermethods, respectively, to retrieve and release buffers. TheGetBuffermethod 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
GetResultsBufferfunction of the Finger stream data structure
void CFingerStreamData::GetResultsBuffer(Ptr* buffer, long* bufferLen) { *buffer = fResultsBuffer; *bufferLen = fResultsBufferSize; fResultsBuffer = nil; fResultsBufferSize = 0; fStatus &= ~kCDDataAvailable; }TheReleaseBuffermethod of the Finger service stream (not shown) releases the storage held by the buffer.Terminating a Stream
You must override your Cyberdog stream'sAbortmethod to specify the actions to take when the download operation is terminated. Listing 27 shows theAbortmethod of the Finger service stream. The method terminates the TCP connection.Listing 27 The
Abortmethod 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
IsLastModTimeAvailablemethod to return whether or not the time the data was last placed in the stream is available. TheIsLastModTimeAvailablemethod of the Finger service stream returnskODFalse, indicating the time is not available.- Override the
GetLastModTimemethod to return the last time that data was placed in the stream. TheGetLastModTimemethod of the Finger service stream returnskCDLastModTimeUnknowable, indicating that the time is not known.- Override the
IsTotalDataSizeAvailablemethod to return whether or not the total size of the stream data is available. TheIsTotalDataSizeAvailablemethod of the Finger service stream returnskODFalse, indicating the size is not available.- Override the
GetTotalDataSizemethod to return the number of bytes available for downloading. TheGetTotalDataSizemethod of the Finger service stream returnskCDTotalDataSizeUnknowable, indicating that the stream's size is unknown.- Override the
GetStreamErrormethod to return nontrivial errors during stream operations. TheGetStreamErrormethod of the Finger service stream returns thefErrorfield of the CFingerStreamData structure (see Listing 21), which is set by the stream's thread.- Override the
GetStreamStatusmethod to return the current flags associated with the stream. TheGetStreamStatusmethod of the Finger service stream returns thefStatusfield of the CFingerStreamData structure (see Listing 21), which is updated by the stream's thread.- Override the
GetStatusStringmethod to specify the current status of the stream. TheGetStatusStringmethod 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 toGetStatusStringwill not indicate a change. (The flag is set whenever the string is set.)
GetStatusStringmethod 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; }