Showing posts with label Ant. Show all posts
Showing posts with label Ant. Show all posts

Wednesday, 15 January 2020

Javascript in ANT

Earlier I wrote about an ANT script to scan JCA adapters files in your projects home, subversion working copy or github local repo.

In my current project we use sensors to kick-of message-archiving processes, without cluttering the BPEL process. I'm not sure if I would do that like that if I would do on a new project, but technically the idea is interesting. Unfortunately, we did not build a registry what BPEL processes make use of it and how. So I tought of how I could easily find out a way to scan that, and found that based on the script to scan JCA files, I could easily scan all the BPEL sensor files. If you have found the project folders, like I did in the JCA scan script, you can search for the *_sensor.xml files.

So in a few hours I had a basic sript. Now, in a second iteration, I would like to know what sensorActions the sensors trigger. For that I need to interpret the accompanying *_sensorAction.xml file. There for, based on the found sensor filename I need to determine the name of the sensor action file.

The first step to that is to figure out how to do a substring in ANT. With a quick google on "ant property substring", I found a nice stackoverflow thread, with a nice example of an ANT script defininition based on Javascript:
  <scriptdef name="substring" language="javascript">
    <attribute name="text"/>
    <attribute name="start"/>
    <attribute name="end"/>
    <attribute name="property"/>
    <![CDATA[
       var text = attributes.get("text");
       var start = attributes.get("start");
       var end = attributes.get("end") || text.length();
       project.setProperty(attributes.get("property"), text.substring(start, end));
     ]]>
  </scriptdef>

And that can be called like:
    <substring text="${sensor.file.name}" start="0" end="20"   property="sensorAction.file.name"/>
    <echo message="Sensor Action file: ${sensorAction.file.name1}"></echo>

The javascript substring() function is zero-based, so the first character is indexed by 0.
Not every sensor file name has the same length, the file is called after the BPEL file that it is tight too. And so to get the base name, the part without the "_sensor.xml" postfix, we need to determine the length of the filename. A script that determines that can easily be extracted from the script above:
  <scriptdef name="getlength" language="javascript">
    <attribute name="text"/>
    <attribute name="property"/>
    <![CDATA[
       var text = attributes.get("text");
       var length = text.length();
       project.setProperty(attributes.get("property"), length);
     ]]>
  </scriptdef>

Perfect! Using this I could create the logic in ANT to determine the sensorAction file name. However, I thought that it would be easier to determine the filename in Javascript all the way. Using the strength of the proper language at hand:
  <!-- Script to get the sensorAction filename based on the sensor filename. 
  1. Cut the extension "_sensor.xml" from the filename.
  2. Add "_sensorAction.xml" to the base filename.
  -->
  <scriptdef name="getsensoractionfilename" language="javascript">
    <attribute name="sensorfilename"/>
    <attribute name="property"/>
    <![CDATA[
       var sensorFilename = attributes.get("sensorfilename");
       var sensorFilenameLength = sensorFilename.length();
       var postfixLength = "_sensor.xml".length();
       var sensorFilenameBaseLength=sensorFilenameLength-postfixLength;
       var sensorActionFilename=sensorFilename.substring(0, sensorFilenameBaseLength)+"_sensorAction.xml";
       project.setProperty(attributes.get("property"), sensorActionFilename);
     ]]>
  </scriptdef>
And then I can get the sensorAction filename as follows:
    <getsensoractionfilename sensorfilename="${sensor.file.name}" property="sensorAction.file.name"/>
    <echo message="Sensor Action file: ${sensorAction.file.name}"></echo>

Superb! I found ANT a powerfull language/tool already. But with a few simple JavaScript snippets you can extend it easily.
Notice by the way also the use of xslt in the Scan JCA adapters files article. You can read xml files as properties, but to do that conveniently you need to transform a file like the sensors.xml in a way that you can easily reference the properties following the element-hierarchy. This is also explained in the Scan JCA adapters files article.
I'll go further with my sensors scan script. Maybe I'll write about it when done.

Wednesday, 28 November 2018

Using ANT to investigate JCA adapters

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.

First let's talk the first parts of scanJCAFiles.xml.

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:
  1. 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.
  2. 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.




Thursday, 7 June 2012

Delete svn subfolders

Here and there migrate/upgrade projects for SoaSuite10g to SoaSuite11g are running. Most of the time the source is committed to Subversion. It turns out that JDeveloper makes a full copy of the project folder at upgrade.
This means that also the .svn folders are copied. And you probably don't want that. I would have the new project in a different branch in my subversion folder.

At a former customer there was a registry file that creates a menu item in the Windows explorer pop-up menu. This would hierarchically delete all .svn folders in the selected folder. But not allways you're able to install such a shell-command.

So I created a little ant script. Since we're working with JDeveloper11g, we have an Ant install. I also created a windows bat file to run the ant script with a folder that can be given as a command line parameter.

The build.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<project default="run">
  <target name="run">
    <tstamp/>
    <property environment="env"/>
    <property name="scan.folder" value="${env.SCAN_FOLDER}" />
    <!--<property file="./build.properties"/>-->
    <echo message="Delete all .svn from ${scan.folder}"></echo>
    <delete includeemptydirs="true"  verbose="true">
      <fileset dir="${scan.folder}" includes="**/.svn/**,**/.svn" defaultexcludes="false"/>
    </delete>
  </target>
</project>
The fileset in the delete can be found as an example in the doc of the Ant Delete Task. However, I found that the example did not work as such, for .svn folders with content. So I expanded the includes property to "**/.svn/**,**/.svn" to delete the content of .svn folders, before deleting them.
I set the verbose attribute, to see which folders/files are actually deleted.

The delete deleteSVNFolders.bat file is:
@echo off
cls
set ORACLE_HOME=d:\oracle\Product\JDeveloper11116\Middleware
set ANT_HOME=%ORACLE_HOME%\jdeveloper\ant
set PATH=%ANT_HOME%\bin;%PATH%
set JAVA_HOME=%ORACLE_HOME%\jdk160_24
set SCAN_FOLDER=%1
ant

You'll have have to change the ORACLE_HOME setting to point to your JDeveloper middleware home.
Of course this works on linux as well, but than you'll have to transform the bat file to a bash script. But that's no rocket science...