Lincoln Baxter III's complete blog can be found at: http://ocpsoft.com

Items:   1 to 5 of 67   Next »

Wednesday, April 25, 2012

Ever experience a wonderfully fantastic “green bar!” in Eclipse, NetBeans, or IntelliJ, only to find that when you run your ANT or Maven build, you get an equally catastrophic build failure? If you have, you’ve probably tried what most of us tried, and you’ve attempted to increase Maven’s heap capacity using MVN_OPTS:

export MAVEN_OPTS="-Xmx1024m -XX:MaxPermSize=512m"

That would normally work fine if the error we received is something like:

java.lang.OutOfMemoryError: PermGen space

Oops…

Unfortunately, though, this doesn’t solve the problem because Maven actually uses a separate JVM for each JUnit test execution! So while we have successfully enabled Maven to be a hog, our tests are still running in a constrained environment. What we need to do instead is increase the memory capacity of the Maven Surefire launcher:

Increasing JUnit Test Memory in Maven
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <argLine>-Xms256m -Xmx512m -XX:MaxPermSize=512m</argLine>
  </configuration>
</plugin>

Our solution

This will expand your test memory capacity significantly, and if it’s still not enough, you can play with these numbers for your environment; however, to all you Maven users out there who’ve run into these problems, may the Force be with you, because sometimes it’s not obvious when a memory issue is the root of your problems. However, if you are using Arquillian in your test suite, chances are that you are going to run into a memory issue at some point, because the memory required to start up a container like Jetty or Tomcat in your test suite is significant. If you are using JBoss AS7 or GlassFish, then your server container will run in a different JVM and this will not typically be an issue.

A very confusing example

Here is an interesting JVM core dump in the Rewrite and PrettyFaces test suites that was actually being caused by an out-of-memory error leading to native code execution failure, but nothing in the error trace would lead us to think it! (Unimportant bits omitted:)

Core dump caused by Out-of-memory error
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0x7) at pc=0x00007fac3c24e3bf, pid=32027, tid=140377731290880
#
# JRE version: 7.0_03-b04
# Java VM: Java HotSpot(TM) 64-Bit Server VM (22.1-b02 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [libc.so.6+0x1313bf]  __nss_hosts_lookup+0x1117f
#
# Core dump written. Default location: ~/Projects/ocpsoft/prettyfaces/annotations/core or core.32027
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

---------------  T H R E A D  ---------------

Current thread (0x0000000000bef800):  JavaThread "main" [_thread_in_native, id=32030, stack(0x00007fac3cbed000,0x00007fac3ccee000)]

siginfo:si_signo=SIGBUS: si_errno=0, si_code=2 (BUS_ADRERR), si_addr=0x00007fac3534c000

Registers:
...

Top of Stack: (sp=0x00007fac3cce7b68)
...

Instructions: (pc=0x00007fac3c24e3bf)
...

Register to memory mapping:
...


Stack: [0x00007fac3cbed000,0x00007fac3ccee000],  sp=0x00007fac3cce7b68,  free space=1002k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libc.so.6+0x1313bf]  __nss_hosts_lookup+0x1117f
C  [libzip.so+0x5057]  ZIP_GetNextEntry+0x57
j  java.util.zip.ZipFile.getNextEntry(JI)J+0
j  java.util.zip.ZipFile.access$500(JI)J+2
j  java.util.zip.ZipFile$1.nextElement()Ljava/util/zip/ZipEntry;+54
j  java.util.zip.ZipFile$1.nextElement()Ljava/lang/Object;+1
j  java.util.jar.JarFile$1.nextElement()Ljava/util/jar/JarFile$JarFileEntry;+4
j  java.util.jar.JarFile$1.nextElement()Ljava/lang/Object;+1
j  sun.misc.URLClassPath$JarLoader.validIndex(Ljava/lang/String;)Z+42

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.util.zip.ZipFile.getNextEntry(JI)J+0
j  java.util.zip.ZipFile.access$500(JI)J+2
j  java.util.zip.ZipFile$1.nextElement()Ljava/util/zip/ZipEntry;+54
j  java.util.zip.ZipFile$1.nextElement()Ljava/lang/Object;+1
j  java.util.jar.JarFile$1.nextElement()Ljava/util/jar/JarFile$JarFileEntry;+4
j  java.util.jar.JarFile$1.nextElement()Ljava/lang/Object;+1
j  sun.misc.URLClassPath$JarLoader.validIndex(Ljava/lang/String;)Z+42
J  sun.misc.URLClassPath$JarLoader.getResource(Ljava/lang/String;ZLjava/util/Set;)Lsun/misc/Resource;
J  sun.misc.URLClassPath$JarLoader.getResource(Ljava/lang/String;Z)Lsun/misc/Resource;
j  sun.misc.URLClassPath$JarLoader.findResource(Ljava/lang/String;Z)Ljava/net/URL;+3
j  sun.misc.URLClassPath.findResource(Ljava/lang/String;Z)Ljava/net/URL;+17
j  java.net.URLClassLoader$2.run()Ljava/net/URL;+12
j  java.net.URLClassLoader$2.run()Ljava/lang/Object;+1
v  ~StubRoutines::call_stub
j  java.security.AccessController.doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;+0
j  java.net.URLClassLoader.findResource(Ljava/lang/String;)Ljava/net/URL;+13
j  java.lang.ClassLoader.getResource(Ljava/lang/String;)Ljava/net/URL;+30
j  java.net.URLClassLoader.getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;+2

Conclusion

It takes a lot of work to crash the JVM like this, so here’s to more memory, more memory for Maven, more memory for all!


Wednesday, April 18, 2012

Close your eyes, take a deep breath, then repeat after me: “Trying to test any Enterprise application by manually executing a test suite is just preposterous, and an application with any complexity at all quickly becomes too burdensome to test without automation.”

Now repeat it a hundred times – still think you can live without automation?

In part one we set up our tools, part two we did project configuration, and in part three we ended with a fully functional application; however, now that you’ve become familiar with the Red Hat tools and Facebook API, it’s time we get to a topic we ideally should have started with in the first place: TESTING!

If you’re just joining us now and want a quick way to catch up, you can download the starter project we created through in the first 3 articles of this series: v1.0.zip — Create the Game.

Attributes of ideal tests

There are several attributes that an automated test should have, and if any one of these are missing, your test suite could quickly become more burdensome to execute, and more costly to maintain.

  • Tests should live within the code.

    Automating tests with tools like Hewlet Packard’s “QuickTest Pro,” or “Selenium IDE” is literally a waste of time. If your “automated” test suite doesn’t live in the code, and can’t be run via an un-manned continuous integration server, then your “automated” test suite is practically useless; you’re wasting time, money, and worst of all: developer motivation.

    Tests in the code provides us with two key factors:

    1. The automatic version control of our tests with our code. We can run the test suite for any version of our project, and that test suite should successfully test that version of the code.
    2. Tests in the project code (src/test/java) enable us to easily perform the tests on every build, and/or with a simple mvn test command.
  • Tests need to be fast!

    Entire suites of hundreds or thousands of tests should be able to execute within a few minutes or less.

  • Tests should not rely on mocks!

    “Don’t mock me!” Tests should use actual resources, not rely on Mocks that represent perfect best case scenarios for inputs (and in worst case, are laden with bugs themselves). We want to be able to commit data, retrieve that data, modify the data, take actions, and validate logic all within the same end-to-end test.

  • Tests should be query and assert against your application’s APIs.

    If you use CDI or a dependency injection container, your tests should be able to inject bean instances so we can inspect and validate any of our service or model values at any point in any test.

Simple JUnit tests are fast and live directly in the code, but when complex Mock-objects are required, it becomes impossible to test actual functionality of end-to-end flows. Using Selenium with JUnit can help address the HTTP/Web testing problem, but you still wont be able to inspect server-side values during test execution; Selenium only grants tests access to the information presented to the browser, so how do we test browser-based flows, while still having access to assert against our server-side data?

Enter Arquillian

Arquillian is an Integration Test Framework developed by JBoss. It enables real testing of Java applications “in-container,” granting access to all server-side resources running on an actual environment like the one in which your application will be deployed. We can query the database in our test, inject services into our test, change data values in our test, and best of all, Arquillian makes all of this possible simply by extending your Junit (or TestNG) unit tests; your test suite can run with every build, against any environment.

Arquillian is an integration testing framework, and should be used to supplement (not replace) your unit test suite. Proper unit testing is essential to maintain the ability to refactor internal code and services, and should not be omitted in favor of pure integration testing.

Setup Arquillian in our project

If you’d like to follow the official Arquillian guides, feel free to learn and go through them at your own pace. Or you can follow the instructions below for a more tailored example and explanation.

The first thing we will do is re-open our project in Forge, then we will install the Forge-Arquillian plugin that will greatly simplify all of our configuration and setup work. Use the following commands to return to your project (replace “my_facebook_project” with the path to your actual project,) and install the Forge-Arquillian plugin.

forge> cd my_facebook_project
forge> forge install-plugin arquillian

This may take several seconds, and should end with a message stating, “BUILD SUCCESSFUL,” and Forge writing a bunch of files to /home/username/.forge/plugins/org/arquillian/forge/arquillian-plugin/

Now it’s time to install an Arquillian container, and while you may use any of the containers in the plugin (just hit <TAB> to see your options), I continue to suggest using JBoss AS 7. The following command will take care of this configuration for us:

forge> arquillian setup --container JBOSS_AS_MANAGED_7.X

You can simply hit <Enter> to select all the defaults for Arquillian version, JUnit version, and Container version. This will ensure that you have a working Arquillian installation configured for JBoss AS 7 and JUnit.

Create a sample test

Now that we have Arquillian installed and set up (that was easy!), it’s time to create our first Arquillian test; we again turn to Forge for help. We’re going to start out by creating a test class for Player in which we’ll end up testing some basic functionality around storing and retrieving players.

forge> arquillian create-test --class com.example.domain.Player.java --enableJPA

Lets go in and open the test file in your IDE and make the following changes.

/src/test/java/PlayerTest.javaView complete file
@RunWith(Arquillian.class)
public class PlayerTest {
    @Inject private MyWebService myWebService;

    @Deployment public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(MyWebService.class)
                .addClasses(User.class)
                .addClasses(Player.class)
                .addClasses(Link.class)
                 .addAsResource("META-INF/persistence.xml")
                 .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    public void testIsDeployed() {
        Assert.assertNotNull(myWebService);
    }
    
}

Taking a closer look at this code, the @RunWith(Arquillian.class) and @Deployment attributes are required by the Arquillian framework.

Within our Deployment, we tell Arquillian what classes and resources we want to load into our test container so we can have access to them for each of our tests. Notice that we’ve added not just the class under test Player, but also MyWebService, User, and Link, which are all used within MyWebService the thing we want to inject. Failing to add all these classes to the Deployment will cause test failures.

Lastly, we use standard JUnit @Test methods to perform our assertions; our first test confirms that Arquillian has been set up correctly, and that we have access to injectable services from our application within the test itself.

Confirm that our setup and test creation worked correctly by running the following command. Remember that if the mywebservice reference isn’t null in our test, then we have proved Arquillian is correctly giving us access to our resources. Make sure to use the profile of the container we added earlier (if you are uncertain which container you configured, check your pom.xml file.) For the JBoss AS7 container you may type:

forge> build --profile JBOSS_AS_MANAGED_7.X
If you have trouble running this command from the Forge Console in Eclipse, a native Forge terminal may provide better success.

We should get:

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...
BUILD SUCCESSFUL

Congratulations!!! You’ve just created and run your first automated Arquillian test.

Get caught up

If you had any trouble following along, simply grab the code to this point from github tag: v1.1.zip — Install Arquillian and Sample Test .

Arquillian In-Container Example

Now we know we have access to those injected services, lets test a few of them out. Add the following code just below the @Test currently in our test class:

/src/test/java/PlayerTest.javaView complete file
@Test
public void testPlayerInContainer() {
	ArrayList<String> friendIDList = new ArrayList<String>();
	friendIDList.add("newArray");
	friendIDList.add("67890");
	friendIDList.add("newArray");
	friendIDList.add("One Friend");
	Player player = myWebService.playerPOSTRequest(999, "Ima Player", friendIDList);
	User user = myWebService.getUser(67890);
		
	//Asserts after POST
	Assert.assertTrue(player.getFriendList().size()==1);
	Assert.assertTrue(player.getPlayerInfo().getName().equals("Ima Player"));
	Assert.assertTrue(user.getImageURL().equals(myWebService.resolveURL(67890)));
		
	long points = myWebService.getPlayerPoints(999);
		
	//Assert after GET
	Assert.assertTrue(points==100);
		
	//Remove the Player and make sure it happened
	String result = myWebService.playerRemovalRequest(999);
	Assert.assertTrue(result.equals("Player removed with FacbookID: 999"));
}

In this next test we use playerPOSTRequest to post a new player with 1 friend, which means we are also creating One Friend as a User and then adding him to the friendList. We test a few of these things with Assertions, and then perform a GET on points, and then a playerRemovalRequest and Assert those as well.

Now when we do another build --profile JBOSS_AS_MANAGED_7.X in Forge we see we’ve successfully tested that our internal workings of our web service (specifically several services around Players) are all working correctly!

This is great! We can see how to create automated test cases where we can inject our databeans and assert the value of various attributes even in the middle of a use case flow! In our case however, you may be wondering, “But what about ensuring the WebService calls through http GET and POST requests like a real user would be doing?” This is a fair point since the above test basically bypasses the fact we have a WebService with endpoints by simply calling the desired method directly.

Arquillian allows for 2 ways to run your tests; in-container mode (the default) which you’ve now seen, and client mode which lets you test outside the container just like an actual user. Lets set up another Test Class for MyWebServiceTest.java where we’ll put all our other tests, and we’ll setup this entire class to run in client mode so we can test the actual GET and POST calls just like a client would.

Get caught up

If you had any trouble following along, simply grab the code to this point from github tag: v1.2.zip — Arquillian In-Container Example .

Create setup() and tearDown() methods

Now it’s time to set up some data for our tests. It is worth noting that there is never any guarentee as to which @Test method will run before any other, no matter where you put them in your class. The only methods with guarenteed order are annotated @Before and @After test execution. These methods run before each test is run, and after each test is run, respectively. We can use this to set up a bunch of data that we can use in all our tests, ensuring that it is reset for each @Test method.

We can do this by creating some useful GET and POST methods in a Util class that can reference our WebService. Showcasing the true power of Arquillian, we are GETing and POSTing true database objects! You’ll see absolutely no mocking in any of these tests!!!

The next step is to set up Gson (Google’s JSON / Java library) in our project so we can convert JSON responses from our web-service back into actual Java classes, effectively creating a simple REST client inside our test case. We can do this by simply adding the dependency to our pom, and the gson-2.1.jar file to our project classpath.

Add GSON to our pom.xml
<project>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>org.jboss.arquillian.junit</groupId>
      <artifactId>arquillian-junit-container</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Insert GSON dependency here -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  ...
</project>

Now download the jar file, and add it to your project classpath by going to Project-> Properties-> Java Build Path -> Libraries tab and click on Add
External Jars… and navigate to where you extracted gson-2.1.jar.

We’re now ready to create src/test/java/TestUtils, the utility class where we will set up a number of useful GET and POST calls we can reference from our test classes:

src/test/java/TestUtils.javaView complete file
package com.example.domain;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import com.google.gson.Gson;

public class TestUtils {

	public static String doGET(String URLstring) {
		try {
			String returnVal = "";

			URL url = new URL(URLstring);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");
			conn.setRequestProperty("Accept", "application/json");

			if (conn.getResponseCode() != 200) {
				returnVal = "Failed : HTTP error code : "
						+ conn.getResponseCode();
			}

			BufferedReader br = new BufferedReader(new InputStreamReader(
					(conn.getInputStream())));

			String output;
			while ((output = br.readLine()) != null) {
				returnVal += output;
			}

			conn.disconnect();
			return returnVal;

		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static String doPOST(String URLstring, String JSONInput) {
		try {
			String returnVal = "";

			URL url = new URL(URLstring);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoOutput(true);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("Content-Type", "application/json");

			OutputStreamWriter wr = new OutputStreamWriter(
					conn.getOutputStream());
			wr.write(JSONInput);
			wr.flush();

			// Get the response
			BufferedReader rd = new BufferedReader(new InputStreamReader(
					conn.getInputStream()));
			String line;
			while ((line = rd.readLine()) != null) {
				returnVal += line;
			}
			wr.close();
			rd.close();
			return returnVal;

		} catch (MalformedURLException e) {
			e.printStackTrace();
			return "printStackTrace error: " + e;
		} catch (IOException e) {
			e.printStackTrace();
			return "IOException error: " + e;
		}
	}

	public static Player createPlayerWithFriends(URL baseContext,
			long facebookID, String name, ArrayList<String> friendIDList) {
		String targetURL = baseContext + "rest/webService/UserRequest/"
				+ facebookID + "/" + name;
		String JSONInput = "";
		String response = doPOST(targetURL, JSONInput);

		targetURL = baseContext + "rest/webService/PlayerRequest/" + facebookID
				+ "/" + name;
		JSONInput = friendIDList.toString();
		response = doPOST(targetURL, JSONInput);

		targetURL = baseContext + "rest/webService/Player/" + facebookID;
		response = doGET(targetURL);

		Gson gsonPlayer = new Gson();
		Player responsePlayer = gsonPlayer.fromJson(response, Player.class);

		return responsePlayer;
	}

	public static User getUser(URL baseContext, long facebookID) {
		String targetURL = baseContext + "rest/webService/User/" + facebookID;
		String response = doGET(targetURL);

		Gson gsonUser = new Gson();
		User responseUser = gsonUser.fromJson(response, User.class);

		return responseUser;
	}

	public static Player getPlayer(URL baseContext, long facebookID) {
		String targetURL = baseContext + "rest/webService/Player/" + facebookID;
		String response = doGET(targetURL);

		Gson gsonPlayer = new Gson();
		Player responsePlayer = gsonPlayer.fromJson(response, Player.class);

		return responsePlayer;
	}

	public static String removePlayer(URL baseContext, long facebookID) {
		String targetURL = baseContext
				+ "rest/webService/PlayerRemovalRequest/" + facebookID;
		String JSONInput = "";
		String response = doPOST(targetURL, JSONInput);

		return response;
	}
}

The code above should be understandable as is, since we are simply creating 2 methods which execute our GET request and POST requests, respectively. This is straightforward template code that you can find anywhere on the internet for performing GET and POST calls from Java. The HTTPResponse from each request is returned as a string and then converted to it’s proper Java object through Gson so we can examine it in our test.

There are a few more helper functions that utilize each of the doGET and doPOST methods to perform specific actions that will be called over and over in our tests. These methods are separated into a different class to keep the MyWebServiceTest class as relatively short and clean as possible (since it’s going to house basically all our tests in this demo), and to share functionality with other test classes in the future.

Now that we have all the setup and Util methods done, lets create our setup() and tearDown() methods and a @Test to make sure it’s working.

/src/test/java/MyWebServiceTest.javaView complete file
package com.example.domain;

import java.net.URL;
import java.util.ArrayList;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class MyWebServiceTest {

	@Deployment(testable = false)
	// testable = false to run as a client
	public static WebArchive createDeployment() {
		return ShrinkWrap.create(WebArchive.class, "FBTutorialDemo.war")
				.addClasses(MyWebService.class).addClasses(User.class)
				.addClasses(Player.class).addClasses(Link.class)
				.addAsResource("META-INF/persistence.xml")
				.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
	}

	@ArquillianResource
	private URL baseContext; // http://localhost:8080/FBTutorialDemo/

	@Before
	public void setup() throws Exception {
		// Put all the sample data into our database

		ArrayList<String> friendIDList = new ArrayList<String>();
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"newArray\"");
		TestUtils.createPlayerWithFriends(baseContext, 1000,
				"Ihave%20ZeroFriends", friendIDList);

		friendIDList.clear();
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"67890\"");
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"One Friend\"");
		TestUtils.createPlayerWithFriends(baseContext, 1001,
				"Ihave%20OneFriends", friendIDList);

		friendIDList.clear();
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"67890\"");
		friendIDList.add("\"76543\"");
		friendIDList.add("\"89012\"");
		friendIDList.add("\"21098\"");
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"One Friend\"");
		friendIDList.add("\"Two Friend\"");
		friendIDList.add("\"Three Friend\"");
		friendIDList.add("\"Four Friend\"");
		TestUtils.createPlayerWithFriends(baseContext, 1004,
				"Ihave%20FourFriends", friendIDList);

		friendIDList.clear();
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"67890\"");
		friendIDList.add("\"76543\"");
		friendIDList.add("\"89012\"");
		friendIDList.add("\"21098\"");
		friendIDList.add("\"91234\"");
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"One Friend\"");
		friendIDList.add("\"Two Friend\"");
		friendIDList.add("\"Three Friend\"");
		friendIDList.add("\"Four Friend\"");
		friendIDList.add("\"Five Friend\"");
		TestUtils.createPlayerWithFriends(baseContext, 1005,
				"Ihave%20FiveFriends", friendIDList);

		friendIDList.clear();
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"67890\"");
		friendIDList.add("\"76543\"");
		friendIDList.add("\"89012\"");
		friendIDList.add("\"21098\"");
		friendIDList.add("\"91234\"");
		friendIDList.add("\"77441\"");
		friendIDList.add("\"88552\"");
		friendIDList.add("\"99663\"");
		friendIDList.add("\"11223\"");
		friendIDList.add("\"44556\"");
		friendIDList.add("\"newArray\"");
		friendIDList.add("\"One Friend\"");
		friendIDList.add("\"Two Friend\"");
		friendIDList.add("\"Three Friend\"");
		friendIDList.add("\"Four Friend\"");
		friendIDList.add("\"Five Friend\"");
		friendIDList.add("\"Six Friend\"");
		friendIDList.add("\"Seven Friend\"");
		friendIDList.add("\"Eight Friend\"");
		friendIDList.add("\"Nine Friend\"");
		friendIDList.add("\"Ten Friend\"");
		TestUtils.createPlayerWithFriends(baseContext, 1010,
				"Ihave%20TenFriends", friendIDList);
	}

	@After
	public void tearDown() throws Exception {
		// Clear all the sample data out of our database now that we're done
		// running all the tests.

		String response = TestUtils.removePlayer(baseContext, 1000);
		Assert.assertTrue(response
				.equals("Player removed with FacbookID: 1000"));
		response = TestUtils.removePlayer(baseContext, 1001);
		Assert.assertTrue(response
				.equals("Player removed with FacbookID: 1001"));
		response = TestUtils.removePlayer(baseContext, 1004);
		Assert.assertTrue(response
				.equals("Player removed with FacbookID: 1004"));
		response = TestUtils.removePlayer(baseContext, 1005);
		Assert.assertTrue(response
				.equals("Player removed with FacbookID: 1005"));
		response = TestUtils.removePlayer(baseContext, 1010);
		Assert.assertTrue(response
				.equals("Player removed with FacbookID: 1010"));

		System.out.println("\n\n\n\n\n\n\n\nbaseContext: " + baseContext);
	}

	@Test
	public void testSetupData() {
		// Retrieve the sample data from our database and make sure it's what we
		// expect

		Player playerWith0Friends = TestUtils.getPlayer(baseContext, 1000);
		Player playerWith1Friends = TestUtils.getPlayer(baseContext, 1001);
		Player playerWith4Friends = TestUtils.getPlayer(baseContext, 1004);
		Player playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);
		Player playerWith10Friends = TestUtils.getPlayer(baseContext, 1010);

		Assert.assertTrue(playerWith0Friends.getFriendList().size() == 0);
		Assert.assertTrue(playerWith1Friends.getFriendList().size() == 1);
		Assert.assertTrue(playerWith4Friends.getFriendList().size() == 4);
		Assert.assertTrue(playerWith5Friends.getFriendList().size() == 5);
		Assert.assertTrue(playerWith10Friends.getFriendList().size() == 10);

		Assert.assertTrue(playerWith10Friends.getFriendList().get(0) == 67890);
		User twoFriend = TestUtils.getUser(baseContext, playerWith5Friends
				.getFriendList().get(1));
		Assert.assertTrue(twoFriend.getName().equals("Two Friend"));
	}

}

It’s time to refresh and build our project to make sure all our changes are picked up.

forge> build --profile JBOSS_AS_MANAGED_7.X

If our updates were successful, we can now use the any of the data we’ve added in the setup() method in any of our @Test methods.

Get caught up

If you had any trouble following along, simply grab the code to this point from github tag: v1.3.zip — Create setup() and tearDown() for Arquillian tests.

Create tests for our business logic

Now that we know that our database and WebService is working well in our Arquillian tests, it’s time to implement a couple of @Test methods that will ensure our application works as expected.

We’ll start by ensuring our GameLink is generated correctly for players who have fewer than five friends. Remember, according to our design, four or fewer friends is not enough to play, so the player should be sent back to index.html if they do not meet this requirement.

/src/test/java/MyWebServiceTest.javaView complete file
@Test
public void testGameLinkForNotEnoughFriends() {
    	Player playerWith0Friends = TestUtils.getPlayer(baseContext, 1000);
		
	//Test that the Player has 0 friends and has the correct link.
	String expectedOnCLickMethod = 
		"(function (){alert('You do not have enough friends to play the game.');return false;});";
	Assert.assertTrue(playerWith0Friends.getFriendList().size()==0);
	Assert.assertTrue(playerWith0Friends.getGameLink(). getOnClickMethod().equals(expectedOnCLickMethod));
	Assert.assertTrue(playerWith0Friends.getGameLink(). getHref().equals("index.html"));
		
	Player playerWith4Friends = TestUtils.getPlayer(baseContext, 1004);
		
	//Test that the Player with 4 friends also has the correct link.
	Assert.assertTrue(playerWith4Friends.getFriendList().size()==4);
	Assert.assertTrue(playerWith4Friends.getGameLink(). getOnClickMethod().equals(expectedOnCLickMethod));
	Assert.assertTrue(playerWith4Friends.getGameLink() .getHref().equals("index.html"));
}

In theory, we’ve just tested boundary conditions of zero friends and four friends, and the code should look nearly identical to the test of the GET and POST requests. The WebService is simply expecting a single ArrayList as input with two elements of "newArray" to signify the start of friendList and friendName arrays respecively. We can put as many or as few longs and Strings respectively inro our data arrays to add any number of friends to our Player for a given test method execution.

forge> build --profile JBOSS_AS_MANAGED_7.X

Another build in Forge, and another BUILD SUCCESSFUL!!! Now you’re really getting the hang of it!

The next test is for a successful generation of a valid game link for a user with five or more friends.

/src/test/java/MyWebServiceTest.javaView complete file
@Test
	public void testGameLinkForValidNumOfFriends(){
    		Player playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);
		
		//Test that the Player has 5 friends and has the correct link.
		String expectedHrefBeginning = "playGame.html?playerID="
				+ playerWith5Friends.getPlayerInfo().getFacebookID() + "&playerName="
				+ playerWith5Friends.getPlayerInfo().getName() + "&playerPoints="
				+ playerWith5Friends.getPoints() + "&friendIDList=";
		Assert.assertTrue(playerWith5Friends.getFriendList().size()==5);
		Assert.assertTrue(playerWith5Friends.getGameLink().getOnClickMethod().equals(""));
		Assert.assertTrue(playerWith5Friends.getGameLink().getHref().startsWith(expectedHrefBeginning));
		
		//Since the friendIDList and friendNameList passed in the GameLink.Href are random,
		//we can't validate them in any simple manner.  However, we can verify that our
		//requirements are still in working order.  We need to make sure that the names
		//that go with all 3 friendIDList entries are among the 5 friendNameList entries
		Assert.assertTrue(isValidGameLinkLists(expectedHrefBeginning, playerWith5Friends));
		
		
		Player playerWith10Friends = TestUtils.getPlayer(baseContext, 1010);
		expectedHrefBeginning = "playGame.html?playerID="
				+ playerWith10Friends.getPlayerInfo().getFacebookID() + "&playerName="
				+ playerWith10Friends.getPlayerInfo().getName() + "&playerPoints="
				+ playerWith10Friends.getPoints() + "&friendIDList=";
		
		//Test that the Player with 10 friends also has a correct link.
		Assert.assertTrue(playerWith10Friends.getFriendList().size()==10);
		Assert.assertTrue(playerWith10Friends.getGameLink().getOnClickMethod().equals(""));
		Assert.assertTrue(playerWith10Friends.getGameLink().getHref().startsWith(expectedHrefBeginning));
		Assert.assertTrue(isValidGameLinkLists(expectedHrefBeginning, playerWith10Friends));
	}
    
	private boolean isValidGameLinkLists(String HrefBeginning, Player player){
		//Parse the player's GameLink to get the friendIDList and friendNameList
		int friendIDStart = HrefBeginning.length();
		int friendIDEnd = player.getGameLink().getHref().indexOf("&friendNameList=");
		String friendIDs = player.getGameLink().getHref().substring(friendIDStart, friendIDEnd);
		int friendNameStart = player.getGameLink().getHref().indexOf("friendNameList=") + 15; //we need to add len("friendNameList=")
		String friendNames = player.getGameLink().getHref().substring(friendNameStart);

		ArrayList<String> friendIDList = getListFromString(friendIDs);
		ArrayList<String> friendNamesList = getListFromString(friendNames);
		for (String friendID : friendIDList) {
			//Check that each friendID matches to a name in the friendNameList
			
			User curUser = TestUtils.getUser(baseContext, Long.valueOf(friendID));
			if(curUser==null){
				System.out.println("No User found in DB for ID: " + friendID);
				return false;
			}
			else {
				if(userNameInList(curUser, friendNamesList) == false){
					System.out.println(curUser.getName() + ", ID [" + friendID + 
							"was not  in the friendNamesList");
					return false;
				}
			}
			
			//Ensure each friendID is a member of the player's friendList
			if(player.getFriendList().contains(Long.valueOf(friendID)) == false){
				System.out.println("ID [" + friendID + "] is not among the players friendlist"
						+ player.getFriendList());
				return false;
			}
		}
		return true;
	}
	
	public boolean userNameInList(User user, ArrayList<String> friendNamesList){
		String curName = user.getName();
		for (String friendName : friendNamesList) {
			if(curName.equals(friendName)){
				return true;
			}
		}
		return false;
	}
	
	public ArrayList<String> getListFromString(String text){
		 ArrayList<String> list = new ArrayList<String>();
             //NOTE: Need to add import for StringTokenizer!!!
	     StringTokenizer tokens = new StringTokenizer(text,",");
	     while(tokens.hasMoreTokens()){
	    	 list.add((String) tokens.nextElement());
	     }
	     return list;
	}

Most of this test is nearly identical to the last, but I’ve added a few helper methods to allow us to do something that goes a step further. As opposed to our check in the last test, which was for our alert to match a static String in the OnClickMethod, here we actually validate that the GameLink we create meets our requirements. Namely we validate that each friendID in the GameLink is actually a friend of the player, and that each friendID matches to one of the friendNames in the same GameLink via a GET request lookup. This test guarantees us that the user will see a game page with valid friend pictures and names at the top of their screens.

Note, we only ensure that each ID has a matching name, not vice-versa. Since there are 3 IDs in the list, and 5 names, the user still has to guess – leaving 2 names out.

The helper methods should be easy enough to understand. From the bottom up: getListFromString() simply converts a comma seperated string into an ArrayList. The userNameInList() method checks if the name of a User is within an ArrayList. And in isValidGameLinkLists() we parse the Player’s GameLink to get the friendIDList and friendNameList it contains, then perform a GET request on each friendID and check that it’s both an actual friend of the Player, and that it matches 1 of the 5 friendNames. If any of these checks fail, we return false immediately since there’s no sense in checking the rest of the data.

Get caught up

If you had any trouble following along, simply grab the code to this point from github tag: v1.4.zip — Arquillian tests for business logic.

True End-To-End Integration Testing

Now that we know the GameLinks are getting set up correctly for Players, it’s time to set up tests for the final step: We’re going to simulate a person going to our app, playing our game, and submitting answers back to our web service.

This starts with two tests that will cover all our if statements. One test submits incorrect answers to our web service, and the other submits entirely correct answers.

/src/test/java/MyWebServiceTest.javaView complete file
@Test
	public void testSubmitAllWrongAnswers() {
		Player playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);

		// Take note of the player's points before they submit the wrong answers
		long playerPointsOriginal = playerWith5Friends.getPoints();

		// Submit 3 incorrect answers to our WebService as a POST request
		String targetURL = baseContext
				+ "rest/webService/GameAnswers/"
				+ "1005/67890/76543/89012/Four%20Friend/Five%20Friend/One%20Friend";
		String JSONInput = "";
		String response = TestUtils.doPOST(targetURL, JSONInput);

		// Test that we get the correct String back from the incorrect answers
		// and our points were deducted
		String expectedResponse = "First entry was INCORRECT "
				+ "Second entry was INCORRECT " + "Third entry was INCORRECT "
				+ "You will have a total of [" + 30 + "] points deducted.";

		// Re-GET the player now that the score should be updated
		playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);
		Assert.assertTrue(response.equals(expectedResponse));
		Assert.assertTrue(playerWith5Friends.getPoints() == (playerPointsOriginal - 30));
	}

	@Test
	public void testSubmitAllCorrectAnswers() {
		Player playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);

		// Take note of the player's points before they submit the correct
		// answers
		long playerPointsOriginal = playerWith5Friends.getPoints();

		// Submit 3 correct answers to our WebService as a POST request
		String targetURL = baseContext
				+ "rest/webService/GameAnswers/"
				+ "1005/67890/76543/89012/One%20Friend/Two%20Friend/Three%20Friend";
		String JSONInput = "";
		String response = TestUtils.doPOST(targetURL, JSONInput);

		// Test that we get the correct String back from the incorrect answers
		// and our points were deducted
		String expectedResponse = "First entry was correct "
				+ "Second entry was correct " + "Thrid entry was correct "
				+ "You will have a total of [" + 30 + "] points added!";

		// Re-GET the player now that the score should be updated
		playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);
		Assert.assertTrue(response.equals(expectedResponse));
		Assert.assertTrue(playerWith5Friends.getPoints() == (playerPointsOriginal + 30));
	}

With another build in Forge, we should see something like:

Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.873s
[INFO] Finished at: Tue Apr 10 22:56:13 EDT 2012
[INFO] Final Memory: 16M/340M
[INFO] ------------------------------------------------------------------------

Get caught up

If you had any trouble following along, simply grab the code to this point from github tag: v1.5.zip — True End-To-End Integration Tests.

Boundary conditions and getting errors

Now that we have virtually everything covered in our series of tests, we’re going to create just one more – there’s a boundary condition we haven’t yet considered. What if a Player goes to our game site and decides to click the submit button leaving some or all of their answers blank? Arquillian will come to our rescue yet again, and our new test should look nearly identical to our testSubmitAllWrongAnswers() Test, but having replaced the names in the “answer POST” with nothing. Take a look at what I’ve done:

/src/test/java/MyWebServiceTest.javaView complete file
@Test
	public void testSubmitAllBlankAnswers() {
		String appName = "FBTutorialDemo";
		Player playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);

		// Take note of the player's points before they submit the blank answers
		long playerPointsOriginal = playerWith5Friends.getPoints();

		// Submit 3 blank answers to our WebService as a POST request
		String targetURL = baseContext + "rest/webService/GameAnswers/"
				+ "1005/67890/76543/89012///";
		String JSONInput = "";
		String response = TestUtils.doPOST(targetURL, JSONInput);

		// Test that we get the correct String back from the blank answers and
		// our points were deducted
		String expectedResponse = "First entry was INCORRECT "
				+ "Second entry was INCORRECT " + "Third entry was INCORRECT "
				+ "You will have a total of [" + 30 + "] points deducted.";

		System.out.println("\n\n\n\n\n\n response: " + response);
		System.out.println("\n\n\n\n\n\n");

		// Re-GET the player now that the score should be updated
		playerWith5Friends = TestUtils.getPlayer(baseContext, 1005);
		Assert.assertTrue(response.equals(expectedResponse));
		Assert.assertTrue(playerWith5Friends.getPoints() == (playerPointsOriginal - 30));
	}

Notice how I put a couple of System.out.println statements in there so we can see the response that comes back from the answer POST. When we run this build in Forge, we should now see a failure!

Failed tests: 
  testSubmitAllBlankAnswers(com.example.domain.MyWebServiceTest)
 
Tests run: 8, Failures: 1, Errors: 0, Skipped: 0
 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.539s
[INFO] Finished at: Tue Apr 10 23:11:44 EDT 2012
[INFO] Final Memory: 13M/211M
[INFO] ------------------------------------------------------------------------

And if we scroll all the way up in log, we should see the source of this failure. The WebService doesn’t know how to process this POST request because it can’t match something that looks like rest/webService/GameAnswers/1245/90123/76543/89012/// to any @Path we have defined. The WebService is looking for some value for each {name#}, so if any of them are left blank, we’ll run into this issue. If you haven’t tried it before, you can run this manually on your current app at the facebook URL and see the app blow up! We generate an alert (among other things) telling us that the requested resource is unavailable.

To remedy this, we’ll need to make a change to our actual application. At least now that we have all our scenarios covered with full end-to-end tests, we can make this change, update any tests that require it, and then with 1 simple build command make sure we’re not only back in business with our full functionality, but that we plugged this hole!

We first need to update the MyWebService class to update the postForAnswers() method like this:

/src/main/java/MyWebService.javaView complete file
// Performs validation on answers submitted, and adjust points.
	@POST
	@Path("/GameAnswers/{playerID}/{id1}/{id2}/{id3}")
	@Consumes("application/json")
	public String postForAnswers(@PathParam("playerID") long playerID,
			@PathParam("id1") long id1, @PathParam("id2") long id2,
			@PathParam("id3") long id3, ArrayList<String> JsonInput) {

		Player player = getPlayerByFacebookID(em, playerID);
		User user1 = getUserByFacebookID(em, id1);
		User user2 = getUserByFacebookID(em, id2);
		User user3 = getUserByFacebookID(em, id3);
		String name1 = "";
		String name2 = "";
		String name3 = "";
		if (null == player || null == user1 || null == user2 || null == user3) {
			// This should never happen, but lets exit gracefully if it somehow
			// does.
			return "Sorry, there was an error trying to validate your answers. Please try again.";
		}
		if (JsonInput.size() != 3) {
			// This needs to be the 3 names the Player is guessing
			return "Sorry, we couldn't understand your 3 guesses. Please try again";
		} else {
			name1 = JsonInput.get(0);
			name2 = JsonInput.get(1);
			name3 = JsonInput.get(2);
		}

		boolean correctName1 = user1.getName().equals(name1);
		boolean correctName2 = user2.getName().equals(name2);
		boolean correctName3 = user3.getName().equals(name3);

		String returnString = "";
		long pointChange = 0;
		if (correctName1) {
			returnString += "First entry was correct \n";
			pointChange += 10;
		} else {
			returnString += "First entry was INCORRECT \n";
			pointChange -= 10;
		}
		if (correctName2) {
			returnString += "Second entry was correct \n";
			pointChange += 10;
		} else {
			returnString += "Second entry was INCORRECT \n";
			pointChange -= 10;
		}
		if (correctName3) {
			returnString += "Thrid entry was correct \n";
			pointChange += 10;
		} else {
			returnString += "Third entry was INCORRECT \n";
			pointChange -= 10;
		}

		returnString += "You will have a total of [" + Math.abs(pointChange)
				+ "] points ";
		if (pointChange > 0) {
			returnString += "added!";
		} else {
			returnString += "deducted.";
		}

		player.setPoints(player.getPoints() + pointChange);
		em.persist(player);

		return returnString;
	}// postForAnswers

Simply update the @Path and the parameters to the method, and a quick check to make sure JSONInput contains the three name guesses, then assign the three name variables from the JSONInput.

Almost done, we need to update our playGame.html so that we can pass our JSONInput as a string of the three name guesses.

/src/main/webapp/playGame.htmlView complete file
function doPostAnswers(playerID, ID1, ID2, ID3, JSONInput){
        var POSTAnswersURL = "rest/webService/GameAnswers/" + playerID + "/" +
              ID1 + "/" + ID2 + "/" + ID3;
          var myObj = null;
          var xmlhttp = null;
          if (window.XMLHttpRequest) {
             xmlhttp = new XMLHttpRequest();
             if ( typeof xmlhttp.overrideMimeType != 'undefined') {
               xmlhttp.overrideMimeType('application/json');
             }
          } else if (window.ActiveXObject) {
             xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
          } else {
             alert('Your browser does not support xmlhttprequests. Sorry.');
          }

          xmlhttp.open('POST', POSTAnswersURL, false);
          xmlhttp.setRequestHeader('Content-Type', 'application/json');
          xmlhttp.send(JSONInput);
          alert(xmlhttp.responseText);
          window.location='index.html';

          xmlhttp.onreadystatechange = function() {
                if (xmlhttp.readyState == 4) {
                    if(xmlhttp.status == 200) {
                        //myObj = eval ( '(' + xmlhttp.responseText + ')' );
                        if(isDEBUG){
                            alert("POST Success");
                          }
                        //alert(xmlhttp.responseText);
                        document.getElementById('submitMessage').innerHTML = xmlhttp.responseText;
                    }
                    else {
                        if(isDEBUG){alert("POST Fail - status: " + xmlhttp.status + " - " + xmlhttp.responseText);}
                    }
                } else {
                    // wait for the call to complete
                }
         };
          return null;
      }

   ...

    function submitAnswers(){
        var playerID = getURLParam("playerID");
        var ID1 = document.getElementById('friendID1').innerHTML;
        var ID2 = document.getElementById('friendID2').innerHTML;
        var ID3 = document.getElementById('friendID3').innerHTML;
        var name1 = document.getElementById('inputName1').value;
        var name2 = document.getElementById('inputName2').value;
        var name3 = document.getElementById('inputName3').value;
        var JSONInput = "[\"" + name1 + "\",\"" + name2 + "\",\"" + name3 + "\"]";
        doPostAnswers(playerID, ID1, ID2, ID3, JSONInput);
    }

We update the doPostAnswers() changing the parameters, URL, and passing JSONInput in the xmlhttp.send and we just go back to our tests and update the three tests that submit answers.

/src/test/java/MyWebServiceTest.javaView complete file
@Test
public void testSubmitAllWrongAnswers() throws IOException{
        ...
	//Submit 3 incorrect answers to our WebService as a POST request
	String targetURL = baseContext + "rest/webService/GameAnswers/" +
	     "1005/67890/76543/89012";
	String JSONInput = "[\"" + "Four Friend" + "\",\"" + "Five Friend" + "\",\"" + "One Friend" + "\"]";
	String response = TestUtils.doPOST(targetURL, JSONInput);
        ...
}

@Test
public void testSubmitAllCorrectAnswers() throws IOException{
        ...
	//Submit 3 correct answers to our WebService as a POST request
	String targetURL = baseContext + "rest/webService/GameAnswers/" +
	     "1005/67890/76543/89012";
	String JSONInput = "[\"" + "One Friend" + "\",\"" + "Two Friend" + "\",\"" + "Three Friend" + "\"]";
	String response = TestUtils.doPOST(targetURL, JSONInput);
        ...
}

 @Test
public void testSubmitAllBlankAnswers() throws IOException {
        ... 
	//Submit 3 blank answers to our WebService as a POST request
	String targetURL = baseContext + "rest/webService/GameAnswers/" +
	     "1005/67890/76543/89012";
	String JSONInput = "[\"" + "\",\"" + "\",\"" + "\"]";
	String response = TestUtils.doPOST(targetURL, JSONInput);	
        ...
}

Finished at last! We’ll run the build one more time, and make sure to refresh our project and fully clean the IDE just to make sure all our changes are going to be picked up. If it doesn’t work, try restarting your local server, and republish your application, just make sure to stop the local server again before you kick off the Arquillian tests.

Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17.062s
[INFO] Finished at: Wed Apr 11 02:39:37 EDT 2012
[INFO] Final Memory: 15M/337M
[INFO] ------------------------------------------------------------------------

Congratulations! You now not only have a fully functional app, but one that’s fully covered through Automated Integration Tests. You should have no problems adding more functionality now, you’ll know right away if anything happens to break from future changes!

Get caught up

If you had any trouble following along, simply grab the code to this point from github tag: v1.7.zip — Fixing Issues and updating tests .

Bonus: Setup a MySQL Database with OpenShift

As sanity check, we will prove that right now, player’s points will still reset on every restart of the app server since we’re still using hibernate only with create-drop.

OpenShift supplies us with a few commands that make this setup very easy, so exit out of Forge (as these OpenShift commands are not part of the Forge plugin).

First install the openshift command line tools:

StandardTerminal $ sudo apt-get install rubygems1.8
StandardTerminal $ sudo gem insall rhc

Now go to your app on Facebook and submit a group of answers that will change your point total. Now from a system prompt, run the following commands to stop and then restart the app server again. Go back to your app and notice your points are reset.

StandardTerminal app$ rhc app stop -a FBTutorialDemo
StandardTerminal app$ rhc app start -a appName

Since our experiment has confirmed that our database is being deleted every time the server restarts, lets now install MySQL and then we’ll change our persistence settings so data persists even if we upload future changes, or stop the server for any reason.

StandardTerminal app$ rhc-ctl-app -a FBTutorialDemo -e add-mysql-5.1
Make sure you take careful note of the info provided in the terminal as we will need this info on future steps

Now we need to update standalone.xml for the MySQL Datasourse. set the enable flag to true, update the connectionURL, username, and password.

FBTutorialDemo/.openshift/config/standalone.xmlView complete file
<subsystem xmlns="urn:jboss:domain:datasources:1.0">
            <datasources>
                <!-- disable the hibernate datasource so you don't accidently get confused -->
                <datasource jndi-name="java:jboss/datasources/ExampleDS" enabled="false" use-java-context="true" pool-name="H2DS">
                    <connection-url>jdbc:h2:${jboss.server.data.dir}/test;DB_CLOSE_DELAY=-1</connection-url>
                    <driver>h2</driver>
                    <security>
                        <user-name>sa</user-name>
                        <password>sa</password>
                    </security>
                </datasource>
                <!-- enable the mysqul datasource and set up the config stuff for it -->
                <datasource jndi-name="java:jboss/datasources/MysqlDS" enabled="true" use-java-context="true" pool-name="MysqlDS">
                    <connection-url>jdbc:mysql://127.6.202.129:3306/FBTutorialDemo</connection-url>
                    <driver>mysql</driver>
                    <security>
                      <user-name>admin</user-name>
                      <password>ltSWR4claqRZ</password>
                    </security>
                </datasource>
          ...
        </subsystem>

The above is a sample for my app. Make sure you update the connectionURL, and password to your own info.

Then we update our persistence.xml file to set the jta-data-source to java:jboss/datasources/MysqlDS and remove the “create-drop” setting so our data will not get blown away at each server startup.

/src/main/resources/META-INF/persistence.xmlView complete file
<persistence-unit name="forge-default" transaction-type="JTA">
    <description>Forge Persistence Unit</description>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/MysqlDS</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <!-- Using "create-drop" will wipe out your data at each app-server restart.
          Set it to "update" to have your data persist even after uploading app changes.
          Or leave it as-is if you always want a fresh database each time you start the app.
      -->
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hibernate.transaction.flush_before_completion" value="true"/>
    </properties>
  </persistence-unit>

Lastly, we can install PHPMyAdmin for some finer control of our MySQL DB from a graphical interface on the web.

StandardTerminal app$ rhc-ctl-app -a FBTutorialDemo -e add-phpmyadmin-3.4

Lets go back into forge and do one last deploy

forge> git add -A
forge> git commit "Added mySQL DB and phpmyAdmin"
forge> rhc-express deploy

Then we can go to: https://fbtutorialdemo-schwarzwaldomain.rhcloud.com/phpmyadmin/ and retype the user and password OpenShift just gave us for a web UI view of our database!

If you go to the FBTutorialDemo(AppName) database, we can see our tables, and if you access the facebook app to POST yourself as a User and Player (and all your friends as Users). You can then go back to the phpmyadmin page and into a table like User and hit the Show: button to see all the users now in your mySQL database.

Go back and re-run the test from the beginning of this section, change your points (either through the app, or directly in the phpmyadmin site if you know what you’re doing, then stop and restart the server. Make sure your points have not been reset back to the 100 default, and we should be good to go!

Conclusion

I hope you enjoyed going through this final article in the series as much as I enjoyed writing it. No application should ever be considered complete without a solid testing strategy and implementation. Arquillian provides us everything we need, since we have all our business logic in our Java WebService class. If you’re currently using Selenium integrat


Wednesday, March 28, 2012

According to a recent research paper by the Aspect Security Group, entitled The Unfortunate Reality of Insecure Libraries, “Eighty percent of the code in today’s applications comes from libraries and frameworks, but the risk of vulnerabilities in these components is widely ignored and under appreciated. A vulnerable library can allow an attacker to exploit the full privilege of the application, including accessing any data, executing transactions, stealing files, and communicating with the Internet. Organizations literally trust their business to the libraries they use.”

When validating user input from forms and exposed services, we often ignore the URL or think to ourselves, “that information is validated later, it’ll be fine,” but when hacks like the following start turning up – in common web-frameworks – it’s time to start thinking seriously about URL validation. Fortunately, it’s easy to accomplish using a number of methods, but first, let’s look at how these attacks work.

You can request a copy of the security research paper from Aspect Security’s website.

If you look at the attacks sited as examples in the Aspect Security report, you’ll see that both use the URL as the attack vector, or point of access, and both use the execution of Runtime.getRuntime().exec("system command"), which gives the ability to run any native system command available to the user under which the Java Virtual Machine (JVM) is running; once you are running code on the host system, as the article explains, the potential threat exists of losing control of the server entirely. The first example is Struts 2 – still a popular framework in many places.

I found it rather interesting that two of the top three recent security exploits used web-application URLs as a vector of attack, and this came at a timely moment for me, the day I was to present “Security and Usability: URL-rewriting for the next-generation web-user” at the Philadelphia Java User’s Group, a talk in which I discuss the vulnerabilities of URLs, and the risks of improper (or missing) URL validation. I quickly jumped to incorporate relevant pieces of information in my talk.



The next example, surprisingly enough, is applications that use the Spring Expression Language (EL). I wouldn’t be too shocked if this problem actually extends to most EL implementations at the time when this exploit was discovered. After doing a bit of digging, JBoss Seam suffered from a similar vulnerability, and has subsequently been updated to close the gap; I’m sure Spring has done the same, but there are still compromised versions of these frameworks running on live servers across the world.



The last vulnerability was related to Apache CXF, a framework for creating flexible, you guessed it, mostly URL-based web-services. The advisory did not directly explain the vector for this exploit, but if anyone knows, I would love to hear how it worked. (Please add a comment!)

How can we protect ourselves?

This leads to the obvious question: if the frameworks we are using may have inherent insecurities, how can we patch the holes and lock down a potentially vulnerable application? Also, how can we do this without modifying potentially sensitive application code or business logic? The answer is simple – we can apply validation at the URL level, either using Apache (or another Proxy-based solution,) or using a URLRewriteFilter such as Rewrite, an open-source solution from OCPsoft.

The principle is simple. If we scrub as many possible URLs in our application for characters that we are certain should never be used, then we can effectively reduce the risk that someone might be able to inject and execute code in our application. URLs are not the only vectors for attack, so we’ll also take a look at how to secure additional forms of user input.

Secure the URL

For the following examples, we will assume that we already have the Rewrite framework installed in our application, but if you need to install it, it’s as simple as adding one JAR file to your project: Install Rewrite. Once this is done, we will start to craft our Security rules.

You can also use a URL-rewriting framework of your choice, but you will need to “convert” this configuration to your framework’s configuration.

The first thing we will do is ensure that all traffic to our website is blocked by Rewrite. This is done by adding a very simple rule, which intercepts traffic in the inbound (requested) direction, and asserts that the request must have come from an internal source (via a Servlet Forward,) otherwise the request will be blocked by returning the HTTP 404 "Not found" error code. We also set a low-enough priority so we can be certain that no other providers will execute first.

SecurityRewriteConfiguration.java
public class SecurityRewriteConfiguration extends HttpConfigurationProvider
{
   @Override
   public Configuration getConfiguration(ServletContext context)
   {
      return ConfigurationBuilder.begin()

               /*
                * Block any in-bound request not forwarded 
                * from within the appication itself.
                */
               .defineRule()
               .when(Direction.isInbound().andNot(DispatchType.isForward()))
               .perform(SendStatus.error(404));
   }

   @Override
   public int priority()
   {
      /*
       * Providers with lower priority are executed first.
       */
      return -1000;
   }

}

Simple enough. Now that we’ve blocked all external traffic to our site, we need to start allowing legitimate traffic back in; otherwise, our site will not be of much use to anyone.

We do this by creating a rule that inspects the inbound URLs and checks to make sure that only pre-approved characters are used – meaning we know what a secure URL should look like, and if it contains characters we’ve dubbed as “not secure”, then we aren’t going to accept it.

You’ll need to choose acceptable characters to meet your website’s requirements, but if you are not doing anything to fancy with your URLs, then we can expect that only a reasonable few special characters will be required.

SecurityRewriteConfiguration.java
@Override
public Configuration getConfiguration(ServletContext context)
{
   Constraint<String> selectedCharacters = new Constraint<String>() {
      @Override
      public boolean isSatisfiedBy(Rewrite event, 
                            EvaluationContext context, String value)
      {
         return value.matches("[a-zA-Z0-9/:&?.-=_+]*");
      }
   };

   return ConfigurationBuilder.begin()

            .defineRule()
            .when(Direction.isInbound()
                     .andNot(DispatchType.isForward())
                     .and(Path.captureIn("path"))
                     .and(URL.captureIn("url")
                         .where("url").constrainedBy(selectedCharacters)))
            )
            .perform(Forward.to("{path}"))

            /*
             * Block any in-bound request not forwarded by our previous rule.
             */
            .defineRule()
            .when(Direction.isInbound().andNot(DispatchType.isForward()))
            .perform(SendStatus.error(404));
}

Congratulations! We’ve now secured our URL paths from many types of malicious attacks including those used against Struts, Spring, and Seam. This is a valiant step forward, and it was as easy as 10-15 lines of code. We are, however, still missing an important part of the story.

Secure all HTTP parameters

The URL of our application may now be much more secure for GET requests and query-parameters, but we haven’t done anything about our HTTP POST parameters. Without securing these, any inputs from HTML <form> will be vulnerable (or if people just start sending us random requests,) and it’s easy to add this support using another few conditional statements in our configuration.

We need only use the RequestParameter condition to inspect the value of any provided HTTP parameters (including query-string GET and form POST parameters.) Notice that we also use .orNot(RequestParameter.exists("{}")), which ensures that having no parameters is still an acceptable scenario.

Add validation of HTTP parameters
.defineRule()
.when(Direction.isInbound()
         .andNot(DispatchType.isForward())
         .and(Path.captureIn("path"))
         .and(URL.captureIn("url")
                   .where("url").constrainedBy(selectedCharacters))

         .and(RequestParameter.matchesAll("{name}", "{value}")
                  .where("name").constrainedBy(selectedCharacters)
                  .where("value").constrainedBy(selectedCharacters)
                  .orNot(RequestParameter.exists("{}")))
)
.perform(Forward.to("{path}"))

So… Coffee’s still hot. What next?

Customize the rules

While this global rule may work for a large number of cases in our application, it’s worth noting that you need not open up all URLs using a single rule. We could just as easily have defined custom rules to match certain or specific addresses in our application. For instance, if we want to allow more characters in a password field, we can add an additional rule, which only applies to our Login page, and opens up this functionality.

Notice how we have added the .where("name").matches("(?!password).*") statement in order to apply our constraints to all fields except the password, and we’ve only exposed this on POST requests, via the .and(Method.isPost()) statement.
Exposing the login page form submission
.defineRule()
.when(Direction.isInbound()
         .and(Method.isPost())
         .andNot(DispatchType.isForward())
         .and(Path.matches("/login"))

         .and(RequestParameter.matchesAll("{name}", "{value}")
                  .where("name").matches("(?!password).*").constrainedBy(selectedCharacters)
                  .where("value").constrainedBy(selectedCharacters)
                  .orNot(RequestParameter.exists("{}")))
)
.perform(Forward.to("/login"))

However, in this scenario, it may actually be simpler just to write our own Condition to perform the restriction we want. Writing custom conditions is easy, and means that you have full control over the Rule matching algorithm.

Writing a custom Condition
.and(new Condition() {
   @Override
   public boolean evaluate(Rewrite event, EvaluationContext context)
   {
      // TODO Return true if the condition is satisfied
      return false;
   }
})

But there’s more…

Secure the HTTP headers

If we really want to do our diligence, we also need to secure the HTTP headers as well – this can also be done using a URL-rewriting framework. It will look very similar to the way we secured HTTP request parameters.

Securing the HTTP headers
.defineRule()
.when(Direction.isInbound()
         .and(Method.isPost())
         .andNot(DispatchType.isForward())
         .and(Path.captureIn("path"))

         .and(Header.matchesAll("{name}", "{value}")
                  .where("name").constrainedBy(selectedCharacters)
                  .where("value").constrainedBy(selectedCharacters)
                  .orNot(Header.exists("{}")))
)
.perform(Forward.to("{path}"))

Conclusion

Enough code, more talk. This proof of concept work shows how it is possible to secure, or at least improve upon the existing security of, existing Java web-applications. It also shows how a few simple rules can be used to facilitate securing many pages, or very specific pages, and the important thing to remember is that no matter what web-framework you are using – you can still add additional validation and security on top using URL-rewriting techniques.

Keep in mind that you should implement security at all levels of your application. For securing your business methods, Apache DeltaSpike Security will be very helpful for CDI-based applications, Seam Security and Spring Security also have good solutions. Do not consider this article to be “the answer” for security. It is just one option, and one additional technique you can use to protect your sensitive applications and information.

If you would like to learn more about the OCPsoft Rewrite framework, you can visit the project homepage, or check out the sources on GitHub. There are Regular development meetings held every Monday at 2pm EST (GMT+5) on Google Hangout.


Wednesday, March 14, 2012

Hi Java web-developers. I hope you are already familiar with PrettyFaces. If not, I will give you a very short introduction taken from the project documentation:

Rewrite, on the other hand, is a URL-rewriting framework built for extendability, for use with any web-framework or pure Servlet itself, and will be used for the core of PrettyFaces 4.0 – once released, bringing the best of both worlds… so that sounds very cool thus far, but what do we want to achive with it? Why would we use either of these frameworks?

Just compare these two URLs:

Very ugly one : http://www.example.com/blog.html?author=w0mbat&post_id=23&year=2012

A very pretty one : http://www.example.com/blog/w0mbat/2012/23

This is something that both PrettyFaces and Rewrite can accomplish for us, but what if, for example, we wanted to intercept all URLs and require a login? This is where PrettyFaces can no longer help us, but rewrite is ready to come to our aid!

“Got it” ? “Move on to the next section” : “Go back to the ‘Introduction’”;

Why migrate?

PrettyFaces itself is really great but the configuration is not runtime dynamic. There is a feature called “DynaView”, which can be used to determine the page to display for a given URL-mapping at runtime, but it is fairly limited and is difficult to use when things get hairy. To achieve some level of dynamism, one can implement what is called a “RewriteProcessor,” but it’s basically all manual coding; there are no dynamic rules that one would need to e.g. display a login page for every requested URL if the user is not logged in.

This is only one of many cool features that Rewrite offers in comparison to PrettyFaces.

In this post we are going to migrate a PrettyFaces project which is NOT annotation based. We just use the pretty-config.xml to map everything.

Part 1 : Stock-taking

I created a small JEE 6 sample webapp with JBoss Forge which you can fork or clone from Github if you want. This application will show us several things:

  • a small pom.xml with only a few dependencies
  • an XHTML template to be DRY
  • four pages: index, about, profile, login
  • a WEB-INF/pretty-config.xml

We will start with our pretty-config.xml shown below:

WEB-INF/pretty-config.xml
<pretty-config xmlns="http://ocpsoft.com/prettyfaces/3.3.2" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xsi:schemaLocation="http://ocpsoft.com/prettyfaces/3.3.2 http://ocpsoft.com/xml/ns/prettyfaces/ocpsoft-pretty-faces-3.3.2.xsd">
	<url-mapping id="home"> 
		<pattern value="/" /> 
		<view-id value="/index.xhtml" />
	</url-mapping>
	
	<url-mapping id="about"> 
		<pattern value="/About" />
		<view-id value="/about.xhtml" />
	</url-mapping>
	
	<url-mapping id="profile"> 
		<pattern value="/Profile" />
		<view-id value="/profile.xhtml" />
	</url-mapping>
</pretty-config>

This file just maps / to index.xhtml, /About to about.xhtml and /Profile to profile.xhtml. The pages just have links to each other, to provide some sort of interaction. Nothing too special ;)

Below you can see some content of the index.xhtml to get an idea of how the navigation with PrettyFaces works:

/src/main/webapp/index.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
				xmlns:h="http://java.sun.com/jsf/html"
				xmlns:f="http://java.sun.com/jsf/core"
				xmlns:ui="http://java.sun.com/jsf/facelets"
				template="/WEB-INF/templates/default.xhtml">
				
	<ui:define name="links">
		<h:link outcome="pretty:about" value="About"/>
		<h:outputText value="	" />
		<h:link outcome="pretty:profile" value="Profile"/>
	</ui:define>							
</ui:composition>

All the other files(web.xml, faces-config.xml,..) are pretty straight forward and you can check them out via Github.

Part 2 : Migration

First we will have to change the pom to exclude PrettyFaces and include Rewrite:

/pom.xml
<dependency>
	<groupId>org.ocpsoft.rewrite</groupId>
	<artifactId>rewrite-servlet</artifactId>
	<version>1.0.0.Final</version>
</dependency>

<dependency>
	<groupId>org.ocpsoft.rewrite</groupId>
	<artifactId>rewrite-integration-cdi</artifactId>
	<version>1.0.0.Final</version>
</dependency>

Next we need to create several ConfigurationProvider classes. These classes have to extend org.ocpsoft.rewrite.servlet.config.HttpConfigurationProvider (for servlet environments.) A ConfigurationProvider has a method which returns a priority and a method which returns the configuration. Have a look at the AccessRewriteConfiguration below:

We’ve specified this configuration as NonEnriching, since we do not need CDI enrichment, and we can shave a few microseconds from each request by disabling the extra features.
at.w0mb.prettyMigration.rewrite.AccessRewriteConfiguration
package at.w0mb.prettyMigration.rewrite;

import javax.servlet.ServletContext;

import org.ocpsoft.common.services.NonEnriching;
import org.ocpsoft.rewrite.bind.El;
import org.ocpsoft.rewrite.config.*;
import org.ocpsoft.rewrite.servlet.config.*;

public class AccessRewriteConfiguration extends HttpConfigurationProvider implements NonEnriching {
	@Override
	public Configuration getConfiguration(final ServletContext context) {
		return ConfigurationBuilder.begin()
				.addRule(Join.path("/").to("/index.xhtml"))
				.addRule(Join.path("/about").to("/about.xhtml"))
				.addRule(Join.path("/profile").to("/profile.xhtml"))
				.addRule(Join.path("/login").to("/login.xhtml"))

				// Authentication
				.defineRule()
				.when(Direction.isInbound().and(Path.matches("/logout")))
				.perform(Invoke.binding(El.retrievalMethod("identity.logout"))
						.and(Redirect.temporary(context
							.getContextPath() + "/")));
	}

	@Override
	public int priority() {
		return 10;
	}
}

With this Configuration, we mainly implemented what we saw earlier in the pretty-config.xml; but additionally, we have defined a ‘virtual’ URL which doesn´t map to an *.xhtml file. This URL just invokes a bean method which in this case performs the logout and redirects to /.

Question: What happens if we try to invoke the application now?!? No idea? Ok.
Answer: We will get error messages because JSF cannot find any mappings for our ourcomes e.g. pretty:about, pretty:profile, …

So what do we need to do now? We have to do the mapping ourselves in the faces-config.xml:

WEB-INF/faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
   version="2.0">
   
   <navigation-rule>
		<from-view-id>*</from-view-id>
		<navigation-case>
			<from-outcome>pretty:home</from-outcome>
			<to-view-id>/index.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>
	
	<navigation-rule>
		<from-view-id>*</from-view-id>
		<navigation-case>
			<from-outcome>pretty:about</from-outcome>
			<to-view-id>/about.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>
	
	<navigation-rule>
		<from-view-id>*</from-view-id>
		<navigation-case>
			<from-outcome>pretty:profile</from-outcome>
			<to-view-id>/profile.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>
</faces-config>

Soo, that would nearly work now. But how about the login? We will define another ConfigurationProvider as shown below – notice how we are able to @Inject our Identity object directly into the configuration. This is due to the rewrite-integration-cdi extension module, which enriches our rewrite configuration objects with CDI support, and gives us access to the Unified Expression Language (not shown.)

at.w0mb.prettyMigration.rewrite.LoginInterceptor
package at.w0mb.prettyMigration.rewrite;

import javax.inject.Inject;
import javax.servlet.ServletContext;

import at.w0mb.prettyMigration.Identity;

import org.ocpsoft.rewrite.config.*;
import org.ocpsoft.rewrite.servlet.config.*;

public class LoginInterceptor extends HttpConfigurationProvider {

	@Inject
	private Identity identity;

	@Override
	public Configuration getConfiguration(ServletContext arg0) {
		ConfigurationBuilder config = ConfigurationBuilder.begin();

		if (!identity.isLoggedIn()) {
			return config
					.defineRule()
					.when(DispatchType.isRequest().and(Direction.isInbound())
					.and(Resources.excluded()))
					.perform(Forward.to("/login"))
					.addRule(Join.path("/login").to("/login.xhtml"));
		}

		return config;
	}

	@Override
	public int priority() {
		return 0;
	}
}

Now we are very very near to the end. We just have to tell Rewrite which ConfigurationProvider classes to use at runtime:

META-INF/services/org.ocpsoft.rewrite.config.ConfigurationProvider
at.w0mb.prettyMigration.rewrite.LoginInterceptor
at.w0mb.prettyMigration.rewrite.AccessRewriteConfiguration

Part 3 : Conclusion

We are now able to call our application under http://localhost:8080/prettyMigration and we will see our login page. This page gets displayed no matter which URL we will call. After clicking on ‘login’ we will be ‘logged in ‘ and redirected to the index page. Once we open http://localhost:8080/prettyMigration/logout, we are ‘logged out’ and we will again see the login page.

You can grab this project from Github. The project was build and tested on JBoss 7.1, in my opinion the fastest and most advanced application server on the market.

I hope you liked my first post on this blog and if you have any comments/questions/improvements, please just post a comment here or contact me!

Tuesday, March 6, 2012

PrettyFaces is an OpenSource Filter-based Servlets extension with enhanced support for JavaServer Faces – JSF 1.1, 1.2 and 2.0 – enabling creation of bookmark-able, pretty URLs. PrettyFaces solves the “RESTful URL” problem elegantly, including features such as: page-load actions, seamless integration with faces navigation, dynamic view-id assignment, managed parameter parsing, and configuration-free compatibility with other web frameworks.

Get version 3.3.3 of PrettyFaces now! See how simple URL-rewriting can be, with the power of open-source tools at your fingertips.

Thanks goes out to all of the developers who contributed to this release; a lot of work has been done, particularly in the realms of stability, performance, and inter-operability with other frameworks.

Features & Enhancements:

  • Exceptions are no longer wrapped by PrettyFilter
  • Added configuration parameter to enable a workaround for ORCHESTRA-60 (#125)
  • Major performance improvements – thanks to Piotr Jastrzebski and Frank Caputo
  • Enabled mode in web-fragment.xml, for clustering support

Bugfixes:

  • Fixed potential NullPointerException in annotation scanning code
  • Fixed PrettyContext.JSESSIONID_REGEX to support clustered session IDs
  • Empty segments are now handled correctly – thanks to Piotr Jastrzebski for the patch (#123)
  • Fixed encoding for square brackets and less/greater than characters
  • Don’t apply rewrite rule to requests forwarded by URL-mappings anymore
  • Fixed encoding/decoding of vertical bar (|) character
  • Fixed bug that caused mappings to also match viewIds of other mappings (#128)
  • Fixed detection of jsessionid for GAE application (#15)

Items:   1 to 5 of 67   Next »