Monday, March 7, 2016

Few maven tips and tricks

I was working on migrating an existing application that was using WebLogic Workshop (yes you read it right, using an IDE that is out of support) to maven. There were a few gotchas during the journey which i wanted to jot down here for anyone who might find it useful and specially for myself just as a reference.

The overall application was using Apache XMLBeans to deal with everything to do with XML and this was the first part I was migrating to maven. Maven did have a maven plugin for XMLBeans and the following snippet explains how you can incorporate this plugin to your project;


 
 <build>
    <plugins>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>xmlbeans-maven-plugin</artifactId>
        <version>2.3.3</version>
  <configuration> 
   <javaSource>1.5</javaSource> 
   </configuration> 
        <executions>
        <execution>
         <phase>generate-sources</phase>
            <goals>
              <goal>xmlbeans</goal>
            </goals>
        </execution>
        
      
        </executions>
      </plugin>
    </plugins>
  </build>

The one gotcha here is that you need to use the <javaSource>1.5</javaSource> tag if you want the XMLBeans code generated to have the "List" data structure for elements that have a maxoccurs set to unbounded. This is only if your code is already using the list type. Without this tag, this plugin will just generate the Array type for the unbounded elements.

Next up, it was the time to migrate the modules which exposed the web services of the application. As this was running on WebLogic, it used the "jwsc" task to generate the require artifact. I could not find an out of the box maven plugin which catered for this requirement and after some searching around I came across the solution where an ant build was invoked via the maven ant run plugin. Let us look at the configuration changes required on the pom.xml;


 
<plugin>
      <groupId>org.codehaus.gmaven</groupId>
      <artifactId>gmaven-plugin</artifactId>
      <version>1.3</version>
      <executions>
        <execution>
          <id>set-main-artifact</id>
          <phase>package</phase>
          <goals>
            <goal>execute</goal>
          </goals>
          <configuration>
            <source>
              project.artifact.setFile(new File(project.build.directory+'/'+project.artifactId+'-'+project.version+'.war'))
            </source>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.6</version>
        <executions>
            <execution>
                <phase>prepare-package</phase>
                <configuration>
                    <target>
                        <property name="maven.compile.classpath" refid="maven.compile.classpath" />
                        <property name="maven.runtime.classpath" refid="maven.runtime.classpath" />
                        <property name="maven.test.classpath" refid="maven.test.classpath" />
                        <property name="maven.plugin.classpath" refid="maven.plugin.classpath" />
                        <ant antfile="src/main/ant/build.xml" target="all" />
                    </target>
                </configuration>
                <goals>
                    <goal>run</goal>
                </goals>
            </execution>
        </executions>
        <dependencies>
            <dependency>
                <groupId>org.apache.ant</groupId>
                <artifactId>ant</artifactId>
                <version>1.7.1</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>ant-contrib</groupId>
                <artifactId>ant-contrib</artifactId>
                <version>1.0b2</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
   <groupId>weblogic</groupId>
   <artifactId>weblogic</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
   <dependency>
   <groupId>weblogic</groupId>
   <artifactId>xmlbeans</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
   <dependency>
   <groupId>weblogic</groupId>
   <artifactId>wlserv</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
   <dependency>
   <groupId>weblogic</groupId>
   <artifactId>jaxwsrt</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
   <dependency>
   <groupId>weblogic</groupId>
   <artifactId>beadescriptor</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>weblogic</groupId>
   <artifactId>beadescriptorbinding</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>weblogic</groupId>
   <artifactId>beadescriptorsettable</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>weblogic</groupId>
   <artifactId>staxb</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
   <dependency>
    <groupId>org.apache.xmlbeans</groupId>
    <artifactId>xmlbeans</artifactId>
    <version>2.4.0</version>
  </dependency>
  <dependency>
   <groupId>weblogic</groupId>
   <artifactId>webservices</artifactId>
   <version>10.3.0</version>
   <scope>compile</scope>
  </dependency>
            <dependency>
                <groupId>com.sun</groupId>
                <artifactId>tools</artifactId>
                <version>1.5.0</version>
                <scope>system</scope>
                <systemPath>${java.home}/../lib/tools.jar</systemPath>
            </dependency>
        </dependencies>
    </plugin>
    
        <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.1.1</version>
            <configuration>
                <encoding>UTF-8</encoding>
            </configuration>
           
            <executions>
                  <execution>
                      <id>default-war</id>
                      <phase>none</phase>
                  </execution>
              </executions>
             
        </plugin>

Note that the dependency items with the groupId set with "weblogic" was installed on the maven repository manually using the maven install file command. The jar libraries required are as follows;


  • wlfullclient.jar (this jar was built as per the instructions specified here)
  • webserviceclient.jar
  • webservices.jar
  • wls-api.jar
  • xercesImpl.jar
  • xmlParserAPIs.jar
  • com.bea.core.descriptor.settable.binding_1.4.0.0.jar
  • com.bea.core.descriptor.wl.binding_1.1.0.0.jar
  • com.bea.core.descriptor.wl_1.1.0.0.jar
  • com.bea.core.xml.beaxmlbeans_1.0.0.0_2-4-0.jar
  • com.bea.core.xml.staxb.buildtime_1.3.0.0.jar
  • glassfish.jaxws.rt_2.1.3.jar
The next step is to drop the ant build.xml on to the src/main/ant directory of your project. The build.xml is as follows;


 
<project name="build-webservice" default="all">

    <target name="all" depends="build.webService" />

    <path id="maven_plugin_classpath">
        <pathelement path="${maven.plugin.classpath}" />
    </path>

    <path id="maven_runtime_classpath">
        <pathelement path="${maven.compile.classpath}" />
        <pathelement path="${maven.runtime.classpath}" />
        <pathelement path="${maven.plugin.classpath}" />
        <pathelement path="${weblogic.jar}" />
    </path>

    <taskdef name="jwsc"
             classname="weblogic.wsee.tools.anttasks.JwscTask"
             classpath="${weblogic.jar}"
             classpathref="maven_plugin_classpath"
    />

    <target name="build.webService" description="Compile the web services if not up2date">
        <!--
            Eclipse compiles and places classes into target/classes when the workspace is building.
            If this folder exists when jwsc runs, then any classes that are already compiled will NOT
            be included in the final WAR file.  Thus, this directory is removed prior to created the
            webServices WAR fie.
        -->
        <delete dir="target/classes" />
        <jwsc srcdir="${project.build.sourceDirectory}"
              destDir="target"
              classpathref="maven_runtime_classpath"
              keepGenerated="yes"
              applicationxml="${project.build.directory}/application.xml"
              fork="true"
              memorymaximumsize="256m"
              verbose="true"
              debug="on"
        >
            <module contextPath="ws" name="${project.artifactId}-${project.version}">
                <jwsfileset srcdir=".">
                   <include name="**/*.java" />
                   <exclude name="**/*Test.java" />
                 </jwsfileset>
            </module>
        </jwsc>    
    </target>    
</project>

Note that there are no changes required to be made on this build.xml.

Next up, it was about building the EAR module to be deployed to weblogic. Looking at the EAR built up by WebLogic Workshop, I could see that all the required third-party libraries were being bundled up into a folder called APP-INF/lib which was located in the root directory of the EAR. Also the WAR files did not have any jar files in the lib directory and I wanted to mimic this functionality when building the EAR using maven. The following configuration allowed me to do that;

 
 <build>
  <finalName>ErrorXAEAR</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-ear-plugin</artifactId>
        <version>2.10.1</version>
        <configuration>
          <defaultLibBundleDir>APP-INF/lib/</defaultLibBundleDir>
          <skinnyWars>true</skinnyWars>
    <modules>
             <jarModule>
              <groupId>mtn.sa.errorxa</groupId>
       <artifactId>errorxa-ejb</artifactId>
               <bundleDir>/</bundleDir>
       <bundleFileName>ErrorXAEJB.jar</bundleFileName>
             </jarModule>
       <webModule>
               <groupId>mtn.sa.errorxa</groupId>
       <artifactId>errorxa-service</artifactId>
               <bundleDir>/</bundleDir>
       <bundleFileName>ErrorXAService.war</bundleFileName>
             </webModule>
          </modules>
        </configuration>
  
      </plugin>
    </plugins>
  </build>

The tag <skinnyWars> is what enables the lib directory of the war file not be populated with the third-party libraries required which is now bundled up in the APP-INF/lib directory on the EAR. The tag <defaultLibBundleDir> handles copying all required libraries to a folder called APP-INF/lib within the EAR.

One other thing with respect to the generation of the EAR is that I did not want maven to generate the application.xml file as this file along with the weblogic-application.xml was already generated on the project and I wanted use the same. To achieve this, all i had to do was to drop both those files into the folder src/main/application and the default application.xml was overridden.

I found the mvn dependency:tree tool of maven to be quite useful when building up the EAR to identify and remove the unnecessary dependencies being dragged into the EAR via recursive dependencies. With a simple exclusion tag I was able to remove the unwanted libraries.

That is about it for this post. I will keep updating the post with any things I may come across. The next step is to use maven to do the deploy and un-deploy of each application as part of the build process.