Wednesday, January 12, 2011

Java: REST with ease :)

Update: you can get the latest Http client from Maven central:
http://search.maven.org/#search|gav|1|g%3A%22org.javalite%22%20AND%20a%3A%22javalite-common%22

Ever wanted to just send an HTTP request from Java? What should be easy is actually not. There are myriads of ways to do this in Java; you can write a half of page of ugly code with exceptions, use Apache HTTPClient library (which has its own dependencies), find another library, learn its API, etc. I told myself: "... but I just need to call a service and get a reply back!".
Eventually I got tired of this situation, and wrote my own tiny library for doing this: JavaLite.

Here is an example. If all you need is to send a GET request and get a response back, you could do this in one line:
Get get = Http.get("http://yahoo.com");
System.out.println(get.text());
System.out.println(get.headers());
System.out.println(get.responseCode());
The implementation has no dependencies and relies solely on standard Java API. The library even sets sensible defaults for timeouts. Also supports POST, DELETE and PUT.
The reason I developed it is because we are heavily relying on using REST services and a simple call to a service is really a must have.
So, an example of a service call would look like this:
if(Http.post("http://host/context/resources", postContent).text().equals("OK")){
    //..success
else{
    //failure
}
Eat that, SOAP! The code is so unobtrusive, sometimes it is hard to see. Oh, boy, I can breath now... head is cool, feet are warm...live is better now :)
Happy coding!
igor

29 comments:

bahadir said...

Hi Igor

How I can download your JavaTiny library for Rest services, and give it a try. It sounds interesting really.

Cheers
Baha

Igor Polevoy said...

bahadir, this is part of the ActiveJDBC library (probably need to pull this into a separate project), the latest snapshot can be pulled from :http://ipsolutionsdev.com/snapshots/javalite/javalite-common/1.1-SNAPSHOT/, and you can see teh sources here:
http://code.google.com/p/activejdbc/source/browse/#svn%2Ftrunk%2Fjavalite-common%2Fsrc%2Fmain%2Fjava%2Fjavalite%2Fhttp

thanks
Igor

pibu said...

Hi Igor

Your javalite is exactly what I've been looking for. Can you show example how to add header (basic authorization) before making a connection?


thanks
Piotr

Igor Polevoy said...

Piotr, this was an oversight on my part. I added Basic Auth to the library, please see later post.

thanks
igor

naj said...

Are you posting your source, or just the jar? It's hard to figure out how to use it if there's no javadoc or source. Thanks.

Igor Polevoy said...

naj, as I posted above, the link to sources is: http://code.google.com/p/activejdbc/source/browse/#svn%2Ftrunk%2Fjavalite-common%2Fsrc%2Fmain%2Fjava%2Fjavalite%2Fhttp

thanks,
igor

naj said...

Thanks so much! I completely overlooked the link. My apologies!

danhvidding said...

Hi,

I used your library to call a REST service, I like it- it's very simple and well commented. One thing I might point out. I had to do a post to the service and set the request header of content-type to "application/json". I used the following lines,

Post post = Http.post("http://api.edgecast.com/v2/pcc/customers", postContent);
String token = DBHelper.getEC_API_Token();
post.header("Authorization",("TOK:"+token));
post.header("Accept","application/json");
post.header("Content-Type","application/json");
post.header("Host","api.edgecast.com");
post.header("Content-Length",Integer.toString(bytes));

System.out.println("**********Response Code*****************");
System.out.println(post.responseCode());
System.out.println("**********End Response Code*****************");

However, the POST was hardcoding the content-type header to 'text/plain' after I was setting the content-type manually. I went ahead and commented out where that header was set in Post.java and Put.java. Just thought I would drop a comment there in case anyone else runs into it.

Igor Polevoy said...

danhvidding, you are correct, what was I thinking? :)
I corrected the problem and rebuilt the library, feel free to get the latest build.

By the way, the method header(x, y) returns self, so that you could chain them like this:

header(a, b).header(c, d)...

thanks,
Igor

Johan said...

I've made something similar as this called Rest Assured. It also lets you parse XML and JSON responses very easily and authentication etc etc. Perhaps we can do something together.

Igor Polevoy said...

Johan, I looked at RestAssured, and it does indeed look good. Somewhat similar to JavaLite Http, but probably goes further in the way you can use it for testing. I also like the DSL you use, I use a similar approach this across all my projects now. I'm working on a new Java web framework ActiveWeb that is similar to Ruby on Rails with ActiveJDBC as a ORM, and using RestAssured for automated testing just makes sense. It should be possible to describe user scenarios in RA. We can certainly collaborate on this.
thanks
igor

danhvidding said...

Hi Igor,

I also made one small change to the source code of your library... In my case, the api I'm calling also may contain a message in the response body if there is an error response code returned. For instance if the message I send to the service is malformed, and missing a required field- the message I get back will be a 400 Bad Request and the body will have some text detailing what was missing. The code as is throws an exception if I try to read the response body when there is not a 200 OK response. So I changed the following line in the text method in Request.java...


public String text() {
try {
connect();
String result = (this.responseCode() >= 400) ? Util.read(connection.getErrorStream())
: Util.read(connection.getInputStream());

dispose();
return result;
} catch (IOException e) {
throw new HttpException(e);
}
}

Igor Polevoy said...

danhvidding, thanks for yet another contribution :)
I incorporated it into code, and the latest build is available for download

igor

Pierre V. said...

Thanks Igor,
This is a very useful and simple solution that I was looking for. I am not familiar with Java, coming from Python I was in misery reading code to do for a simple POST to retrieve some JSON data. In Python it's one or two line of code.
This is so simple to use. Love it.

Cheers,

Yadu said...

If you need simple single file executable interface to play with rest services, it's here

http://tinyurl.com/rest-client

invisble said...

Hi Igor,

I am newbiew in using the REST services.. Can you please post a simple example of connect, GET and POST using your API's. That would be very helpful.

Thanks.
Rivya

Emmanuel Kpoudosu said...

I keep getting Caused by: java.net.SocketTimeoutException: Read timed out when i try to retrieve large information using your library. Is there something I am doing wrong?

This same url opens perfectly on a browser

Igor Polevoy said...

Emmanuel, apparently you endpoint takes time to respond with data, use this method and set your timeouts:
http://ipsolutionsdev.com/activejdbc/org/javalite/http/Http.html#get(java.lang.String, int, int)

thanks

Unknown said...

Hi Igor,
A newbie in all things web and delving into it using wavemaker as a development tool for a web application. I need to use Java to make use of zotero.org citation facility that uses GET/POST methods to read citations and write new ones. It however requires use of https.
My questions, is javalite supporting https.
I have been reading on domain restrictions and wonder if using java method bypasses this limitation.

Igor Polevoy said...

JavaLite supports HTTPS because it is supported by Java. JavaLite is a very thin layer on top.

Unknown said...

Igor,
making a try in using javalite within wavemaker.
I am getting following errors

--snip--
1. ERROR in \services\javaInsert\src\com\wavemaker\javaInsert.java (at line 54)
Get get = Http.get("http://yahoo.com");
^^^
Get cannot be resolved to a type
----------
2. ERROR in \services\javaInsert\src\com\wavemaker\javaInsert.java (at line 54)
Get get = Http.get("http://yahoo.com");
^^^^
Http cannot be resolved
--snip--

Tried to "import org.javalite.http;" but this mentioned that javalite is a package hence assume I should just be able to use the sample code provided in this post but no joy.

Please bear with me, learning java and need assistance and directions.

kotin karwak

Igor Polevoy said...
This comment has been removed by the author.
Igor Polevoy said...

Please, post support questions on the forum: ActiveJDBC Google Groups

thanks

arindam said...

JavaLite works like breeze ...

Get get = Http.get("http://localhost:8080/bpm/rest/pageflow/list");
get.header("Authorization", "Basic dGliY28tYWRtaW46c2VjcmV0");
get.header("Accept", "application/json");
get.header("Content-Type", "application/json");
get.header("Host", "http://localhost:8080");

System.out.println(get.text());

Post post = Http
.post("http://localhost:8080/bpm/rest/pageflow/start/%2FTestRESTService%2FProcess%20Packages%2FTestRESTService.xpdl/TestRESTPageFlows/1.0.0",
"");
post.header("Authorization", "Basic dGliY28tYWRtaW46c2VjcmV0");
post.header("Accept", "application/json");
post.header("Content-Type", "application/json");
post.header("Host", "http://localhost:8080");


System.out.println(post.text());

Put put = Http.put("http://localhost:8080/bpm/rest/pageflow/update/pvm:0a101/pvm:001g1.7", "");
put.header("Authorization", "Basic dGliY28tYWRtaW46c2VjcmV0");
put.header("Accept", "application/json");
put.header("Content-Type", "application/json");
put.header("Host", "http://localhost:8080");


System.out.println(put.text());

Delete delete = Http.delete("http://localhost:8080/bpm/rest/pageflow/cancel/pvm:0a102");
delete.header("Authorization", "Basic dGliY28tYWRtaW46c2VjcmV0");
delete.header("Accept", "application/json");
delete.header("Content-Type", "application/json");
delete.header("Host", "http://localhost:8080");


System.out.println(delete.text());

Yogesh Gandhi said...

How to configure proxies with Javalite.???
I tried connecting to http://yahoo.com in office and I got the followign exception.

Exception in thread "main" org.javalite.http.HttpException: Failed URL: http://yahoo.com
at org.javalite.http.Request.responseCode(Request.java:102)
at org.javalite.http.Request.text(Request.java:153)
at resttester.RESTTester.main(RESTTester.java:22)
Caused by: java.net.ConnectException: Connection refused: connect

Igor Polevoy said...

Yogesh, this is no different than any other service. Check this out: http://stackoverflow.com/questions/4599933/how-to-use-an-http-proxy-in-java

Ian Fallon said...

Hi - this looks neat. What licencing rules are you releasing it under ?

Igor Polevoy said...

Ian, this is under Apache 2 license:
LICENSE.
You can find the library in Maven Central: JavaLite Common

Hope this helps, cheers

Igor Polevoy said...

All information related to JavaLite HTTP can be found here: JavaLite HTTP

thanks