Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Tuscany SCA For C++ - scagen C++ generator tool =============================================== 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 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.bat for Windows and scagen.sh for Unix. The parameters are: -dir -output e.g. scagen -dir c:\mycomposites\composite1 -output c:\mycomposites\bld\composite1 What scagen does ---------------- The input directory passed to the scagen tools as the -dir parameter method is taken to be the SCA composite root directory. All the sca.composite and .fragment files in that directory are inspected to resolve all the elements within them. Each element found is inspected to see if it has a element within it. Each 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 and elements. For each element that is found that contains a element within it, the header attribute of the 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 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 .composite 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.composite/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 CompositeOrFragmentFileHandler.java) that are the classes that tie this package to the ...common package and which are called by the DirectoryTree walker. Walking the composite root input directory --------------------------------------- The main method of the scagen class creates an instance of "DirectoryScanner" and registers with it a file handler of type "CompositeOrFragmentFileHandler" for all files that end in ".composite" or ".fragment". On calling the "walkTree" method on the scanner it will in turn call the actOnFile method on the CompositeOrFragmentFileHandler for appropriate files. Scanning the .composite and .fragment files ---------------------------------------- The scanning of these files by the respective "CompositeOrFragmentFileHandler" 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 "/composite/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) composite 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.composite/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* ::newImplementation() { return new (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"> _ _Proxy 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/TestAllCompositesTest.java will dynamically look for all the subdirectores of the directory path given by TuscanyTestCase.junit_composites and run the scagen tool on them as if they were composites 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 composites 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//expected_output where input data is taken from /tuscany/cpp/sca/tools/scagen/junit/testinput/composites/