XMLFoundation

XMLFoundation 2009

XMLFoundation 

Alternate Source Download

Tech Support Thread

Nearly every C++ application can benefit from the XMLFoundation (even if it does not use XML).

21. Conclusion                         


XML in the Foundation

As the name suggests it provides a foundation for XML support in an application, however this is much more than just another XML parser.  It applies a unique approach to handling XML that allows your application code to focus on the application rather than traversing DOM or subscribing to SAX events.  The most unique feature of the XMLFoundation is the object oriented encapsulation that provides XML support in the application layer.  XMLFoundation allows you to easily integrate XML with your GUI, or with your server objects,  and it natively supports COM, DCOM, and CORBA objects.

XMLFoundation contains a small, fast, and portable XML tokenizer that has been refined and optimized in many large software projects for about 10 years.  My involvement with XML pre-dates the finalization of the XML 1.0 recommendation by W3C.  For years, the only XML Parser that could match XMLFoundation tokenization performance was "Xpat" by James Clark - but as you will see the unique ability to bypass DOM and SAX altogether makes XMLFoundation the fastest solution available for moving XML to and from application layer objects - and it requires far less lines of code to do it.  

The performance of the stack based XML parser is at the top of its class for non-validating parsers.  Parsing and tokenization is only half the task, the other half is getting the results into the member variables, lists, and objects that they need to be in to be useful in the application layer - it is in that task that XMLFoundation is in a class of its own.  The performance is unparalleled because the memory buffer that contains the source XML parses directly into your custom class objects without ever being copied or temporarily stored in a DOM tree.  It parses directly into your lists, objects, arrays, indexed data structures, and all native C++ data types.  It even has support for common containers of element data such as MFC CStrings.  It's been used in Java too.  That said, speed of execution is less impressive than the speed of development and overall reduction in lines of code required to effectively use XML in your application.

 

The Solid Foundation 

If you are building an application that does not use XML and never will..... XMLFoundation is still a very valuable tool available to solve many very common development tasks.  The data structure classes alone ( List, Hash, Stack, Tree, Array, QSort ) are very useful.  They all have "Iterator" objects so that data structures can be read-referenced by multiple threads at the same time without blocking.  The interface is standard to all data structures.  If you find MFC or Rogue Wave Standard C++ library data structures useful, you will likely find XMLFoundation data structures even more so. 

XMLFoundation also has standard algorithm implementations ( Encryption, Compression, Data hash, Encoding ).  These are based on the works of other authors.  They have been included into the XMLFoundation in a simplified build format.  They all compile under C++, so if you are using them on AS/400, AIX, Solaris, Linux, or other like platforms - you do not even need to reference a C compiler from the makefile, only your C++ compiler.  They are also organized into single .CPP files for each implementation - often a consolidation of many individual C source files in the original authors publications.

XMLFoundation also has a plethora of application utilities including ( Sorts, Performance Timers, Disk Directory, Exceptions,  INI Profiles, Caching, String, Stream ).  XMLFoundation has many utilities that MFC does not.  They are complete, comment documented with examples, and thoroughly tested on many software projects.

XMLFoundation is very portable.  It builds on all versions of Windows (Win95 through Windows7 and Windows Mobile).  Portions were initially developed on a RISC machine, and it was used in Solaris and Linux as early as 2001.  Some of the compilers that have been used to build XMLFoundation include: CC5.0, Xlc, gcc, IntelC++,  KAIc++, ForteC++, Visual C++, and eMc++.  However I believe it works with any C++ compiler found here, because it does not use namespaces, iostreams, or STL - all areas that are prone to porting problems from my experience.  It does have template classes but their inclusion is optional as part of the implementation rather than part of the foundation.

The build dependencies are meticulously correct.  Smart linkers leave out everything you don't use, so don't expect to see code bloat as a punishment for using XMLFoundation.  Other development libraries were not designed as well from a build perspective.  Your application will not load any DLL's as a result of using the XMLFoundation.  Consider a utility built on the XMLFoundation, PackMan - It uses nearly every feature in the XMLFoundation aside from the XML support.  In the future - Packman will be ready for XML related features that come quick and simple because it has a good application platform.  

I suppose an entire article could be written about each of the foundational classes, and I'm certain that they will be written.  They are all well commented and coded with a highly experienced approach.  The String class uses stack space when possible to avoid heap allocations.  It's the best string implementation I've ever seen.  The INI Profile class uses triggers that allows your application to pick up real-time configuration changes much like RegNotifyChangeKeyValue() in the Windows SDK.  Exceptions can be configured to unwind the call stack to a memory buffer like Java's printStackTrace().  The Tree has an iterator.  The Directory can delete recursively - on all platforms.  The Stack is entirely inline, with standard and macro methods - It could not be any faster if it was coded directly in assembly.  The StringList puts MFC's CStringList to shame.

 

History and Future

You cannot build a house on foundation of wet cement that has not cured yet.  With cement, minimizing stress prior to curing minimizes cracking in your foundation.  The same is true of software.  The XMLFoundation is sold and completely cured.  It would be too bold to say that the XMLFoundation has no bugs in, but it has none that I am aware of and the code has been heavily used.  It is a complete foundation.  Building an application on any foundation like Java 1.0 or .NET 1.0 or anything 1.0 means that if you don't get slowed down by the bugs, you will be slowed down when you find all the missing functionality.  This is Version 2009 of XMLFoundation.  7 years ago - the XMLFoundation was very mature for it's age - it came from a good family - it's mother had already been used on the largest software project in the world.  Since then I have built several complex applications on it.  It was completely stress tested with SMP hardware during a recent Fortune 50 proof of concept implementation. 

The mother of the XMLFoundation was "The XML Object Framework", born in 1998 & 1999(it was a long labor) for a client of mine.  The XMLFoundation was born the following year.  The XMLFoundation sported a completely new implementation of the xml parser based on the custom GString stream class that was also born in 2000.  XML Journal Magazine reviewed a product built on the early XMLFoundation object factorization and called it "5 Star / World Class" (note: they did not review XMLFoundation they reviewed TransactXML).  XMLFoundation was heavily developed the following two years.  In July of 2002 - a mostly undocumented - and mostly unnoticed release of the XMLFoundation was made public.  This project is mature.

XML Foundation absolutely IS the future in certain technology subsets.  It is a gift to the world of engineering, and it comes with all the source code.  Universities that want to teach algorithms, applications, or  OO Design will find the XMLFoundation to be a great source code to base a curriculum on.  Independent authors who want to write about cutting edge technology will find XMLFoundation a worthy subject.  The future was written in the past.

 

 

The Need for Objects from XML

XML is data.  "Objects from data" is not a new concept.  Programmers have been doing that for years, even before they were called objects.  We still need to get data into objects today.  The data can be XML or a result set, and the object might be a CDialog, a CORBA Object, a COM object, or your own invention.  You still need to get the same thing done.  Programmers have been doing this as long as there have been programmers.

If you apply enough force you can make the cube fit into the round hole.  If you apply enough force you can do anything - even police California.  The brute force approach is to parse the XML into a DOM tree, and traverse the tree to gather the data required by application/object variables.  This approach causes volumes of "simple" source code to move data from XML into Structured Objects,  a poor approach with respect to implementation time and long term maintenance.

Alternatively, the OO approach generalizes this process into reusable functionality that enables objects to serialize to and from XML directly.  Software developers of every language have a similar need.  They must either: 

A.  Write their own Object-XML tools,

B.  Find some production quality framework ready to use or 

C.  Use brute force and budget for maintenance programmers.

 

 

Don't Be Square

From a procedural perspective we put data in square sets just to make programming simple.  Consider this example data that is a "Customer" with a list of "Orders" where each order has a list of "LineItems".  That is not a square dataset - but for the sake of the application layer we have forced it to be square for the last 4 decades.

CUSTOMER CUST_ID ORDER_ID ORDER_DATE LINEITEM_ID LINEITEM_DESC PRICE
Brian 777 1 July 4, 1777 7 Firecrackers $111
Brian 777 1 July 4, 1777 14 Ariel Shells $222
Brian 777 1 July 4, 1777 21 Party Favors $444
Brian 777 2 July 4, 2009 28 Attorney Fees $222
Brian 777 2 July 4, 2009 35 State Fines $555

The repetition in red filled the hole in to make non-square data be square.  The data in red is normally a pointer reference to the last sort break at the DBMS kernel level, but various toolsets often expand it long form so that 1 instance of a "Row" object does not rely on the data in another instance.  It's a terrible situation that has plagued applications for as long as I can remember.  The problem is that in reality there is no such thing as a "Row" object - it was more of a temporary/tool-object to get the data into real objects like Customers, Orders and LineItems.  Countless data access products in the form of VBX, OCX, ActiveX and various frameworks and libraries serve up square data sets to applications that MANUALLY code the transfer of data into their application objects with volumes of code that looked something like this:

Customer::Load()
{
    Tool.GetData()
    while( Tool.MoreRows() )
    {
         Row = Tool.GetNextRow();
         // Loop until we find a sort break on OrderID
         if (Row.GetColumn("Order_ID") != LastOrderID)
         {
              LastOrderID = Row.GetColumn("Order_ID")
     
              // Make an Order object - set the values - then add it to the customer
              Order = new OrderObject;
              Order.date = Tool.GetColumn("Order_Date")
              Order.id = Tool.GetColumn("Order_ID")
              AddOrderToCustomer(Order)
         }
         // Make a new line item object - set the values - then add it to the order
         LineItem = new LineItemObject;
         LineItem.id = Tool.GetColumn("LineItem_ID")
         LineItem.desc = Tool.GetColumn("LineItem_Desc")
         LineItem.price = Tool.GetColumn("Price")
     
         Order.AddLineItem( LineItem )

    }
}

Notice all the use of Tool that represents some sort of data set tool, class or library.  Building software to accomplish this task of copying data into objects without such a tool would dramatically increase the lines of code required to move the square dataset into your application objects.  Knowing what tools to use can be the difference between the success or failure of an entire project.  One bad tool, or one missing tool can make all the difference in the world to a software developer.

 

Learn To Think Outside The Box

This is the same example data from the square result set represented in XML

<Customer id=777>
    <Name>Brian</Name>
    <Order id=1>
        <Date>July 4, 1777</Date>
        <LineItem id=7>
            <Desc>Firecrackers</Desc>
            <Price>111</Price>
        </LineItem>
        <LineItem id=14>
            <Desc>Ariel Shells</Desc>
            <Price>222</Price>
        </LineItem>
        <LineItem id=21>
            <Desc>Party Favors</Desc>
            <Price>444</Price>
        </LineItem>
    </Order>
    <Order id=2>
        <Date>July 4, 1777</Date>
        <LineItem id=72>
            <Desc>Attorney Fees</Desc>
            <Price>222</Price>
        </LineItem>
        <LineItem id=42>
            <Desc>State Fines</Desc>
            <Price>555</Price>
        </LineItem>
    </Order>
</Customers>

 

There is nothing square about XML.  XML is an N-airy tree. That's why we naturally use DOM (Document Object Model) to traverse the data.  For 1000's of years we thought that the world was flat.  Engineers made it be square because that was easier for them to cope with and now we live in the days where it begins to take it's true shape.  Unfortunately as of 2009, the opportunity of the paradigm data shape shift has not been harnessed by most programmers that grew up in the square world and are only familiar with square tools.  They take the most obvious development path.  If you presented the problem of sorting to someone who has no tools, they will likely build a "bubble sort" - because that is the most obvious and immediate solution.

Typically the XML is parsed into a tree structure.  This means that the linear and contiguous memory buffer of source XML is copied into many fragmented pieces of memory across the heap - each element and in many cases each token gets it's own heap space.  This makes the XML elements and attributes programmatically accessible with loops and recursion, just like Tool did for square datasets.  The XML parser puts the Elements and Attributes into this temporary fragmented memory tree structure so that the application programmer can get at the information to copy it once more into a final structure that can be displayed on the GUI or used by the application.  It is likely going to take as much or more code to get from the temporary DOM tree into the objects as it did to get from the square result set into the objects.  In many cases it will require recursion that is difficult to debug - much more difficult than the old fashioned iterative code required to copy from square result sets.  Below is a code sample of some common tasks:

    Due to the large amount of code to change a DOM node name this article only 
    includes the basic steps involved.  This is what it takes to change an element 
    tag name using DOM, it's much more involved using SAX.  
    -------------------------------------------------------
    // 1. create a new node with the desired tag name
    // 2. preserve old value to copy later
    // 3. move all children of current node to new node
    // 4. get parent of current node and replaceChild the current node with the new node
    // 5. copy old value or new value to new node
    // NOTE: Attributes are handled differently!




    // This is how to add a new attribute using DOM
    MSXML::IXMLDOMNamedNodeMapPtr pAttrList = m_pCurNode->Getattributes();
    _bstr_t bstrAttrName = (_bstr_t)(LPCTSTR)m_strName;
    MSXML::IXMLDOMAttributePtr pNewAttr = m_pDOMDoc->createAttribute(bstrAttrName);
    _bstr_t bstrAttrValue = (LPCTSTR)m_strValue;
    pNewAttr->PutnodeValue((_variant_t)bstrAttrValue);
    pAttrList->setNamedItem(pNewAttr);
    // TODO: The user must first search to be sure the attribute name is unique, or your XML will be invalid!
    // One of the well-formedness constraints that the XML 1.0 specification lists is that no attribute name 
    // may appear more than once in the same start tag or empty element tag.  DOM never deals with this issue.
    // XML is case sensitive, so attribute "Name" is different from attribute "name", some tools built into DOM
    // sure would have been useful - good thing we have the XMLFoundation.
    AddNodeToTree(pNewAttr, m_hCurItem);

 

New World Order

The square world is becoming part of history like the flat world.  I remember back in the early 90's we tried to rid ourselves of the square world with something called "The Object Database".  It was a great concept and the only reason square prevailed against it is because nobody could implement an Object Database that was fast enough.  Who cares how clean the code is if the application is dysfunctional because it is too slow?  This is why XMLFoundation is so performance oriented - that's what it takes to change the world.  The clean code alone is not enough.

Now I'll explain how to accomplish the task of loading up your object with the information in the XML using a fully object oriented approach to data handling.  Customer, Order, and LineItem are derived from XMLObject.  They must implement 1 virtual method called MapMembers() that would look like this:

    void Customer::MapXMLTagsToMembers()
    {
        MapMember(&m_OrderList, Order::GetStaticTag());
        MapAttribute(&m_nCustomerID, "id");
        MapMember(&m_strName, "Name");
    }
   
void Order::MapXMLTagsToMembers()
    {
        MapAttribute(&m_nOrderID, "id");
        MapMember(&m_LineItemList, LineItem::GetStaticTag());
        MapMember(&m_strDate, "Date");    // can also be mapped to a date/time object
    }

      void LineItem::MapXMLTagsToMembers()
    {
        MapAttribute(&m_nLineItemID, "id");
        MapMember(&m_strDesc, "Desc");
        MapMember(&m_strPrice, "Price");    
    }

Now all the object assignment and creation code is summed up into this one line.

    // This assigns ALL member variables and creates sub-objects.
    Customers.FromXML( pzXML )  

If you had a trace statement in the constructor of the Order, you would see that it was called  for every appearance of an Order in the XML.

Now suppose you wanted to manipulate some member variables then regenerate the XML: just assign your member variables normally then regenerate your XML - that's just as easy

     char *pzXML = Order.ToXML()

MapXMLTagsToMembers() defines everything needed for your objects to read or write XML as a base method.  Without the XMLFoundation you would  have to code all that looping and mapping 2 times if you wanted both reading and writing XML.  Without the XMLFoundation you will have a larger maintenance issue if any XML document structure changes because you'll have to hunt through your looping and recursion routines to find the Element name to change.  XMLFoundation provides countless other niceties such as mapping any number of XML tags to the same member, and conditional inclusion of members in the output XML based on tag name or the member state such as DIRTY indicating that the member was updated and you only want ToXML() to generate a delta of the data rather than the entire set.  You can specify element order or have them output alphabetically.  Common needs that can all be accomplished in 1 line of code rather than pages of code.

 

    The following code is so involved using DOM only the basic steps are described.
    This is what it takes to change an element tag name using XMLFoundation.
    -------------------------------------------------------
     // This is how backward compatibility is achieved when Element tag
    // names change. For example, if m_nVersion is mapped to an Element
    // named "VersionNumber" but you want all future protocols to refer to this 
    // Element as to "ProtocolVersion". This is achieved by the following code:
    // This allows either "VersionNumber" or "ProtocolVersion" to set the value of 
    // m_nVersion, but always refers to it as "ProtocolVersion" while serializing XML.
     MapMember(&m_nVersion,"VersionNumber");
    MapMember(&m_nVersion,"ProtocolVersion");
    SetMemberSerialize("VersionNumber", false );


     // This is how to add a new attribute using XMLFoundation
    // if nUpdate=1 the attribute list will be searched and updated if there is an existing 
    // attribute called [pzName]if not found or if nUpdate=0 the new attribute will be added.

     void AddAttribute( const char * pzName, const char * pzValue, int nUpdate=0 );

It's fun to compare the differences between DOM and XMFoundation, but much of the functionality in the XMLFoundation cannot be compared to anything in DOM.  For example, the XMLFoundation maintains a bit flag field for each member that it manages.  These are the values that can be managed:

    // The value does not sync with the original value set by the Object Factory
    #define DATA_DIRTY 0x01 
    // The member has been set by either the Object or the Object Factory.
    #define DATA_NOT_NULL 0x02
    // The member has been assigned a value from the Object Factory
    #define DATA_CACHED 0x04
    // The member has never been assigned a value, it is uninitialized
    #define DATA_NULL 0x08 
    // The member should be included in the xml serialization stream
    #define DATA_SERIALIZE 0x10 
      

The following interface uses some of the member state flags:

        // When objects are populated from the XML stream, they have a state of
        // Not dirty and Not null. When the members are assigned by the derived 
        // class through SetMember() or SetMemberByTag() the state becomes dirty.
        // When using the SetMember() or SetMemberByTag() there is no need to 
        // setMemberDirty(), but if the derived class does direct assignments
        // to members that should be serialized, setMemberDirty() should be called.
        // if you ever want to serialize or track the state change only.
        // Setting bDirty to 0 will clear the dirty flag for a member variable.
        bool setMemberDirty(void *pAddressOfMemberToSet, int bDirty = 1);
        bool setMemberDirty(char *pzTagNameOfMemberToSet, int bDirty = 1);

        // true if the memory state does not sync with the 
        // original value set by the Object Factory
        bool isMemberDirty(void *pAddressOfMemberToCheck);
        bool isMemberDirty(char *pzTagNameOfMemberToCheck);

        // true if the member has never been assigned a value, it is uninitialized
        bool isMemberNull(void *pAddressOfMemberToCheck);
        bool isMemberNull(char *pzTagNameOfMemberToCheck);

        // true if the member has been assigned a value from the Object Factory
        bool isMemberCached(void *pAddressOfMemberToCheck);
        bool isMemberCached(char *pzTagNameOfMemberToCheck);

 

 

XMLFoundation also has many options available during the creation of the XML.
DOM has nothing that compares.

    ///////////////////////////////////////////////////////////////////////////////////////
    // serialization flags for ToXML()
    ////////////////////////////////////////////////////////////////////////////////////////
    // Otherwise XML tags appear in the order they were mapped
    #define ORDER_MEMBERS_ALPHABETICALLY       0x01  
    // deeply recurse without including dirty members
    #define RECURSE_OBJECTS_DEEP               0x02  
    // includes any member with a state of DATA_CACHED
    #define INCLUDE_ALL_CACHED_MEMBERS         0x04  
    #define EXCLUDE_SHORT_TERMINATION          0x08
    // Do not serialize any data from MapAttribute() members
    #define EXCLUDE_MAPPED_ATTRIBUTES          0x10  
    // Do not serialize attributes that came in via XML but were unmapped with MapAttribute()
    #define EXCLUDE_UNMAPPED_ATTRIBUTES        0x20  
    // Adds the DOCTYPE to the beginning of the XML
    #define INCLUDE_DOCTYPE_DECLARATION        0x40  
    // includes any member regardless of it's state
    #define FULL_SERIALIZE                     0x80  
    // include OID's only
    #define USE_OBJECT_MARKERS                 0x100 
    // more compact, faster, less human readable, The XML will have no Tabs, Carriage returns or Linefeeds
    #define NO_WHITESPACE                      0x200 
    // do not include empty strings in XML
    #define NO_EMPTY_STRINGS                   0x400 
    // do not include the ObjectDataHandler registered with setObjectDataHandler()
    #define EXCLUDE_OBJECT_VALUE               0x800 

 

It also has a SAX like (but faster and far simpler) way to subscribe to notifications


// When a tag is encounterted that does not have a MapMember() entry
// associated with it, this handler is called for the developer
// to supply an "on-the-fly" MemberDescriptor during the Factory process.
// This is useful for dynamic objects. 
virtual MemberDescriptor *HandleUnmappedMember( const char *pzTag );


// Generic Event Handler for custom object behavior. Abstract to reduce virtual method table
// -----------------------------------------------------------------------------------------
// nCase = "XMLAssign", (member tag, XML value, value len, null)
// when the FromXML() contains a value for a member set dirty by setMemberDirty()
// see MemberDescriptor::Set() in MemberDescriptor.cpp for details.
// nCase = "NonNumeric" (member tag, XML value, value len, null)
// when non-numeric XML data is mapped to an numeric only type.
// nCase = "EmptyString"(member tag, XML value, value len, null)
// when an empty("") value is assigned to a string, empty often differs from 'unknown' or 'unassigned'
// nCase = "ObjectUpdate" when OBJECT_UPDATE_NOTIFY is a set behavior flag. (oid, null, flags, pObjSrc)
// nCase = "MemberUpdate" when MEMBER_UPDATE_NOTIFY is a set behavior flag. (tag, value, valuelen, null)
virtual void *ObjectMessage( int nCase, char *pzArg1, char *pzArg2, unsigned int nArg3, void *pArg4)

 

Faster than Fast

The approach used by XMLFoundation is faster than SAX.  Since the object factory and the XML tokenizer were built for each other they did some unusual tricks for each other.  The tokenizer uses a unique approach to begin with.  It's purely pointer based.  Tokens are structures that point into the source XML, except for entities that get expanded into a special memory region.  Tokens do not hold copies of any data.  During object factorization it becomes necessary to have the token data in a null terminated string format.  The big performance boosting hack is that to obtain null terminated strings, the tokenizer actually plunks a null down over the first byte past the end of the token data.  It keeps track of the data it clobbers and restores it before  parsing out the next token.  There are no event calls that needlessly push data on the stack just to immediately pop it back off.  Performance profilers showed that call stack pushes and pops were the single largest consumer of CPU cycles in the tokenization process.  XMLFoundation eliminates them by "pulling" the data through a call to [void getToken(token *tok)], rather than the SAX approach that gets the data "pushed" into the application events with between 2 and 7 arguments depending on the token type - the SAX specification defines the interface so it has nothing to do was individual XML parser implementations.  The XMLFoundation is the only XML parser that uses this approach.  It is non-standard, and not in compliance with W3C interfaces to an XML Parser - For our uses, It's better than any W3C standard.

I realize that the vast majority of people who use XMLFoundation would never care about these grungy technical details.  To say that it is very fast is enough for most people, but I am also writing to the people at the Apache Foundation, and Microsoft, and IBM, W3C, and the many other people who have built their own XML Parser implementations.  Fast is an understatement.  Performance is a prevailing design pattern found throughout the XMLFoundation.  For example the XMLObject class is carefully designed to add minimal CPU cycles during construction because it is to the XMLFoundation what CObject is to MFC.  It has been carefully designed to add minimal entries to the virtual method table.  In many cases virtual calls were consolidated for that purpose.

 

Object Factorization

The Object Factory is the part of the XMLFoundation that instantiates objects for you based on certain element tags in the source XML.  It is based on the same principle as DECLARE_DYNCREATE() that allows MFC to instantiate CView derived classes for you.  In the XMLFoundation it is called DECLARE_FACTORY().  The XMLFoundation uses DECLARE_FACTORY() to instantiate COM and CORBA objects as well.  If a tag is mapped to an object in a list or tree structure, then every time that tag is encountered at the level it is mapped it will create a new instance for you and put it in the data structure you specified with all it's member variables already assigned from the source XML as you have them mapped.

 

Member Mapping

XMLFoundation has support for mapping to all native C++ datatypes.  It also has support for mapping into data container objects.  It has specific support for RWCString, CString, and GString and it's very easy to add support for other by deriving from the XMLFoundation class "StringAbstraction" and supplying the 5 pure virtual methods that will enable any kind of data container class to interoperate with the Object Factory for automatic member assignments.  These are the MemberMap Methods in XMLObject:

     // Map an int / long int / or very long int
     void MapMember(int *pValue,const char *pTag);
     void MapMember(long *pValue,const char *pTag);
     void MapMember(__int64 *pValue, const char *pTag);

     // Map a string, see StringAbstraction.h for interface and samples
     void MapMember(void *pValue,const char *pTag,StringAbstraction *pHandler);

     // Map an object into a hash table, binary tree, or QSort array
     void MapMember(void *pDataStructure,
                         KeyedDataStructureAbstraction *pHandler,
                         const char *pzObjectName,
                         const char *pNestedInTag = 0);

     // Map a collection of Strings
     void MapMember(void *pStringCollection,     const char *pzElementName,
                         StringCollectionAbstraction *pHandler,
                         const char *pNestedInTag = 0);

     // Map a dynamically growing Integer array
     void MapMember(void *pIntegerArray,     const char *pzElementName,
                         IntegerArrayAbstraction *pHandler,
                         const char *pNestedInTag = 0);

     // Map a sub-object using a tag other than defined in the DECLARE_Factory
     void MapMember(XMLObject *pObj,     const char *pDefaultTagOverride = 0, 
                                             const char *pzWrapper = 0 );
     // Map a collection of Objects
     void MapMember(void *pList,const char *pObjectTag,
                         ListAbstraction *pHandler,const char *pNestedInTag = 0, 
                                                     ObjectFactory pFactory = 0 );
     // Map a sub-object pointer to an object residing in the ObjectCache
     void MapMember(XMLObject **pObj,const char *pzTag, 
                              const char *pNestedInTag= 0, ObjectFactory pFactory=0);

 

Object Model Navigation

By using the XMLFoundation you inherit some powerful navigation features that can be used to help you debug your application with the Dump() member.  Because the factory manages all the object relationships, a new kind of object navigation arises: objects know their creators so an "Order" can know at runtime if it resides inside a list in a "Customer", or some other kind of object, or if it is not contained by another object at all.  This is what a full Dump() output looks like: 

Object Instance name: MyOrder
{
     string     OID = 
     string     UpdateTime = 
               References = 1     
     --------------------------------
     Type  :List<XMLObject *>
     Tag   :LineItem
     
     Contains:3 items
     Object Instance name: MyOrderLineItem
     {
          string     OID = 1121.0000
          string     UpdateTime = 
                    References = 26          
          --------------------------------
          Type  :string
          Tag   :Description
          Value :
          State :(Clean | Null | Uncached)
          Kind  :Element
          --------------------------------
          Type  :int
          Tag   :ProductID
          Value :11
          State :(Clean | Valid | Cached)
          Kind  :Element
          --------------------------------
          Type  :string
          Tag   :UnitPrice
          Value :21.0000
          State :(Clean | Valid | Cached)
          Kind  :Element
     }
     Object Instance name: MyOrderLineItem
     {
          string     OID = 332.5000
          string     UpdateTime = 
                    References = 21          
          --------------------------------
          Type  :string
          Tag   :Description
          Value :
          State :(Clean | Null | Uncached)
          Kind  :Element
          --------------------------------
          Type  :int
          Tag   :ProductID
          Value :33
          State :(Clean | Valid | Cached)
          Kind  :Element
          --------------------------------
          Type  :string
          Tag   :UnitPrice
          Value :2.5000
          State :(Clean | Valid | Cached)
          Kind  :Element
     }
     Object Instance name: MyOrderLineItem
     {
          string     OID = 7234.8000
          string     UpdateTime = 
                    References = 23          
          --------------------------------
          Type  :string
          Tag   :Description
          Value :
          State :(Clean | Null | Uncached)
          Kind  :Element
          --------------------------------
          Type  :int
          Tag   :ProductID
          Value :72
          State :(Clean | Valid | Cached)
          Kind  :Element
          --------------------------------
          Type  :string
          Tag   :UnitPrice
          Value :34.8000
          State :(Clean | Valid | Cached)
          Kind  :Element
     }
     --------------------------------
     Type  :string
     Tag   :OrderDate
     Value :1997-05-06
     State :(Clean | Valid | Cached)
     Kind  :Element
     --------------------------------
     Type  :string
     Tag   :ShippedDate
     Value :1997-05-09
     State :(Clean | Valid | Cached)
     Kind  :Element
}

 

GUI Objects

This is an example of what is involved to get XML to the GUI.  The XML is somewhat complex to show how simple the code will be.  The XML is a "Customer" with a list of "Orders" where each order has a list of "LineItems".  This is the XML:

<Customer>
     <ContactName>New Dude</ContactName>
     <City>Chocolate City</City>
     <Country>Hipville</Country>
     <Order>
          <ShippedDate>1997-09-02</ShippedDate>
          <OrderDate>1997-08-25</OrderDate>
          <LineItem>
               <UnitPrice>45.6000</UnitPrice>
               <ProductID>28</ProductID>
               <Description/>
          </LineItem>
          <LineItem>
               <UnitPrice>18.0000</UnitPrice>
               <ProductID>39</ProductID>
               <Description/>
          </LineItem>
     </Order>
     <Order>
          <ShippedDate>Futuristic</ShippedDate>
          <OrderDate>Tomorrow</OrderDate>
          <LineItem>
               <UnitPrice>1234567.77</UnitPrice>
               <ProductID>1234567</ProductID>
               <Description/>
          </LineItem>
     </Order>
</Customer>

 

Notice that the XML foundation will parse directly in to the CStrings that are already DDX bound to MFC's UpdateData().  This is accomplished through Multiple Inheritance.  Our Dialog class derives from both MFC's CDialog, and XMLFoundation's XMLObject.

The sample application reads XML and displays it in the GUI where it can be changed by the user, then saved back out to XML that reflects the users changes.

The complete code for this example is in "XMLDialog", but for the purpose of understanding what it takes to integrate XMLFoundation with an MFC Dialog this shows you ALL the code of interest.


//////////////////////////////////////////////////////////////
// Begin XMLDialog.h
#include "xmlObject.h"
#include "GList.h"

class CXMLDialogDlg : public CDialog, public XMLObject
{
    //This is XMLFoundation releated code
     GList m_lstOrders;
     virtual void MapXMLTagsToMembers();
     virtual void *ObjectMessage( int nCase, char *pzArg1, char *pzArg2, unsigned int nArg3 = 0, void *pArg4 = 0 );
     DECLARE_FACTORY(CXMLDialogDlg, Customer);


    //This is code created by App Wizard
    //{{AFX_DATA(CXMLDialogDlg)
     CString     m_strCity;
     CString     m_strCountry;
     CString     m_strName;
     CString     m_strRichEditXML;
     //}}AFX_DATA
}

// End XMLDialog.h
//////////////////////////////////////////////////////////////
 


/////////////////////////////////////////////////////////////
// Begin XMLDialog.cpp
IMPLEMENT_FACTORY(CXMLDialogDlg,          Customer)

void CXMLDialogDlg::MapXMLTagsToMembers()
{
     MapMember(&m_strName,      "ContactName",           &gC);
     MapMember(&m_strCity,      "City",                  &gC);
     MapMember(&m_strCountry,   "Country",               &gC);
     MapMember(&m_lstOrders,    MyOrder::GetStaticTag(), &gGListHandler, 0 );
}

void CXMLDialogDlg::OnBtnMakeXML() 
{
     UpdateData(TRUE); // pickup the changes from the GUI into the member variables
     m_strRichEditXML = ToXML();  // create the new XML
     UpdateData(FALSE);           // display the new XML in the edit box
}

void CXMLDialogDlg::OnBtnLoadGUI() 
{
     FromXML(m_strRichEditXML);  // parse the XML into 'this'
     UpdateData(FALSE);// update everything on the GUI that AppWizard has a DDX map for


     // note:UpdateData() does not push the list of 'Orders'[m_lstOrders] into the 
     // ListCtrl. The simplest way is to iterate [m_lstOrders] that contains the 
     // 'Orders' after the call to FromXML() is complete.
     // This shows you the complex, SAXish like, way that will add 'Order' objects 
     // to the GUI as they are added to [m_lstOrders] by the Object Factory during 
     // the call to FromXML(). This requires adding some code to this's constructor: 
     // ModifyObjectBehavior(SUBOBJECT_UPDATE_NOTIFY); 
     // This causes the XMLFoundation to call ObjectMessage(), as each "Order" gets
     // it's data from the XML.  This Actually adds data to the GUI DURING THE
     // PARSING PROCESS, as opposed to the 'simplest' way that will add the data 
     // into the CListCtrl AFTER the parsing process. 

}

// The Order object is yellow, the complexity is CListCtrl, not the XMLFoundation.
void *CXMLDialogDlg::ObjectMessage( int nCase, char *pzArg1, char *pzArg2, int nArg3, void *pArg4 )
{
     if(nCase == MSG_SUBOBJECT_UPDATE)
     {
          MyOrder *pO = (MyOrder *)pArg4;
          int nItemIndex = m_List.InsertItem(LVIF_TEXT|LVIF_PARAM, 0, pO->m_strOrderDate
                                                                     0, 0, 0, (long)pO);
          m_List.SetItemText(nItemIndex, 1, pO->m_strShippedDate);

          // uncomment this to see that we can generate XML subsets very easily
          //AfxMessageBox( pO->ToXML() ); // call base class method
     }
     return 0;
}

 

 

CORBA Objects

The XMLFoundation was designed and built for CORBA before it ever added any support for MFC.  If you have a pre-existing CORBA system that needs some XML tools you have come to the right place.  If you are building a new CORBA system - this is best tool available for XML support.

If you have read this document all the way to this point then you will likely understand how the XMLFoundation works for CORBA by showing you this tiny piece of code:

class CustomerImpl : public virtual CustomerBOAImpl, public virtual XMLObject

along with the IMPLEMENT_ORB_FACTORY() macro defined in XMLObject.h,  this is how CORBA can natively support the FromXML() and ToXML()  by using the XMLFoundation.  The Object Factory can instantiate your interface objects for you based on the XML.

CORBA implementations can be done in Java or C++.  The XMLFoundation supports both.  CORBA breaks down the language barrier allowing Java applications to easily, and natively deal with C++ objects.  This example details the creation of C++ CORBA objects - The Java implementation is nearly identical further blurring the lines between Java/C++ within the same project.   

The C++ CORBA implementation will bridge into J2EE Application servers everywhere, it will work for any ORB but a few of the most popular ones have been tested, and the makefiles are included with the CORBA sample that ships with the XMLFoundation.  The three makefiles included are for:

 

 

            Borland/Enterprise Studio     -        Visibroker

 

            IONA/iPortal Enterprise      -        Orbix

 

            BEA/Weblogic Enterprise    -        ObjectBroker    (works great with Tuxedo implementations)

 

This example extends the ORB to provide native XML accessors.  The sample CORBA application is based around 1 very simple object type.  It has a unique integer we call a CustomerID and a string we call a CustomerName.  Each customer may contain 0 to n references to another object of the same type as it's self, a MyCORBAObject.  This would model something like a list of Customers that were referred by 'this' customer.

The IDL looks like this:

module ExCORBA
{
     interface MyCORBAObject
     {
          void getXMLState(out string s);
          void setXMLState(in string s);
          void setState(in string s, in long l);
          void addSubObject(in string s, in long l);
          void delSubObjects();
          MyCORBAObject getSubObjectIOR(in long l);
          void dumpState(out string s);
     };
};

Follow this 12 step program

This is a very simple application.  The client application makes 12 calls to the server.  Every even numbered call is exactly the same - it is a call to getXMLState() to see what's going on in the server. The client obtains an initial IOR from a server serialized IOR upon server startup.

1. Assign some state in a native CORBA call. This is a typical CORBA data assignment operation. Two values are set in the object. The client assignes two members on the server. The code looks like this on the client:


    CustObject1->setState("Root",777);

2. View the state of the object in XML. This uses the XML accessor to return the state of the object. The code looks like this on the client:


     CORBA::String_var s;
     CustObject1->getXMLState(s);

and looks like this on the server:


     void ExCORBAImpl::getXMLState( CORBA::String_out s)
     { 
          const char *p = ToXML();
          s = CORBA::string_dup(p);
     }

and the result XML is this:


     <MyCORBAImpl>
           <CustomerID>777</CustomerID>
          <CustomerName>Root</CustomerName>
     </MyCORBAImpl>


The tag names are configured by the ExCORBAImpl object like this:


     void ExCORBAImpl::MapXMLTagsToMembers()
     {
          MapMember(&_nCustID, "CustomerID");
          MapMember(&_strCustName, "CustomerName", &gGenericStrHandler);
          MapMember(&m_lstCMyImplObjs, "MyCORBAImpl", &gGListHandler,0);
     }

3. Update the state of the object through XML. Step 1 used a typical object accessor to assign the state. Step 3 accomplishes the same through XML. The Code on the client looks like this:

     CustObject1->setXMLState("SuperUser");

On the server the code looks like this:


     void ExCORBAImpl::setXMLState( const char* pzXML ) 
     {
           FromXML( pzXML );
     }

4. View the modified object state in XML. This is the exact same code (client and server) as Step 2. We're calling getXMLState() again, and the result is:

     <MyCORBAImpl>
           <CustomerID>777</CustomerID>
           <CustomerName>SuperUser</CustomerName>
     </MyCORBAImpl>


5. Add CORBA Sub-Objects through XML. Step 5 is a lot like step 3 where we updated the name "root" to "SuperUser" through an XML assignment. This time we'll add an object reference.B The client code looks like this:

     CustObject1->setXMLState(
     "<MyCORBAImpl>"
           "<MyCORBAImpl>"
                 "<CustomerID>123</CustomerID>"
                 "<CustomerName>Al Gore</CustomerName>"
           "</MyCORBAImpl>"
           "<MyCORBAImpl>"
                 "<CustomerID>456</CustomerID>"
                 "<CustomerName>George Bush Jr.</CustomerName>"
           "</MyCORBAImpl>"
     "</MyCORBAImpl>");

and this is the code on the server:

     void ExCORBAImpl::setXMLState( const char* pzXML ) 
     {
           FromXML( pzXML );
     }

6. (exactly like steps 2 & 4) - View the object's XML stateB


     <MyCORBAImpl>
           <CustomerID>777</CustomerID>
           <CustomerName>SuperUser</CustomerName>
           <MyCORBAImpl>
                 <CustomerID>123</CustomerID>
                 <CustomerName>Al Gore</CustomerName>
           </MyCORBAImpl>
           <MyCORBAImpl>
                 <CustomerID>456</CustomerID>
                 <CustomerName>George Bush Jr.</CustomerName>
           </MyCORBAImpl>
     </MyCORBAImpl>

7. Get a CORBA object reference for object instance 456. On the client the code looks like this:


     ExCORBA::MyCORBAObject_var CustObject2;
     CustObject2 = CustObject1->getSubObjectIOR(456);

and on the server we walk the list of objects and return the first one that matches the supplied CustomerID like this:

ExCORBA::MyCORBAObject_ptr ExCORBAImpl::getSubObjectIOR(CORBA::Long CustomerID)
{
      // create an iterator[it] for the [m_lstCMyImplObjs] list
      GListIterator it(&m_lstCMyImplObjs);
      while(it()) // while there is more in the list
      {
           XMLObject *pO = (XMLObject *)it++; // get the next Customer Interface
           ExCORBAImpl*pIO = (ExCORBAImpl*)pO->GetInterfaceObject(); // widen the pointer
           
            // test for a match - Use a keyed datastructure in the real world
            if (pIO->GetCustomerID() == CustomerID)  
           {
                // Return the CORBA Interface to the desired object
                  return pIO->_this();
                break;
           }
      }
      return 0;
}

8. Exactly like steps (2, 4 & 6) EXCEPT we are using the Object ref returned by step 7.

     <MyCORBAImpl>
          <CustomerID>456</CustomerID>
          <CustomerName>George Bush Jr.</CustomerName>
     </MyCORBAImpl>

9. Add a Sub-Object without using XML. In the same way we used a traditional member assignment in step 1, we can create a new object reference to demonstrate the two models seamlessly working together. On the client:

     CustObject1->addSubObject("Michelangelo",1475);

and on the server the code looks like this:

     void ExCORBAImpl::addSubObject( const char* s, CORBA::Long l )
     {
           ExCORBAImpl *p = new ExCORBAImpl;
           p->_nCustID = l;
           p->_strCustName = s;
           m_lstCMyImplObjs.AddLast((XMLObject *)p);
     }

10. Get an object reference to the object created in step 9 and display it's state in XML. This is the client code:


     CustObject2 = CustObject1->getSubObjectIOR(1475); // exactly like step 7
     CustObject2->getXMLState(s); // like steps (2, 4, 6, and 8) using the new reference.

and the result is:


     <MyCORBAImpl>
          <CustomerID>1475</CustomerID>
          <CustomerName>Michelangelo</CustomerName>
     </MyCORBAImpl>

11. Deleting Sub objects. All objects, no matter how they were created, are destroyed the same. The list contains both Factory created objects and Objects created the tradional way. Once again this shows how seamlessly the ORB fits together with the XMLFoundations's Object Factory. This the CORBA Implementation/Interface and XMLObject are all one in the same. This cleans up the whole mess.

     CustObject1->delSubObjects();

on the server:


     void ExCORBAImpl::delSubObjects() IT_THROW_DECL((CORBA::SystemException))
     {
           GListIterator it(&m_lstCMyImplObjs);
           while(it())
           {
                 XMLObject *pO = (XMLObject *)it++;
                 pO->DecRef();
           }
           m_lstCMyImplObjs.RemoveAll();
     }

12. To see that step 11 worked, view the XML state like we did in 2,4,6,8, & 10. Now all the contained objects are gone, and "SuperUser" is alone.

     <MyCORBAImpl>
           <CustomerID>777</CustomerID>
           <CustomerName>SuperUser</CustomerName>
     </MyCORBAImpl>

 


COM Objects

Create a basic ATL COM project with Visual Studio.

Visual Studio will write your IDL, and implementation header files.  The following code sample is the standard implementation header file with a few small additions (highlighted in yellow) required for XML support.

 

class ATL_NO_VTABLE CMyATLObj : 
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyATLObj, &CLSID_MyATLObj>,
public IDispatchImpl<IMyATLObj, &IID_IMyATLObj, &LIBID_EXATLCOMLib>,
public XMLObject

{
~CMyATLObj();
DECLARE_REGISTRY_RESOURCEID(IDR_MYATLOBJ)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CMyATLObj)
COM_INTERFACE_ENTRY(IMyATLObj)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

void MapXMLTagsToMembers();

public:
DECLARE_FACTORY(CMyATLObj, Container)
};

In your implementation file you'll need to add the macro at a global scope and implement MapXMLTagsToMembers() to define the Object to XML mappings.  This example maps an integer, a string, and a list of COM objects. 

 

 

IMPLEMENT_ATL_FACTORY(CMyATLObj, Container)

void CMyATLObj::MapXMLTagsToMembers()
{
   MapObjectID("CustomerID",1);
   MapMember(&m_nInteger, "CustomerID");
   MapMember(&m_strString, "CustomerName", &gGenericStrHandler);
   MapMember(&m_lstCMyATLObj, CMyATLObj::GetStaticTag(), &gGListHandler,0);
}

In the ExATLCOM sample application several additional methods have been added to the COM Object.  Most notably put_XMLState() that has the ability to assign members variables and create COM objects when supplied well-formed XML as input. 

STDMETHODIMP CMyATLObj::put_XMLState(BSTR newVal)
{
     _bstr_t b(newVal);
     FromXML((const char *)b);
     return S_OK;
}

 

Object Caching and Instance Management

XMLFoundation has been serving up the XML related needs of the application layer for nearly a decade.  It has been used to build a wide variety of application types.  A common recurring need in the application layer has to do with "data updates".  Any application that receives XML updates might consider the performance advantages and reduction in development labor by using the XMLFoundation to solve the problem for them.

For example, suppose you had some large dump of XML data.  In your application layer you need to quickly access individual pieces of that information.  In just a few lines of code, the XML can be mapped to a keyed data structure for fast indexed reads by your application.  If the initial XML dataset was 100+million records - you will want to provide updates to your indexed information rather than rebuilding the entire index.  You could write the code to search for the data to update, or allow the XMLFoundation to manage it for you.

Another common example in distributed systems:  Data is often cached at a middle tier or in the application itself.  Efficiently designed systems only update an "Address" rather than a whole "Customer" and all his "Orders" when an "Address" changes.  XMLFoundation can greatly simplify this task.  

At the core of caching is something XMLFoundation calls the OID, or Object ID.  It is a unique key to the object, and any object that participates in XMLFoundation caching must have one.  The definition of the OID can come from 2 places.  It can be defined in the XML data, or it can be defined by the object that mapped the data.

This is how a "'MyOrderLineItem" object might define the OID.  It uses a combination of the "ProductID" and "UnitPrice" so in this example a price change constitutes a different object.  Normally an OID has a direct correlation to DBMS indexes in properly normalized data.  ObjectID's can be made to work well over poor data models too.  This example code uses two XML Elements("ProductID" and "UnitPrice") to build the unique object ID.   MapObjectID() also allows you to use Attributes to define the OID.

    void MyOrderLineItem::MapXMLTagsToMembers()
    {
         MapObjectID("ProductID",1,"UnitPrice",1);
    }

Alternatively the OID can be directly defined by the data itself with a special attribute named "OID" - so that NO CODE needs to be written.

<MyOrderLineItem oid='777'>
     <ProductID>123</ProductID>
     <UnitPrice>7.77</UnitPrice>
</MyOrderLineItem>

The sample application "ObjectCache" provides over 30 test cases that detail the usage of object caching. 

 

FiveLoaves

5Loaves is included with the XMLFoundation, but not in the foundation.  It is a tool that uses XMLFoundation.  It needs the XMLFoundation but the XMLFoundation does not need 5Loaves.  5Loaves is implemented in a single file called ServerCore.cpp.  It is a unique piece of code and it is very simple to use.  There is no header file for ServerCore.cpp and it causes no DLL's to be loaded by your application.  It is a portable, properly threaded, and well written server core that can be applied to countless custom server implementations.  It is a Proxy.  It is an HTTP Server.  It is a 'connection joiner'.  ServerCore.cpp is currently used to accept TCP connections for an advanced networking product called Xfer. (note: Xfer is not open source).  The ServerCore has a good portable threading model with some unique features that allow you to limit such things as, number of connections per IP/Subnet and Connections per second.  The 5Loaves HTTP Server works faster than IIS or Apache in some cases.  It has been designed to be fast and includes unique features such as 'content caching' to serve up prebuilt HTTP headers and data from memory rather than from disk.

The Core of 5Loaves is a ground up POSIX threaded TCP server.  This server template can be applied to build many types of applications that service TCP connections.  The sample programs are server applications that use the 5Loaves ServerCore.  One example is the 5Loaves shell console.  It is a command line interpreter like the DOS prompt or a Unix shell written from scratch.  It is a useful application starting point if you ever need a simple shell that has been tested in Linux, Solaris, AIX, HPUX, and Windows.  There is also a Windows Service application.  It contains the proper implementation for integrating 5Loaves with the Windows Service Control Manager.  There is also an example program with all the source to an ActiveX implementation of 5Loaves so you can see that this server core can be embed just about anywhere in any development language.

The following code sample shows you how to start the HTTP service from inside your own process.  You can't do that with IIS or Apache.  The HTTP server does support binary plugins like ISAPI, and there is an example program that creates plugins.  It's an advanced HTTP server.


        // This is a complete source example to embed a solid and high performance 
        // HTTP Server in your application that starts on a variable port with a 
        // variable home directory.
        #include "../Core/ServerCore.cpp"


        char *pzBoundStartupConfig =
        "[System]\r\n"                // <-- notice the [System] section
        "DisableLog=yes\r\n"
        "MaxOpenConnectionsPerIP=5\r\n"
        "Pool=20\r\n"
        "ProxyPool=0\r\n"
        "FrequencyTimeLimit=5\r\n"
        "FrequencyConnectLimit=7\r\n"
        "FrequencyTimeRelease=3600\r\n"
        "FrequencyViolatorRefuse=90\r\n"
        "\r\n"
        "[HTTP]\r\n"                // <-- notice the [HTTP] section
        "Enable=yes\r\n"
        "EnableRange=yes\r\n"
        "RangeZeroBeginOnly=no\r\n"
        "Index=Index.html\r\n"
        "Home=%s\r\n"
        "ContentCache=no\r\n"
        "HTTPHeaderServerName=FiveLoaves\r\n"
        "Port=%s\r\n"
        "UseKeepAlives=yes\r\n"
        "KeepAliveTimeOut=60\r\n";


        void CMyClass::StartHTTPServer(const char *pzHomeDirectory,const char *pzPort)
        {
             // Fill the two variables into the startup config string
             GString strCfgData;
             strCfgData.Format(pzBoundStartupConfig,pzHomeDirectory,pzPort);
            
             // Set the global profile object
             SetProfile(new GProfile( strCfgData, strCfgData.Length()) );
       
             // Start the HTTP service
             server_start();
        }

 

FiveLoaves Tunneling and Messaging

5Loaves provides tunneling with encryption and compression as a base service.  It can be used to secure internet connections much like SSH.  It works by running some form of server with the 5Loaves engine on two machines.  The data passed between those machines can be compressed or encrypted or simply logged.  Starting the server is done exactly the same no matter if you are running the HTTP service or the Tunneling service - only the configuration startup string changes.  The following configuration example will open a listener on port 1972, anything it receives will be encrypted and compressed and sent to [www.ExampleServer.com] on port 2009.  The server will decrypt and decompress the data from port 2009 and forward it to port 1972, so the data on port 1972 at the server will be as if the client had directly sent it.  To open a tunnel entry point on the client side use this:



[Tunnel1]
Enable=yes
LocalPort=1972
RemotePort=2009
RemoteMachine=www.ExampleServer.com
Timeout=30
CompressEnabled=yes
CipherPass=Tiger
RawPacketProxy=no

to exit the tunnel on the server  

[Proxy1]
Enable=yes
LocalPort=2009
RemotePort=1972
RemoteMachine=127.0.0.1  (we could also use an internal resource like 192.168.*)
Timeout=60
RawPacketProxy=no
CompressEnabled=yes
CipherPass=Tiger


Note: you can start any number of tunnels, simply increment the section [Tunnel2] and [Proxy2].  ServerCore will stop loading tunnels at the first break of numeric order of the [sectionN]

 

This type of usage may be of interest especially to web developers or people who are just curious.  Sometimes it's interesting to see the data between the HTTP server and the browser.  Redirects and HTML frames and Javascript can make that difficult.  This section sets up a clear text proxy just for the purpose of seeing the transmission log between the browser and the web-server.  Once this is running, connect with a web browser to 127.0.0.1 and you will really be connecting to www.SampleWebSite.com

 

[Tunnel2]
Enable=yes
LocalPort=80
RemotePort=80
RemoteMachine=www.SampleWebSite.com
Timeout=65000
RawPacketProxy=yes
LogPath=c:\HTMLSpy
LogBinary=no
LogEnabled=yes

In the folder HTMLSpy you will see a log file of the communication between the browser and the web server.  I used Firefox as a browser, and the 5Loaves HTTP server for this example.  I loaded the page twice in the browser so that you can see the caching mechanism working between the browser and the web server in this log file:  The header is defined like this: "tx->s" means "transmit to server" so that is information that came from the browser, it is followed by the time, then the total bytes transmitted, 470 in this case.  The reply is "tx->c" or "transmit to client", you can see that the HTTP server responded with 181 bytes that instructed the browser to use the version it has cached.  You can also see that the "Server:" name was set to "MyWebServer", that is a variable in the 5Loaves HTTP server unlike the static names IIS and Apache use. 

 
tx->s00:51:42-000470>
GET / HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
If-None-Match: 1136664248
Cache-Control: max-age=0


tx->c00:51:42-000181>
HTTP/1.1 304 Not Modified
Server: MyWebServer
Date: Wed, 01 Jul 2009 00:51:42 GMT
Connection: keep-alive
Keep-Alive: timeout=20, max=149
ETag: 1136664248
Content-Length: 0

By changing the log binary to yes, now we can see what a small gif file looks like over the wire from the web server.
[Tunnel2]
LogBinary=yes

tx->c01:05:29-000317>
48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D 0A 44 61 74 65 3A 20 57 65      HTTP/1.1 200 OK..Date: We
64 2C 20 30 31 20 4A 75 6C 20 32 30 30 39 20 30 31 3A 30 35 3A 32 39 20 47      d, 01 Jul 2009 01:05:29 G
4D 54 0D 0A 53 65 72 76 65 72 3A 20 4D 79 57 65 62 53 65 72 76 65 72 0D 0A      MT..Server: MyWebServer..
43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61 6C 69 76 65 0D 0A 4B      Connection: keep-alive..K
65 65 70 2D 41 6C 69 76 65 3A 20 74 69 6D 65 6F 75 74 3D 32 30 2C 20 6D 61      eep-Alive: timeout=20, ma
78 3D 31 34 39 0D 0A 4C 61 73 74 2D 6D 6F 64 69 66 69 65 64 3A 20 53 75 6E      x=149..Last-modified: Sun
2C 20 31 33 20 4D 61 72 20 32 30 30 35 20 32 32 3A 33 32 3A 33 36 20 47 4D      , 13 Mar 2005 22:32:36 GM
54 0D 0A 45 54 61 67 3A 20 31 31 31 30 37 34 39 35 35 36 0D 0A 43 6F 6E 74      T..ETag: 1110749556..Cont
65 6E 74 2D 74 79 70 65 3A 20 69 6D 61 67 65 2F 67 69 66 0D 0A 43 6F 6E 74      ent-type: image/gif..Cont
65 6E 74 2D 6C 65 6E 67 74 68 3A 20 37 34 0D 0A 0D 0A 47 49 46 38 39 61 10      ent-length: 74....GIF89a.
00 10 00 91 00 00 00 00 00 FF FF FF FF FF FF 00 00 00 21 F9 04 01 00 00 02      ..................!......
00 2C 00 00 00 00 10 00 10 00 00 02 1B 94 8F A9 CB 07 AD C0 83 4E 52 23 2D      .,...................NR#-
CD BA F1 BE 7C 5B 76 91 E5 54 5E EA CA 1A 05 00 3B                              ....|[v..T^.....;

 

Messaging

5loaves has a unique network connectivity utility built in.  It allows machines behind a firewall that cannot "listen" for connections outside the network to accept connections from anywhere without any firewall configuration changes.  There is a complete example called "FilePoster" that puts a file on a machine behind a firewall.  This is a bare bones 'proof of concept' implementation that gives you a working model to customize for your own purposes.  It requires 3 machines to see it work as designed.

Machine 1 - (the switchboard) should be located on the internet.  You must run the HTTP service along with the "Switchboard Service", you can see that this example runs it on port 81 just incase you have IIS or Apache already on port 80.

[SwitchBoardServer]
Enable=yes
Name=/PublicPath/

[HTTP]
Enable=yes
Port=81
ContentCache=0
UseKeepAlives=1
HTTPHeaderServerName=5Loaves
KeepAliveTimeOut=20
ShowIPAddressPageName=ShowIP
Home=d:\home


[Trace]
HTTPHeaderTrace=0
ThreadTrace=0
ConnectTrace=1

Machine 2 - (the server) this is the machine behind the firewall that you want to open up a connection path to.  It will poll the switchboard server looking for connections.  It should run 5oaves with the configuration below.  This will accept remote data, and write it to disk in a file at "c:\5LMessages\UBTsAccountForYou".  You could change the application logic that writes the file - you can do anything with the data that may contain commands, database queries, or custom logic.

[Messaging]
Enable=yes
AcceptFrom=UBTsAccountForYou
DefaultSwitchBoardServer=10.20.30.40
DefaultSwitchBoardPort=81
UseBrowserProxy=no


[MsgFrom-UBTsAccountForYou]
Enable=yes
CheckAtSwitchBoard=yes
Name=/PublicPath/UBTsAccountForYou
DiskLocation=c:\5LMessages\UBTsAccountForYou
LetSenderPlaceFile=No
PollIntervalSeconds=20

Machine 3 - (the client) runs the FilePoster sample application.  Machine 3 can reach "the switchboard (machine 1)" but not "the server (machine 2)".  We will send the data to Machine 2 and get a response back from that machine.  In this case we are simply writing the data we send to a file, but the data could just as easily have been an SQL statement and the return data could be the result set rather than just a confirmation that the file was written.

 

How it works:

The "server" polls into the "switchboard" with an HTTP GET.  The "client" pushes a multipart HTTP POST to the "switchboard".   The switchboard joins the connections and proxies the data.  An HTTP GET needs an HTTP  "200 OK" so the "switchboard" server rips off the POST headers from the data sent up by the "client" and replaces them with an HTTP 200 followed by the POST data that gets proxied straight through.  Once this initial message proxy is complete, the client connection that POSTed it 
waits in the switchboard, for the server to POST back a response. Then the Switchboard goes through the same process of ripping off the POST HTTP header and replacing it with a 200 OK before sending the response back to the client. Lastly, the switchboard server replies with an empty HTTP 200 to the servers response POST to complete the normal HTTP request/response design for both the client and the server.  This allows it to pass through HTTP proxy servers and direct support for them is included.  Technically this is a loophole through most networks that only allow HTTP, because as you see we invented a new protocol that looks like HTTP, but in fact it is not.

Plugins and Language Drivers

The XMLFoundation supports "Language Drivers".   Language drivers allow methods written in any language to be invoked programmatically.  Just about every programming language can have a language driver developed for it.  Three complete driver implementations come with the XMLFoundation source code.  Since both the HTTP Server and the Messaging System are built into ServerCore.cpp, they both allow programmers to easily invoke their own code, even if they are not C++ programmers.  For years HTTP servers used CGI to accomplish this task.  They still do.  They do it even better through ISAPI.  XMLFoundation does it better still by using a language abstraction to accomplish the same thing.  The programmer's code is "the plugin" that is executed by the "Language Driver".  All of the source code for everything I speak of is included in the XMLFoundation, see [IntegrationBase.h/cpp][IntegrationLanguages.h/cpp], using DynamicLibrary.h to load the DLL/SO's on many platforms.

A C++ Plugin is as fast as ISAPI.  The other languages are fast too because the "interpreter" or "Virtual Machine" or "runtime" (terminology changes but the concept is the same) is cached in memory.  It certainly is more functional than ISAPI from an "open standard interface" point of view.  Conceptually it allows "methods" to be written in any language or even multiple languages within the same application.  This technology is stable.  It first appeared in TransactXML Server, it was also integrated into my XSLT, and DesignerXML and Xfer which support Perl and Python language drivers as well.

One sample application titled "PluginExample" is devoted to C++ plugins.  It shows how to write several types of POST handler plugins for the HTTP server.  You will find the utility class CMultiPartForm handy if you need to write a handler for a Multipart HTTP POST, other plugin utilities like PlugInController found in PluginBuilder.h make the job of building a plugin as easy as possible.

This is how to build a custom dynamic web page from an HTTP server plugin:

extern "C" __declspec(dllexport) void Test2(void *pHandle, DriverExec Exec,
                                                      const char *One,     // string
                                                       const char *Two,     // string
                                                       const char *Three)     // string


     PlugInController PIC(pHandle, Exec);
     
     // build the string "5Loaves Invoked me with [root] and [777] and [superuser]!"

     PIC.AppendResults("5Loaves HTTP Server Invoked me with [");
     PIC.AppendResults(One);
     PIC.AppendResults("] and [");
     PIC.AppendResults(Two);
     PIC.AppendResults("] and [");
     PIC.AppendResults(Three);
     PIC.AppendResults("]!");
}

from this URL: http://127.0.0.1/test2WWWPage&root&777&superuser

or this HTML:
  
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE>United Business Technologies</TITLE><BODY>
<FORM method=post Action="/Test2WWWPage">
<P><INPUT name=One>
<P><INPUT name=Two>
<P><INPUT name=Three>
<INPUT type=submit value=Submit name=B1>
<INPUT type=reset value=Reset name=B2></P></FORM></BODY></HTML>


This is the complete source for a C++ Messaging plugin to write bulk data to a file and return a static message confirming receipt of the data.  While receiving a file is no big deal, receiving it from outside the network through the firewall is.

 
extern "C" __declspec(dllexport) void CustomMessagingHandler(void *pHandle, DriverExec Exec,const char *pData)
{

     PlugInController PIC(pHandle, Exec);

     int nArg1Size = PIC.GetArgumentSize();

     FILE *fp = fopen("copyOfMessage.cpy","wb");
     if (fp)
     {
          fwrite(pData,1,nArg1Size,fp);
          int nBytesIn = nArg1Size;
          while ( 1 )
          {
               if ( PIC.GetExpectedDataSize() == PIC.GetTotalBytesIn() )
                    break;

               // get the next chunk of the data
               int nDataSize = 0;
               int nRslt = PIC.NextDataChunk(&nDataSize);
               if (nRslt == -1)
               {
                    // the connection has been broken
                    break;
               }
               else if (nRslt == 1)
               {
                    nBytesIn += nDataSize;
                    fwrite(PIC.GetBuffer1(),1, nDataSize ,fp);
               }
               else if (nRslt == 0)
               {
                    // there is no more data here yet
               }
          }
          fclose(fp);
     }

     PIC << "Your message was received in full and processed by a custom message handler.";
}

 

 

XMLFoundation for Java

XMLFoundation supports Java too, it supports all Java data types like (string byte bool double long short) as well as Java data structures such as (Vector Stack ArrayList LinkedList TreeSet HashSet ).  The XMLFoundation for Java is binary.  If you want to build it yourself, you can because the source code to the entire JavaXMLFoundation is public and included in this release.  There is no need to build it, but it's nice to be able to.  I can hear some uneducated Java programmer already saying "I only want a 'Pure' Java solution".  This is Pure.  It's as pure as the JVM, because if you look close you'll see that it is actually an enhancement to the JVM.

The JVM (Java Virtual Machine) is written in C and C++.  Your Java code runs anywhere the JVM can compile, and the XMLFoundation works the same way.  The good news is that Java programmers don't have to deal with C++ just because their JVM is written in C++.  The same is true of the XMLFoundation for Java.

The XMLFoundation uses JNI (Java Native Interface).  It parses the XML in the native binary (that was created by a C++ compiler just like the JVM) and instantiates 'pure' Java Objects through JNI, then it assigns all the member variables just like the C++ XMLFoundation does.  Can you think of a faster way to get the job done?

This is some sample code that uses the JavaXMLFoundation.  A much more detailed example is included in the source code:

 


// Java source code that shows how to use the XMLFoundation using
// inheritance or containment. It is nearly identical to C++

import java.util.Iterator;
import java.util.Vector;


// MyLineItem has an "ObjectId", that is a value created from
// members and/or attributes of this object that uniquely identify
// it among all instances of it's own type. "ObjectId"'s are
// optional but allow you to perform complex "object updates"
// easily through XML. In main() below, this functionality is
// used when getXML2() is applied. ObjectId's are like a database
// primary key in that they are generally not modified.
class MyLineItem extends XMLObject
{
     private String item;
     private String quantity;
     private int ItemID;
     MyLineItem()
     {
          // call the base class constructor with the XML-tag for 'this'
          super("LineItem");
     }
     MyLineItem(int nID, String itm, String qty)
     {
          super("LineItem");
          item = itm;
          quantity = qty;
          ItemID = nID;
     }

     void MapXMLTagsToMembers()
     {
          //         member      member         xml-tag           (optional)wrapper
          //-----------------------------------------------------------------
          MapMember(quantity,     "quantity",    "Quantity");
          MapMember(item,         "item",        "Description");
          MapMember(ItemID,       "ItemID",      "SKU");

          //////////////////////////////////////////////////////////////////
          // MapObjectId() is optional.
          //////////////////////////////////////////////////////////////////
          MapObjectId(this, "ItemID"); // takes 1 to 5 mapped 'Key parts'
     }
}


// Not "derived from" but "contains" XML support.
class Customer2
{
     public String          name;
     private int            CustID;
     private XMLObject      ContainedXMLObj;
     public MyOrder         objOrder;
     private Vector         vecStrings;

     long l;
     short s;
     double d;
     byte b;
     boolean z;


     public void XMLDump()
     {
          MyExchange("out");
          System.err.println( ContainedXMLObj.toXML() );
     }

     // indirect inheritance manages calls to FromXML() and ToXML()
     // manually, exposing them is optional. Member<-->XML exchange is
     // also done manually, generally following calls to FromXML() or ToXML()
     // this affords the developer full control for atypical implementations.
     void MyExchange(String inOut)
     {
          ContainedXMLObj.Member(this, inOut, b,     "b","byte","", 1);
          ContainedXMLObj.Member(this, inOut, z,     "z","bool","", 1);
          ContainedXMLObj.Member(this, inOut, l,     "l","long","", 1);
          ContainedXMLObj.Member(this, inOut, d,     "d","double","", 1);
          ContainedXMLObj.Member(this, inOut, s,     "s","short","", 1);
          ContainedXMLObj.Member(this, inOut, name,     "name","FirstName","", 1);
          ContainedXMLObj.Member(this, inOut, CustID,     "CustID","CustomerID","", 1);
          ContainedXMLObj.Member(this, inOut, objOrder,     "objOrder",     "Order","MyOrder","");
          ContainedXMLObj.Member(this, inOut, vecStrings,"vecStrings","StringItem","String""StringList Level2Wrapper");
     }

     public Customer2( String strXML ) 
     {
          // create a new 'empty' object that maps to "Customer".
          ContainedXMLObj = new XMLObject("Customer", false );
          ContainedXMLObj.fromXML(strXML);
          MyExchange("in");
     }
     
     // FromXML is not inheriterd, so we can expose the functionality through a
     // controlled accessor.
     public void ApplyXML( String strXML )
     {
          ContainedXMLObj.fromXML(strXML);
          MyExchange("in");
     }
}
// End of sample Java source code 


This is some of the code I developed inside the JavaXMLFoundation that interacts with the JVM.  (Don't worry you'll never have to work with this code )

jobject MakeObjectInstance(JNIEnv *env,const char *pzObjectType,
                                 DynamicXMLObject *pDO,DynamicXMLObject *pDXOOOwner)
{
     if (env->ExceptionOccurred())
     {
          env->ExceptionClear();
     }

     jclass clazzA = env->FindClass(pzObjectType);
     jobject objReturnValue = 0;
     GString strType("Ljava/lang/String;");

     // if this class type exposes a 'ctor that takes a single XMLObject
     // we are using containment.
     jmethodID midctor = env->GetMethodID(clazzA, "", "(LXMLObject;)V");
     if (env->ExceptionOccurred())
     {
          env->ExceptionClear();
     }
     if (midctor)
     {
          // create a new java XMLObject instance
          jclass clazzX = env->FindClass("XMLObject");
          jmethodID midX =
                    env->GetMethodID(clazzX, "", "(Ljava/lang/String;)V");
          jstring     tagX = env->NewStringUTF(pDO->GetObjectTag());
          jobject objX = env->NewObject(clazzA, midX, tagX );

          // assign the object handle into the instance just created.
          jclass clazz = env->GetObjectClass(objX);
          jfieldID fid = env->GetFieldID(clazz, "oH", "I");
          env->SetIntField(objX, fid, CastDXMLO(pDO));

          // create a new instance of some user defined java class that is
          // not extending XMLObject. Pass the XMLObject to the 'ctor.
          objReturnValue = env->NewObject(clazzA, midctor, objX );
     }
     else
     {
          // create an instance of a java object derived from the java XMLObject
          objReturnValue = env->AllocObject(clazzA);

          // assign the base class object handle directly.
          jclass clazz = env->GetObjectClass(objReturnValue);

          // any object that extends XMLObject will have the oH (Object Handle)
          // If the Object created is a String the fid will be 0.
          jfieldID fid = env->GetFieldID(clazz, "oH", "I");
          if (env->ExceptionOccurred())
          {
               env->ExceptionClear();
          }
          if (fid)
          {
               env->SetIntField(objReturnValue, fid, CastDXMLO(pDO));

               // create the jobject to DXO index
               union CAST_THIS_TYPE_SAFE_COMPILERS
               {
                    jobject   mbrObj;
                    void *    mbrVoid;
               }Member; 

               Member.mbrObj = env->NewGlobalRef(objReturnValue);
               pDXOOOwner->addSubUserLanguageObject(Member.mbrVoid);
               // printf("===NewGlobRef[%d]\n",Member.mbrObj);
               pDO->setUserLanguageObject(Member.mbrVoid);
               cacheManager.addAlternate( pDO );
          }
          else if (strType.CompareNoCase(pzObjectType) != 0)
          {
            GString Err;
            Err.Format("Object type [%s] must either be derived from XMLObject\n"
            "or supply a constructor %s(XMLObject o)",pzObjectType,pzObjectType);
          }
     }
     pDO->SetObjectType(pzObjectType);
     return objReturnValue;
}

Java programmers will derive from this class - then the use is nearly identical to the C++ XMLFoundation:

 

import java.util.Vector;
import java.util.Stack;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.TreeSet;
import java.util.HashSet;

public class XMLObject {
     static { System.loadLibrary("JavaXMLFoundation");    }
     private static int InstanceId = 0;

     private native void JavaMap(int oH, int DataType,String strName,String xmlTag,String strWrapper, String ObjType, String strContainerType, int nSource);
     private native void JavaExchange(Object o, int oH, String inout,int nType,String b,String c,String d,String strObjectType, String strContainerType, int nSource);
     private native int  JavaConstruct(int n, String strXMLTag, int bAutoDataSync);
     private native void JavaDestruct(int oH);
     private native void JavaMapCacheDisable(int oH);
     private native void JavaMapOID(int oH, Object o, String Key1, String Key2, String Key3, String Key4, String Key5);
     private native XMLObject JavaGetSubObj(int oH);
     private native void JavaFromXML(int oH, String strXML);
     private native String JavaToXML(int oH);
     private native void JavaRemoveAll(int oH);

     private int          oH; // XML-Object Handle


     public void MemberRemoveAll()
     {
          JavaRemoveAll(oH);
     }

     protected void finalize()
     {
          JavaDestruct(oH);
     }
     protected void freedom()
     {
          JavaDestruct(oH);
     }

     public XMLObject(String strXMLTag) 
     {
          oH = ++InstanceId;
          oH = JavaConstruct(oH, strXMLTag, 1);
     }
     public XMLObject(String strXMLTag, boolean bAutoDataSync)
     {
          oH = ++InstanceId;
          if (bAutoDataSync)
               oH = JavaConstruct(oH, strXMLTag, 1);
          else
               oH = JavaConstruct(oH, strXMLTag, 0);
     }

     // Override this 'virtual' method to map member to XML tags
     // through calls to MapMember();
     private void MapXMLTagsToMembers()
     {
     }
     void DontCacheMemberMaps()
     {
          JavaMapCacheDisable(oH);
     }
     void fromXML(String strXML)
     {
          JavaFromXML(oH, strXML);
     }
     String toXML()
     {
          return JavaToXML(oH);
     }


     private String MakeObjectName(String strIn)
     {
          if (strIn.compareToIgnoreCase("String") == 0)
          {
               strIn = "Ljava/lang/String;";
          }
          else
          {
               String sTemp = "L" + strIn + ";";
               strIn = sTemp;
          }
          return strIn;
     }


     //
     //	MapAttrib for each native data type (1st without the optional 'nested' argument)
     //	
     public void MapAttrib(long z, String pzName, String pzXMLTag)
     {
          JavaMap(oH,0,pzName,pzXMLTag,"","", "", 2);
     }
     public void MapAttrib(double z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,1,pzName,pzXMLTag,"","", "", 2);
     }
     public void MapAttrib(short z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,2,pzName,pzXMLTag,"","", "", 2);
     }
     public void MapAttrib(byte z, String pzName, String pzXMLTag)
     {
          JavaMap(oH,3,pzName,pzXMLTag,"","", "", 2);
     }
     public void MapAttrib(String z, String pzName, String pzXMLTag)
     {
          JavaMap(oH,4,pzName,pzXMLTag,"","", "", 2);
     }
     public void MapAttrib(int z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,5,pzName,pzXMLTag,"","", "", 2);
     }
     public void MapAttrib(boolean z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,6,pzName,pzXMLTag,"","", "", 2);
     }

     //
     //	MapAttrib for each native data type (now with the optional 'nested' argument)
     //	
     public void MapAttrib(long z, String pzName, String pzXMLTag, String pzNestedInTag)
     {
          JavaMap(oH,0,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }
     public void MapAttrib(double z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,1,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }
     public void MapAttrib(short z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,2,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }
     public void MapAttrib(byte z, String pzName, String pzXMLTag, String pzNestedInTag)
     {
          JavaMap(oH,3,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }
     public void MapAttrib(String z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,4,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }
     public void MapAttrib(int z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,5,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }
     public void MapAttrib(boolean z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,6,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
     }




     
     //
     //	MapMember for each native data type (1st without optional nested-in-tag)
     //	
     public void MapMember(long z, String pzName, String pzXMLTag)
     {
          JavaMap(oH,0,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(double z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,1,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(short z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,2,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(byte z, String pzName, String pzXMLTag)
     {
          JavaMap(oH,3,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(String z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,4,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(int z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,5,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(boolean z, String pzName, String pzXMLTag )
     {
          JavaMap(oH,6,pzName,pzXMLTag,"","", "", 1);
     }
     public void MapMember(Object ob, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,7,pzName,pzXMLTag,"",MakeObjectName(strObjectType), "", 1);
     }
     public void MapMember(Vector a, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/Vector;", 1);
     }
     public void MapMember(Stack a, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/Stack;", 1);
     }
     public void MapMember(ArrayList a, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/ArrayList;", 1);
     }
     public void MapMember(LinkedList a, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/LinkedList;", 1);
     }
     public void MapMember(TreeSet a, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/TreeSet;", 1);
     }
     public void MapMember(HashSet a, String pzName, String pzXMLTag, String strObjectType )
     {
          JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/HashSet;", 1);
     }





     //
     //	MapMember for each native data type (now with optional nested-in-tag)
     //	
     public void MapMember(long z, String pzName, String pzXMLTag, String pzNestedInTag)
     {
          JavaMap(oH,0,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(double z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,1,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(short z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,2,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(byte z, String pzName, String pzXMLTag, String pzNestedInTag)
     {
          JavaMap(oH,3,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(String z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,4,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(int z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,5,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(boolean z, String pzName, String pzXMLTag, String pzNestedInTag )
     {
          JavaMap(oH,6,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
     }
     public void MapMember(Object ob, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,7,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType), "", 1);
     }
     public void MapMember(Vector a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/Vector;", 1);
     }
     public void MapMember(Stack a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/Stack;", 1);
     }
     public void MapMember(ArrayList a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/ArrayList;", 1);
     }
     public void MapMember(LinkedList a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/LinkedList;", 1);
     }
     public void MapMember(TreeSet a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/TreeSet;", 1);
     }
     public void MapMember(HashSet a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/HashSet;", 1);
     }





     //
     //	Map ObjectID's - the JavaXMLFoundation works just the same with respect to object caching
     //	
    public void MapObjectId(Object o, String Key1)
     {
          JavaMapOID(oH, o, Key1, "", "", "", "");
     }
     public void MapObjectId(Object o, String Key1, String Key2)
     {
          JavaMapOID(oH, o, Key1, Key2, "", "", "");
     }
     public void MapObjectId(Object o, String Key1, String Key2, String Key3)
     {
          JavaMapOID(oH, o, Key1, Key2, Key3, "", "");
     }
     public void MapObjectId(Object o, String Key1, String Key2, String Key3, String Key4)
     {
          JavaMapOID(oH, o, Key1, Key2, Key3, Key4, "");
     }
     public void MapObjectId(Object o, String Key1, String Key2, String Key3, String Key4, String Key5)
     {
          JavaMapOID(oH, o, Key1, Key2, Key3, Key4, Key5);
     }


     //
     //	Get/Set Member for each native data type
     //	
     public void Member(Object o, String xfer, long z, String pzName, String pzXMLTag, String pzNestedInTag, int nSource )
     {
          JavaExchange(o,oH,xfer,0,pzName,pzXMLTag,pzNestedInTag,"","", nSource);
     }
     public void Member(Object o, String xfer, double z, String pzName, String pzXMLTag, String pzNestedInTag, int nSource )
     {
          JavaExchange(o,oH,xfer,1,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
     }
     public void Member(Object o, String xfer, short z, String pzName, String pzXMLTag, String pzNestedInTag, int nSource)
     {
          JavaExchange(o,oH,xfer,2,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
     }
     public void Member(Object o, String xfer, byte z, String pzName, String pzXMLTag, String pzNestedInTag, int nSource )
     {
          JavaExchange(o,oH,xfer,3,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
     }
     public void Member(Object o, String xfer, String z, String pzName, String pzXMLTag, String pzNestedInTag , int nSource)
     {
          JavaExchange(o,oH,xfer,4,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
     }
     public void Member(Object o, String xfer, int z, String pzName, String pzXMLTag, String pzNestedInTag, int nSource )
     {
          JavaExchange(o,oH,xfer,5,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
     }
     public void Member(Object o, String xfer, boolean z, String pzName, String pzXMLTag, String pzNestedInTag, int nSource )
     {
          JavaExchange(o,oH,xfer,6,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
     }
     public void Member(Object o, String xfer, Object o2, String pzName, String pzXMLTag, String pzObjectType, String pzNestedInTag )
     {
          JavaExchange(o,oH,xfer,7,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(pzObjectType),"",1);
     }
     public void Member(Object o, String xfer, Vector a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/Vector;",1);
     }
     public void Member(Object o, String xfer, Stack a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag)
     {
          JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/Stack;",1);
     }
     public void Member(Object o, String xfer, ArrayList a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/ArrayList;",1);
     }
     public void Member(Object o, String xfer, LinkedList a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/LinkedList;",1);
     }
     public void Member(Object o, String xfer, TreeSet a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/TreeSet;",1);
     }
     public void Member(Object o, String xfer, HashSet a, String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
     {
          JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),"Ljava/util/HashSet;",1);
     }
}

About the author(s) of XMLFoundation

The XMLFoundation was designed and developed mostly by myself, however I had help from co-designers on the NCIS (National Clinical Information System) Project, and mostly only 1 other co-developer.  Special thanks to the SAIC management who believed in me and gave me the opportunity to develop the mother product.  Thanks to everyone who stood beside me during the long and expensive labor of putting this work together.  "I told you so" to everyone else.  In the end technology prevails over politics and ignorance.  The XMLFoundation contains several independent algorithms that were developed by other authors - and their supporting communities - their source code contains more information about them.   Historically it has been the solo engineers that set the direction for the masses.  Bjorne Stroustrup incremented the work of Brian Kernighan and  Dennis Ritchie.  There are countless contributors from the purest perspective of technology advancement and they could not possibly all be named here.

Conclusion

In conclusion, this started a long time ago and it's not going away anytime soon.  It's a masterpiece conspiracy to end times of high crimes. 



Comments

  • You are too modest :)

    Posted by andpar on 08/19/2009 03:27am

    "It is a gift to the world of engineering"

    • My Ego

      Posted by FastCat on 08/19/2009 09:57am

      yeah it's big. I guess it's just lots of self respect. Have you seen the Intel commercial where the inventor of USB passes through the crowd like he rents himself out for stud? Honestly though - handling XML using this approach is a better way to get the job done - and I believe that anyone who sees it will agree. The XMLFoundation could have been sold, but that would hinder adoption. It's a gift. USB is on every computer - like XML. Where will XMLFoundation be in 3 years? We will see.

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • In support of their business continuity and disaster recovery plans, many midsized companies endeavor to avoid putting all their eggs in one basket. Understanding the critical role of last-mile connectivity and always available Internet access for their enterprises, savvy firms utilize redundant connections from multiple service providers. Despite the good intentions, their Internet connectivity risk may still be in a single basket. That is because internet service providers (ISPs) and competitive local …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds