Reusing the ATL Server Stencil Processor


The stencil processor is the feature of ATL Server that allows content generation (usually an HTML response) based on the stencil SRF files. This mechanism allows separating the static part of the response from the dynamic content. For example, the HTML payload required to describe a table and the font and the background color of the cells is completely separated in the SRF files from the actual values inside those table cells . This separation offers the following benefits:

  • The response format can be easily changed. The response page can be changed to have a different look and feel as the whole site gets updated, while the processing code that fills in the actual information doesn t need to be touched.

  • Localization of the response is now an easy task. It doesn t involve touching the code base ”only the static part of the SRF has to be modified. Alternately, different localized versions of the same SRF file can be stored on the server to generate responses in different languages.

  • The developers of the static part (the GUI) and those of the processing part (the engine) can focus on their specific areas without worrying much about how their work will affect the other side.

Now, these advantages, although particularly significant in the context of a Web application, can t be ignored in other applications either. Just consider the large class of desktop applications that generates reports or must, somehow, generate formatted output. printf or CString::Format are pretty powerful formatters, but you can t use them to render 1,000 records coming from a local database. This section shows how you can use the ATL Server stencil processor to generate some completely non-HTML output in a stand-alone application.

How the ATL Server Stencil Processor Works

First, although we re talking about the stencil processor, ATL Server actually provides two stencil processors. The MSDN documentation for SRF files (see ms-help://MS.VSCC/MS.MSDNVS/vccore/html/ vcconATLServerServerResponseFileDefined.htm ) distinguishes between stencil files and SRF files. There s a basic stencil processor that implements the basic processing code for stencil files, and there s an HTML stencil processor for SRF files.

The HTML stencil processor is tightly integrated with the HTTP ISAPI extension architecture of ATL Server. This section deals with the basic stencil processor, so we ll just say a few words on the HTML stencil processor and then we ll focus on the basic one. The HTML stencil processor inherits from the basic stencil processor and adds some functionality that s very useful in the context of generating HTML response. Among these extensions are

  • The {{handler}} element, which you use to indicate which application DLL should be associated with the current stencil file

  • The {{subhandler}} element, which allows you to use content-generating code from a different source (i.e., a different application DLL)

  • The {{include}} element, which allows you to include external content sources (HTML documents, ATL Server application DLLs, or other SRF files) inside the document being generated

A good reason for enumerating the features in the preceding list is that they are not supported in the basic stencil generator. Of course, you could implement them in another processor inheriting from the basic one, following the model of the HTML stencil.

So, let s see what the basic stencil processor can do. It implements the basic mechanism of content generation:

  • It supports the replacement tags. For example, you can use it to associate the element {{ Name }} with some custom OnName function to have the function called whenever the element is encountered .

  • It supports elements with parameters. For example, you could associate the {{Person}} element with the OnPerson(LPSTR strParam) function and have OnPerson("Name") called whenever {{Person(Name)}} is encountered.

  • It supports the basic processing directives: {{if }} , {{else}} , {{endif}} , {{while}} , and {{endwhile}} .

The stencil processing mechanism is implemented in the CStencil class (defined in the atlstencil.h file). This class can take care of loading a stencil document from a file, from a resource, or from a string, and parsing the stencil. Provided with an object that contains replacement methods, it can also walk the parsed stencil document and call into the replacement methods .

To interoperate with the CStencil class, an object containing replacement methods has to implement the ITagReplacer interface. This interface is used in two moments of the stencil processing. First, some of its methods (such as FindReplacementOffset ) are called during parsing of the stencil to make sure that all the replacement elements have an associated replacement method. This is to ensure that the {{Name}} element (for example) has the OnName method associated with it. Then, during the actual rendering, the RenderReplacement method is called to actually invoke the replacement method.

It s easy to realize that by implementing ITagReplacer directly, you ll end up with a big switch block that returns a function address or calls a function after comparing an input string (the element name) with a set of hard-coded values (the replacement elements supported by the implementation).

To help the implementers, ATL Server provides the ITagReplacerImpl template class, which hides all the implementation details behind macros such as

 BEGIN_REPLACEMENT_METHOD_MAP(Replacer)          REPLACEMENT_METHOD_ENTRY("Name", OnName)   END_REPLACEMENT_METHOD_MAP 

This macro system is far more readable and maintainable .

So, instead of implementing the ITagReplacer interface, the developer of a class containing the replacement methods faces the far more reasonable challenge of inheriting from the ITagReplacerImpl < > base class and focuses on his or her replacement handlers, as follows :

 class CMyReplacementMethodsHolder:          public ITagReplacerImpl< CMyReplacementMethodsHolder> 

Now you can use the CStencil class to both parse and render the stencils. Next , you ll look at what you have to do to use the CStencil processor outside the regular HTML rendering context.

The next question that arises is Where is the content rendered? If you look at the definition of the CStencil class, you notice the Render method which, as you may expect, will render the actual content. Render is defined as follows:

 virtual HTTP_CODE Render(ITagReplacer *pReplacer,          IWriteStream *pWriteStream,          CStencilState* pState = NULL) 

The first parameter is the ITagReplacer implementation, which contains the replacement methods, as we ve already discussed. The second parameter is an IWriteStream implementation, an interface defined as follows:

 __interface IWriteStream  {      HRESULT WriteStream(LPCSTR szOut, int nLen, DWORD *pdwWritten);      HRESULT FlushStream();  }; 

You can implement this interface to write in almost any media. You could write by using an EXTENSION_CONTROL_BLOCK function (in the ISAPI context), or you could write into a socket, a pipe, a file, or the standard output.

The last parameter of the Render function is a CStencilState structure pointer. CStencilState is used to provide state information useful in the ISAPI context. As we don t cover the ISAPI context here, and the parameter is defaulted to NULL , we ignore it in this section.

We use the same approach in this section as we did in the previous section, meaning we present a sequence of steps to be performed to support stencil rendering in a non-Web application and then demonstrate each step in the sample provided for this section.

To use the stencil processor outside of the HTTP context, an application should

  1. Provide an implementation of the IWriteStream interface that will render the content to the desired media.

  2. Provide an implementation of the ITagReplacer interface, most likely by deriving a class from the ITagReplacerImpl helper class, and put into this class the handling methods for the stencil s replacement tags.

  3. Instantiate a CStencil object.

  4. Parse/render the stencil.

In the next section we analyze these steps in the associated sample, StanaloneStencilProc .

Stencil Processor Sample

As we stated in the beginning of this chapter, we show you how to use the stencil in a completely non-HTML- related way. For this purpose, the sample is a wizard for authoring SOAP servers and clients . The application allows users to design a Web service through some (hopefully) clear and intuitive mouse actions, and then it generates the code for the SOAP server and for the client. It goes the extra mile in helping (or annoying) the developer by generating valid code and providing suggestions on how/where the SOAP parameters should be used, allocated, or freed.

The sample builds an in-memory list of all the functions of the Web service, each function containing the list of parameters. Then it uses this list to generate the code for the client and for the server. To make the code more readable, the Microsoft Foundation Classes (MFC) interface is almost completely separated from the actual file rendering code. The only files that are involved in rendering are ContentGenerator.h and SoapWizard.h.

The stencils used for rendering the server and client files are in the sample s Templates subfolder. The code for the actual server and client is rendered based on the files having one of the following extensions: .cpp.srf or .h.srf. The sample also generates a Visual Studio .NET solution file and two project files, but they might not be valid in upcoming versions of Visual Studio.

Let s start by looking at the stencil files in the Templates subfolder. The important code resides in TemplateClient\TemplateClient.cpp.srf and in TemplateServer\TemplateServer.srf. The other files aren t very important and don t contain many replacement tags.

start sidebar
Why Are the Stencil Replacement Tags So Poorly Indented?

The replacement tags indentation often looks weird or just plain ugly. For example, the following fragment

 {{while SelectNextParam}}  {{if Param(IsIn)}}        {{Param(Name)}};  {{endif}}{{endwhile}} 

would be so much better looking and readable if it was indented like this:

 {{while SelectNextParam}}          {{if Param(IsIn)}}                  {Param(Name)}};          {{endif}}  {{endwhile}} 

Well, the reason for this indenting scheme is to ensure that the result code will be good looking. For a function like the following:

 HRESULT DoSomething([in]int a, [out]int* pb, [in]int c) 

the first stencil fragment will render this:

 a;      c; 

while the second will render the following code (empty lines are intentional here):

 a;      c; 
end sidebar
 
Note  

A {{while}} {{endwhile}} block will render each and every character between these two replacement tags, including the new lines and white spaces. The same applies to an {{if}} {{endif}} statement.

The main stencil processor code lies in the ContentGenerator.h file. It starts with the CWriteStreamOnFile class, an implementation of the IWriteStream interface that allows content rendering into a file using the C++ streams.

Besides the required WriteStream and Flush methods, it provides operator < < implementations for some very common types such as LPCTSTR , CString & , and unsigned int , which are widely used in the replacement handler implementation.

The second component is the CSAHandler class (the StandAlone replacement handler), which implements the ITagReplacer interface by inheriting from ITagReplacerImpl . The class holds a pointer to the stream to render to and contains a replacement map, a set of REPLACEMENT_METHOD_ENTRY macros that map internal methods to replacement tags that can occur inside a stencil.

In this particular implementation, CSAHandler also controls the CStencil object. It provides a single entry-point for rendering (the renderFile method) that executes steps 3 and 4 in the algorithm described previously for supporting stencil processing in non-Web applications.

The main code path (with error checking removed for clarity) for what renderFile does is as follows:

 CStencil     stencil;  hRet    =    stencil.LoadFromFile(szSRFFile);  bRet    =    stencil.ParseReplacements(this);  stencil.FinishParseReplacements();  hRet    =    stencil.Render(this, pStream); 

The function calls perform the following actions:

  • LoadFromFile loads the stencil. It could be replaced with LoadFromResource or LoadFromString .

  • ParseReplacements maps the replacement tags inside the stencil to actual methods inside the ITagReplacer object passed as parameter (in this case, the CSAHandler object itself).

  • FinishParseReplacements ensures the correctness of the parsed stencil (it ensures that all of the {{if}} tags are matched by {{endif}} tags and so forth).

  • Render starts rendering the content into the specified pStream parameter using the ITagReplacer object passed as the first parameter (the CSAHandler object) as the provider of replacement methods.

The last class in the file, CContentGenerator , does the management work. It creates the destination folder, instantiates a CSAHandler object, and calls renderFile for all the files that are part of the combined client/server solution.

After you execute the sample, you have to open and build the generated project in Visual Studio through these steps:

  1. Build the server project.

  2. Right-click the WSDL file in the client and select Update Web Reference. This is to run the sproxy tool and generate the proxy header file.

  3. Build and run the client application.

The servers (and clients) generated by this sample don t completely cover the SOAP support provided by ATL Server. The wizard doesn t support compound types inside the SOAP methods (structs and arrays), and it doesn t allow specifying SOAP headers. The reason for this limitation is to keep the stencils as simple as possible.




ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net