Upgrading Struts 2.0.x to 2.1.x

Now that Struts 2.1.2 is officially out you may want to upgrade. I’ve found the upgrading instructions at the Struts wiki to be extremely useful, and pretty much you’ll need to read the whole thing.

Two things that I’ve contributed to the wiki:

Ensure no dependencies in the freemarker groupId are used as the latest version used by Struts is now under the org.freeemarker groupId and you’ll get classpath conflicts if you added a freemarker dependency.

In the unit tests you may get a NullPointerException as ActionContext.getContext() does not create a context on demand, I think there are better alternatives to avoid the direct use of getContext, but this snippet will fix the problem.


ConfigurationManager configurationManager = new ConfigurationManager();
configurationManager.addContainerProvider(new XWorkConfigurationProvider());
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();

ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().put(ActionContext.CONTAINER, container);
ActionContext.setContext(new ActionContext(stack.getContext()));

assertNotNull(ActionContext.getContext());

Poznan, Poland

Poznan is one of the oldest cities in Poland, and the fifth in size. Poznan’s cathedral is the oldest in the country, containing the tombs of the first Polish rulers.

Stary Rynek, Poznan`
Stary Browar, Poznan
Stary Rynek, Poznan
Stary Rynek, Poznan
Cytadela, Poznan

New York, New York (3)

Last batch of pictures from New York, I promise ;).

IMG_8246
Staten Island Ferry
Manhattan
Manhattan
Jersey City
Manhattan panorama
Manhattan panorama
Brooklyn Bridge panorama
Manhattan panorama
Liberty and Ellis Islands panorama
Manhattan panorama
Manhattan and Jersey City panorama

Brooklyn bridge 360 panorama
Brooklyn bridge panorama
Times Square at night, New York
Manhattan
Ground Zero, New York
Ground Zero, New York
Ground Zero, New York
Ground Zero, New York
Wall Street
Wall Street
Charging Bull, Wall Street
Statue of Liberty
Statue of Liberty
Manhattan panorama
Statue of Liberty
Manhattan panorama
Statue of Liberty

Functional testing with Maven, Cargo and Selenium

Setting up automated functional integration tests is not too hard if you have the right tools. It can take you a bit of time to setup but in the long run you’ll benefit from reduced QA times, reduced risks, a more confident development team, the ability to do safe refactorings, and many more advantages.

I’m going to explain how Maven, Selenium, Cargo and JBoss 4.2 can be setup to run automatically in a continuous integration server such as Continuum customizing the server configuration as needed and deploying any webapp automatically. Every time the webapp is changed the CI server will execute the tests against the latest version ensuring you are always in a safe state.

The biggest difference with other tutorials I’ve found is that most of them cover just Jetty and are not updated to the latest versions of libraries and tools, so here it is my contribution.

Architecture

  • A new project is setup with dependencies to the war project to be tested. Also required a dependency to selenium java client.
  • Cargo will download and install the application server (JBoss)
  • We will copy any required configuration and libraries (ie. jdbc driver)
  • Cargo will start the application server
  • The Selenium server is started
  • Surefire executes the junit tests that interact with the selenium server and test the running app
  • Cargo will stop the app server

We use profiles to enable different combination of browser/application server. By default cargo uses jetty.

Config Profiles
JBoss 4.2 and Firefox (default) -Pjboss42x,firefox
JBoss 4.2 and Internet Explorer -Pjboss42x,iexplore
Jetty and Firefox -Pfirefox
Jetty and Internet Explorer -Piexplore

The POM

Dependencies

<dependencies>
    <dependency>
      <groupId>com.acme</groupId>
      <artifactId>mywebapp</artifactId>
      <version>${project.version}</version>
      <type>war</type>
    </dependency>
    <!-- the jdbc driver we need to copy to the appserver -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
      <groupId>org.openqa.selenium.client-drivers</groupId>
      <artifactId>selenium-java-client-driver</artifactId>
      <version>1.0-SNAPSHOT</version> <!-- required for firefox 3 else use 1.0-beta-1 -->
      <scope>test</scope>
    </dependency>
  </dependencies>

Properties used in several places

Ports, where to uncompress the application server,…

<properties>
    <cargo.install.directory>${project.build.directory}/installs</cargo.install.directory>
    <selenium.port>14444</selenium.port>
    <servlet.port>18880</servlet.port>
    <selenium.background>true</selenium.background>
  </properties>

Plugin configuration

JDBC driver

Copy mysql jdbc driver to the app server lib folder

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-jdbc-lib</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <includeGroupIds>mysql</includeGroupIds>
              <outputDirectory>${lib.target}</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>

Cargo

Install the application server in an early phase so we can customize it with our configuration files (see profiles). Then start before integration tests and stop afterwards. Parameters are used so different profiles can use different application servers.

      <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven2-plugin</artifactId>
        <executions>
          <execution>
            <id>install</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>install</goal>
            </goals>
          </execution>
          <execution>
            <id>start-container</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>start</goal>
            </goals>
            <configuration>
              <wait>false</wait>
            </configuration>
          </execution>
          <execution>
            <id>stop-container</id>
            <phase>post-integration-test</phase>
            <goals>
              <goal>stop</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <container>
            <containerId>${container.name}</containerId>
            <zipUrlInstaller>
              <url>${container.url}</url>
              <installDir>${cargo.install.directory}/${container.name}</installDir>
            </zipUrlInstaller>
            <log>${project.build.directory}/logs/${container.name}.log</log>
            <output>${project.build.directory}/logs/${container.name}.out</output>
            <timeout>600000</timeout>
          </container>
          <configuration>
            <!--
            <home>${project.build.directory}/${container.name}conf</home>
            <type>existing</type>
            -->
            <properties>
              <cargo.servlet.port>${servlet.port}</cargo.servlet.port>
              <cargo.jboss.configuration>default</cargo.jboss.configuration>
              <cargo.rmi.port>1099</cargo.rmi.port>
            </properties>

            <deployables>
              <!-- application to deploy -->
              <deployable>
                <groupId>com.acme</groupId>
                <artifactId>mywebapp</artifactId>
                <type>war</type>
                <properties>
                  <context>acontext</context>
                </properties>
              </deployable>
            </deployables>
          </configuration>
        </configuration>
      </plugin>

Selenium

Make surefire skip tests during test phase and run them in the integration-test phase. Pass some properties as system properties so they are accessible from the junit test case.

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <!-- Skip the normal tests, we'll run them in the integration-test phase -->
          <skip>true</skip>
          <systemProperties>
            <property>
              <name>browser</name>
              <value>${browser}</value>
            </property>
            <property>
              <name>servlet.port</name>
              <value>${servlet.port}</value>
            </property>
            <property>
              <name>selenium.port</name>
              <value>${selenium.port}</value>
            </property>
          </systemProperties>
        </configuration>
        <executions>
          <execution>
            <phase>integration-test</phase>
            <goals>
              <goal>test</goal>
            </goals>
            <configuration>
              <skip>false</skip>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>selenium-maven-plugin</artifactId>
        <!-- to run headless in a Unix server with a virtual framebuffer X server Xvfb
             you need to call first the goal selenium:xvfb ie. "mvn clean selenium:xvfb install"
             see http://mojo.codehaus.org/selenium-maven-plugin/examples/headless-with-xvfb.html -->
        <executions>
          <execution>
            <id>start-selenium</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>start-server</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <background>${selenium.background}</background>
          <port>${selenium.port}</port>
          <logOutput>true</logOutput>
        </configuration>
      </plugin>

Application server profiles

We can configure a different profile for each application server and set some specific application server configuration.

<profiles>
    <profile>
      <id>jboss42x</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <container.name>jboss42x</container.name>
        <container.url>http://internap.dl.sourceforge.net/sourceforge/jboss/jboss-4.2.1.GA.zip</container.url>
        <jboss.version>4.2.1.GA</jboss.version>
        <jboss.conf.directory>${cargo.install.directory}/${container.name}/jboss-${jboss.version}/jboss-${jboss.version}/server/default</jboss.conf.directory>
        <lib.target>${jboss.conf.directory}/deploy/lib</lib.target>
        <war.target>${jboss.conf.directory}/deploy</war.target>
      </properties>

      <dependencies>
        <dependency>
          <groupId>org.jboss</groupId>
          <artifactId>jboss</artifactId>
          <version>${jboss.version}</version>
          <type>zip</type>
          <scope>test</scope>
        </dependency>
      </dependencies>
      <build>
        <plugins>
          <!-- copy to the application server directory any customized configuration files that we need -->
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
              <execution>
                <phase>process-resources</phase>
                <configuration>
                  <tasks>
                    <copy todir="${jboss.conf.directory}" overwrite="true">
                      <fileset dir="${basedir}/src/test/${container.name}"/>
                    </copy>
                  </tasks>
                </configuration>
                <goals>
                  <goal>run</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>

Browser profiles

As with the application servers we have a profile for each browser

<profile>
      <id>firefox</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <browser>*firefox</browser>
      </properties>
    </profile>
    <profile>
      <id>iexplore</id>
      <properties>
        <browser>*iexplore</browser>
      </properties>
    </profile>
    <profile>
      <id>otherbrowser</id>
      <properties>
        <browser>*custom ${browserPath}</browser>
      </properties>
    </profile>

Enabling testing during development

Make selenium not to run in the background so we can execute tests from the IDE

    <profile>
      <id>dev</id>
      <properties>
        <selenium.background>false</selenium.background>
      </properties>
    </profile>

Repositories

Required for Selenium dependencies

<repositories>
    <repository>
      <id>openqa.org</id>
      <name>OpenQA Repository</name>
      <url>http://archiva.openqa.org/repository/releases</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <releases>
        <enabled>true</enabled>
      </releases>
    </repository>
    <!-- for selenium 1.0-SNAPSHOT -->
    <repository>
      <id>snapshots.openqa.org</id>
      <name>OpenQA Sanpshots Repository</name>
      <url>http://archiva.openqa.org/repository/snapshots</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <releases>
        <enabled>false</enabled>
      </releases>
    </repository>
  </repositories>

Running in the build server

In an Unix server without X running you can still run Selenium tests using Xvfb (virtual framebuffer X server) by calling selenium:xvfb provided it’s properly configured.

Also you can pass the path to the browser binary if not in the PATH

mvn clean selenium:xvfb install -Dbrowser="*firefox /usr/lib64/firefox-1.5.0.12/firefox-bin"

The JUnit test

public class SeleniumHelloWorldTest
    extends TestCase
{
    private DefaultSelenium selenium;

    private String baseUrl;

    @Override
    public void setUp()
        throws Exception
    {
        super.setUp();
        String port = System.getProperty( "servlet.port" );
        baseUrl = "http://localhost:" + port;
        selenium = createSeleniumClient( baseUrl );
        selenium.start();
    }

    @Override
    public void tearDown()
        throws Exception
    {
        selenium.stop();
        super.tearDown();
    }

    protected DefaultSelenium createSeleniumClient( String url )
        throws Exception
    {
        String browser = System.getProperty( "browser" );
        String port = System.getProperty( "selenium.port" );
        return new DefaultSelenium( "localhost", Integer.parseInt( port ), browser, url );
    }

    public void testHelloWorld()
        throws Exception
    {
        selenium.open( baseUrl + "/mycontext/" );
        assertTrue( selenium.isTextPresent( "acme" ) );
    }
}

Debugging and troubleshooting (update)

You can check JBoss logs in target/logs/jboss42x.out and Selenium server logs in target/selenium/server.log

References

Other wiki entries and blogs

New York, New York (2)

A lot more pictures from my trip to New York.

Chrysler building, New York
Chrysler building, New York
United Nations, New York
United Nations, New York
Queensboro bridge, New York
Queensboro bridge, New York
Central Park, New York
Washington arch, New York
Washington arch, New York
Washington arch, New York
Central Park panorama
Guggenheim Museum, New York
5th avenue panorama
Girl in boat

New York, New York

Some pictures from last year’ trip to New York

Metropolitan Life Insurance Company tower
Empire State at night
Metropolitan Life Insurance Company tower


From Empire State building

Manhattan at night from Empire State
Manhattan from Empire State
Manhattan panorama from Empire State
Manhattan at night 360 panorama from Empire State
Manhattan from Empire State
Manhattan 360 panorama from Empire State
Manhattan panorama from Empire State
Manhattan from Empire State
Manhattan panorama from Empire State


Times Square

Times Square night panorama
Times Square at night


Central Park

Giant piano at FAO Schwarz Toy Store
Giant piano at FAO Schwarz Toy Store
Central Park panorama
Central Park panorama
Central Park panorama
Central Park panorama
Central Park panorama
Central Park
American Museum of Natural History panorama


Empire State at night
Chrysler building at night
Foggy New York
Egyptian tomb panorama at New York Metropolitan Museum of Art

Eclipse IAM passes creation review, Maven support at Eclipse Foundation soon

This week we have gone through the creation review for Eclipse IAM (Eclipse Integration for Apache Maven), and so we are starting with the paperwork at the Eclipse Foundation to create the project in the incubator and start moving the code from Q4E, the codebase will be donated to IAM as start point.

Development won’t stop in the meantime and we don’t expect any interruption for Q4E users.

Speaking at CommunityOne on Monday

Seems that I have been volunteered to do a lightening talk at CommunityOne on Monday at the Moscone as part of the Atlassian session.

Thanks to Cenqua’ Pete for offering free drinks in exchange (he hasn’t realized yet the mistake he has made) and entertainment like last year (hopefully with some differences in critical parts).

See you there.

JavaOne starts next Monday

If nothing goes wrong I’ll be next week in San Francisco "attending" JavaOne. "Attending" means I’ll just go to the RedMonk Unconference on Monday CommunityOne and networking at the evening parties (as usual)

I’ll drive from LA to SF on Friday or Saturday and back the next weekend, hopefully doing some sightseeing, anyone around for a drink during the weekend ?

Q for Eclipse 0.6.0 released

A new release of Q4E is out, 0.6.0, with WTP support and the ability to do a exclude all/force version from the dependency analysis UI. I particularly like the exclude all because if you had to do it by hand you would go to each dependency and add the exclusions. This way you select the dependency you don’t want and Q4E will add all necessary exclusions for you.

At last, the long awaited WTP support
is present on a public release. Enhanced support for resource
processing and fine-grained control of the incremental builds is also
supported, along with dependency management features available right
from your favorite dependency analysis view.

What’s new

  • Welcome Mike Poindexter as a new q4e committer.
  • Web Tools Platform (WTP) support, see the demo video
  • Ability to choose which goals get executed during the incremental build.
  • Profile view displays the available and enabled profiles.
  • Files generated by maven are marked derived in eclipse.
  • Dependency management functions (Force Version and Exclude All) available from the dependency analysis view.

Installation instructions.