This One's for Pat

Two years ago I was cautiously optimistic. The Seahawks had a great defense, the Broncos had an great offense and I was looking forward to a great Super Bowl. That lead to a beat down as painful to watch as the Super Bowl XXIV thrashing.

This year I was guardedly pessimistic. All season long I had been saying that the Broncos didn’t need Peyton Manning at the height of his powers - they just needed Peyton to play at a replacement level and the defense could do the rest. Unfortunately, Peyton wasn’t able to play at a replacement level. The result was a game in which Denver never trailed, but kept me on the edge of my seat most of the game.

After obsessively reviewing the post game analysis, I subscribe to the theory that the Carolina coaching staff let their team down while the Denver staff understood how to the Broncos could/would win. With the Broncos unlikely to put together a long TD drive, it was imperative that the Panthers manage field position and not give the Broncos a short field. Instead the Panthers gave up a defensive touchdown on their second position and the Broncos were on their way.

I really think Carolina’s coaches didn’t appreciate the kind of game they were in, and it led to handing the Broncos their best chance to win the game. If Rivera and Co. had approached it with the sort of thinking that goes along with trying to win a game 16-13 or 13-9, all sorts of things might have been different. In particular, if Carolina approaches the game with the 1st principal being giving the Broncos zero chance to get a turnover on a very short field, the game might turn out different.

Theme and Site Update

Website viewers may notice a couple of site changes.

  1. My tag cloud is now shown at the bottom of the front page.
  2. The main menu links to “Home”, “Technology Category”, “Sports Category”, and “Java Tag”.

I thought that my front page ended a bit abruptly after removing the ability to page through the entire weblog. The tag cloud provides some guidance in exploring my back catalog and is enabled by the latest update to my hexo-theme-landscape work branch.

It’s feels odd, not having a link to my Java Tutorials. Getting my tutorial page on DMOZ was a big step 12 years ago. It was a strong effort back then, but I let it wither on the vine. Now it is time to move on.

CloudFront Checkpoint/Hexo Theme Update

CloudFront is looking good. My added bandwidth cost is about $.20 a month - a very cost effective way to add SSL to my S3 hosted weblog. There is one financial gotcha - the first 1000 URL invalidations are free, but it is $.005 per invalidation after that. I blew through my free invalidations when I added SSL and regenerated every page last month. I think 1000 free invalidations a month would be ample for most people, but not for me.

I have almost 900 total posts. That means I invalidate about 90 URLs with every post (10 posts per page). Or about 10 posts a month, unless I’m willing to pay. I don’t think anyone really wants to page through my entire weblog. And I already updated my archive (2015), category (technology), and tag (java) pages to present a single page of titles last year. So I’ve updated the hexo landscape theme to only generate the first index page.

My fork of hexo-theme-landscape is on github. Take the First Step uses the work branch. I cherry-pick commits from work to master so that I can generate clean pull requests from master.

Cucumber-java and TestNG

With the addition of my jersey client tests, I thought I was ready to add cucumber support to my project. I was wrong. The documentation on using Cucumber and TestNG is a bit sparse and I was getting a bit flustered on where to put the feature definition file.

It turns out that I was making a mountain out of a mole hill. If you put the feature file in the wrong place, then cucumber will tell you where it was looking.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ mvn clean test -q -Dskip=client,browser

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running TestSuite
Configuring TestNG with: TestNG652Configurator
No features found at [classpath:com/ideoplex/tutorial]

0 Scenarios
0 Steps
0m0.000s

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.806 sec - in TestSuite

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

Read More

Jersey Client with Gson

It doesn’t make sense to have full test coverage of the jersey-gson project in selenium. Let’s fill in the gaps with some jersey-client unit tests.

First, we need a UserMap deserializer to mirror the existing serializer. The existing serializer sends the UserMap as an object whose value is an Array of rows (this simplifies interaction with the DataTables front end). We deserialize the UserMap by iterating through the array, turning each row into a User object, and adding each User object to the Map.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
public class UserMapUnmarshall implements JsonDeserializer<UserMap> {
private static Gson gson = new Gson();

@Override
public UserMap deserialize(JsonElement mapFormat, Type typeOfSrc, JsonDeserializationContext context) {
Iterator<JsonElement> iterate = mapFormat.getAsJsonObject()
.get("data")
.getAsJsonArray()
.iterator();

UserMap users = new UserMap();
while( iterate.hasNext() ) {
JsonObject user = ((JsonElement) iterate.next()).getAsJsonObject();
String email = user.get("email").getAsString();
User add = new User();
add.setEmail( email );
add.setSurname( user.get("surname").getAsString() );
add.setGivenName( user.get("givenName").getAsString() );
users.put( email, add );
}

return users;
}
}

Read More

jersey-gson Content-Type Woes

Another update to my jersey-gson project today. I was trying to add some jersey-client unit tests to the project, but I just couldn’t get the GsonReader and GsonWriter classes to work on the client. The tests worked with manually generated JSON, but failed with automatically generated JSON.

I tried adding a LoggingFilter to examine the actual request being sent to the server, but nothing jumped out at me. Then I added Mono Fiddler as a debugging proxy server and I saw it.

The request succeeded with “Content-Type: application/json”
The request failed with “Content-Type: application/json; charset=UTF-8”

A quick change to GsonWriter and my client requests started working.

1
2
3
4
5
6
7
8
9
10
11
diff --git a/src/main/java/com/ideoplex/tutorial/GsonWriter.java b/src/main/java/com/ideoplex/tutorial/GsonWriter.java
index 9ef181d..95a12c3 100644
--- a/src/main/java/com/ideoplex/tutorial/GsonWriter.java
+++ b/src/main/java/com/ideoplex/tutorial/GsonWriter.java
@@ -42,7 +42,6 @@ public class GsonWriter<T> implements MessageBodyWriter<T> {
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)
throws IOException, WebApplicationException {
- httpHeaders.get("Content-Type").add("charset=UTF-8");
entityStream.write(gson.toJson(t).getBytes("UTF-8"));
}

Of course, code changes always cascade. Now my selenium browser tests were failing. A little exploration in the browser developer tools revealed that JSON.parse was the culprit. Another code update and the tests were succeeding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
index 9568c94..f147ace 100644
--- a/src/main/webapp/index.html
+++ b/src/main/webapp/index.html
@@ -134,13 +134,13 @@
type: "POST",
url: "webapi/myresource/user/post",
data: JSON.stringify(json),
- contentType: "application/json; charset=utf-8",
+ contentType: "application/json",
error: function(xhr,status,error) {
bootbox.alert(xhr.responseText);
},
success: function(data,status,xhr){
form[0].reset();
- $('#users').DataTable().row.add(JSON.parse(data)).draw(false);
+ $('#users').DataTable().row.add(data).draw(false);
}
});
}
@@ -151,14 +151,14 @@
type: "POST",
url: "webapi/myresource/user/edit",
data: JSON.stringify(json),
- contentType: "application/json; charset=utf-8",
+ contentType: "application/json",
error: function(xhr,status,error) {
bootbox.alert(xhr.responseText);
$('#users tbody tr.selected').removeClass('selected');
},
success: function(data,status,xhr){
form[0].reset();
- $('#users').DataTable().row('.selected').data(JSON.parse(data)).draw(false);
+ $('#users').DataTable().row('.selected').data(data).draw(false);
$('#users tbody tr.selected').removeClass('selected');
}
});

25 Oct Autostart Jetty in Maven
01 Nov Jersey Client with Gson

Autostart Jetty in Maven

I’ve made some administrative updates to my jersey-gson project.

First, I’ve updated the pom.xml to automatically start the application in jetty during the maven process-test-classes phase and to automatically stop the jetty instance during the maven install phase. This insures that the webapp will be running and available in the maven test, integration-test and verify phases.

1
diff --git a/pom.xml b/pom.xml
index 318325d..47b6a2c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,22 @@
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-maven-plugin</artifactId>
               <version>${jetty.version}</version>
+              <executions>
+                <execution>
+                  <id>start-jetty</id>
+                  <phase>process-test-classes</phase>
+                  <goals>
+                    <goal>start</goal>
+                  </goals>
+                </execution>
+                <execution>
+                  <id>stop-jetty</id>
+                  <phase>install</phase>
+                  <goals>
+                    <goal>stop</goal>
+                  </goals>
+                </execution>
+              </executions>
             </plugin>
 
             <plugin>

Read More

DataTables Column Search

I spend a lot of time displaying data. And I have developed a bit of a code crush on DataTables since I learned about them. Here’s an example of how the data displayed in two tables can be linked together by a select element and DataTables column search, taken from my example of xml transformed to html in the browser. Here’s how it looks when you narrow the view down to a single group (groups from 3 to 1 and users from 14 to 5)

Narrow to a Single Group

Read More