Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

Friday, April 26, 2013

Standalone Java Webapp made easy with Maven

Introduction

As you may already know, creating a war archive and deploying it on a preinstalled Servlet Container is not the only way running a Java webapp, since you can also embed the Web-Server in the application itself. Sometimes such artifacts are delivered as standalone executable programs that can be easily started and stopped in the commandline and even run as a service in the background. Maybe you should also consider producing your app this way and benefit from its huge advantages:
  • No preinstalled Webserver/Servlet Container required that would take additional maintenance.
  • Easy Cross-Platform. Same artifact can of course be run on any System.
  • Complete Standalone Software
Apache Sonar is a great Example of such Software. It uses an embedded Jetty Server and creates an embedded H2 Database for persistence that can be replaced in configuration. As you can see here they prepared startup scripts for any Platform that execute the actual StartServer class.

Embedded Tomcat

In my tutorial ill use an embedded Tomcat as a Servlet Container. So we want to add the Maven dependencies first.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- embedded Tomcat -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>7.0.37</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-logging-log4j</artifactId>
    <version>7.0.37</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <version>7.0.37</version>
</dependency>

We can use them now to create a StartServer class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public final class StartServer {

    private StartServer() {
    }

    public static void main(String[] args) throws Exception {
        
        // We will set the basedir Systemproperty accordingly when running the Program
        // Actually the mavenplugin appassembler will do this for us
        String basedir = System.getProperty("basedir");
        String webappLocation = new File(basedir + "/webapp").getAbsolutePath();
        int port = 8080;

        Tomcat tomcat = new Tomcat();
        tomcat.setPort(port);
        tomcat.addWebapp("/", webappLocation);
        tomcat.start();
        tomcat.getServer().await();
    }
}

Exploded War

Now we need to make an exploded war in our "target" directory. I choose to create a folder inside "target" that holds the application as a whole including bin files, configuration and the webapp itself. This makes it easy to transport the complete app to another machine.
  • src/...
  • target/myapp/bin
  • target/myapp/conf
  • target/myapp/webapp
Ill include the maven-war-plugin to create the exploded war.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
    <executions>
        <execution>
            <id>war-exploded</id>
            <phase>package</phase>
            <goals>
                <goal>exploded</goal>
            </goals>
            <configuration>
                <webappDirectory>${project.build.directory}/myapp/webapp</webappDirectory>
                <archiveClasses>true</archiveClasses>
            </configuration>
        </execution>
    </executions>
</plugin>

If you run mvn clean package right now you should get your webapp compiled into target/myapp/webapp

Startup Scripts

Finally we need startup Scripts to actually run our application. We need a shell script for unix Systems and a bat file for Windows. The Appassembler maven plugin comes in handy here. It is a very thoughtout plugin that creates exactly what we need and even sets the basedir Systemproperty. I'll configure it to use our exploded webapp and StartServer class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>appassembler-maven-plugin</artifactId>
    <version>1.3</version>
    <configuration>
        <assembleDirectory>${project.build.directory}/myapp</assembleDirectory>
        <repositoryLayout>flat</repositoryLayout>
        <repositoryName>webapp/WEB-INF/lib</repositoryName>
        <generateRepository>false</generateRepository>
        <copyConfigurationDirectory>true</copyConfigurationDirectory>
        <configurationDirectory>conf</configurationDirectory>
        <programs>
            <program>
                <mainClass>com.myapp.StartServer</mainClass>
                <name>myapp</name>
            </program>
        </programs>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>assemble</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Done. Appassembler now creates executable scripts for all platforms inside the target/myapp/bin directory. You could even put configuration files into src/main/config that would be copied into target/myapp/conf automatically by the plugin.

Result


If we run mvn clean package now we get a completely transportable standalone webapp in our target directory!

Sunday, March 24, 2013

Automatically generated Class Diagrams using Maven & UMLGraph

Drawing Class Diagrams preemptive is sometimes nice to optimize your thought of design. But it will still most likely change in the development phase. Since your Class Diagram is part of your documentation it should be up to date all the time. There is no point in having a Class Diagram if the code does not fit it. You could keep adapting the changes to the Class Diagram by hand, but that can get very time-consuming and exhausting. You should rather automate that process, and there are several ways to achieve exactly that. You could for example search for a UML Class Diagram Tool supporting roundtrip engineering. There are some commercial, and even some free ones available. Not all of them work very well and it can be a frustrating task to figure out which way to go. You would probably try out some and throw them away because they are just not what you expected.
Thats why i want to show you a different approach: Letting a Javadoc Doclet draw the Class Diagrams in build phase and embed them into your javadoc automatically. UMLGraph is such a doclet. It analyzes your code, draws Class Diagrams automatically and embed them into your javadoc.

Since maven can run javadoc at build phase it's perfect. You just have to configurate the maven-javadoc-plugin to use UMLGraph.

Configurate Maven to use UMLGraph


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<reporting>                                                                              
    <plugins>                                                                            
        <plugin>                                                                         
            <groupId>org.apache.maven.plugins</groupId>                                  
            <artifactId>maven-javadoc-plugin</artifactId>                                
            <version>2.9</version>                                                       
            <configuration>                                                              
                <doclet>org.umlgraph.doclet.UmlGraphDoc</doclet>                         
                <docletArtifact>                                                         
                    <groupId>org.umlgraph</groupId>                                      
                    <artifactId>umlgraph</artifactId>                                    
                    <version>5.6</version>                                               
                </docletArtifact>                                                        
                <additionalparam>-views -all</additionalparam>                           
                <useStandardDocletOptions>true</useStandardDocletOptions>                
            </configuration>                                                             
        </plugin>                                                                        
    </plugins>                                                                           
</reporting>                                                                             

Beware that the javadoc doclet api has changed from jdk 1.6 to jdk 1.7 and most of the online tutorials are outdated because of that. UMLGraph adapted to jdk 1.7 since version 5.5.

If we run mvn site right now, we will just get that error:
java.io.IOException: Cannot run program "dot": java.io.IOException: error=2, No such file or directory 
This means that the program dot was not found which is a part of the graphviz library.
 

Install Graphviz

For UMLGraph to run you need graphviz installed. Go to the Download Page, and choose your package. On linux you could even sudo aptget install graphviz. Beware that you might have to reboot or relogin to use graphviz, because the Systems Path is extended during installation.

First Result

Run mvn site, and open target/site/apidocs/index.html afterwards.


As you can see its drawing beautiful and accurate UML Class Diagrams.

Custom Configuration

If you dont want attributes/methods or other extras you are totally free to configurate those graphs. See Class Diagram Options. Just add them to the
<additionalparam>-views -all</additionalparam>
tag in pom.xml

Saturday, March 2, 2013

Selenium Test Automation with Maven

Today i want to help you manage your Automated GUI Tests (Selenium) better. In the past i have seen many different ways people handle this. Some people just write those plain HTML TestCases with Selenium-IDE, store it somewhere on the HDD and run manually when needed. Others dont even use Selenium-IDE. They write pure Java for Example, and automate their execution with JUnit. My todays solution lies inbetween.

Precondition

  • I want plain HTML TestCases, created with Selenium-IDE. So that someone with little Programming skills can still create them.
  • I want these GUI Tests to be run automatically in my Build process, so my CI-Tool can notify me on errors.
  • I also want all TestCases under Versioncontrol in my Projects Repository since the Tests grow with the Source.
  • I want the most little effort with the highest outcome. So i dont want to export JUnit Tests out of my HTML TestCases since it would be kind of a Duplication - and i want to stick to the DRY Principle.

Solution

First of all i create a Folder in my Project for the Selenium-Tests.

Folder Structure
        1. TestSuite
        2. SomeTest1
        3. SomeTest2
  1. pom.xml

Example TestSuite

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
    <title>Test Suite</title>
</head>
<body>
<table id="suiteTable" cellpadding="1" cellspacing="1" border="1" class="selenium">
    <tbody>
    <tr>
        <td><b>Test Suite</b></td>
    </tr>
    <tr>
        <td><a href="./SomeTest1.html">SomeTest1</a></td>
    </tr>
    <tr>
        <td><a href="./SomeTest2.html">SomeTest2</a></td>
    </tr>
    </tbody>
</table>
</body>
</html>

 

Example Test

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="selenium.base" href=""/>
    <title>SomeTest1.html</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
    <thead>
    <tr>
        <td rowspan="1" colspan="3">SomeTest1</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>open</td>
        <td>/</td>
        <td></td>
    </tr>
    <tr>
        <td>waitForElementPresent</td>
        <td>//div[@id='someId']</td>
        <td></td>
    </tr>
    <tr>
        <td>click</td>
        <td>css=button.create</td>
        <td></td>
    </tr>
    <!-- Some Steps -->
    <tr>
        <td>assertText</td>
        <td>
            //div[@id='someId']
        </td>
        <td>${expectedText}</td>
    </tr>
    </tbody>
</table>
</body>
</html>

 

Setup WebServer

So i have my TestSuite in place. But how do i run them? Most importantly, it should run within the Maven Build Process, so it will also run on Jenkins-CI or whatever. As we are testing against a real running WebApp this is an End-to-End-Test per definition. In Maven we have the opportunity to run such Tests within the integration-test Phase. If you want to learn more about the Maven Build Life-cycle and its phases check out this. So we need some kind of WebServer to run our WebApp, otherwise the tests wont work. The WebServer should be started before the integration-test phase, and be stopped afterwards. We could Use Tomcat7 or Jetty for example. In this example i will use the tomcat7-maven-plugin. I configure my pom.xml to start Tomcat7 pre-integration-test.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.0</version>
    <executions>
        <execution>
            <id>tomcat-run</id>
            <goals>
                <goal>run-war-only</goal>
            </goals>
            <phase>pre-integration-test</phase>
            <configuration>
                <port>8080</port>
                <fork>true</fork>
            </configuration>
        </execution>
        <execution>
            <id>tomcat-shutdown</id>
            <goals>
                <goal>shutdown</goal>
            </goals>
            <phase>post-integration-test</phase>
        </execution>
    </executions>
</plugin>

If you are running multiple Projects on a CI-Server you might consider using a different Port-Number for each Project.

Finally: Run the Tests

Last but not least we need to run the tests. Luckily there is this selenium-maven-plugin available that does the job.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>selenium-maven-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <browser>*firefox</browser>
        <suite>src/test/selenium/TestSuite.html</suite>
        <startURL>http://localhost:8080</startURL>
    </configuration>
    <executions>
        <execution>
            <id>run-selenium-tests</id>
            <phase>integration-test</phase>
            <goals>
                <goal>selenese</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Now,  whenever we execute mvn clean verify or even mvn clean install in console, the Tests are run and reports are stored within the target directory. This will also be done by your CI-Tool.

Conclusion

We do have a complete and clean Setup.
  • We have a place to store our Tests,  
  • They are within the Sourcecode and Version control
  • They can be run automatically by CI-Tools
  • Even Non-developers can add new TestCases
Btw: Dont give up if something is not working as intended. Selenium seems a little buggy, and some times you have to dig a little to solve problems. But it really works, i figured it out.
I hope you enjoyed this Guide. Greetings.