Creating a JAX-RS Compliant Stub for RESTTemplate

8:15 AM , 0 Comments

I really like RESTTemplate, but I have a small beef with the lack of JAX-RS support (go to Arjen Poutsuma's comments below for Spring's rationale for not building it in). So, if you are like me and want to use JAX-RS annotations and RESTTemplate together, here is what I did.

First, a little bit of background on what JAX-RS looks like. JAX-RS is the result of JSR 311 which includes a set of method and class annotations. These annotations are interpreted by a JAX-RS REST provider like Jersey or CXF. A typical class might be annotated in the following way:


@Path("/myResource")
@Consumes("application/json")
public interface MyEndpoint {
@GET
@Path("/myAction/{resourceId}")
@Produces("application/json")
MyClass getResource(@PathParam("resourceId") String resourceId);
}


Then, with some configuration, CXF and the like will create the client that translates method invocations into the appropriate HTTP request.

So, the goal is to create some sort of proxy or instrumentation piece that will take this interface and translate invocations to it into the correct RESTTemplate method invocation.

I only needed a handful of the features from the JAX-RS spec which basically included:
  • Support for GET, POST, PUT, and DELETE rest headers.

  • Support for url templates via PathParam, QueryParam, and FormParam.

  • Support for Path annotations at the class and method level.
One possibly important thing for other people that I DIDN'T tackle:
  • Support for one method parameter having no JAX-RS annotation.
(Note: I decided to create a java InvocationHandler, but there are several ways to go about this, including Spring AOP.)

In the case of a JAX-RS client, the HTTP methods actually translate into different RESTTemplate method calls. Since I wasn't doing location requests, I have a one-to-one mapping from JAX-RS HTTP method annotations and RESTTemplate method calls:


if ( httpMethod instanceof POST ) {
return restTemplate.postForObject(url + queryParamExtractor.getExtracted(), formParamExtractor.getExtracted(), method.getReturnType(), pathParamExtractor.getExtracted());
} else if ( httpMethod instanceof GET ) {
return restTemplate.getForObject(url + queryParamExtractor.getExtracted(), method.getReturnType(), pathParamExtractor.getExtracted());
} else if ( httpMethod instanceof DELETE ) {
restTemplate.delete(url + queryParamExtractor.getExtracted(), pathParamExtractor.getExtracted());
} else if ( httpMethod instanceof PUT ) {
restTemplate.put(url + queryParamExtractor.getExtracted(), formParamExtractor.getExtracted(), pathParamExtractor.getExtracted());
}


The more involved part is checking each method parameter and appropriately interpreting the JAX-RS method parameter annotations. I just needed PathParam, QueryParam, and FormParam:


for ( int i = 0; i < allParameterAnnotations.length; i++ ) {
Annotation[] parameterAnnotations = allParameterAnnotations[i];
Object arg = args[i];
for ( Annotation parameterAnnotation : parameterAnnotations ) {
if ( parameterAnnotation instanceof PathParam ) {
pathParamExtractor.extractFrom(((PathParam)parameterAnnotation).value(), arg);
} else if ( parameterAnnotation instanceof QueryParam ) {
queryParamExtractor.extractFrom(((QueryParam)parameterAnnotation).value(), arg);
} else if ( parameterAnnotation instanceof FormParam ) {
formParamExtractor.extractFrom(((FormParam)parameterAnnotation).value(), arg);
}
}
}


Everything else is just helper classes. Pay no attention, for example, to the variables 'pathParamExtractor', etc. They are simply helpers for accumulating the values of those parameters in a way to satisfy the url template.

Hope this gets you started, too.

0 comments:

Some Findbugs detectors

At our company, we've been creating various static analysis rules using PMD, Findbugs, and a home-grown tool all aggregated into a Sonar dashboard. It's pretty cool.

Here are some of the rules that we created and why we chose each particular tool:

Findbugs



Anemic Domain Modeling


Sounds pretty lofty, I know, but I think that we found a decent strategy that catches 80% of these issues.

To detect the practice of Anemic Domain Modeling, it seemed best to look from the point of view of the service provider where the logic usually is coded. We wanted to look for something like this:

public double getOrderTotal(Order o) {
double total = 0.0;
for ( OrderItem oi : o.getItems() ) {
total += oi.getQuantity() + oi.getPrice();
}
return total;
}

(By the way, it isn't my purpose here to debate why Anemic Domain Modeling might, indeed, be an anti-pattern. Read the article referenced to get started on that.)

What we noticed is that in these simple cases 1) no service state was being referenced and 2) no exit point other than those that originated from the method parameter was being invoked. So, our bug checks for these two cases and, finding both, logs a bug.

The reason for Findbugs here over PMD has to do with Findbugs, operating on the Java bytecode, has finer-grained access to the data types of each reference. This was the most common reason for choosing Findbugs over PMD when we did.


Unsaved Object Operation Detector


One of the bugs that has bitten me in the past is when I use an API that appears to mutate the calling object, but doesn't. Consider BigInteger, for example:

BigInteger original = BigInteger.ONE;
original.add(BigInteger.ONE);
// what is the value of 'original'?

The value of 'original' is still one because BigInteger's are immutable. In other words, and method call on BigInteger that returns a BigInteger is actually returning a new instance and not the original modified. This happens with String and several other classes.

To check for this, we currently maintain a list of API's that exhibit this behavior and then detect when a method that returns a value is called and whose return value is not set in the calling code, just as above.

Findbugs was used here because, by using the byte code, it can derive the data type of a reference after it has been declared. PMD can't do this (at least not to our knowledge) because it represents the code as an Abstract Syntax Tree, and it turns out to be fairly difficult to infer cross-transverse relationships in a hierarchical representation.


PMD




Avoid Run On Method Statements


The decision here was to try and make code more maintainable and modular by encouraging developers to not use run-on statements:

Object var2 = var1.method1().method2().method3().method4(var3).method5();

The code above has at least two problems. First, if method2 returns null, then it will result in a NullPointerException that is hard to debug. Second, unit tests become difficult to write because now several levels need to be mocked in order to prepare the variable 'var2'. A possible third problem would be that the method containing this line of code will have a much higher RPC making it more prone to faults and more brittle to change. (The reason I think it's only a possible third is because, even if these were separated into 5 different statements, the method would still have this same problem.)

In this detector, then, we find statements that make more than three chained invocations and notify the developer.

We chose PMD for this because in the byte code world, it became very, very difficult to decide when a method invocation was in the same chain. To be able to tell the difference between an invocation chain and not would be to keep track of a symbol table and program stack, which sounded like a lot of work.

Chains can be easily verified when looking at Java syntax, though, which is what PMD does. What took us 12 hours of trying and re-trying in Findbugs took us only 20 minutes in PMD.


Avoid Methods With Same Name As Class


I stole this one from Java Puzzlers. I didn't even know that this was allowable in Java:

public class MyClass {
public void MyClass() {
// body
}
}

You'll notice that the second line appears to be the signature for a constructor, but instead is the signature for a method! (Notice the 'void' keyword. That makes it a method) This would be crazy confusing, I think, if someone actually did it, so we made a rule.

Again, since this was really verifying syntax, PMD was the easiest approach.


Home-grown


I'll talk about our home-grown stuff in another post, because I have a lot of background to give. The basic idea though, is that the overall health of a project isn't limited to the Java code. Ideally, we'd like to take a stab at HTML, Javascript, CSS, Maven dependencies, and more. There didn't appear to be anything out there that analyzed this types of files, so we created one. More next time.

0 comments:

Findbugs and Source Code Lookup in Eclipse

11:29 AM 0 Comments

One of the first roadblocks that I ran into with Findbugs was how to see the Findbugs source code in Eclipse.

This is actually pretty simple, but I hadn't taken the time to do it, yet.

First, download the Findbugs source code:


wget http://sourceforge.net/projects/findbugs/files/findbugs/1.3.9/findbugs-1.3.9-source.zip/download


Second, unzip.

Third, go to the src/java directory, and jar up the sources:


cd findbugs-1.3.9/src/java
jar cvf findbugs-1.3.9-sources.jar .


Fourth (if you are using Maven), copy into .m2/repository/findbugs/findbugs/1.3.9.

Now, when you try to open one of the classes in Eclipse, it will ask you to attach the source code, which you can by referring to the location of that jar. Wohoo!

0 comments: