http://www.developer.com/ws/brew/article.php/3626146/Adding-New-Transaction-Engines-to-QUALCOMM-BREWs-IWeb.htm
Because BREW is a connected platform, most BREW developers have at least seen IWeb, even if they haven't had the opportunity to use it much. Its interface is both simple and appealing: Set some options, call IWEB_GetResponse with a callback, and a little while later, IWeb invokes your callback with a result and any data retrieved is available on an ISource from which you can read. Posting data is easy, too; just include an ISource in the outbound request containing the data you want to send to a remote server, and IWeb reads the data from the source and sends it to the remote server prior to calling your callback with the result. But what if you need to create a new transaction interface? Perhaps you want a Web-based application to access local and remote data using the same engine, or perhaps you want to develop an application using an Internet protocol such as HTTP or SMTP not supported by BREW. Do you need to create a wholly new interface wrapping or replacing IWeb? Not at all—you can extend IWeb with your protocol, letting clients of your protocol use the same familiar interface provided by IWeb. Even if you don't intend to extend IWeb, it's interesting to understand how IWeb actually works because it provides a good example of how to structure various interrelated components that solve a complex problem in a general way within Qualcomm BREW. Figure 1 shows the relationship among the various interfaces in the IWeb family. As you can see, it isn't actually the IWeb interface that's responsible for handling URL protocols such as HTTP; in fact, the responsibility is handed off to interfaces implementing the IWebEng interface. The IWeb interface searches the system registry for classes registered with the HTYPE_BROWSE base class and specific protocol schemes (instead of MIME types), selecting the class that matches the indicated scheme. Once found, IWeb creates an instance of the class and invokes its Transaction method to perform the necessary request. For all this to work, the class created by IWeb must: The devil is in the details: You need to create a new IWebEng subclass for your protocol. 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: 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: 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: Next up is the implementation of the interface inherited from IQueryInterface; these, too, are very simple: 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: The transaction must: 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: 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. Although extending IWeb is not difficult, it's not for the faint at heart, either. From a distribution perspective, this can be a private extension if you're the only developer planning on using the protocol, or you can make the extension available to others as a public extension. Extending IWeb is best if: Good examples of this sort of behavior include the Internet protocols FTP, SMTP, SNMP, POP, and IMAP. It makes little sense for a connectionless protocol, however, or one that doesn't follow the client-server paradigm, because these don't follow the IWeb API model. Bidirectional streaming might be an example of this, although with enough BREW API subclasses, you certainly could make it work (say, by using an outgoing ISource for the data being streamed from the handset, and an incoming ISource for the incoming data). Although most developers treat IWeb as simply an engine for loading Web, resource, and file resident content, it's possible to do quite a bit more by creating IWebEng subclasses. If you're looking for a way to generalize IWeb, or need a generic interface for client-server activities, check out the relationship among the IWeb, IWebEng, IWebRequest, and IWebResponse classes. Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today's wireless devices. Ray Rischpater is the author of several books on software development, including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active amateur radio operator. Contact Ray at kf6gpe@lothlorien.com.
Adding New Transaction Engines to QUALCOMM BREW's IWeb
August 14, 2006
Peering Under IWeb's Hood

Click here for a larger image.
Creating a IWebEng Interface
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))
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;
}
}
#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;
}
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;
}
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 );
}
Choosing to Extend IWeb
Conclusion
About the Author