I want to create a REST API client, which for the same REST API server will call different URLs. This different calls will return JSON and XML to the client.
What would be a good design pattern for this situation ?
So far I've come up with a combination of Strategy and Command:
public interface IRestCall {
/** Setup the URL for the call. */
void setup(IRestSetup setup) throws Exception;
/** Execute the call, using the URL set up in the first step.
* #throws Exception */
void call() throws Exception;
/** Process the result and return to the user. */
<T> T getResult(IRestResultProcessor<T> result) throws Exception;
}
This is the Strategy interface. The context for the Strategy will
be in some Get/Post/Put methods in a Facade class.
IRestSetup and IRestResultProcessor are interfaces for Command objects
which will setup the URL for the REST API and will process the result.
I don't think you need some special design pattern to handle that situation. I personally would just add some generic methods for GET and POST requests. The code below handles only GET requests but it should be enough to get an idea what I mean.
Your client method processing GET requests might look something like
/**
restUrl - url to the service
resClz - the actual POJO object that the JSON or XML response will be mapped to
resType - type of MIME object returned by the server (typically MediaType.APPLICATION_JSON or MediaType.APPLICATION_XML)
*/
<T> T doRestGet(String restUrl, Class<T> resClz, String resType){
T obj;
Client client = ClientBuilder.newClient();
WebTarget target = client.target(UriBuilder.fromUri("http://localhost:8088/Your_WS_root").build());
try{
obj =(T) target.path(restUrl).request()
.accept(resType).get().readEntity(resClz);
}catch(Exception e){
System.out.println(e);//log or propagate the exception
return null;
}
return obj;
}
Your server method might look something like
#GET
#Path("/userMsg")
#Produces(MediaType.APPLICATION_JSON)
public UserMessage getUsersOne() {
UserMessage uMsg = new UserMessage();
uMsg.setUserMsg("successful call");
return uMsg;
}
Now you'd call your service
UserMessage uMsg = (UserMessage)doRestGet("userMsg", UserMessage.class, MediaType.APPLICATION_JSON );
Related
I'm working with Postman and I see that it has many modes. I was able to implement a restRequest object that "knows" how to send a request in Post or Get method.
This is part of my code:
#Override
public RestResponse sendRequest() {
return data.accept(new RequestDataVisitor<RestResponse>() {
#Override
public RestResponse visit(GetData getData) {
return new RestResponse(webTarget.request().headers(headers).get());
}
#Override
public RestResponse visit(PostFormData post) {
return new RestResponse(webTarget.request(post.getMediaType()).headers(headers).post(post.getEntity()));
}
#Override
public RestResponse visit(PostRawData post) {
return new RestResponse(webTarget.request(post.getMediaType()).headers(headers).post(post.getEntity()));
}
#Override
public RestResponse visit(DeleteData deleteData) {
return new RestResponse(webTarget.request(deleteData.getMediaType()).headers(headers).delete());
}
});
}
How do I get my webTarget to send a request in Patch mode?
You can use the method method with first argument set to "PATCH".
method is a generalized method (as opposed to post, get, delete which have the http-method hardwired) and allows you to call an arbitrary method:
webTarget.request(mediaType).headers(headers).method("PATCH", entity);
See the documentation for additional details: WebTarget.request gives you an instance of an Invocation.Builder which inherits method from SyncInvoker.
Note: depending on the JDK version and the JAX-RS library that you are using, you may run into problems when calling the PATCH method. If you do, see if any of these help:
PATCH request using Jersey Client
HttpURLConnection Invalid HTTP method: PATCH)
Invalid HTTP method: PATCH
The issue was in WebTarget object:
WebTarget target = client.target(baseUrl).path(resourcePath)
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND,"true");
PATCH method only works when SET_METHOD_WORKAROUND is true.
i am working on android app , I often get HTTP error 500 when i try to access the url due to bad connectivity which causes my app to fail . Response returned from URL is in JSON format so in order to Parse this json i used jackson api
JsonNode monthlyUrlNode = objectMapper.readValue(url, JsonNode.class);
In case of failure i want to reconnect to url with a delay of 30 seconds
i referred this Retry a connection on timeout in Java , but it is not of much use to me
Have you thought of using a Proxy object? Proxy let's you wrap an interface in a way that you can perform the same intervention independent of the method being called. Let's assume you've already created a client object for accessing interface SomeService. Then you can create a proxy class with a 30-second retry built in:
public class ServiceProxy implements InvocationHandler {
private final SomeService realClient;
public ServiceProxy(SomeService realClientObject) {
this.realClient = realClientObject;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(realClient, args);
if (result instanceof URL) {
JsonNode urlNode = objectMapper.readValue(url, JsonNode.class);
if (some condition on urlNode) {
// Wait and retry
Thread.sleep(30000);
result = method.invoke(realClient, args);
}
}
return result;
}
}
You create the proxy object by passing it the original interface, like:
public class ProxyFactory {
public static SomeService get(SomeService originalClient) {
return (SomeService)Proxy.newProxyInstance(SomeService.class.getClassLoader(),
new Class[]{SomeService.class},
new ServiceProxy(originalClient));
}
}
If you need this sort of fine control, do not pass a URL to Jackson. Use an appropriate HTTP library to read the content from the URL into memory, retrying as needed, and then feed it to Jackson.
I have a simple REST client with GET POST and DELETE methods.
Weird things is that only GET methods work, neither POST nor DELETE doesn't even get hit and response is "404 Not Found" of course.
Here's my REST service and the client:
Interface:
public interface MyInterface {
#GET
#Path("/content")
#Produces(MediaType.APPLICATION_JSON)
Response getAirports();
#DELETE
#Path("/content/{id}")
#Produces(MediaType.APPLICATION_JSON)
Response deleteAirport(#PathParam("id") String id);
}
Implementation:
#Path("/source")
public class SourceService extends AbstractService implements MyInterface {
#Override
public Response getContent() {
DBCollection collection = getDBCollection("content");
DBCursor cursor = collection.find();
String serialize = JSON.serialize(cursor);
return Response.status(Response.Status.OK).entity(serialize).build();
}
#Override
public Response deleteContent(#PathParam("id") Integer id) {
DBCollection collection = getDBCollection("content");
BasicDBObject query = new BasicDBObject();
query.append("id", id);
collection.remove(query);
return Response.status(Response.Status.OK).build();
}
}
Client:
// This is working
public void getContent() {
WebTarget path = collect.path("/content");
Response response = path.request().get();
LOGGER.info("collect.ping: " + response.readEntity(String.class) + "\n");
}
// This is not working
public void deleteContent(Integer id) {
WebTarget path = collect.path("/content/"+id);
Response response = path.request(MediaType.APPLICATION_JSON).delete();
System.out.println("object deleted:"+response);
}
I've tried requesting with jersey or apache clients but all of them return 404 and I'm like hopeless now.
Hope you can give me a direction.
This looks like a possible duplicate of Inheritance with JAX-RS. Have you tried replicating all annotations in the subclass or none, means do not use #PathParam in the implementation class at all?
If you actually can debug your client and you are indeed able to "Step through" the client code?
If you place a break-point within your server code and you never actually "break" on it? Then the problem is with the way you are exposing your web service and how you are then trying to consume it.
Try to change the parameter type expected by the Server and the type you pass from your client.
If you can change it on the server and client to a simpler type.. i.e.. an integer.. and then you can actually capture a breakpoint in both client and server, then you know that the problem is in your types.
I hope you can understand what I'm saying? You really need to simplify your parameters and/or try it without parameters first.
When you get something simpler working, then you can extend it to something else.
try just changing it to a string... such as "airport" Also, you are passing a parameter in the client as this:
public void deleteAirport(String iata) {
But you don't use "iata" in your client code...
I use a RESTful Web Service. In this web service I must pass a bean that I want to save as a parameter.
Here is the server code:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Unidade inserir(Unidade unidade){
Session s = ConnectDb.getSession();
try {
s.getTransaction().begin();
s.save(unidade);
s.getTransaction().commit();
return unidade;
} catch (Exception e) {
e.printStackTrace();
s.getTransaction().rollback();
return null;
} finally {
s.close();
}
}
I have the following code in the client:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Builder builder = webResource.accept(MediaType.APPLICATION_JSON);
GenericType<Unidade> genericType = new GenericType<Unidade>() {};
Unidade u = new Unidade();
u.setUnidSigla("KG");
//How to pass this bean as parameter?
Unidade response = builder.post(genericType);
System.out.println(response);
How can I pass the bean to the method as a parameter?
Using Jackson as a Serializer/DeSerializer
If your Unidade object is annotated with Jackson and/or a Deserializer is registered then you should be able to POST with a BODY that contains the JSON representing the Unidade object. It should be magically deserialized and rebuilt as an object on the server side.
Important
Make sure that you add a Content-Type header in the POST request with a value of application/json. Without this header your Jersey might not know what to do with the body.
You would use the Jackson ObjectMapper to serialize your Unidade object to JSON and send that instead of whatever that GenericType stuff is.
I have both Jersey and RESTEasy implementations that work seamlessly with Jackson in this manner.
How can I pass the bean to the method as a parameter?
Checkout the documentation for the post method:
/**
* Invoke the POST method with a request entity that returns a response.
*
* #param <T> the type of the response.
* #param c the type of the returned response.
* #param requestEntity the request entity.
* #return an instance of type <code>c</code>.
* #throws UniformInterfaceException if the status of the HTTP response is
* greater than or equal to 300 and <code>c</code> is not the type
* {#link ClientResponse}.
* #throws ClientHandlerException if the client handler fails to process
* the request or response.
*/
<T> T post(Class<T> c, Object requestEntity)
throws UniformInterfaceException, ClientHandlerException;
The method takes two parameters. First parameter is the expected response type, and second one is the entity which is going to be put in the request body.
What happens here, when performing the request Jersey would serialize the object passed as a request entity into the JSON string (hence you've set the header - accept(MediaType.APPLICATION_JSON)). When the response from the server arrives, Jersey will deserialize it (the inverted process as in case of requestEntity) and return you the object.
And what if my method receives more than 1 parameter? Because the post
method only acepts 1
Well you cannot do it with JAX-RS, and it makes little sense actually. You can pass multiple parameters to the method as #PathParam or a #MatrixParam, but there can be only one associated with the body (well you have only one body in our request, right?). Checkout answer to this question and checkout how to use #PathParam or #MatrixParam
Let's suppose instead of returning a "Unidade" class, my method
returns a String. So, it will receive a "Unidade" as parameter and
return a "String". How can i retrieve it in this case, passing the
"Unidade" instance as before?
I believe you could achieve that with post(String.class, unidadeInstance). The first parameter doesn't have to be the same as the second. It's valid to accept one parameter and return another. It is even valid to take a parameter and return nothing in the body (like you have done it in the code attached to your question). You could accept the body and send back response containing status 201 Created and Location header entry pointing to the URL of the newly created resource.
Not sure what's your purpose for GenericType. Anyway, try the code below.
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
Unidade u = new Unidade();
u.setUnidSigla("KG");
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Unidade response = webResource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.post(Unidade.class, u);
I'm not sure if it helps but i had a similar problem.
My scenario was i need a webservice which had to receiver a bunch of values which are organized as a kind of profile. But this service has to handle that there are comming more profiles where still old clients using the service. The interface had to be as static as possible.
Our solution is very simple. We only post a single text field as content of the post. But this includes the serialized state of an profile object in JSON.
Pseudo code:
public class Profile1 {
...
public String asJSON() {
JSONObject obj = new JSONObject();
obj.put("profileAtr1", profileAtr1);
...
return obj.toString()
}
}
formParams.put("profile", profile.asJSON());
client.post(formParams);
This way it's not automaticly deserialized but it's easy to do it by hand.
We do this with a generic Profile object which can be created out of JSON String in the constructor.
Pseudo code:
public GenericProfile {
public GenericProfile(String json) {
JSONObject obj = new JSONObject(json);
String profileName = obj.getString("profileName");
if (profileName.equals("Profile1") {
this = new Profile1(obj); // I know this is not working ;) My impl. is a litle bit more complecated as that. I think i use a static method in the generic profile to create an instance i need.
} ...
}
}
And then in your webservice only have this one form param to handle and to deserialize ;)
Pseudo code:
public ResponseEnvelope coolServiceFunction(#FormParam("profile") String profileData) {
GenericProfile profile = new GenericProfile(profileData);
if (profile instanceof Profile1) {
do what you want
}
}
Sorry for the pseudo code, but i had alleady shutdown my dev vm and have no access to any repository any more :(
I think the biggest benefits from this solution is:
1. It can transport anything you can pack in JSON. I transfer BASE64 encoded binary blocks and heavyly encrypted textdata this way.
2. The easiest tutorial example of your REST Framework of the POST Service will provide all you need to do this.
3. You can be sure that your interface will stay for a long period of time.
Hope that helps
So I have this GWT code that handles RPC requests maintain states(ready, waiting, error etc).
And I would like to check if the class change its states correctly after each call, set response variables etc.
Now how should I proceed to test that without making actual requests to the server(that could run into errors in the server it self).
I think I could mock the request callback class somehow but it is invisible to the test.
I'm lost, help!
Sample of the code below(I'll post the whole thing later in case anyone wants).
public class RPCHandler
{
public RPCHandler(String method,String[] argumentsName,
String[][] argumentsValues)
{
this.method = method;
this.argumentsName = argumentsName;
this.argumentsValues = argumentsValues;
}
/**
* Method that creates a RPC request using JSON in a POST
*
*/
public void rpcRequest(){
if(currentState == HandlerState.WAITING_RESPONSE)return;
currentState = HandlerState.WAITING_RESPONSE;
// Append watch list stock symbols to query URL.
url = URL.encode(url);
url += "action=";
url += method;
// Send request to server and catch any errors.
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
String requestData = parseToJSON(argumentsName, argumentsValues);
try{
Request request = builder.sendRequest(requestData, new RequestCallback()
{
public void onError(Request request, Throwable exception)
{
setRPCException(new Exception("Error while saving. Action="+method));
setCurrentState(HandlerState.ON_ERROR);
}
//Few other error, response received hander methods after this point.
}
}
It looks like you're trying to mock out the actual transport so you should build a mock of the RequestBuilder class. In JMockit, you could write:
public class MockRequestBuilder
{
public void $init( int method, String url)
{
/* check values and/or store for later */
}
public Request sendRequest( String data, RequestCallback callback )
{
/* check values and/or store for later */
}
}
You'll need to fill in the details of the what you want the mock to do. Also, you can isolate the callback testing if you moved the callback to a named class instance inside of your outer class:
public class MyGWTClass
{
protected static class RpcCallback extends RequestCallback
{
public void onError(...) { ... }
}
}
By moving the callback object into a class and using a factory method, you can create tests that only check the callback.