1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
|
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>
|