October 30, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Adding New Transaction Engines to QUALCOMM BREW's IWeb

  • August 14, 2006
  • By Ray Rischpater
  • Send Email »
  • More Articles »

Creating a IWebEng Interface

Creating an IWebEng for use with IWeb involves making an extension; if that sounds new or scary, see my previous article in Developer on the topic. The IWebEng interface has four methods:

  • AddRef: To increase the interface's reference count.
  • Release: To decrement the interface's reference count and free the interface when the reference count goes to zero.
  • QueryInterface: To return EBADCLASS for all arguments.
  • Transaction:To perform the network transaction for IWeb.

To take a closer look, observe a trivial protocol that returns the current date and time; the form of its URL will be date:arbitrary-string.

The interface declaration can be written thus:

typedef struct IDateWebEng IDateWebEng;
AEEINTERFACE(IDateWebEng)
{
   INHERIT_IQueryInterface(IDateWebEng);
   int (*Transaction)(IDateWebEng *piwe, IWeb *piw,
                      IWebReq *piwreq, AEECallback *pcb,
                      IWebResp **ppiwresp);
};
#define IDATEWEBENG_AddRef(p) 
   AEEGETPVTBL((p),IDateWebEng)->AddRef((p))
#define IDATEWEBENG_Release(p) 
   AEEGETPVTBL((p),IDateWebEng)->Release((p))
#define IDATEWEBENG_QueryInterface(p,i,ppo) 
   AEEGETPVTBL((p),IDateWebEng)->QueryInterface((p),(i),(ppo))
#define IDATEWEBENG_Transaction(p,w,q,cb,a)
   AEEGETPVTBL((p),IDateWebEng)->Transaction((p),(w),(q),(cb),(a))

Perhaps oddly, I could not find an INHERIT_IWebEng macro the way INHERIT_IQueryInterface is provided, but it's almost as easy to write out the Transaction method declaration anyway, so that doesn't matter much.

Unfortunately, BREW lets you only inherit interfaces (like Java's implements keyword), so when setting out to create a new interface, you have to do everything from scratch, even if a superclass interface has the same behavior. Thus, we're forced to rewrite the entire constructor and inherited methods such as AddRef and Release.

The constructor is self-explanatory, allocating memory for the object, its vtable, and the necessary structures for performing the request. As previous readers of my articles will know, I'm a big fan of preallocationg resources when I can, because it lets me put the bulk of my error handling in just a few functions:

typedef struct SDateWebEng
{
   int nRefs;
   IShell *pis;
   IWeb *piw;
   IWebReq *piwreq;
   IWebResp *piwres;
   ISourceUtil *pisu;
   char *pszResult;
   AEECallback cb;
   AEECallback *pcbUser;
} SDateWebEng;

static int IDATEWEBENG_New( IShell *pIShell, IDateWebEng **ppOut )
{
   SDateWebEng *p;
   AEEVTBL(IDateWebEng) *modFuncs;
   int16 nSize = sizeof( SDateWebEng ) +
       sizeof( AEEVTBL(IDateWebEng) ) ;
   int result = EBADPARM;

   if ( !pIShell || !ppOut ) return result;
   result = ENOMEMORY;
   p = (SDateWebEng *)MALLOC( nSize );
   if ( !p ) return result;
   ->
   modFuncs = (AEEVTBL(IDateWebEng) *)((byte *)p +
      sizeof( SDateWebEng ));
   modFuncs->AddRef         = DateWebEng_AddRef;
   modFuncs->Release        = DateWebEng_Release;
   modFuncs->QueryInterface = DateWebEng_QueryInterface;
   modFuncs->Transaction    = DateWebEng_Transaction;

   INIT_VTBL(p, IModule, *modFuncs);

   p->nRefs  = 1;
   p->pis    = pIShell;
   p->piw    = NULL;
   p->piwreq = NULL;
   result = ISHELL_CreateInstance( p->pis, AEECLSID_WEBRESP,
      (void **)&p->piwres );
   if ( result == SUCCESS )
      result = ISHELL_CreateInstance( p->pis, AEECLSID_SOURCEUTIL,
                                      (void **)&p->pisu );
   if ( result != SUCCESS )
   {
      DateWebEng_Release( (IDateWebEng *)p );
      return result;
   }
   else
   {
      ISHELL_AddRef( p->pis );
      *ppOut = (IDateWebEng *)p;
      return AEE_SUCCESS;
   }
}

Next up is the implementation of the interface inherited from IQueryInterface; these, too, are very simple:

#define ME_FROM_INTERFACE(p) 
   SDateWebEng *pMe = (SDateWebEng *)p; if ( !p ) return EBADPARM;

static int DateWebEng_AddRef( IDateWebEng *p )
{
   ME_FROM_INTERFACE(p);
   return ++pMe->nRefs;
}

static int DateWebEng_Release( IDateWebEng *p )
{
   ME_FROM_INTERFACE( p );
   if ( --pMe->nRefs == 0 ){ CALLBACK_Cancel( &pMe->cb );
      CALLBACK_Cancel( pMe->pcbUser );
      if ( pMe->pis ) ISHELL_Release( pMe->pis );
      if ( pMe->piw ) IWEB_Release( pMe->piw );
      if ( pMe->piwreq ) IWEBREQ_Release( pMe->piwreq );
      if ( pMe->piwres ) IWEBRESPONSE_Release( pMe->piwres );
      if ( pMe->pisu ) ISOURCEUTIL_Release( pMe->pisu );
      FREEIF( szResult );
      FREE( pMe );
      return 0;
   } else return pMe->nRefs;
}

static int DateWebEng_QueryInterface( IDateWebEng *p, AEECLSID cls,
                                      void **ppOut )
{
   cls;
   if ( !p || !ppOut ) return EBADPARM;
   *ppOut = NULL;
}

The AddRef and QueryInterface interfaces are boilerplate. Release is too, although it's important to remember that I'm releasing result structures passed to the application using IWeb like the IWebResponse, so I need to increment its reference count when I return it to the caller.

The meat of the engine is in its Transaction method, which must perform a single transaction asynchronously. In your case, because you don't use the contents of the URL for anything, it's pretty simple, too:

static int DateWebEng_Transaction(IDateWebEng *p, IWeb *piw,
                                  IWebReq *piwreq,
                                  AEECallback *pcb,
                                  IWebResp **ppiwresp)
{
   ME_FROM_INTERFACE( p );
   pMe->piw = piw;
   IWEB_AddRef( pMe->piw );
   pMe->piwreq = piwreq;
   IWEBREQ_AddRef( pMe->piwreq );
   pMe->pcbUser = pcb;
   IDATEWEBENG_AddRef( p );
   CALLBACK_Init( &pMe->cb, (PFNNOTIFY)DateWebEng_ReturnDate,
                  (void *)pMe );
   ISHELL_Resume( pMe->pis, &pMe->cb );
   *ppiwresp = pMe->piwres;
   IWEBRESP_AddRef( pMe->piwres );
   return SUCCESS;
}

The transaction must:

  • Cache aside the IWeb pointer in case the IWebEng needs to obtain Web options configured by the caller
  • Set aside the Web request pointer piwreq, from which the desired URL can be obtained using its GetUrl method
  • Set aside the result callback, which the IWebEng invokes on completion of the request
  • Set up the asynchronous transaction
  • Incremement the IWebEng's reference count

Why must the IWebEng increment its own reference count? The answer is simple, although not obvious: the IWeb engine releases the IWebEng as soon as its Transaction method is called because it has no other way of knowing when the transaction is complete! Thus, to maintain its scope, it must increment its own reference count and release itself once it's done processing.

Because BREW runs in a single thread on the handset, the IWebEng runs in the same thread as everything else, and it must operate asynchronously via callbacks or IThreads. Typically, you use IWebUtil to parse the URL for information, make your remote connection, and then use the ISocket or ISockPort interfaces (see last month's article "New Network Interfaces in Qualcomm BREW") to perform the client request. When you're done, you wrap up the resulting data in a source and invoke your client's callback:

static void DateWebEng_ReturnDate( IDateWebEng *p )
{
   char *szResult;
   ISource *pis;
   WebRespInfo *pwri;
   JulianType jt;
   ME_FROM_INTERFACE(p);
   // Create a string with the current date.
   szResult = pMe->pszResult;
   GETJULIANDATE( GETTIMESECONDS(), &jt );
   SPRINTF( szResult, "%2d/%2d/%4d %2d:%2d:%2d",
   jt.wMonth, jt.wDay, jt.wYear,
   jt.wHour, jt.wMinute, jt.wSecond );
   // Now make it a source
   ISOURCEUTIL_SourceFromMemory( pMe->pisu, szResult,
                                 STRLEN( szResult ) + 1,
                                 NULL, NULL, (ISource **)&pis );
   // Detatch ourselves from the result
   pMe->pszResult = NULLl
   // Now completely fill out the result structure.
   pwri = IWEBRESP_GetInfo( pMe->piwres );
   pwri->cpszCharset = NULL;
   pwri->cpszContentType = "text/plain";
   pwri->lContentLength = STRLEN( szResult ) + 1;
   pwri->nCode = 200;
   pwri->pisMessage = pis;
   pwri->tExpires = GETTIMESECONDS();
   pwri->tModified = GETTIMESECONDS();
   // Now call the user back
   ISHELL_Resume( pMe->pis, pMe->pcbUser );
   // Clean up after that.
   CALLBACK_Init( &pMe->cb, (PFNNOTIFY)DateWebEng_Release, (void *)p ) ;
   ISHELL_Resume( pMe->pis, &pMe->cb );
}

The code begins simply enough, using a JulianDate structure and SPRINTF to create a time string for the result. You wrap this string in a source using ISourceUtil, making sure to nil your reference to the string so you don't release it, too. Then, you get the response's WebRespInfo pointer, and fill out the results for your client. Finally, you call the client's callback, and then release yourselves asynchronously as well.

For all of this to work, the extension needs to register itself with the BREW shell using either ISHELL_RegisterHandler or by registering the protocol name and class ID in the Module Information File using the type HTYPE_BROWSE.





Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel