I recently looked at
JBOSS RESTEasy as a way to create and
test RESTful APIs. The platform looks very promising with a lot of praise from developers. Also the documentation seems very extensive and precise.
I started by downloading
RESTEasy 1.2.1 GA and tried the sample code. I started with a java client to access existing
RESTful Web Services and APIs. Among the api-clients, there is a
Twitter small client that works out-of-the box (located under
/RESTEASY_1_2_1_GA/examples/api-clients/src/main/java/org/jboss/resteasy/examples/twitter).
However when I started to extract the code and wanted to create a
Maven 2 based stand-alone project, I encountered some issues related to JAR dependency conflicts, including the following error message also described
here.
java.lang.NoClassDefFoundError: Could not initialize class com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl
The project (eclipse) structure looks as below:
I managed to fix these issues by modifying the POM file as follow:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jboss.resteasy.examples</groupId>
<artifactId>api-clients</artifactId>
<version>1.2.1.GA</version>
<dependencies>
<!-- Resteasy Core -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>
<!-- JAXB support -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-bom</artifactId>
<version>1.2.1.GA</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Build Settings -->
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<!-- Environment Settings -->
<repositories>
<repository>
<id>jboss</id>
<name>jboss repo</name>
<url>http://repository.jboss.org/maven2</url>
</repository>
</repositories>
</project>
The most important piece, beside the cleaning of the POM file, was to include a pom that can be imported so the versions of the individual modules do not have to be specified (see
RESTEasy documentation -
Chapter 43. Maven and RESTEasy).
I also made sure to have correct dependencies for
resteasy-jaxrs and
resteasy-jaxb-provider.
As a result, I was able to compile the whole project without any errors (mvn clean compile) and run it to access the Twitter REST API
mvn exec:java -Dexec.mainClass="org.jboss.resteasy.examples.twitter.TwitterClient" -Dexec.args="<userid> <password>"
(Replace last parameters by your twitter user and password).
The small client in question leverages JAX-RS annotations to read and write the Twitter API resources:
package org.jboss.resteasy.examples.twitter;
import java.util.Date;
import java.util.List;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.client.ClientExecutor;
import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
public class TwitterClient
{
static final String friendTimeline = "http://twitter.com/statuses/friends_timeline.xml";
public static void main(String[] args) throws Exception
{
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
final ClientExecutor clientExecutor = new ApacheHttpClientExecutor(createClient(args[0], args[1]));
TwitterResource twitter = ProxyFactory.create(TwitterResource.class,
"http://twitter.com", clientExecutor);
System.out.println("===> first run");
printStatuses(twitter.getFriendsTimelines());
twitter
.updateStatus("I programmatically tweeted with the RESTEasy Client at "
+ new Date());
System.out.println("===> second run");
printStatuses(twitter.getFriendsTimelines());
}
public static interface TwitterResource
{
@Path("/statuses/friends_timeline.xml")
@GET
Statuses getFriendsTimelines();
@Path("/statuses/update.xml")
@POST
Status updateStatus(@FormParam("status") String status);
}
private static void printStatuses(Statuses statuses)
{
for (Status status : statuses.status)
System.out.println(status);
}
private static HttpClient createClient(String userId, String password)
{
Credentials credentials = new UsernamePasswordCredentials(userId,
password);
HttpClient httpClient = new HttpClient();
httpClient.getState().setCredentials(AuthScope.ANY, credentials);
httpClient.getParams().setAuthenticationPreemptive(true);
return httpClient;
}
@XmlRootElement
public static class Statuses
{
public List<Status> status;
}
@XmlRootElement
public static class Status
{
public String text;
public User user;
@XmlElement(name = "created_at")
@XmlJavaTypeAdapter(value = DateAdapter.class)
public Date created;
public String toString()
{
return String.format("== %s: %s (%s)", user.name, text, created);
}
}
public static class User
{
public String name;
}
}
The small DateAdapter class is a utility class for date formatting:
package org.jboss.resteasy.examples.twitter;
import java.util.Date;
import java.util.List;
package org.jboss.resteasy.examples.twitter;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.jboss.resteasy.util.DateUtil;
public class DateAdapter extends XmlAdapter<String, Date> {
@Override
public String marshal(Date date) throws Exception {
return DateUtil.formatDate(date, "EEE MMM dd HH:mm:ss Z yyyy");
}
@Override
public Date unmarshal(String string) throws Exception {
try {
return DateUtil.parseDate(string);
} catch (IllegalArgumentException e) {
System.err.println(String.format(
"Could not parse date string '%s'", string));
return null;
}
}
}