My current customer has a SOA Suite implementation dating from the 10g era. They use many queues (JMS serves by AQ) to decouple services, which is in essence a good idea.
However, there are quite a load of them. Many composites have several adapter specifications that use the same queue, but with different message selectors. But also over composites queues are shared.
There are a few composites with ship loads of .jca files. You would like to replace those with a generic adapter specification, but you might risk eating messages from other composites. This screendump is an anonymised of one of those, that actually still does not show every adapter. They're all jms adapter specs actually.
So, how can we figure out which queues are used by which composites and if they read or write?
I wanted to create a script that reads every .jca file in our repository and write a line to a CSV file for each JCA file, containing:
-
Name of place
- Name of the jca file
-
Type of the adapter
- Is it an activation (consume) or interaction (type)
-
What is the location (eis JNDI )
-
Destination
-
Payload
-
Message selector (when consumption)
Amongst some other properties.
Using ANT to scan jca Files
I found that ANT is more than capable for the job. I put my project on GitHub, so you can all the files
there.
Since I want to know the project that the .jca file belongs to, I first select all the .jpr files in the repository. Because the project folders are spread over the repository, although structured they're not neatly in a linear row of folders, finding the .jpr files gives me a list of all the projects.
<!-- Initialisatie -->
<target name="clean" description="Clean the temp folder">
<delete dir="${jcaTempDir}"/>
<mkdir dir="${jcaTempDir}"/>
</target>
<!-- Perform all -->
<target name="all" description="Scan All SOA applications" depends="clean">
<echo>FMW_HOME=${fmw.home}.</echo>
<echo file="${outputFile}" append="false"
message="project name,jcaFile,adapter-config-name,adapter-type,connection factory location,endpoint type,class,DestinationName,QueueName,DeliveryMode,TimeToLive,UseMessageListener,MessageSelector,PayloadType,ObjectFieldName,PayloadHeaderRequired,RecipientList,Consumer${line.separator}"></echo>
<foreach param="project.file" target="handleProject" delimiter=';' inheritall="true">
<path>
<fileset id="dist.contents" dir="${svnRoot}" includes="**/*.jpr"/>
</path>
</foreach>
</target>
Side note, as can be seen in the snippet, I re-create a folder for transformed jca files (as described later) and I create a new output file, in which I write a header row with all the column names, using
echo to a
file with the
append properties to
false.
So, I do a
foreach over a fileset, using the svnRoot property in the
build.properties, that includes ever
.jpr file anywhere in the structure. For each file the
handleProject target is called with the file in the
project.file property. Foreach is an
antcontrib addition to ANT. So you need to add that as a task definition (one thing I do as a first thing).
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="${ant-contrib.jar}"/>
</classpath>
</taskdef>
With the name of the .jpr file I have the name of the project and the location:
<target name="handleProject">
<echo message="projectFile: ${project.file}"></echo>
<dirname property="project.dir" file="${project.file}"/>
<echo message="project dir: ${project.dir}"></echo>
<basename property="project.name" file="${project.file}" suffix=".jpr"/>
<foreach param="jca.file" target="handleJca" delimiter=";" inheritall="true">
<path>
<fileset id="dist.contents" dir="${project.dir}" includes="**/*.jca"/>
</path>
</foreach>
</target>
In this snippet the
dirname ANT task trims the filename from the
project.file property, to provide me the project folder into the
project.dir property. The
project.name can be determined from the
project.file using the
basename task. Nice touch is that it allows you to trim the suffix (
.jpr) from it. Within the project location I can find all the
.jca file, and in the same way as the
.jpr files I can use a
foreach on the
project.dir and call the
handleJca target for each
.jca file.
Using XSL to pre-process the jca files
Fortunately, jca files are simply XML files and ANT turns out to be able to read XML as a property file, using the
xmlproperty task, which came in handy. Those properties can be appended to an output file using
echo, very easily.
However, there are two main problems with the structure of the jca files:
- The jca files for the interaction type (the write kind) are different from the activation type (the read kind), So I would need to distinguish those.
- The properties like DestinationName, payload and message selector are name value pair properties in the .jca file. The interprets the names of the properties as separate property values with the name. I can't select specifically the Destination Name for instance.
So I decided to create an xml stylesheet to transform the JCA files to a specific schema, that merges the endpoint interaction and activation elements and has the properties I'm interested in as separate elements. To do so, I created an xsd from both types of jca files. JDeveloper can help me with that:
Just follow the wizard, but emtpy the target namespace. As said I did this for both kinds of jca files (the interaction and activation kinds) and merge them into
jcaAdapter.xsd with a
xsd:choice:
Out of that I created
jcaAdapterProps.xsd where the xsd:choice elements are merged into spec element. I changed the targetnamespace and created specific property elements:
That allowed me to create the XSL Map jcaAdapter.xsl easily:
For the
xmlproperty task it is important that the resulting xml is in a default namespace and that the elements depend on the default namespaces, they should not reference a specific namespace (not even the default one).
With that I can finish off with the
handleJca target of my script:
<target name="handleJca">
<basename property="jca.file.name" file="${jca.file}"/>
<property name="jca.file.props" value="${jcaTempDir}/${jca.file.name}.props"/>
<echo message="Jca File: ${jca.file.name}"></echo>
<xslt style="${jcaPropsXsl}" in="${jca.file}" out="${jca.file.props}"/>
<xmlproperty file="${jca.file.props}" collapseattributes="true"/>
<!-- see https://ant.apache.org/manual/Tasks/xmlproperty.html -->
<property name="cf.location" value="${adapter-config.connection-factory.location}"/>
<property name="ep.class" value="${adapter-config.endpoint.spec.className}"/>
<property name="ep.type" value="${adapter-config.endpoint.spec.type}"/>
<property name="ep.DestinationName" value="${adapter-config.endpoint.spec.DestinationName}"/>
<property name="ep.DeliveryMode" value="${adapter-config.endpoint.spec.DeliveryMode}"/>
<property name="ep.TimeToLive" value="${adapter-config.endpoint.spec.TimeToLive}"/>
<property name="ep.UseMessageListener" value="${adapter-config.endpoint.spec.UseMessageListener}"/>
<property name="ep.MessageSelector" value="${adapter-config.endpoint.spec.MessageSelector}"/>
<property name="ep.PayloadType" value="${adapter-config.endpoint.spec.PayloadType}"/>
<property name="ep.QueueName" value="${adapter-config.endpoint.spec.QueueName}"/>
<property name="ep.ObjectFieldName" value="${adapter-config.endpoint.spec.ObjectFieldName}"/>
<property name="ep.PayloadHeaderRequired" value="${adapter-config.endpoint.spec.PayloadHeaderRequired}"/>
<property name="ep.RecipientList" value="${adapter-config.endpoint.spec.RecipientList}"/>
<property name="ep.Consumer" value="${adapter-config.endpoint.spec.Consumer}"/>
<echo file="${outputFile}" append="true"
message="${project.name},${jca.file.name},${adapter-config.name},${adapter-config.adapter},${cf.location},${ep.type},${ep.class},${ep.DestinationName},${ep.QueueName},${ep.DeliveryMode},${ep.TimeToLive},${ep.UseMessageListener},${ep.MessageSelector},${ep.PayloadType},${ep.ObjectFieldName},${ep.PayloadHeaderRequired},${ep.RecipientList},${ep.Consumer}${line.separator}"></echo>
</target>
With the
xslt task the jca file is transformed to the
jcaTempDir folder. And using the
xmlproperty task the transformed
.jca is read as an xml property file. Because the property references are quite long, I copy them in a shorter named property and then
echo them as a comma separated line into the
outputFile using the
append attribute to
true.
Note that I used
collapseattributes attribute set to
true.
Conclusion
And that is actually about it. ANT is very handy to find and process files in a controlled way. Also the combination with XSL makes it powerfull. In this project I concentrated on JMS and AQ adapters, as far as the properties are concerned. But you can extend this for DB Adapters and File Adapters, etc. quite easily. Maybe even create an output file per type.
I can't share the output with you, due to company policy contraints. Just try it out.