Retrofit + OkHTTP with EBay's Inventory API - java

I'm trying to execute the following two requests using EBay's Inventory API:
POST: bulkUpdatePriceQuantity (create new listing)
PUT: createOrReplaceInventoryItem (update price/quantity of listing) using
I'm fairly new to Retrofit and OKHTTP and was wondering if someone could post a simple example of how to create a new listing and update price/quantity of an existing listing.
I've spent a couple of days reading about Retrofit and OKHTTP and it seems very confusing. Like I don't understand where/how to add the EBay authorization token and how to pass the data to EBay (such as the new price/quantity or the details of a new listing).
So far this is what I've come up with for Retrofit:
public interface RetrofitEBaySellAPIService {
#Headers("X-EBAY-C-PACKED-EXAMPLE: Authorization: Bearer <TOKEN_GOES_HERE>")
#POST("/bulk_update_price_quantity")
// https://api.ebay.com/sell/inventory/v1/bulk_update_price_quantity
Call<List<Task>> getTasks();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.ebay.com/sell/inventory/v1/")
.addConverterFactory(GsonConverterFactory.create()) // error: GsonConverterFactory cannot be resolved
.build();
RetrofitEBaySellAPIService service = retrofit.create(RetrofitEBaySellAPIService.class);
Response response = service.getClientList("").execute();
}
And this is what I've come up with for OKHTTP:
public class OKHTTPPostExample {
public OKHTTPPostExample()
{
}
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
public String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
String header = "Authorization: Bearer <TOKEN_GOES_HERE?>";
Headers headerbuild = Headers.of(header);
Request request = new Request.Builder()
.url(url)
.post(body)
.headers(headerbuild)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
public String revisePriceAndQuantity(String sku) {
return "{
'requests' : [
{
'sku' : 'SKU_STRING',
"shipToLocationAvailability" :
{
'quantity' : 'integer'
}";
}
}
However, in both cases I'm getting numerous errors. I've read about both technologies for hours (my head is spinning) but I do not understand it clearly.
If someone could post a simple example of how to do both of these operations I would greatly appreciate it.

Unfortunately I don't have developer account to check that it actually works but here is an example of bulkUpdatePriceQuantity
package example;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Body;
import retrofit2.http.HeaderMap;
import retrofit2.http.POST;
public class Runner {
//DTOs
public static class Offer {
public Integer availableQuantity;
public String offerId;
public Price price;
}
public static class ShipToLocationAvailability {
public Integer quantity;
}
public static class Price {
public String currency;
public String value;
}
public static class Request {
public List<Offer> offers = null;
public ShipToLocationAvailability shipToLocationAvailability;
public String sku;
}
public static class Response {
public String offerId;
public String sku;
public Integer statusCode;
}
public static class RequestBody{
public List<Request> requests;
}
public static class ResponseBody{
public List<Response> responses;
}
//api interface
public static interface RetrofitEBaySellAPIService {
#POST("/bulk_update_price_quantity")
Call<ResponseBody> bulkUpdatePriceQuantity(#HeaderMap Map<String, String> headers, #Body RequestBody object);
}
//entry point
public static void main(String[] args) throws IOException {
/**
* request should be initialized.
* you can do it by creating all necessary objects manually
* or by deserializing the object from json like this
* RequestBody request = new Gson().fromJson(jsonString, RequestBody.class);
*
* where jsonString is a string that contains json representation of your request body
*
*/
RequestBody request = null;
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.ebay.com/sell/inventory/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RetrofitEBaySellAPIService service = retrofit.create(RetrofitEBaySellAPIService.class);
Map<String, String> headers = new HashMap<>();
//token should hold a valid active token
String token = null;
//put all the headers you need in that map
headers.put("Authorization", "Bearer " + token);
ResponseBody response = service.bulkUpdatePriceQuantity(headers, request).execute().body();
}
}
You need to have converter-gson, gson and retrofit in your classpath
Here is a fragment from my pom.xml
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
Hope it helps

Related

Can I send an excel file and JSON body with a description of file in same REST API Response

I have an API which returns APPLICATION_OCTET_STREAM as Media Type in response. I need to enhance it to also send a JSON body with some details regarding the file, say counts of right and wrong records in the file. So basically I need two kinds of response in same API. Is this doable ?
It's possible, but you will need to use a Multipart response. Keep in mind though that some clients will not be able to handle this type of response. You'll normally see this data type using in uploading files, but is not very often used as a response data type.
That being said, below is a complete example using the Jersey Test Framework. In the resource, a file and some extra data are being sent in the response, with the use of Jersey's FormDataMultiPart
#Path("test")
public static class TestResource {
#GET
#Produces(MediaType.MULTIPART_FORM_DATA)
public Response get() throws Exception {
final MultiPart multiPart = new FormDataMultiPart()
.field("json-data", new Model("Test Value"), MediaType.APPLICATION_JSON_TYPE)
.bodyPart(new FileDataBodyPart("file-data", new File("test.txt")));
return Response.ok(multiPart).build();
}
}
To make the test succeed, you should have a file called test.txt with the content "Some Test Data in File" (without quotes) on the first line of that file. This multipart response has two parts, the json-data part, which uses a Model class to model the JSON, and the file-data part which has the contents of the file.
To make the Multipart work, we need to have the MultiPartFeature register on the server and the client (for client side deserialization) and we need to have the multipart dependency in our project.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey2.version}</version>
</dependency>
On the client, to get the multipart out of the response, we should read the entity as FormDataMultiPart, then we can get individual parts by name and extract them by their data type.
Response res = target("test").request().get();
FormDataMultiPart multiPart = res.readEntity(FormDataMultiPart.class);
FormDataBodyPart jsonPart = multiPart.getField("json-data");
FormDataBodyPart filePart = multiPart.getField("file-data");
Model jsonData = jsonPart.getValueAs(Model.class);
InputStream file = filePart.getValueAs(InputStream.class);
Below is the complete test.
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import static org.assertj.core.api.Assertions.assertThat;
public class MultipartResponseTest extends JerseyTest {
public static class Model {
private String value;
public Model() {}
public Model(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
}
#Path("test")
public static class TestResource {
#GET
#Produces(MediaType.MULTIPART_FORM_DATA)
public Response get() throws Exception {
final MultiPart multiPart = new FormDataMultiPart()
.field("json-data", new Model("Test Value"), MediaType.APPLICATION_JSON_TYPE)
.bodyPart(new FileDataBodyPart("file-data", new File("test.txt")));
return Response.ok(multiPart).build();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.register(MultiPartFeature.class);
}
#Override
public void configureClient(ClientConfig config) {
config.register(MultiPartFeature.class);
}
#Test
public void testIt() throws Exception {
final Response res = target("test")
.request().get();
FormDataMultiPart multiPart = res.readEntity(FormDataMultiPart.class);
FormDataBodyPart jsonPart = multiPart.getField("json-data");
FormDataBodyPart filePart = multiPart.getField("file-data");
Model jsonData = jsonPart.getValueAs(Model.class);
InputStream file = filePart.getValueAs(InputStream.class);
BufferedReader fileReader = new BufferedReader(new InputStreamReader(file));
String fileData = fileReader.readLine();
file.close();
fileReader.close();
System.out.println(jsonData.getValue());
System.out.println(fileData);
assertThat(jsonData.getValue()).isEqualTo("Test Value");
assertThat(fileData).isEqualTo("Some Test Data in File");
}
}
To use the test framework, you should add the following dependency
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>${jersey2.version}</version>
</dependency>

Getting response out of AsyncHttpClient

Im using this lib: http://loopj.com/android-async-http/ to make all my requests && The biggest thing currently making my code messy is that I havent been able to successfully get my Async Http Request Responses out of onSuccess() so that the data is available to the rest of the class freely. Up till this point I have been parsing all my request responses inside the curlys of my responsehandlers onSuccess(){}
I came across this post on Stack:How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
but have yet to get it to work with this lib.
import com.loopj.android.http.*;
public class TwitterRestClient {
private static final String BASE_URL = "http://api.twitter.com/1/";
private static AsyncHttpClient client = new AsyncHttpClient();
public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.get(getAbsoluteUrl(url), params, responseHandler);
}
public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.post(getAbsoluteUrl(url), params, responseHandler);
}
private static String getAbsoluteUrl(String relativeUrl) {
return BASE_URL + relativeUrl;
}
}
import org.json.*;
import com.loopj.android.http.*;
class TwitterRestClientUsage {
public void getPublicTimeline() throws JSONException {
TwitterRestClient.get("statuses/public_timeline.json", null, new JsonHttpResponseHandler() {
#Override
public void onSuccess(JSONArray timeline) {
// Pull out the first event on the public timeline
JSONObject firstEvent = timeline.get(0);
String tweetText = firstEvent.getString("text");
// Do something with the response
System.out.println(tweetText);
}
});
}
}
I do not know if this helps, but I have had to face the same problem (I am quite the novice at java and android). Used an interface. http://www.gdgankara.org/2013/03/25/android-asynchronous-http-client-a-callback-based-http-client-library-for-android-and-android-smart-image-view/
Try this:
public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.get(getAbsoluteUrl(url), params, responseHandler);
String response = new String(responseHandler);
Log.i("RESPONSE", response);
}

How to create a oAuth request using java?

I need to make a connection with Viagogo website using oAuth. Referring to their documentation I need to create a request similar to the following one
Using the example in step 1A, this means you may generate a signature base string that looks like the following:
GET&http%3A%2F%2Fapi.viagogo.net%2FPublic%2FSimpleOAuthAccessRequest&oauth_consumer_key%3Dtestkey%26oauth_nonce%3Dmyn0nc3%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1292404912%26oauth_version%3D1.0%26scope%3DAPI.Public
I am using the following code but when I comment lines 1,2 it return unauthorized error, and when I use them it shows oauthService.signRequest returns void.
TradeKingAPI.java
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class TradeKingAPI extends DefaultApi10a {
#Override
public String getRequestTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAccessTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
}
main.java
import org.scribe.builder.ServiceBuilder;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.oauth.OAuthService;
import api.TradeKingAPI;
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.OAuthConstants;
import org.scribe.oauth.OAuthService;
........
OAuthService oauthService = new ServiceBuilder()
.provider(TradeKingAPI.class)
.apiKey("My consumer key")
.apiSecret("My secret")
.scope("API.Public")
.build();
Long seconds = (System.currentTimeMillis() / 1000);
System.out.println(">>>" + seconds);
String stSeconds = seconds.toString();
OAuthRequest request = new OAuthRequest(Verb.GET, "http://api.viagogo.net/Public
/SimpleOAuthAccessRequest");
request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "My consumer key");
request.addOAuthParameter(OAuthConstants.NONCE, "myn0nc3");
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, "HMAC-SHA1");
request.addOAuthParameter(OAuthConstants.TIMESTAMP, seconds.toString());
request.addOAuthParameter(OAuthConstants.VERSION, "1.0");
request.addOAuthParameter("scope", "API.Public");
1 String signature = oauthService.signRequest(OAuthConstants.EMPTY_TOKEN, request);
2 request.addOAuthParameter(OAuthConstants.SIGNATURE,signature);
Response response = request.send();
System.err.println(">>" + response.isSuccessful());
System.err.println(">>" + response.getMessage());
System.err.println(">>" + response.getBody());
From what I understand from Viagogo public API access documentation, the token you get in the step 1, is the equivalent to a request token in a complete OAuth 1.0a "dance".
So, you should be able to use scribe-java internal classes to get this token without doing it by hand. The only difference is that in scribe, this request sends also a callback url to the OAuth server for the next step of OAuth "dance".
As I can't get a consumer account I can only make assumption here. So let's have 2 scenarios :
Scenario 1 : Viagogo server tolerate extra parameter (i.e. call back URL)
so you can go with this code
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class TradeKingAPI extends DefaultApi10a {
#Override
public Verb getRequestTokenVerb()
{
return Verb.GET;
}
#Override
public String getRequestTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAccessTokenEndpoint() {
return "none";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "none";
}
}
Then your calling code will be :
OAuthService service = new ServiceBuilder()
.provider(TradeKingAPI.class)
.signatureType(QueryString)
.apiKey("My consumer key")
.apiSecret("My secret")
.scope("API.Public")
.build();
Token requestToken = service.getRequestToken();
//make your API calls
OAuthRequest request = new OAuthRequest(Verb.GET,
"http://api.viagogo.net/Public/Event/235");
service.signRequest(requestToken, request);
Response response = request.send();
System.out.println(response.getBody());
But as I said, if Viagogo security is a bit strict and it refuses the useless param oauth_callback, you'll need to switch to scenario 2
Scenario 2 : Build your own OAuthService
In this scenario you have to create a new OAuthService to avoid dealing with OAuthCallback parameter.
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.*;
import org.scribe.oauth.OAuth10aServiceImpl;
import java.util.Map;
public class OAuth10aServiceForViagogo extends OAuth10aServiceImpl {
private OAuthConfig config;
private DefaultApi10a api;
public OAuth10aServiceForViagogo(DefaultApi10a api, OAuthConfig config) {
super(api, config);
this.api = api;
this.config = config;
}
private void addOAuthParams(OAuthRequest request, Token token) {
request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds());
request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce());
request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey());
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod());
request.addOAuthParameter(OAuthConstants.VERSION, getVersion());
request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope());
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token));
}
private String getSignature(OAuthRequest request, Token token) {
String baseString = api.getBaseStringExtractor().extract(request);
String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret());
return signature;
}
private void appendSignature(OAuthRequest request) {
for (Map.Entry<String, String> entry : request.getOauthParameters().entrySet()) {
request.addQuerystringParameter(entry.getKey(), entry.getValue());
}
}
#Override
public Token getRequestToken(RequestTuner tuner) {
OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint());
addOAuthParams(request, OAuthConstants.EMPTY_TOKEN);
appendSignature(request);
Response response = request.send(tuner);
String body = response.getBody();
return api.getRequestTokenExtractor().extract(body);
}
}
TrakingApi class will be slightly different to create the an OAuth10aServiceForViagogo when calling createService :
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class TradeKingAPI extends DefaultApi10a {
#override
public OAuthService createService(OAuthConfig config)
{
return new OAuth10aServiceForViagogo(this, config);
}
#Override
public Verb getRequestTokenVerb()
{
return Verb.GET;
}
#Override
public String getRequestTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAccessTokenEndpoint() {
return "none";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "none";
}
}
Then your calling code will be the same :
OAuthService service = new ServiceBuilder()
.provider(TradeKingAPI.class)
.signatureType(QueryString)
.apiKey("My consumer key")
.apiSecret("My secret")
.scope("API.Public")
.build();
Token requestToken = service.getRequestToken();
//make your API calls
OAuthRequest request = new OAuthRequest(Verb.GET,
"http://api.viagogo.net/Public/Event/235");
service.signRequest(requestToken, request);
Response response = request.send();
System.out.println(response.getBody());
I didn't test all this code because I can't access consumer and secret key, but it should be close to what you need.
I'm assuming you're trying to get the access token (e.g you're calling SimpleOAuthAccessRequest). Scribe's OauthService has methods to handle this.
BUT ... if you're going to do it manually, here is what's wrong with your code - at least with what you've listed here. I'm assuming you've configured scribe correctly.
don't pass the consumer secret with your request, that is only for signing the request
you should use addOauthParameter vs addQueryStringParameter
you should use the Scribe constants
you need to sign the request (again, Scribe's OauthService has help method for signing request)
Here's your updated snippet of code.
UPDATE:
Have Scribe provide all the Oauth parameters for you
OAuthRequest request = new OAuthRequest(Verb.GET, ...
//since you're just passing Oauth parameters and nothing else,
//you can use signRequest will create Oauth Parameters for you
service.signRequest(OAuthConstants.EMPTY_TOKEN, request)
Response response = request.send()

Jersey client: How to add a list as query parameter

I'm creating a Jersey client for a GET service that has a List as query parameter. According to the documentation, it's possible to have a List as a query parameter (this information is also at #QueryParam javadoc), check it out:
In general the Java type of the method parameter may:
Be a primitive type;
Have a constructor that accepts a single String argument;
Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String) and java.util.UUID.fromString(String)); or
Be List, Set or SortedSet, where T satisfies 2 or 3 above. The resulting collection is read-only.
Sometimes parameters may contain more than one value for the same name. If this is the case then types in 4) may be used to obtain all values.
However, I can't figure out how to add a List query parameter using Jersey client.
I understand alternative solutions are:
Use POST instead of GET;
Transform the List into a JSON string and pass it to the service.
The first one is not good, because the proper HTTP verb for the service is GET. It is a data retrieval operation.
The second will be my option if you can't help me out. :)
I'm also developing the service, so I may change it as needed.
Thanks!
Update
Client code (using json)
Client client = Client.create();
WebResource webResource = client.resource(uri.toString());
SearchWrapper sw = new SearchWrapper(termo, pagina, ordenacao, hits, SEARCH_VIEW, navegadores);
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
params.add("user", user.toUpperCase());
params.add("searchWrapperAsJSON", (new Gson()).toJson(sw));
ClientResponse clientResponse = webResource .path("/listar")
.queryParams(params)
.header(HttpHeaders.AUTHORIZATION, AuthenticationHelper.getBasicAuthHeader())
.get(ClientResponse.class);
SearchResultWrapper busca = clientResponse.getEntity(new GenericType<SearchResultWrapper>() {});
#GET does support List of Strings
Setup:
Java : 1.7
Jersey version : 1.9
Resource
#Path("/v1/test")
Subresource:
// receive List of Strings
#GET
#Path("/receiveListOfStrings")
public Response receiveListOfStrings(#QueryParam("list") final List<String> list){
log.info("receieved list of size="+list.size());
return Response.ok().build();
}
Jersey testcase
#Test
public void testReceiveListOfStrings() throws Exception {
WebResource webResource = resource();
ClientResponse responseMsg = webResource.path("/v1/test/receiveListOfStrings")
.queryParam("list", "one")
.queryParam("list", "two")
.queryParam("list", "three")
.get(ClientResponse.class);
Assert.assertEquals(200, responseMsg.getStatus());
}
If you are sending anything other than simple strings I would recommend using a POST with an appropriate request body, or passing the entire list as an appropriately encoded JSON string. However, with simple strings you just need to append each value to the request URL appropriately and Jersey will deserialize it for you. So given the following example endpoint:
#Path("/service/echo") public class MyServiceImpl {
public MyServiceImpl() {
super();
}
#GET
#Path("/withlist")
#Produces(MediaType.TEXT_PLAIN)
public Response echoInputList(#QueryParam("list") final List<String> inputList) {
return Response.ok(inputList).build();
}
}
Your client would send a request corresponding to:
GET http://example.com/services/echo?list=Hello&list=Stay&list=Goodbye
Which would result in inputList being deserialized to contain the values 'Hello', 'Stay' and 'Goodbye'
i agree with you about alternative solutions which you mentioned above
1. Use POST instead of GET;
2. Transform the List into a JSON string and pass it to the service.
and its true that you can't add List to MultiValuedMap because of its impl class MultivaluedMapImpl have capability to accept String Key and String Value. which is shown in following figure
still you want to do that things than try following code.
Controller Class
package net.yogesh.test;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import com.google.gson.Gson;
#Path("test")
public class TestController {
#Path("testMethod")
#GET
#Produces("application/text")
public String save(
#QueryParam("list") List<String> list) {
return new Gson().toJson(list) ;
}
}
Client Class
package net.yogesh.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.ws.rs.core.MultivaluedMap;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.core.util.MultivaluedMapImpl;
public class Client {
public static void main(String[] args) {
String op = doGet("http://localhost:8080/JerseyTest/rest/test/testMethod");
System.out.println(op);
}
private static String doGet(String url){
List<String> list = new ArrayList<String>();
list = Arrays.asList(new String[]{"string1,string2,string3"});
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
String lst = (list.toString()).substring(1, list.toString().length()-1);
params.add("list", lst);
ClientConfig config = new DefaultClientConfig();
com.sun.jersey.api.client.Client client = com.sun.jersey.api.client.Client.create(config);
WebResource resource = client.resource(url);
ClientResponse response = resource.queryParams(params).type("application/x-www-form-urlencoded").get(ClientResponse.class);
String en = response.getEntity(String.class);
return en;
}
}
hope this'll help you.
One could use the queryParam method, passing it parameter name and an array of values:
public WebTarget queryParam(String name, Object... values);
Example (jersey-client 2.23.2):
WebTarget target = ClientBuilder.newClient().target(URI.create("http://localhost"));
target.path("path")
.queryParam("param_name", Arrays.asList("paramVal1", "paramVal2").toArray())
.request().get();
This will issue request to following URL:
http://localhost/path?param_name=paramVal1&param_name=paramVal2
GET Request with JSON Query Param
package com.rest.jersey.jerseyclient;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
public class JerseyClientGET {
public static void main(String[] args) {
try {
String BASE_URI="http://vaquarkhan.net:8080/khanWeb";
Client client = Client.create();
WebResource webResource = client.resource(BASE_URI);
ClientResponse response = webResource.accept("application/json").get(ClientResponse.class);
/*if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}
*/
String output = webResource.path("/msg/sms").queryParam("search","{\"name\":\"vaquar\",\"surname\":\"khan\",\"ext\":\"2020\",\"age\":\"34\""}").get(String.class);
//String output = response.getEntity(String.class);
System.out.println("Output from Server .... \n");
System.out.println(output);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Post Request :
package com.rest.jersey.jerseyclient;
import com.rest.jersey.dto.KhanDTOInput;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
public class JerseyClientPOST {
public static void main(String[] args) {
try {
KhanDTOInput khanDTOInput = new KhanDTOInput("vaquar", "khan", "20", "E", null, "2222", "8308511500");
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put( JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
// final HTTPBasicAuthFilter authFilter = new HTTPBasicAuthFilter(username, password);
// client.addFilter(authFilter);
// client.addFilter(new LoggingFilter());
//
WebResource webResource = client
.resource("http://vaquarkhan.net:12221/khanWeb/messages/sms/api/v1/userapi");
ClientResponse response = webResource.accept("application/json")
.type("application/json").put(ClientResponse.class, khanDTOInput);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code :" + response.getStatus());
}
String output = response.getEntity(String.class);
System.out.println("Server response .... \n");
System.out.println(output);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Passing an object to a REST Web Service using Jersey

I have a simple WS that is a #PUT and takes in an object
#Path("test")
public class Test {
#PUT
#Path("{nid}"}
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(#PathParam("nid") WolRequest nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getId());
return response;
}
and my client side code is...
WebResource wr = client.resource(myurl);
WolResponse resp = wr.accept("application/xml").put(WolResponse.class, wolRequest);
I am trying to pass an instance of WolRequest into the #PUT Webservice. I am constantly getting 405 errors trying to do this..
How can I pass an object from the client to the server via Jersey ? Do I use a query param or the request ?
Both my POJOs (WolRequest and WolResponse) have the XMlRootElement tag defined so i can produce and consume xml..
I think the usage of the #PathParam is not correct here. A #PathParam is can basically be a String (see its javadoc for more info).
You can
use the #PathParam as a String parameter or
don't define WolRequest as a #PathParam.
The first approach:
#Path("test")
public class Test {
#PUT
#Path("{nid}")
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(#PathParam("nid") String nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid);
return response;
}
This will accept urls like: "text/12", 12 will then be the String nid. It doesn't look like this will help what you are trying to do.
The second approach:
#Path("test")
public class Test {
#PUT
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(WolRequest nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getId());
return response;
}
Your client code can be like you specified, only the url for PUT is: "test". Perhaps you need a combination of both one #PathParam for your id and one "normal" parameter to get your request data.
Check this link https://www.vogella.com/tutorials/REST/article.html
As per the code sample of method putTodo of class TodoResource ,
your code should be like this.
#Path("test")
public class Test{
#PUT
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(JAXBElement<WolRequest> nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getValue().getId());
return response;
}
}
Hope this will solve your problem.
You can try something like this
#POST
#Path("/post")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response callWol(WolRequest nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getValue().getId());
return Response.status(Status.OK).entity(response).build();
}
You can try #PUT instead of #Post as well. Hope this helps
I had the same problem I solved in 3 Steps with Jackson in Netbeans/Glashfish btw.
1)Requirements :
some of the Jars I used :
commons-codec-1.10.jar
commons-logging-1.2.jar
log4j-1.2.17.jar
httpcore-4.4.4.jar
jackson-jaxrs-json-provider-2.6.4.jar
avalon-logkit-2.2.1.jar
javax.servlet-api-4.0.0-b01.jar
httpclient-4.5.1.jar
jackson-jaxrs-json-provider-2.6.4.jar
jackson-databind-2.7.0-rc1.jar
jackson-annotations-2.7.0-rc1.jar
jackson-core-2.7.0-rc1.jar
If I missed any of the jar above , you can download from Maven here http://mvnrepository.com/artifact/com.fasterxml.jackson.core
2)Java Class where you send your Post.
First ,Convert with Jackson the Entity User to Json and then send it to your Rest Class.
import com.fasterxml.jackson.databind.ObjectMapper;
import ht.gouv.mtptc.siiv.model.seguridad.Usuario;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.simple.JSONObject;
public class PostRest {
public static void main(String args[]) throws UnsupportedEncodingException, IOException {
// 1. create HttpClient
DefaultHttpClient httpclient = new DefaultHttpClient();
// 2. make POST request to the given URL
HttpPost httpPost
= new HttpPost("http://localhost:8083/i360/rest/seguridad/obtenerEntidad");
String json = "";
Usuario u = new Usuario();
u.setId(99L);
// 3. build jsonObject
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", u.getId());
// 4. convert JSONObject to JSON to String
//json = jsonObject.toString();
// ** Alternative way to convert Person object to JSON string usin Jackson Lib
//ObjectMapper mapper = new ObjectMapper();
//json = mapper.writeValueAsString(person);
ObjectMapper mapper = new ObjectMapper();
json = mapper.writeValueAsString(u);
// 5. set json to StringEntity
StringEntity se = new StringEntity(json,"UTF-8");
// 6. set httpPost Entity
httpPost.setEntity(se);
// 7. Set some headers to inform server about the type of the content
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
// 8. Execute POST request to the given URL
HttpResponse httpResponse = httpclient.execute(httpPost);
// 9. receive response as inputStream
//inputStream = httpResponse.getEntity().getContent();
}
}
3)Java Class Rest where you want to receive the Entity JPA/Hibernate .
Here with your MediaType.APPLICATION_JSON you recieve the Entity in this way :
""id":99,"usuarioPadre":null,"nickname":null,"clave":null,"nombre":null,"apellidos":null,"isLoginWeb":null,"isLoginMovil":null,"estado":null,"correoElectronico":null,"imagePerfil":null,"perfil":null,"urlCambioClave":null,"telefono":null,"celular":null,"isFree":null,"proyectoUsuarioList":null,"cuentaActiva":null,"keyUser":null,"isCambiaPassword":null,"videoList":null,"idSocial":null,"tipoSocial":null,"idPlanActivo":null,"cantidadMbContratado":null,"cantidadMbConsumido":null,"cuotaMb":null,"fechaInicio":null,"fechaFin":null}"
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.apache.log4j.Logger;
#Path("/seguridad")
public class SeguridadRest implements Serializable {
#POST
#Path("obtenerEntidad")
#Consumes(MediaType.APPLICATION_JSON)
public JSONArray obtenerEntidad(Usuario u) {
JSONArray array = new JSONArray();
LOG.fatal(">>>Finally this is my entity(JPA/Hibernate) which
will print the ID 99 as showed above :" + u.toString());
return array;//this is empty
}
..
Some tips : If you have problem with running the web after using this code may be because of the #Consumes in XML ... you must set it as #Consumes(MediaType.APPLICATION_JSON)
Try this it will work
Server Side:
#PUT
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addRecord(CustomClass mCustomClass)
{
///
///
///
return "Added successfully : "+CustomClass.getName();
}// addRecord
Client Side:
public static void main(String[] args)
{
///
///
///
CustomClass mCustomClass = new CustomClass();
Client client = ClientBuilder.newClient();
String strResult = client.target(REST_SERVICE_URL).request(MediaType.APPLICATION_XML).put(Entity.xml(mCustomClass), String.class);
}

Categories