Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: /
Creating a Cyberdog Service


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 the CyberStream 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 the IFingerStream 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's Open method to initiate the download. Listing 23 shows the Open 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's GetBuffer 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 the kCDDownloadComplete 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's GetBuffer and ReleaseBuffer methods, respectively, to retrieve and release buffers. The GetBuffer 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;
}
The ReleaseBuffer method of the Finger service stream (not shown) releases the storage held by the buffer.

Terminating a Stream

You must override your Cyberdog stream's Abort method to specify the actions to take when the download operation is terminated. Listing 27 shows the Abort 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 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;
}

Previous Book Contents Book Index Next

© Apple Computer, Inc.
1 JUNE 1996