Thursday, June 9, 2016

Seamless source "migration" from SOA Suite 12.1.3 to 12.2.1 using WLST and XSLT

When you migrate sources from SOA Suite 12.1.3 to SOA Suite 12.2.1, the only change I've seen JDeveloper do to the (SCA and Service Bus) code is updating versions in the pom.xml files from 12.1.3 to 12.2.1 (and some changes to jws and jpr files). Service Bus 12.2.1 has some build difficulties when using Maven. See Oracle Support: "OSB 12.2.1 Maven plugin error, 'Could not find artifact com.oracle.servicebus:sbar-project-common:pom' (Doc ID 2100799.1)". Oracle suggests updating the pom.xml of the project, changing the packaging type from sbar to jar and removing the reference to the parent project. This however will not help you because the created jar file does not have the structure required of Service Bus resources to be imported. To deploy Service Bus with Maven I've used the 12.1.3 plugin to create the sbar and a custom WLST file to do the actual deployment of this sbar to a 12.2.1 environment. A similar solution is described here.

Updates to the pom files can easily be automated as part of a build pipeline. This allows you to develop 12.1.3 code and automate the migration to 12.2.1. This can be useful if you want to avoid keeping separate 12.1.3 and 12.2.1 versions of your sources during a gradual migration. You can do bug fixes on the 12.1.3 sources and compile/deploy to production (usually production is the last environment to be upgraded) and use the same pipeline to compile and deploy the same sources (using altered pom files) to a 12.2.1 environment.


In order to achieve this I've created a WLST script to use XSLT transformations to update the pom files. For SOA and Service Bus the transformation to get from 12.1.3 to a working 12.2.1 project is slightly different. You can expand this to also update the group Id for your artifact repository to keep the 12.1.3 and 12.2.1 code separated there also. The transform.py file which is provided in this blog can be used to do other XSLT transformations from WLST also.

WLST

The WLST file (transform.py):
Usage: <jython|wlst> transform.py -<parameter>=<value> <stylesheetfile> [inputfile] [outputfile]

If you do not specify an outputfile, the output is send to the console. If you do not specify an inputfile, the console is used as input. You can specify XSLT parameters which will be used in the transformation. I've taken the sample code to do the XSLT transformation in WLST from here and expanded it. When using WLST to execute the script and pipe it to a file (not specifying an outputfile) do mind that you will get "Initializing WebLogic Scripting Tool (WLST) ..." and such above your actual script output.

 import sys  
 from java.io import FileReader, PrintWriter  
 from java.lang import System  
 from javax.xml.transform import TransformerFactory, Transformer  
 from javax.xml.transform.stream import StreamSource, StreamResult  
 def transform(source, stylesheet, result, parameters):  
   transformer = TransformerFactory.newInstance().newTransformer(stylesheet)  
   for (p, v) in parameters: transformer.setParameter(p, v)  
   transformer.transform(source, result)  
 args = sys.argv[1:]  
 parameters = []  
 while args and args[0].startswith('-'):  
   try:  
     i = args[0].index('=')  
   except ValueError:  
     parameters.append((args[0], ""))  
   else:  
     parameters.append((args[0][1:i], args[0][i+1:]))  
   args = args[1:]  
 if len(args) == 1: source = StreamSource(System.in)  
 elif len(args) >= 2: source = StreamSource(FileReader(args[1]))  
 else: raise "Usage: <jython|wlst> transform.py -<parameter>=<value> <stylesheetfile> [inputfile] [outputfile]"  
 if len(args) == 3: output = args[2]  
 else: output = ""  
 stylesheet = StreamSource(FileReader(args[0]))  
 if len(output) == 0:  
      result = StreamResult(PrintWriter(System.out))  
 else:  
      result = StreamResult(FileWriter(File(output)))  
 transform(source, stylesheet, result, parameters)  
 stylesheet.reader.close()  
 source.reader and source.reader.close()  
 result.writer.close()  

XSLT

The transformation for SCA project pom files (migratesca.xsl):

 <xsl:stylesheet  
   version="1.0"  
   xmlns:src="http://maven.apache.org/POM/4.0.0"   
   xmlns="http://maven.apache.org/POM/4.0.0"    
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
      <xsl:namespace-alias stylesheet-prefix="src" result-prefix=""/>  
      <xsl:template match="/src:project/src:parent/src:version">  
           <src:version>12.2.1-0-0</src:version>  
      </xsl:template>  
      <xsl:template match="/src:project/src:groupId">  
           <src:groupId>  
                <xsl:call-template name="string-replace-all">  
                     <xsl:with-param name="text" select="." />  
                     <xsl:with-param name="replace" select="'oldgroupid'" />  
                     <xsl:with-param name="by" select="'newgroupid'" />  
                </xsl:call-template>  
           </src:groupId>  
      </xsl:template>  
      <xsl:template match="/src:project/src:build/src:plugins/src:plugin[src:groupId='com.oracle.soa.plugin']/src:version">  
           <src:version>12.2.1-0-0</src:version>  
      </xsl:template>  
      <xsl:template match="@*|node()">  
           <xsl:copy>  
                <xsl:apply-templates select="@*|node()"/>  
           </xsl:copy>  
      </xsl:template>  
 <!-- below for Java embedding in BPEL processes -->  
      <xsl:template match="/src:project/src:build/src:plugins/src:plugin[src:groupId='com.oracle.soa.plugin']">  
           <xsl:copy>  
                <xsl:apply-templates select="node()|@*"/>  
                <xsl:if test="not(src:dependencies)">  
                     <dependencies>  
                          <dependency>  
                               <groupId>javax.el</groupId>  
                               <artifactId>javax.el-api</artifactId>  
                               <version>3.0.0</version>  
                          </dependency>  
                     </dependencies>  
                </xsl:if>  
           </xsl:copy>  
      </xsl:template>  
      <xsl:template name="string-replace-all">  
           <xsl:param name="text" />  
           <xsl:param name="replace" />  
           <xsl:param name="by" />  
           <xsl:choose>  
                <xsl:when test="$text = '' or $replace = ''or not($replace)" >  
                     <!-- Prevent this routine from hanging -->  
                     <xsl:value-of select="$text" />  
                </xsl:when>  
                <xsl:when test="contains($text, $replace)">  
                     <xsl:value-of select="substring-before($text,$replace)" />  
                     <xsl:value-of select="$by" />  
                     <xsl:call-template name="string-replace-all">  
                          <xsl:with-param name="text" select="substring-after($text,$replace)" />  
                          <xsl:with-param name="replace" select="$replace" />  
                          <xsl:with-param name="by" select="$by" />  
                     </xsl:call-template>  
                </xsl:when>  
                <xsl:otherwise>  
                     <xsl:value-of select="$text" />  
                </xsl:otherwise>  
           </xsl:choose>  
      </xsl:template>  
 </xsl:stylesheet>  

The transformation for Service Bus pom files (migratesb.xsl):

 <xsl:stylesheet  
   version="1.0"  
   xmlns:src="http://maven.apache.org/POM/4.0.0"   
   xmlns="http://maven.apache.org/POM/4.0.0"    
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
      <xsl:namespace-alias stylesheet-prefix="src" result-prefix=""/>  
 <!--  
      if you want to remove the parent  
      <xsl:template match="/src:project/src:parent"/>  
 -->  
      <xsl:template match="/src:project/src:groupId">  
           <src:groupId>  
                <xsl:call-template name="string-replace-all">  
                     <xsl:with-param name="text" select="." />  
                     <xsl:with-param name="replace" select="'oldgroupid'" />  
                     <xsl:with-param name="by" select="'newgroupid'" />  
                </xsl:call-template>  
           </src:groupId>  
      </xsl:template>  
 <!--  
      if you want to change the packaging  
      <xsl:template match="/src:project/src:packaging[text()='sbar']">  
           <src:packaging>jar</src:packaging>  
      </xsl:template>  
 -->  
      <xsl:template match="@*|node()">  
           <xsl:copy>  
                <xsl:apply-templates select="@*|node()"/>  
           </xsl:copy>  
      </xsl:template>  
      <xsl:template name="string-replace-all">  
           <xsl:param name="text" />  
           <xsl:param name="replace" />  
           <xsl:param name="by" />  
           <xsl:choose>  
                <xsl:when test="$text = '' or $replace = ''or not($replace)" >  
                     <xsl:value-of select="$text" />  
                </xsl:when>  
                <xsl:when test="contains($text, $replace)">  
                     <xsl:value-of select="substring-before($text,$replace)" />  
                     <xsl:value-of select="$by" />  
                     <xsl:call-template name="string-replace-all">  
                          <xsl:with-param name="text" select="substring-after($text,$replace)" />  
                          <xsl:with-param name="replace" select="$replace" />  
                          <xsl:with-param name="by" select="$by" />  
                     </xsl:call-template>  
                </xsl:when>  
                <xsl:otherwise>  
                     <xsl:value-of select="$text" />  
                </xsl:otherwise>  
           </xsl:choose>  
      </xsl:template>       
 </xsl:stylesheet>  

You can download the sources, sample pom files and a sample command-line here: https://github.com/MaartenSmeets/migrate1213to1221