E-commerce components Hello, and let me introduce myself. My name is Christian Gross and I am the author of the components that you are about to use. I can be reached at cgross@eusoft.com or Serpent_Mage@csi.com. We will be opening the news group component.microsoft at the server jupiter.eusoft.com where you can post any question regarding these components. This way if you want to extend them or use them, we will answer those questions. As a side line note, these components have not restrictions on their use, but there is no warranty and I would prefer that you do not call them your own. What are the purpose of these components? The purpose is to show you how Visual C++ can be used to build professional components that are used in an e-commerce environment. The components that have been authored are not pie in the sky components. They are components that we have used to solve problems for our clients. There are of the utility type, and therefore reusable. First how many components are there? There are four and they are: Notification: This component is used to supply a notification of action. When a client comes to a web site they may buy something or register themselves to be on some kind of list. The notification component is used to provide feedback by some kind of mechanism. In this example the feedback is email (SMTP) Security: Logging onto a web site is a requirement. And providing a mechanism to logon is simple. However, very often you will need to implement a need to know security mechanism. This component provides both of those security requirements. The security database used is a physical database. Once Windows 2000 becomes production code, it should be replaced with Active Directory. XML Components: These components are the biggest, and they provide the ability to consume and publish XML. The publisher component will take a COM object and serialize it as a series of XML nodes. The consumer component implements the XML SAX model and generates events when the tree is being parsed. Generic Container: A container that has built in filtering mechanism. This makes it possible to keep running totals on objects and filter information before the container is filled. Before I dig into the details of using the components is there a way to understanding them? Yes and here is the list: Understand Visual C++ and ATL Understand COM interfaces and how you implement them Next the complexity order is Security, Notification and then the XML components Some general notes on programming style In most of the applications and components there is a general programming style that I use. 1. 2. 3. IDL definitions: When there are COM interfaces that need to be implemented or reused they are stored in the project’s common directory in an IDL file that has the naming convention xxxcommon.idl. Global definitions: There are three types of global definitions. The first is system wide. These sort of definitions are stored in the project’s common directory in a header file that has the naming convention xxxcommon.h. The second global definition is project wide and they are stored in the projects stdafx.h file. Finally the last global definition is stored as a static within the implementation file. COM compiler support: There is frequent and heavy use of COM compiler support. This is because smart pointers make it much easier to manage the various COM interfaces. It also makes it easy to do COM factoring. The one downside is that when COM interfaces are returned from a smart pointer an additional reference count must be made. As well very often the data types _bstr_t and _variant_t are used. 4. 5. 6. STL: Whenever possible I have attempted to use STL. Namespaces: Default namespaces are not used. This is to avoid name collisions, since an interface could have different meanings to different contexts. Test scripts: Within the common project directory very often test scenarios are created using the Windows Scripting Host. Specially they are JavaScript files (xxx.js) Generic Container Details The purpose of the Generic container is to show how to write a shopping cart application. The problem very often is that you surf to a web site and then you want to purchase or track multiple items. These items need to be added to a container. This could be accomplished using a standard collection class. However, in the case of a shopping cart example you will want to keep a running total. To do this you can dynamically check each item and keep that tally in some other object. The Generic Container simplifies this by adding the concept of a filter. The filter is associated with the container. And then whenever any item is added to the container, the filter can either ok it or reject it. Examples of rejecting included out of stock, wrong product id or credit limit is exceeded. The concept of the filter makes writing a shopping cart simpler because the container is dynamic and you do not need to constantly tally or query for correctness of the items in the container. Project Location GenContainer Directories common: Contains the Filter and Generic Item interface definitions GenericContainer: Contains the Container project and a simple item value implementation ShoppingCart: Contains a sample filter CTotalFltImpl that tallies the total of the items currently present in the container. How it’s implemented The core container is implemented in the COM object CContainer. It allows storage of COM objects called items in the container. The items must be IDispatch derived and must implement the IGenContItem interface. The items can be added to the container using the IContainer::add method. If the addition is successful then the BOOL return code is TRUE. If it is FALSE, the item has not been added to the container. To retrieve the reason why the item could be not be added reference the property IContainer::errorMessage. It is possible to navigate through the various items using the methods IContainer::next, IContainer::prev, IContainer::rewind. To retrieve the current item reference the property IContainer::currItem. The interface IGenContItem is a generic interface that all items must implement. The purpose of the container is to be able to store different types of COM objects. Some items may reference products and some items may reference credit limits. The point is that the container can handle an IDispatch based object. With these varying objects the filters must have a common interface that they can use to see if the item can be added to the container. This interface is called IGenContItem. This interface is like a variant and it allows the possibility to store and retrieve indexed properties. In the GenericContainer project a simple implementation of IGenContItem has been supplied. How to extend it To extend this project to fit your own situation you need to build the GenericContainer project. This is the core. Then for your own filters you will need to implement the interface IGenContFilter. For custom items you will need to implement IGenContItem. Once this has been done you are ready to use the GenericContainer COM object. Warnings Currently the container project is marked to handle COM threading “both”. However, there is no concurrency control when adding items. This would need to be added. Items cannot be removed, the only way to do this is to delete the container and then recreate it. Security Details The purpose of the security component is to implement need to know security and logon in one component. Typically the situation in a web site is that you need log-on and information filtering capabilities. One of the ways to solve this is to use Web Server security and restrict access to specific files. The problem with doing this is three-fold. First I myself am not overly enthusiastic about putting several thousand users in my Windows NT security database. The problem that we face is that we need to be able to move users from one machine in one domain to another machine in another domain. Using Windows NT security in Windows NT 4.0 does not make this simple. As a side note this situation has been dramatically improved with the release of Active Directory within Windows 2000. The second reason why we typically use database access is because it gives us the ability to filter information based on the user’s information viewing level. The last reason is very often we need to cross-reference the data with other platforms (UNIX or CORBA as examples). And there is no compatibility between the various security schemes. This component is a simple to use component that is added to the ASP page and then it handles everything itself. It will check if the user is logged on and if not load up a login page. That page will then be used to define a security level. Examples of security levels include user and administrator. Project Location Security Directories NTKSecurity: Contains the Security COM object DBUsers: Contains sample Access database and SQL Server scripts to create the users table SecurityWeb: Contains a web site that shows how to use the component within an ASP page (example1.asp) How it’s implemented This component is an example of a component that uses the ASP object model. It can only be used in IIS 4.0 or later. This is because it retrieves the Context and queries for the ASP properties. It does not use the outdated ASP activation interface implementation. The core object is IUserClear. It is placed on the web page and then the method IUserClear::logon is called. This method queries for the ASP object Session and then asks for the value logonstate. If this value is NOT_LOGGED then the method will load the internally define web page html1.htm. It is sent back to the client using the ASP object Response.Write. Once this page has been sent the connection is terminated and the user only sees the logon web page. This is accomplished by the component telling the ASP Response object that the request has ended. Now the client must logon. The request is redirected back to the same page. This time the logon page queries for the values of the logon, validates them, and then sets a security clearance level. If the logon is successful this time the ASP script can continue. To retrieve the security level the property IUserClear::securityLevel is called. How to extend it This component is self contained and does not need to be extended to suit the individual situation. What does not need to be implemented are a set of components that add the user to the database. Warnings This component can only be used with IIS 4.0 or later. It is also vital that all pages that require security clearance reference this component. The logon state is saved using the ASP Session object. It does mean that if you use Windows 2000 web farming or multiple web sites logon will have to occur multiple times. There is no simple solution for multiple web sites and web farms. When you build this component there may be an error during registration since it requires mtxex.dll. This is something that you use on Windows NT 4.0. If there are questions on how to implement this on a web farm, please post to the above mentioned news group. Notification Details In one of the web site where we implemented a conference registration system, there has been the need to implement notification functionality. The reason is because people do not regularly or timely visit a web site. This is because people have better things to do than constantly visit a web site to check their registration status. What people wanted was feedback via a fax or email. In this example the email part has been implemented. Notification may seem like a trivial task. You get the proper component (for example email) and then write an email and send it. But in fact it is a bit more complicated. The reason is because for the various notifications (email, fax) you have different components. So what we needed was a generic notification interface that generated a document and then sent it via the notification transport system. Project Location Notification Directories common: Contains the ISrvcNotification interface definition and common definitions aspemail: Free email component that is used to send email from the component NotificationWeb: An example website that generates the document that will be sent SMTPNotification: Implementation of the ISrvcNotification interface that using the SMTP protocol tstDispatcher: An example of using component within a Visual C++ console application How it’s implemented In this project all notification components must implement the ISrvcNotification interface. This interface has several properties to define the destination and the sender. There is only one method Send, which sends the notification as per the protocol defined by the implementation. The implementation needs to generate documents on the fly. For example there is a standard document and certain fields need to be replaced. In our case it would have been the cost of registration, to whom the registration is addressed, etc. In this simple example it is a simple Hello to and from document. To do this replacement the simplest way is to use ASP and pass the required parameters via the CGI parameters. In this class there is a helper function CSrvcNotification::addCGIParameter that builds the CGI command string automatically. The output of this command is then defined as the body of the email, which is then sent. It is also required to add the file notification.ini to the windows root directory. This file serves as a configuration file for the notification component. You will need to add the following values [SMTPDispatch] server=[name of where the SMTP server that sends the email] [ name of the template that holds the server reference data set by the property notificationTemplate ] address=[ The URL where the template document is held] title=[ When the email it is given a subject that is this value] server=[ The DNS of the server where the template is held] How to extend it In this example you only see the SMTP implementation. In our website we had fax capabilities. This version could not be released because it used “for cost” components. In our case we used the fax server product from Computer Associates. However, it is possible to use Exchange and the Fax connector. To add fax capabilities you would need to implement the ISrvcNotification interface and then define a proper send command. I would still advise using ASP to generate your document dynamically, since this is the easiest. Most fax components do have the ability to send text or formatted text. Otherwise the component can be used as is. Please note that the SMTP component used is for free and I have included all materials. Warnings The email component is good, but is limiting. For example if you desire to send formatted HTML emails, you will need to understand MIME and how to build MIME encoded sections. The CGI encoding must be thoroughly tested for your situations, because it uses the GET version of HTTP posting. There are length and data type limits. If you want to use binary data, then you will need to encode the data and send it using a HTTP post command. XML Components Details One of the newest technologies and hypes is XML. If you do not understand XML, please reference the Microsoft web site at www.microsoft.com/xml. The problem that we were facing is how to transfer data between the client and server. While it was possible to use a binary format, it was complicated. Often the UNIX box and the NT box would not order the data the same (Sparc vs Intel). Therefore, it was necessary to use a text format. XML was chosen because it was a readable format and a standard. The problem with using XML is bridging it from the COM object to either reading or writing it. Very often reading it was not a problem. Writing was a bigger problem. And when it had to be read you had to navigate a tree, which was not fun. The reason is because with XML the data can exist or it may not exist. It is a dynamic hierarchy. And as well, it cannot be determined when the data would appear. Since XML is a nested concept, nodes at the same level need not have a specific order of appearance. In other words parsing becomes a situations of if’s and’s or but’s. The reading or parsing problem was solved using an event driven architecture. In the XML world there is something called the SAX event interface model. Using this interface definition model you would implement them and wait for the various elements to appear. The writing problem was solved by associating COM properties with XML properties. For example if an object is called Exampleobject and has to COM properties childProperty and exampleProperty then the XML is: <ExampleObject> <childProperty>some value</childProperty> <exampleProperty>another value</exampleProperty> </ExampleObject> If one of the properties is another object then that object is serialized and nested within the root XML tree. Using this approach the programmer did not need to write any additional serialization code or writing XML specific code. Project Location XMLComponents Directories common: Contains the SAX event interface definitions in IDL form and various common headers that define the serialization routines to generate XML ExXMLImpl: Example implementations of objects that serialize to XML and an implementation of a reader object that parses an XML file. The project does both generating and parsing of the same data. tstXML: The test program that tests the COM objects in the project in ExXMLImpl XMLConsumer: The basis implementation of the SAX event object model using COM XMLPublisher: The basis implementation of the XML serialization routines using COM. How it’s implemented To understand how everything is implemented, lets first focus on the generation of the data. It was defined that the COM object should not need extra programming effort to implement XML serialization. In our first implementation privately we did the implementation using IStream and ISequentialStream. However, now that we are releasing the code to the public we cannot use IStream. The reason is because IStream is used for OLE structured storage. Therefore the interface IXMLSerialize was defined and needs to be implemented by any COM object that intends to generate XML data. To simplify this process of implementing the IXMLSerialize interface a template that does the hard work was created. It is called XMLSerializeImpl< class impl> and accepts as parameter the class name. The only other requirement to use this class is to call in the FinalConstruct method setObjectName. This needs to be without spaces and is used to create a root node in the XML. The COM properties are associated to XML items using the macros XML_xxx_PROPERTY. The xxx is replaced with either LONG, BSTR or DISPATCH. These macros implement properties as defined in a COM idl file. Consider the following IDL [ object, uuid(37555201-CCAE-11D2-B708-0010A4F15EA7), dual, helpstring("IExampleChildObject Interface"), pointer_default(unique) ] interface IExampleChildObject : IDispatch { [propget, id(1), helpstring("property someValue")] HRESULT someValue([out, retval] long *pVal); [propput, id(1), helpstring("property someValue")] HRESULT someValue([in] long newVal); [propget, id(2), helpstring("property anotherValue")] HRESULT anotherValue([out, retval] BSTR *pVal); [propput, id(2), helpstring("property anotherValue")] HRESULT anotherValue([in] BSTR newVal); }; This is implemented as follows within the object XML_BSTR_PROPERTY( anotherValue, m_anotherValue) XML_LONG_PROPERTY( someValue, m_someValue) private: char m_anotherValue[ 64]; long m_someValue; This makes implementing properties very simple. The way these macros functions is that they will serialize to XML if they have been set using the put property operator. Therefore, it is absolutely important in your implementations to set the values using the COM properties and not the values directly. Another catch is that the properties must be get and put. The macros are defined to implement both of them. The reader of the XML is a bit different. It implements the various SAX interfaces. First the content is parsed using the Microsoft XML parser in the object CParserImpl::parse. Then the parser walks the XML tree and calls the various SAX events. There are many interfaces, but the only one that you need to concern yourself with is IDocumentHandler. This one is called for the various elements. How to extend it The examples shown are very simple. However, it is not so difficult to write your own. First decide what you want to do, publish XML or consumer XML. For the consumer you need to implement the SAX event object model. The minimum interface that you need to implement is IDocumentHandler. This one is triggered whenever there is an element. For the publisher you need to implement the IXMLSerialize interface using the template XMLSerializeImpl and then add the properties you want to expose. The best way to understand this fully is to look and inspect the code. Warnings There are several major warnings Do not attempt to combine publisher and consumer of XML in the same COM object. It leads to complicated COM objects. The serialization process only serializes the data within the COM object. When the XML is sent, it does not send the COM object with it. The MS parser does allow referencing of the XML attributes using an index notation. You must know the name of the attribute. Therefore in the SAX IAttributeList interface the only method that works is the property value with a variant of BSTR. Everything else does not work. In fact quite a bit of the SAX model is unused. This is not peculiar to this implementation, but is peculiar to most SAX implementations. There is discussion of SAX2 with filtering capabilities Use properly formatted XML. For example stay away from the notation <tag attr=”value” />. I personally do not find this proper XML, even though it is accepted XML. There are situations for it, but if you use it constantly your data is not properly formatted. And there you have it, four components that we have used constantly in our web sites. I hope you like them. Look at them, use them and if you have questions, please post them on the above mentioned news group. If you want immediate attention, send the question to me with a high priority marker. Christian Gross March 31, 1999