From 0425aadc78680e53000fd0108b540d6eca048516 Mon Sep 17 00:00:00 2001 From: gmcdonald Date: Sat, 13 Feb 2010 01:32:03 +0000 Subject: Moving axis svn, part of TLP move INFRA-2441 git-svn-id: http://svn.apache.org/repos/asf/axis/axis2/c/core/trunk@909681 13f79535-47bb-0310-9956-ffa450edef68 --- xdocs/docs/om_tutorial.html | 547 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 547 insertions(+) create mode 100644 xdocs/docs/om_tutorial.html (limited to 'xdocs/docs/om_tutorial.html') diff --git a/xdocs/docs/om_tutorial.html b/xdocs/docs/om_tutorial.html new file mode 100644 index 0000000..70eb038 --- /dev/null +++ b/xdocs/docs/om_tutorial.html @@ -0,0 +1,547 @@ +
AXIOM stands for AXis Object Model and refers to the XML infoset model +that is developed for Apache Axis2. XML infoset refers to the information +included inside the XML. For programmatical manipulation, it is convenient to +have a representation of this XML infoset in a language specific manner. DOM +and JDOM are two such XML models. AXIOM is conceptually similar to such an +XML model in its external behavior but deep down it is very different.
The objective of this tutorial is to introduce the basics of AXIOM/C and +explain the best practices while using AXIOM.
AXIOM/C is a C equivalant of AXIOM/Java. We have done our best to get +almost the same kind of API in C.
This tutorial can be used by anybody who is interested and wants to go +deeper in to AXIOM/C. Knowledge in similar object models such as DOM will be +helpful in understanding AXIOM, but such knowledge has not been assumed. +Several links are listed in the links section that will help you understand +the basics of XML.
+Pull parsing is a new trend in XML processing. The previously popular XML +processing frameworks such as DOM were "push-based", which means that the +control of parsing was with the parser itself. This approach is fine and easy +to use, but it is not efficient in handling large XML documents since a +complete memory model will be generated in the memory. Pull parsing inverts +the control and hence the parser only proceeds at the user's command. The +user can decide to store or discard events generated from the parser. AXIOM +is based on pull parsing. To learn more about XML pull parsing, see the XML pull +parsing introduction.
AXIOM is a lightweight, differed built XML infoset representation based on +StAX API derived from JSR +173, which is the standard streaming pull parser API. AXIOM can be +manipulated as flexibly as any other object model such as JDOM, but underneath, the objects will be +created only when they are absolutely required. This leads to much less +memory-intensive programming.
The following is a short feature overview of AXIOM.
Since different XML parsers offer different kinds of pull parser APIs,
+ we define an API derived from StAX. That API is defined in
+ axiom_xml_reader.h
. Similarly, we define an XML writer API
+ in axiom_xml_writer.h
. These two APIs work as an abstarction
+ layer between any XML parser and AXIOM. So any parser that is going to be
+ used for AXIOM should implement the axiom_xml_reader
API and
+ the axiom_xml_writer
API using a wrapper layer.
Currenly we use Libxml2 as our default XML + parser.
+ +The AXIOM Builder wraps the raw XML character stream through the
+axiom_xml_reader
API. Hence the complexities of the pull event
+stream are hidden from the user.
In a nutshell, SOAP is an information exchange protocol based on XML. SOAP +has a defined set of XML elements that should be used in messages. Since +Axis2 is a "SOAP Engine" and AXIOM is designed for Axis2, a SOAP specific API +was implemented on top of AXIOM. We have defined a number of structs to +represent SOAP constructs, which wrap general AXIOM structures. Learn more +about SOAP.
Before starting the discussion on AXIOM, it is necessary to get a good
+understanding of the basics of Axis2/C. Axis2/C is designed to be pluggable
+to any system written in C or C++. Therefore, Axis2/C has abstracted the
+functionalities that differ from system to system into a structure
+axutil_env_t
, which we refer to as the Axis2 environment. The
+environment holds axutil_allocator_t
, which is used for memory
+allocation and deallocation, axutil_error_t
, which is used for
+error reporting, axutil_log_t
, which is used for logging
+mechanisms, and axutil_thread_t
which is used for threading
+mechanisms.
When creating the Axis2 environment, the first thing is to create the +allocator.
axutil_allocator_t *allocator = NULL;
allocator = axutil_allocator_init(NULL);
We pass NULL
to the above function in order to use the
+default allocator functions. Then the allocator functions will use the
+malloc
, and free
functions for memory management.
+If you have your own allocator structure, with custom malloc and free
+functions, you can pass them instead.
Convenient macros AXIS2_MALLOC
and AXIS2_FREE
+are defined to use allocator functions (please have a look at
+axutil_allocator.h
for more information).
In a similar fashion, you can create the error and log structures.
axutil_log_t *log = NULL;
axutil_error_t *error = NULL;
log = axutil_log_create(allocator, NULL, NULL);
log = axutil_log_create(allocator, NULL, "mylog.log");
Now we can create the environment by parsing the allocator, error and log
+to axutil_env_create_with_error_log()
function.
axutil_env_t *env = NULL;
env = axutil_env_create_with_error_log(allocator, error,
+log);
Apart from the above abstraction, all the other library functions used are +ANSI C compliant. Further, platform dependent functions are also +abstracted.
As a rule of thumb, all create
functions take a pointer to
+the environment as its first argument, and all the other functions take
+pointer to this particular struct as the first argument, and a pointer to the
+environment as the second argument. (Please refer to our coding convention page to learn more
+about this.)
Example,
axiom_node_t *node = NULL;
axiom_node_t *child = NULL;
node = axiom_node_create(env);
child = axiom_node_get_first_child(node, env);
Note that we are passing the node (pointer to axiom_node_t
)
+as the first argument and the pointer to the environment as the second.
This section explains how AXIOM can be built either from an existing +document or programmatically. AXIOM provides a notion of a builder to create +objects. Since AXIOM is tightly bound to StAX, a StAX compliant reader should +be created first with the desired input stream.
In our AXIOM implementation, we define a struct axiom_node_t
+which acts as the container of the other structs. axiom_node_t
+maintains the links that form the linked list used to hold the AXIOM
+structure.
To traverse this structure, the functions defined in
+axiom_node.h
must be used. To access XML information, the 'data
+element' struct stored in axiom_node_t
must be obtained using
+the axiom_node_get_data_element
function. The type of the struct
+stored in the axiom_node_t
struct can be obtained by the
+axiom_node_get_node_type
function. When we create
+axiom_element_t
, axiom_text_t
etc., it is required
+to parse a double pointer to the node struct as the last parameter of the
+create
function, so that the corresponding node struct can be
+referenced using that pointer.
Example
axiom_node_t *my_node = NULL;
axiom_element_t *my_ele = NULL;
my_ele = axiom_element_create(env, NULL, "MY_ELEMENT", NULL,
+&my_node);
Now if we call the axiom_node_get_node_type
function on the
+my_node
pointer, it will return AXIOM_ELEMENT
.
Code Listing 1
+axiom_xml_reader_t *xml_reader = NULL; +axiom_stax_builder_t *om_builder = NULL; +axiom_soap_builder_t *soap_builder = NULL; +axiom_soap_envelope_t *soap_envelope = NULL; + +xml_reader = axiom_xml_reader_create_for_file(env, "test_soap.xml", NULL); + +om_builder = axiom_stax_builder_create(env, xml_reader); + +soap_builder = axiom_soap_builder_create(env, om_builder , AXIOM_SOAP11_SOAP_ENVELOPE_NAMESPACE_URI); + +soap_envelope = axiom_soap_builder_get_soap_envelope(soap_builder, env); + +
As the example shows, creating an AXIOM from xml_reader
is
+pretty straight forward. Elements and nodes can be created programmatically
+to modify the structure as well. Currently AXIOM has two builders, namely the
+axiom_stax_builder_t
and the axiom_soap_builder_t
.
+These builders provide the necessary information to the XML infoset model to
+build the AXIOM tree.
Code Listing 2
+axiom_namespace_t *ns1 = NULL; +axiom_namespace_t *ns2 = NULL; + +axiom_element_t* root_ele = NULL; +axiom_node_t* root_ele_node = NULL; + +axiom_element_t *ele1 = NULL; +axiom_node_t *ele1_node = NULL; + +ns1 = axiom_namespace_create(env, "bar", "x"); +ns2 = axiom_namespace_create(env, "bar1", "y"); + +root_ele = axiom_element_create(env, NULL, "root", ns1, &root_ele_node); +ele1 = axiom_element_create(env, root_node, "foo1", ns2, &ele1_node); + +
Several differences exist between a programmatically created
+axiom_node_t
and a conventionally built
+axiom_node_t
. The most important difference is that the latter
+will have a pointer to its builder, where as the former does not have a
+builder.
The SOAP struct hierarchy is made in the most natural way for a
+programmer. It acts as a wrapper layer on top of the AXIOM implementation.
+The SOAP structs wrap the corresponding axiom_node_t
structs to
+store XML information.
Addition and removal methods are defined in the axiom_node.h
+header file.
Code Listing 3
Add child operation
+axis2_status_t +axiom_node_add_child(axiom_node_t *om_node, + const axutil_env_t *env, + axiom_node_t *child_node); + +
Detach operation
+axiom_node_t * +axiom_node_detach(axiom_node_t *om_node, + const axutil_env_t *env); + +
The detach operation resets the links and removes a node from the AXIOM +tree.
This code segment shows how child addition can be done.
Code Listing 4
+axiom_node_t *foo_node = NULL; +axiom_element_t *foo_ele = NULL; +axiom_node_t *bar_node = NULL; +axiom_element_t *bar_ele = NULL; + +foo_ele = axiom_element_create(env, NULL, "FOO", NULL, &foo_node); +bar_ele = axiom_element_create(env, NULL, "BAR", NULL. &bar_node); +axiom_node_add_child(foo_node, env, bar_node); + +
Alternatively, we can pass the foo_node
as the parent node at
+the time of creating the bar_ele
as follows.
bar_ele = axiom_element_create(env, foo_node, "BAR", NULL, &bar_node); + +
The following shows important methods available in
+axiom_element
to be used to deal with namespaces.
Code Listing 5
+axiom_namespace_t * +axiom_element_declare_namespace(axiom_element_t *om_ele, + const axutil_env_t *env, + axiom_node_t *om_node, + axiom_namespace_t *om_ns); + +axiom_namespace_t * +axiom_element_find_namespace(axiom_element_t *om_ele, + const axutil_env_t *env, + axiom_node_t *om_node, + axis2_char_t *uri, + axis2_char_t *prefix); + +axiom_namespace_t * +axiom_element_find_declared_namespace(axiom_element_t *om_element, + const axutil_env_t *env, + axis2_char_t *uri, + axis2_char_t *prefix); + +axis2_status_t +axiom_element_set_namespace(axiom_element_t *om_element, + const axutil_env_t *env, + axiom_namespace_t *ns, + axiom_node_t *element_node); + +
An axiom_element
has a namespace list, the declared
+namespaces, and a pointer to its own namespace if one exists.
The axiom_element_declare_namespace
function is straight
+forward. It adds a namespace to the declared namespace list. Note that a
+namespace that is already declared will not be declared again.
axiom_element_find_namespace
is a very handy method to locate
+a namespace in the AXIOM tree. It searches for a matching namespace in its
+own declared namespace list and jumps to the parent if it's not found. The
+search progresses up the tree until a matching namespace is found or the root
+has been reached.
axiom_element_find_declared_namespace
can be used to search
+for a namespace in the current element's declared namespace list.
axiom_element_set_namespace
sets axiom_element
's
+own namespace. Note that an element's own namespace should be declared in its
+own namespace declaration list or in one of its parent elements. This method
+first searches for a matching namespace using
+axiom_element_find_namespace
and if a matching namespace is not
+found, a namespace is declared to this axiom_element
's namespace
+declarations list before setting the own namespace reference.
The following sample code segment shows how the namespaces are dealt with +in AXIOM.
Code Listing 6
+axiom_namespace_t *ns1 = NULL; +axiom_namespace_t *ns2 = NULL; +axiom_namespace_t *ns3 = NULL; + +axiom_node_t *root_node = NULL; +axiom_element_t *root_ele = NULL; + +axiom_node_t *ele1_node = NULL; +axiom_element_t *ele1 = NULL; + +axiom_node_t *text_node = NULL; +axiom_text_t *om_text = NULL; + +ns1 = axiom_namespace_create(env, "bar", "x"); +ns2 = axiom_namespace_create(env, "bar1", "y"); + +root_ele = axiom_element_create(env, NULL , "root", ns1, &root_node); +ele1 = axiom_element_create(env, root_node, "foo", ns2, &ele1_node); +om_text = axiom_text_create(env, ele1_node, "blah", &text_node); + +
Serialization of the root element produces the following XML:
+<x:root xmlns:x="bar"> + <y:foo xmlns:y="bar1">blah</y:foo> +</x:root> + +
If we want to produce
+<x:foo xmlns:x="bar" xmlns:y="bar1">Test</x:foo> + +
we can use set_namespace and declare namespace functions as follows.
+axiom_node_t *foo_node = NULL; +axiom_element_t *foo_ele = NULL; +axiom_namespace_t *ns1 = NULL; +axiom_namespace_t *ns2 = NULL; + +foo_ele = axiom_element_create(env, NULL,"foo" ,NULL, &foo_node); + +ns1 = axiom_namespace_create(env, "bar", "x"); +ns2 = axiom_namespace_create(env, "bar1","y"); + +axiom_element_set_namespace(foo_ele, env, ns1, foo_node); +axiom_element_declare_namespace(foo_ele, env, ns2, foo_node); +axiom_element_set_text(foo_ele, env, "Test", &foo_node); +
Traversing the AXIOM structure can be done by obtaining an iterator +struct. You can either call the appropriate function on an AXIOM element or +create the iterator manually. AXIOM/C offers three iterators to traverse the +AXIOM structure. They are:
The iterator supports the 'AXIOM way' of accessing elements and is more
+convenient than a list for sequential access. The following code sample shows
+how the children can be accessed. The children can be of type
+AXIOM_TEXT
or AXIOM_ELEMENT
.
Code Listing 7
+axiom_children_iterator_t *children_iter = NULL; +children_iter = axiom_element_get_children(om_ele, env, om_node); +if(NULL != children_iter ) +{ + while(axiom_children_iterator_has_next(children_iter, env)) + { + axiom_node_t *node = NULL; + node = axiom_children_iterator_next(children_iter, env); + if(NULL != node) + { + if(axiom_node_get_node_type(node, env) == AXIOM_ELEMENT) + { + /* processing logic goes here */ + } + } + + } +} + +
Apart from this, every axiom_node_t
struct has links to its
+siblings. If a thorough navigation is needed, the
+axiom_node_get_next_sibling()
and
+axiom_node_get_previous_sibling()
functions can be used. A
+restrictive set can be chosen by using
+axiom_element_xxx_with_qname()
methods. The
+axiom_element_get_first_child_with_qname()
method returns the
+first child that matches the given axutil_qname_t
and
+axiom_element_get_children_with_qname()
returns
+axiom_children_qname_iterator_t
which can be used to traverse
+all the matching children. The advantage of these iterators is that they
+won't build the whole object structure at once; it builds only what is
+required.
![]() | Internally, all iterator implementations stay
+ one step ahead of their apparent location to provide the correct
+ value for the has_next() function . This hidden
+ advancement can build elements that are not intended to be built at
+ all. |
+ +
AXIOM can be serialized using the axiom_node_serialize
+function. The serialization uses axiom_xml_writer.h
and
+axiom_output.h
APIs.
Here is an example that shows how to write the output to the console (we +have serialized the SOAP envelope created in code listing 1).
Code Listing 8
+axiom_xml_writer_t *xml_writer = NULL; +axiom_output_t *om_output = NULL; +axis2_char_t *buffer = NULL; + +.............. + +xml_writer = axiom_xml_writer_create(env, NULL, 0, 0); +om_output = axiom_output_create(env, xml_writer); + +axiom_soap_envelope_serialize(envelope, env, om_output); +buffer = (axis2_char_t*)axis2_xml_writer_get_xml(xml_writer, env); +printf("%s ", buffer); + +
An easy way to serialize is to use the to_string
function in
+om_element
Code Listing 9
+axis2_char_t *xml_output = NULL; +axiom_node_t *foo_node = NULL; +axiom_element_t *foo_ele = NULL; +axiom_namespace_t* ns = NULL; + +ns = axiom_namespace_create(env, "bar", "x"); + +foo_ele = axiom_element_create(env, NULL, "foo", ns, &foo_node); + +axiom_element_set_text(foo_ele, env, "EASY SERAILIZATION", foo_node); + +xml_output = axiom_element_to_string(foo_ele, env, foo_node); + +printf("%s", xml_output); +AXIS2_FREE(env->allocator, xml_output); + +
Note that freeing the returned buffer is the user's responsibility.
axiom_xml_reader
provides three create functions that can be
+used for different XML input sources.
axiom_xml_reader_create_for_file
can be used to read from
+ a fileaxiom_xml_reader_create_for_io
uses a user defined
+ callback function to pull XMLaxiom_xml_reader_create_for_memory
can be used to read
+ from an XML string that is in a character buffer+ + +ls of the latest version can be found on the Apache Axis2/C +
axiom_xml_writer_create_for_file
can be used to write to a
+ fileaxiom_xml_writer_create_for_memory
can be used to write to
+ an internal memory buffer and obtain the XML string as a character
+ bufferPlease refer to axiom_xml_reader.h
and
+axiom_xml_writer.h
for more information.
You have to be extremely careful when using AXIOM, in order to avoid +memory leaks and double free errors. The following guidelines will be +extremely useful:
1. The axiom_element
struct keeps a list of attributes and a
+list of namespaces, when an axiom_namespace
pointer or an
+axiom_attribute
pointer is added to these lists, which will be
+freed when the axiom_element
is freed. Therefore a pointer to a
+namespace or an attribute should not be freed, once it is used with an
+axiom_element
.
To avoid any inconvenience, clone functions have been implemented for both
+the axiom_namespace
and axiom_attribute
+structures.
2. AXIOM returns shallow references to its string values. Therefore, when
+you want deep copies of returned values, the axutil_strdup()
+function should be used to avoid double free errors.
Example
axiom_namespace_t *ns = NULL;
axis2_char_t *uri = NULL;
ns = axiom_namespace_create(env, "http://ws.apache.org",
+"AXIOM");
uri = axiom_namespace_get_uri(ns, env);
/* now uri points to the same place where namespace struct's uri
+pointer is pointing. Therefore following will cause a double free
+*/
AXIS2_FREE(env->allocator, uri);
axiom_namespace_free(ns, env);
3. When creating AXIOM programatically, if you are declaring a namespace
+with an axiom_element
, it is advisable to find whether the
+namespace is already available in the elements scope using the
+axiom_element_find_namespace
function. If available, that
+pointer can be used instead of creating another namespace struct instance to
+minimize memory usage.
The following code segment shows how to use AXIOM for building a document +completely and then serializing it into text, pushing the output to the +console.
Code Listing 10
+#include <axiom.h> +#include <axis2_util.h> +#include <axutil_env.h> +#include <axutil_log_default.h> +#include <axutil_error_default.h> +#include <stdio.h> + +FILE *f = NULL; +int read_input_callback(char *buffer, int size, void* ctx) +{ + fread(buffer, (char), size, f); +} +int close_input_callback(void *ctx) +{ + fclose(f); +} +axutil_env_t * create_environment() +{ + axutil_allocator_t *allocator = NULL; + axutil_env_t *env = NULL; + axutil_log_t *log = NULL; + + axutil_error_t *error = NULL; + allocator = axutil_allocator_init(NULL); + log = axutil_log_create(allocator, NULL, NULL); + + error = axutil_error_create(allocator); + env = axutil_env_create_with_error_log(allocator, error, log); + env; +} + +build_and_serialize_om(axutil_env_t *env) +{ + axiom_node_t *root_node = NULL; + + axiom_element_t *root_ele = NULL; + axiom_document_t *document = NULL; + axiom_stax_builder_t *om_builder = NULL; + + axiom_xml_reader_t *xml_reader = NULL; + axiom_xml_writer_t *xml_writer = NULL; + axiom_output_t *om_output = NULL; + + axis2_char_t *buffer = NULL; + + f = fopen("test.xml","r"); + xml_reader = axiom_xml_reader_create_for_io(env, read_input_callback, + close_input_callback, NULL, NULL); + (!xml_reader) + -1; + + om_builder = axiom_stax_builder_create(env, xml_reader); + (!om_builder) + { + axiom_xml_reader_free(xml_reader, env); + AXIS2_FAILURE; + } + document = axiom_stax_builder_get_document(om_builder, env); + (!document) + { + axiom_stax_builder_free(om_builder, env); + AXIS2_FAILURE; + } + + root_node = axiom_document_get_root_element(document, env); + (!root_node) + { + axiom_stax_builder_free(om_builder, env); + AXIS2_FAILURE; + } + (root_node) + { + (axiom_node_get_node_type(root_node, env) == AXIOM_ELEMENT) + { + root_ele = (axiom_element_t*)axiom_node_get_data_element(root_node, env); + (root_ele) + { + printf(" %s" ,axiom_element_get_localname(root_ele, env)); + } + } + } + + axiom_document_build_all(document, env); + axiom_document_build_all(document, env); + + xml_writer = axiom_xml_writer_create_for_memory(env, NULL, AXIS2_TRUE, 0, AXIS2_XML_PARSER_TYPE_BUFFER); + + om_output = axiom_output_create(env, xml_writer); + + axiom_node_serialize(root_node, env, om_output); + + buffer = (axis2_char_t*)axiom_xml_writer_get_xml(xml_writer, env); + + printf("The output XML is ->>>>\n %s ", buffer); + + + + axiom_output_free(om_output, env); + + + axiom_stax_builder_free(om_builder, env); + + AXIS2_SUCCESS; + +} +int main() +{ + int status = AXIS2_SUCCESS; + + axutil_env_t *env = NULL; + axutil_allocator_t *allocator = NULL; + env = create_environment(); + + status = build_and_serialize_om(env); + + (status == AXIS2_FAILURE) + { + printf(" build AXIOM failed"); + } + + axutil_env_free(env); + + 0; +} + + +