Friday, September 5, 2014

No room to fit Maven-Ear-Plugin in one pom.xml

After I have the WAR file build completed, then I'm going to build the EAR file. Unfortunately it doesn’t work as in ANT. In Ant I can do all stuff of work in just one build.xml but not in Maven. Too bad huh?!! OK, I understand this can't be done but I still insist want to do it. Anyhow, there is still no EAR file being generate after the build. What a joke? No joking, Maven doesn't allow me to do that. Take the following code snippet as my use case:

    4.0.0
 
    org.huahsin
    MyWebService
    0.0.1-SNAPSHOT
    war
 
    MyWebService

    
        
         org.huahsin
         MyWebService
         0.0.1-SNAPSHOT
         war
        

        ...
        ...

    

    
        
            
             org.apache.maven.plugins
             maven-ear-plugin
             2.9.1
             
                 7
                 
                     
                         org.huahsin
                         MyWebService
                         MyWebService.war
                     
                 
                 MyWebService
             
            
 
            ...
            ...
 
        
    

The <packaging> will cause by the build failed. I mean it would working fine for building WAR file but not for building EAR file. Now I have knew the root cause, changing to ear in <packaging> line but I'm still not satisfied with the final output. Because I have a special requirement to this EAR file not to contain all other libraries except the custom build library. Now the EAR file has mess up all others libraries in it, this could a disaster when I deploy to WebSphere Application Server.

To make it clean, I create another pom.xml that only perform one task, which is EAR packaging. And this pom.xml contain only one dependency which is my newly created WAR. To make the picture clear, the WAR file should contain all only the libraries, whereas EAR file should contain only the WAR file. This is the primary objective of having a separate pom.xml, following code snippet worth thousand of these nonsense.

    4.0.0

    org.huahsin
    MyWebServiceEar
    0.0.1-SNAPSHOT
    ear

    MyWebServiceEar

    
        
         org.huahsin
         MyWebService
         0.0.1-SNAPSHOT
         war
        
    

    
        
            
            org.apache.maven.plugins
            maven-ear-plugin
            2.9.1
            
                7
                
                    
                        org.huahsin
                        MyWebService
                        MyWebService.war
                    
                
                MyWebService
            
            
        
    

Now I have another question, how could I fit 2 pom.xml in one project? I don't know??? I just put them somewhere as long as there wouldn't crash each other in the project. Am I doing this in the right approach?

Wednesday, September 3, 2014

Maven default deployment path locate at target/tomcat/webapps?

In order for me to establish a JNDI connection in Tomcat, I have the data source declare inside server.xml as shown in the code snippet below:
     
         
         
     
And then I have the JNDI data source declared in Spring like this:
    
        
        
        
        
    
Let’s do some experiment on maven-war-plugin, if this plugin went missing, an error message mention Name [comp/env] is not bound in this Context. Unable to find [comp]. as shown in the following stack trace could be seen:
javax.naming.NameNotFoundException: Name [comp/env] is not bound in this Context. Unable to find [comp].
 at org.apache.naming.NamingContext.lookup(NamingContext.java:820)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:168)
 at org.apache.catalina.deploy.NamingResources.cleanUp(NamingResources.java:988)
 at org.apache.catalina.deploy.NamingResources.stopInternal(NamingResources.java:970)
 at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
 at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5495)
 at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:160)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
If the plugin were there (as shown in following code snippet) but without any configuration being done, the above error message could be seen too.
 
  org.apache.maven.plugins
  maven-war-plugin
  2.2
 
Now if <outputDirectory> is configure to the plugin as shown in following code snippet:
 
  org.apache.maven.plugins
  maven-war-plugin
  2.2
  
   ${project.basedir}/target/tomcat/webapps
  
 
Tada! It works. Now change to use <warSourceDirectory> in the configuration as shown in following code snippet and start up the Tomcat, the same error could be seen as well.
 
  org.apache.maven.plugins
  maven-war-plugin
  2.2
  
   ${project.basedir}/src/main/webapp/
  
 
Try make some adjustment to the docBase attribute of <Context> in server.xml as shown in following code snippet, it will works again.
     
         
    ...

     
What a surprise?! If I pay close attention on the stack trace, I would notice MyService folder wasn’t there actually as mention in another message trace as shown in below:
java.lang.IllegalArgumentException: Document base C:\MyService\target\tomcat\webapps\MyService does not exist or is not a readable directory
 at org.apache.naming.resources.FileDirContext.setDocBase(FileDirContext.java:140)
 at org.apache.catalina.core.StandardContext.resourcesStart(StandardContext.java:4906)
 at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5086)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
Another message I got was whenever Maven build is trigger, Maven will expect the output to be deploy into target/tomcat/webapps folder. Since I didn’t configure <outputdirectory> in POM.xml, then Maven will go for the default path searching my web app. By adjusting the docBase attribute 2 lever up will tell Maven "Hey! go search my app there".

Tuesday, September 2, 2014

Let’s see how profiles could help in Maven build?

Taking the following code snippet as my use case:

 ${project.name}
     
 
  
   org.apache.tomcat.maven
   tomcat7-maven-plugin
   ...
   ...
  

  
   org.apache.maven.plugins
   maven-compiler-plugin
   ...
   ...
  

  
   org.apache.maven.plugins
   maven-war-plugin
   ...
   ...
  
 

In most situation, I will just run mvn clean install tomcat7:run to bring up my application instance. But somehow, in some situation, I would like to have some special configuration on the WAR packaging to be different from the regular build. For example, to exclude some particular JAR out from the build during packaging stage, if I'm using the regular configuration as shown in the code snippet above, I will hit error when I bring up my application instance due to some libraries were gone missing during packaging stage.

In order to play well on both mvn clean install tomcat7:run and mvn clean package, my colleague suggest me to use profiles for this situation. As shown in the following code snippet, another piece of maven-war-plugins configuration is created inside the <profile>:
 
    
       CUSTOM
       
         
            
               org.apache.maven.plugins
               maven-war-plugin
               2.2
               
                  %regex[WEB-INF/lib/(?!mycustomlibrary).*.jar]
               
            
         
         myfinalName
       
    
 
With this new configuration, the regular build process will continue to work as expected while I have another separate piece to build a custom made build process for packaging. But separated piece need to be done with following command:

mvn clean package –PCUSTOM

Do take note on the <finalName> usage, it allows me to specify a custom package output file name that is different from the regular ugly Maven style name.

Using regular expression during Maven packaging stage

During maven build, the default Maven implementation will package all libraries into WEB-INF/lib folder. But the requirement has remind me that I shouldn’t include those libraries in the build except those custom build library since those libraries has already been deploy into WebSphere Application Server (WAS). Thus it helps in reducing the resulting jar and making the deployment process run faster in WAS.

Now my objective is to ensure the resulting jar is clean except those custom made library. To achieve this mission, the clue is to use packagingExcludes of maven-war-plugin’s configuration, as shown in the code snippet below:

 org.apache.maven.plugins
 maven-war-plugin
 2.2
 
  ...  
 

The challenge of this mission is to exclude any other libraries except those with custom made. I have been struggling for quite some while until my colleague has discover this could be done by using regular expression as shown below:

 org.apache.maven.plugins
 maven-war-plugin
 2.2
 
  %regex[WEB-INF/lib/(?!mycustomelibrary).*.jar]  
 

The code snippet above telling Maven to exclude any libraries other than mycustomlibrary.jar during the build. This has brought up another issue to me, what if I need to retain 2 libraries while ignoring the rest during the build? Fortunately right after this discovery, I found the solution on this problem which is by using packagingIncludes where I only need to specified the particular library that need to be includes.