bdd0a41aed
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@668359 13f79535-47bb-0310-9956-ffa450edef68
346 lines
14 KiB
Text
346 lines
14 KiB
Text
Tuscany SCA For C++ Developers Guide
|
|
====================================
|
|
|
|
Contents:
|
|
Developing Services in C++
|
|
Running C++ Services
|
|
|
|
|
|
See cpp/build.txt in the parent directory or the cpp/sca/INSTALL file
|
|
for build instructions.
|
|
|
|
Building the tools
|
|
------------------
|
|
|
|
NOTE: this is built and installed by the build step above.
|
|
|
|
Currently, there is only one tool: "scagen". It can be built using the
|
|
ant build script at \tuscany\cpp\sca\tools\scagen\build.xml.
|
|
The default target "all" will build the java jars, documentation,
|
|
scripts and a zip file of the whole thing. This is all the ant
|
|
build tasks apart from "test" which runs all the junit tests.
|
|
|
|
The ant build script can be altered to add the junit tests to the
|
|
default target. Replace the line
|
|
|
|
<property name="junit.jar.folder" value="${basedir}/lib" />
|
|
|
|
The "test" task was not included in "all" as it requires a
|
|
junit jar file to run. This jar is available here:
|
|
http://www.junit.org/index.htm testing has been done with
|
|
Junit version 3.8.1.
|
|
|
|
Running the scagen tool
|
|
-----------------------
|
|
|
|
The scagen tool user interface is quite basic in this initial release.
|
|
It can be run from the scagen.jar file using "java -jar scagen.jar"
|
|
or from small scripts - scagen.cmd for Windows and scagen.sh for Unix.
|
|
The parameters are:
|
|
-dir <the path to the sca module root directory>
|
|
-output <the path to an output directory where the generated file will be placed>
|
|
|
|
e.g.
|
|
scagen -dir c:\mymodules\module1 -output c:\mymodules\bld\module1
|
|
|
|
What scagen does
|
|
----------------
|
|
|
|
The input directory passed to the scagen tools as
|
|
the -dir parameter method is taken to be the SCA
|
|
module root directory. All the sca.module and .fragment
|
|
files in that directory are inspected to resolve all
|
|
the <component/> elements within them.
|
|
|
|
Each <component/> element found is inspected
|
|
to see if it has a <implementation.cpp/> element within it.
|
|
|
|
Each <implementation.cpp/> element should have a
|
|
header attribute that represents a C++ header file
|
|
that contains function prototypes for the C++
|
|
implementation of the service. An optional class
|
|
attribute can be used to select one class if more than
|
|
one that is present in the header file. The default
|
|
class is the one with the same name as the header file.
|
|
The tool will verify that the implementation header
|
|
contains an appropriate class prototype.
|
|
|
|
The directory that contains the implementation header
|
|
should also contain a matching .componentType file for
|
|
the equivalent SCA component. So for example, a
|
|
MyServiceImpl.h file would have a corresponding
|
|
MyServiceImpl.componentType file in the same directory.
|
|
|
|
Each componentType file is inspected for <service/>
|
|
and <reference/> elements. For each <service/> element
|
|
that is found that contains a <interface.cpp/> element
|
|
within it,
|
|
|
|
the header attribute of the <interface.cpp/> is taken
|
|
as the filename of the C++ interface header for the
|
|
SCA service. This C++ header file is opened and used
|
|
as a means for specifying the SCA service resulting
|
|
in an appropriate wrapper and proxy being generated
|
|
for this service interface. Both method bodies and h
|
|
eaders are generated in the given output directory.
|
|
The processing of a <reference/> element is the same
|
|
except that only a proxy header and implementation
|
|
re generated.
|
|
|
|
|
|
Getting started with the code
|
|
-----------------------------
|
|
|
|
The following is a list of tasks that are performed by the scagen tool
|
|
for each task we will describe technically how it is accomplished and
|
|
the location of the code that can be inspected/changed to alter the
|
|
behaviour.
|
|
|
|
Here are the tasks listed, below is a paragraph for each one:
|
|
|
|
o (Overall structure of the code)
|
|
o Walking the input directory
|
|
o Scanning the .module and .fragment files
|
|
o finding the C++ implementation headers
|
|
o finding/checking the classname in the C++ implementation headers
|
|
o find the matching .componentTemplate files
|
|
o going into the componentTemplate files to extract the interface header filenames
|
|
o going into the interface header files and parsing them to extract the method signatures
|
|
into a network of objects we can inspect.
|
|
o taking all the meta data stored as objects and building a DOM for XSLT processing
|
|
o using XSLT to produce a proxy header
|
|
o using XSLT to produce a proxy implementation
|
|
o using XSLT to produce a wrapper header
|
|
o using XSLT to produce a wrapper implementation
|
|
|
|
|
|
Overall structure of the code
|
|
-----------------------------
|
|
|
|
There are two packages org.apache.tuscany.sca.cpp.tools.common and
|
|
org.apache.tuscany.sca.cpp.tools.services. The ...common package is
|
|
taken from some existing code that was also contributed to axis that
|
|
was used to parse C++ code and do various tasks like insert trace.
|
|
This code was repackaged and shipped as a tuscany package but there
|
|
has been a desire not to change it significantly from the equivalent
|
|
org.apache.axis.tools.common package to leave the door open for
|
|
future convergence.
|
|
|
|
Where the ...common package has been amended (for example to cope with
|
|
namespaces better or the provision of an Options.reset method to reset a static
|
|
variable and enable the tuscany junit tests to run independently) these
|
|
have been flagged with a "Tuscany" comment. The ...common package basically
|
|
provides two functions - 1) the ability to go into a directory (see DirectoryTree.java)
|
|
and process files that fit a particular filter (e.g. "*.hpp") by passing them to
|
|
implementer of the FileActor Interface (see the classes "Headers" for the
|
|
actor that processes C++ headers and "XMLFileActor" for the file actor that
|
|
processes the .componentType and sca.module/fragment files.)
|
|
|
|
The ...services package contains the majority of code written afresh for the
|
|
scagen tool including the subclasses of XMLFileActor (see ComponentTypeFileHandler.java
|
|
and ModuleOrFragmentFileHandler.java) that are the classes that tie this
|
|
package to the ...common package and which are called by the
|
|
DirectoryTree walker.
|
|
|
|
Walking the module root input directory
|
|
---------------------------------------
|
|
|
|
The main method of the scagen class creates an instance of
|
|
"DirectoryScanner" and registers with it a file handler of
|
|
type "ModuleOrFragmentFileHandler" for all files that end
|
|
in ".module" or ".fragment". On calling the "walkTree" method
|
|
on the scanner it will in turn call the actOnFile method on the
|
|
ModuleOrFragmentFileHandler for appropriate files.
|
|
|
|
Scanning the .module and .fragment files
|
|
----------------------------------------
|
|
|
|
The scanning of these files by the respective "ModuleOrFragmentFileHandler"
|
|
and "ComponentTypeFileHandler" is mostly handled by the superclass
|
|
"XMLFileActor". This class will recursively goes through the whole
|
|
XML file and considers the name of the XML element it finds.
|
|
"XMLFileActor" contains a map of element names to element handlers
|
|
that will "flatten out" the structure of the XML file "above" the
|
|
level of node we are interested in.
|
|
|
|
So for example the ComponentTypeFile handler sets up the handlers
|
|
map as follows:
|
|
|
|
GenericDomNodeHandler gdnh = new GenericDomNodeHandler();
|
|
handlers.put("componentType", gdnh);
|
|
handlers.put("interface.cpp", gdnh);
|
|
ServiceDomNodeHandler sdnh = new ServiceDomNodeHandler();
|
|
handlers.put("service", sdnh);
|
|
|
|
ReferenceDomNodeHandler rdnh = new ReferenceDomNodeHandler();
|
|
handlers.put("reference", rdnh);
|
|
|
|
The majority of processing done by these DomNOdeHandlers is to
|
|
place the attributes and values discovered into another map that
|
|
maps an (static version of) the XPath of a value to the value itself.
|
|
So for example "/componentType/service/interface.cpp/@header" might contain
|
|
the current ("root to here") value of the header attribute of the current
|
|
interface.
|
|
|
|
Particular handlers for the "leaves" of this tree
|
|
such as ServiceDomNodeHandler and ReferenceDomNodeHandler
|
|
can then consume these values from the map without having
|
|
to be concerned with the actual names of things,
|
|
like the service name, appearing in the key. It should be
|
|
understood though that there are multiple values placed in the map
|
|
for one "key" as the processing works its way through the
|
|
XML tree. For example the processing of a second component will
|
|
overlay its data over the same keys as the first component.
|
|
(After "wiping" the appropriate subtree.)
|
|
|
|
Finding the C++ implementation headers
|
|
--------------------------------------
|
|
|
|
The "/module/component/implementation.cpp/@header" and
|
|
is used to key into the name of the implementation header
|
|
and this is opened directly and passed to the
|
|
actOnFileMethod of a Headers object from the ...common package
|
|
bypassing the DirectoryScanner code. The path is relative to
|
|
the given (-dir option) module root directory.
|
|
|
|
Finding/checking the classname in the C++ implementation headers
|
|
-----------------------------------------------------------------
|
|
|
|
This implementation header is not used to define the
|
|
methods of the SCA service but rather is opened to check
|
|
any given implementation.cpp/@class attribute
|
|
(or find out the name of the implementation class
|
|
in the header if this is not specified in the XML. This
|
|
is done using the same method that later parses the interface
|
|
C++ headers into java objects - we just them inspect the
|
|
class attribute of the "Signature" objects that represent the methods
|
|
we find in the header.
|
|
|
|
Find the matching .componentType files
|
|
------------------------------------------
|
|
|
|
By SCA convention we go to the same directory as the implementation
|
|
files and look for the XXX.componentType files with the same name.
|
|
A instance of the ComponentDOMNodeHandler handles the data in the
|
|
Component Element and pre-creates a ComponentTypeFileHandler that
|
|
will eventually be called to process the .componentType file. This
|
|
object receives a number of "setParameter" calls to poke into it
|
|
matadata that is available prior/outside the the actual .componentType
|
|
file it will read.
|
|
|
|
Go into the componentType files to extract the interface header filenames
|
|
-----------------------------------------------------------------------------
|
|
|
|
We open up the .componentTemplateFiles with exactly the same
|
|
mechanism as we read the sca.module/fragment file (by creating
|
|
a DOM and descending through it this time using a ComponentTypeFileHandler that it
|
|
has had various data values ( e.g. the implementation class and namespace used later)
|
|
poked into it. The ComponentTypeFileHandler itself has individual
|
|
handlers for the service and reference XML/DOM element/nodes
|
|
that is comes across (ServiceDomNodeHandler and ReferenceDomNodeHandler
|
|
respectively). Each these handlers will pull out the name of
|
|
a C++ interface header and use it to resolve the interface of the
|
|
SCA Service.
|
|
|
|
Parsing the interface header files for signatures
|
|
-------------------------------------------------
|
|
|
|
The Service/Reference DOM Node hander both call the
|
|
ServicesGenerator.handleInterfaceHeader(parameters, true);
|
|
method, the second parameter is used to differentiate
|
|
the call source as we don't need wrapper files for
|
|
SCA references (just proxies).
|
|
|
|
The ServicesGenerator uses the Headers file actor from
|
|
the ...common package to create a List of Signature
|
|
objects that describe the interface methods in the C++
|
|
header.
|
|
|
|
Take all the meta data stored as objects and build a DOM
|
|
--------------------------------------------------------
|
|
We now have a List of Signature objects and a map that
|
|
represents the flattened information that we have pulled
|
|
from the XML files in the ServiceGenerator class.
|
|
We call a "createDOMofMethods" method
|
|
to consolidate all this information into one DOM
|
|
(this task should be split into more than one method as the
|
|
signature/parameter list of the method is too large).
|
|
|
|
Use XSLT to produce the output files (Proxy/Wrapper headers and Implementations)
|
|
--------------------------------------------------------------------------------
|
|
|
|
The ServicesGenerator.handleInterfaceHeader(parameters, forReference);
|
|
method closes of with the code:
|
|
|
|
createProxyCPPFromDom(outputDir, dom);
|
|
createProxyHeaderFromDom(outputDir, dom);
|
|
|
|
if (!forReference) {
|
|
createWrapperCPPFromDom(outputDir, dom);
|
|
createWrapperHeaderFromDom(outputDir, dom);
|
|
}
|
|
|
|
|
|
Each of the create methods sets up the output
|
|
file name and a different XSLT transform and calls
|
|
"createOutputFromDom" to transform/filter the data in the
|
|
"model" that is held in our DOM of the data to a particular
|
|
"view" as expressed in the C++ output file.
|
|
|
|
The four XSLT style sheets are in rough order of the output
|
|
file and this corresponds very roughly to a depth first descent
|
|
of the DOM tree so, for example, we could have in a stylesheet:
|
|
|
|
...
|
|
void* </xsl:text>
|
|
<xsl:value-of select="$class"/><xsl:text>::newImplementation()
|
|
{
|
|
return new </xsl:text><xsl:value-of select="../@implClass"/><xsl:text><xsl:text>(target);
|
|
}
|
|
|
|
which would be output as:
|
|
|
|
void* MyClassImpl_MyClass_Proxy::newImplementation()
|
|
{
|
|
return new MyClassImpl(target)
|
|
}
|
|
|
|
given appropriate valies for $class and "../@implClass" and
|
|
$class might be defined to be:
|
|
xsl:variable name="clazz">
|
|
<xsl:value-of select="../@implClass"/>
|
|
<xsl:text>_</xsl:text>
|
|
<xsl:value-of select="../@nameOfServiceOrReference"/>
|
|
<xsl:text>_Proxy</xsl:text>
|
|
</xsl:variable>
|
|
|
|
giving "MyClassImpl_MyClass_Proxy"
|
|
|
|
The stylesheets can be found in the xsl subdirectory of the
|
|
org.apache.tuscany.sca.cpp.tools.services package.
|
|
|
|
Unit Testing Scagen Code Changes
|
|
--------------------------------
|
|
|
|
The junit unit test
|
|
/tuscany/cpp/sca/tools/scagen/
|
|
junit/org/apache/tuscany/sca/cpp/tools/junit/TestAllModulesTest.java
|
|
|
|
will dynamically look for all the subdirectores of the directory
|
|
path given by TuscanyTestCase.junit_modules and run the scagen
|
|
tool on them as if they were modules roots.
|
|
|
|
By convention an "expected_output" directory is located
|
|
(see the CVS tree or the test program) and the actual
|
|
and expected results compared. This testcase is thus a
|
|
good first/basic regression test for any changes.
|
|
|
|
New test cases can thus be added without having to write
|
|
any new junit java code by by creating new SCA modules and
|
|
the associated expected Scagen output - perhaps by using the tool
|
|
initially and checking the output is satisfactory before copying
|
|
it to the expected output directory at:
|
|
|
|
/tuscany/cpp/sca/tools/scagen/junit/testoutput/<module>/expected_output
|
|
where input data is taken from
|
|
/tuscany/cpp/sca/tools/scagen/junit/testinput/modules/<module>
|