| 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> |