Upgrading from Jersey Client 1.x to Jersey Client 2.x - java

I am using jersey-client-1.9. sample code:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
Client client = Client.create();
webResource = client.resource("http://localhost:8047/storage/hive.json");
String input = //rest request
ClientResponse response = webResource.type("application/json").post(ClientResponse.class, input);
String queryRespose = response.getEntity(String.class);
As this project has changed from com.sun.jersey.api.client to org.glassfish.jersey.client. How to achieve this in jersey-client-2.8 ?
Edit:
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8047/query.json");
String input =//rest request
Response response = target.request().post(Entity.json(input));
String queryRespose = response.readEntity(String.class);
This worked...:)

With Jersey 2.x, you can build the Client with ClientBuilder
Client client = ClientBuilder.newClient();
In Jersey 2.x WebTarget is analogous to Jersey 1.x WebResource, and instead of calling client.resource() to get the WebResource, you call client.target() to get the WebTarget
WebTarget target = client.target(url);
Then you need to call request() on the WebTarget to get an Invocation.Builder, which will allow you to chain other calls
Invocation.Builder invocation = target.request();
To send an entity, we need to pass an Entity to one of the Invocation.Builder's request method. For instance
Response response = builder.post(Entity.json(input);
To read the response, use response.readEntity(String.class). So altogether, you can do
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url);
Response response = target.request().post(Entity.json(input));
String entity = response.readEntity(String.class);
See Also:
how to send json object from REST client using javax.ws.rs.client.WebTarget
Client API documentation
UPDATE
You may also need the following dependency for JSON/POJO support
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
Then register the JacksonFeature with the client. This is so input (if you want to use a POJO instead of String) can be serialized to JSON
client.register(JacksonFeature.class);

Related

Delete rest with parameters

I need to write this REST request in java using Httpdelete or any other library.
curl -X DELETE -d '{"ruleid":"1" }' http://192.168.1.1:8080/wm/acl/rules/json
I couldn't find a way to parse the Json data !
Thanks for your help !
Like others said, it is unusual that a DELETE request contains a body. But it is not strictly impossible as long as the server supports it.
There are many ways to build a REST Client in Java (see https://stackoverflow.com/a/5024571/1018443). A common way is to use Jersey 2.
In Jersey 2, the .delete() method does not contain a parameter for the body entity. But you can use .build to create a DELETE request with a body. Here is an example:
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
public class RestClient {
public static void main(String[] args) {
Model model = new Model();
ClientConfig config = new ClientConfig();
config.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://192.168.1.1:8080/");
String response = target
.path("wm/acl/rules/json")
.request(MediaType.APPLICATION_JSON)
.build("DELETE", Entity.entity(model, MediaType.APPLICATION_JSON))
.invoke(String.class);
System.out.println(response);
}
private static class Model {
public int ruleid = 1;
}
}
Note that you need to configure the client with Property ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION = true. Otherwise you get an Exception: Entity must be null for http method DELETE.
You will find many examples on how to build a Java REST client with Jersey. For example: https://howtodoinjava.com/jersey/jersey-restful-client-examples/
You have to use POST request instead of DELETE, because body of DELETE request is ignored.
From spec:
The DELETE method requests that the origin server delete the resource identified by the Request-URI
reference link

Jersey 2.x does not contain WebResource and resource class. What can I use instead?

I am trying to create a web API using Jersey. I am trying to run a method similar to this:
WebResource r = c.resource("http://localhost:8080/Jersey/rest/contacts");
However Jersey 2.x does not have a WebResource or Resource class. So what class can I use instead in order to have the uri http://localhost:8080/Jersey/rest/contacts as a parameter?
This will be ran in a ContactClient class
Have a look at the Client API from the Jersey documentation. With Jersey 2.x you instead want to use WebTarget. For example
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url);
Response response = target.request().get();
See the documentation I linked to for much more information and examples.
JAX-RS 2.0 Client API:
JAX-RS 2.0 introduces a new client API so that you can make http requests to your remote RESTful web services.
It is a 'fluent' request building API with really 3 main classes:
Client,
WebTarget, and
Response.
1. Making a simple client request
Jersey 1.x way:
Client client = Client.create();
WebResource webResource = client.resource(restURL).path("myresource/{param}");
String result = webResource.pathParam("param", "value").get(String.class);
JAX-RS 2.0 way:
Client client = ClientFactory.newClient();
WebTarget target = client.target(restURL).path("myresource/{param}");
String result = target.pathParam("param", "value").get(String.class);
2. Attaching entity to request
Jersey 1.x way:
Client client = Client.create();
WebResource webResource = client.resource(restURL);
ClientResponse response = webResource.post(ClientResponse.class, "payload");
JAX-RS 2.0 way:
Client client = ClientFactory.newClient();
WebTarget target = client.target(restURL);
Response response = target.request().post(Entity.text("payload"), Response.class);

Is it possible to call a Restful web service from an EJB2 client

I have been looking around for examples of how to call a Restful service written in Spring 3 from an EJB2 client. If i understand REST correctly, it should not matter what technology/language the service is written in so i should be able to call the service from an EJB2 client.
I couldn't find a simple example or reference to guide me on how to implement an EJB2 client that can call a restful service. Does this mean that is not possible to call a Restful service from an EJB2 client? If it is possible, could you please point me to a document or example that shows or describe how the two can interface/talk to each other.
Most of the references/documentation i come across are related to how to expose an EJB as a web service whereas i am interested in how to call a web service from an EJB2.
I am particularly interested in how i can send an XML document to the service. For example, is it possible to use a Jersey client and JAXB with EJB2 and how would i pass the unmarshalled XML over HTTP using EJB2?
Thanks in advance.
Below are a couple of programmatic options for accessing a RESTful service in Java.
Using JDK/JRE APIs
Below is an example of calling a RESTful service using the APIs in the JDK/JRE
String uri =
"http://localhost:8080/CustomerService/rest/customers/1";
URL url = new URL(uri);
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
JAXBContext jc = JAXBContext.newInstance(Customer.class);
InputStream xml = connection.getInputStream();
Customer customer =
(Customer) jc.createUnmarshaller().unmarshal(xml);
connection.disconnect();
Using Jersey APIs
Most JAX-RS implementations include APIs that make accessing RESTful services easier. A client API is being included in the JAX-RS 2 specification.
import java.util.List;
import javax.ws.rs.core.MediaType;
import org.example.Customer;
import com.sun.jersey.api.client.*;
public class JerseyClient {
public static void main(String[] args) {
Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/CustomerService/rest/customers");
// Get response as String
String string = resource.path("1")
.accept(MediaType.APPLICATION_XML)
.get(String.class);
System.out.println(string);
// Get response as Customer
Customer customer = resource.path("1")
.accept(MediaType.APPLICATION_XML)
.get(Customer.class);
System.out.println(customer.getLastName() + ", "+ customer.getFirstName());
// Get response as List<Customer>
List<Customer> customers = resource.path("findCustomersByCity/Any%20Town")
.accept(MediaType.APPLICATION_XML)
.get(new GenericType<List<Customer>>(){});
System.out.println(customers.size());
}
}
For More Information
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-55.html

Unable to override a response header using a Jersey client filter

I am trying to use the Jersey client API to consume a third-party REST service. I plan to use the automatic POJO deserialisation to go from JSON responses to Java objects.
Unfortunately, the third party service returns the responses using the content type "text/javascript". My Jersey client fails to understand that this should be considered as a JSON object and fails to deserialise the object.
I wrote a simple Jersey server application to verify that by changing the content type from "text/javascript" to "application/json" that the deserialisation works.
Armed with this information, I set about to use a Jersey client filter to modify the response headers. The code comes from a comment by the author of this question. In fact, the question appears to be exactly the same as mine - however the answerer mis-answered the question and shows how to modify the request headers (rather than the response headers). The original author was able to use the answer to create his solution, but, it seems his stated solution fails to work.
The filter code is:
client.addFilter(new ClientFilter() {
#Override public ClientResponse handle(ClientRequest cr)
throws ClientHandlerException {
ClientResponse response = getNext().handle(cr);
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
return response;
}
});
When executed however, an UnsupportedOperationException is raised:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.clear(Collections.java:1035)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:78)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:56)
at App$1.handle(App.java:49)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:680)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
at App.main(App.java:63)
The returned headers appear to be wrapped in an unmodifiable collection.
I then attempted to copy all of the headers to a new collection, but there is no way that I can see to set a map of headers back into the response.
Finally, I thought perhaps I can create a new ClientResponse containing my amended headers. However, the constructor for ClientResponse has this signature:
public ClientResponse(int status,
InBoundHeaders headers,
InputStream entity,
MessageBodyWorkers workers)
It is trivial to copy the status, headers and entity variables from the original. However, I can see no way of getting a reference to the workers field.
How can I use a Jersey client filter to modify the response header from "text/javascript" to "application/json" so that my POJO deserialisation will work?
In Jersey 2, register an implementation of a ClientResponseFilter with the ClientConfig in order to manipulate the HTTP headers of incoming responses.
For example, this seems to work well with Jersey 2.3.1 for manipulating HTTP header:
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.ClientRequestContext;
import org.glassfish.jersey.client.ClientConfig;
/* Ensure that there is an "application/xml" Content-Type header on
* successful responses without a content type header. */
#Provider
public static class EnsureXmlContentTypeFilter implements ClientResponseFilter {
#Override
public void filter(ClientRequestContext requestContext,
ClientResponseContext responseContext) {
if (200 == responseContext.getStatus() &&
null == responseContext.getHeaderString(HttpHeaders.CONTENT_TYPE)) {
responseContext.getHeaders().add(
HttpHeaders.CONTENT_TYPE, "application/xml"
);
}
}
}
private final ClientConfig config = new ClientConfig()
// Registering this filter adds a "Content-Type: application/xml"
// header to each response that lacks Content-Type headers.
.register(EnsureXmlContentTypeFilter.class)
;
private final Client client = ClientBuilder.newClient(config);
The Jersey documentation on Filters and Interceptors isn't perfect, but it does have some links to the javadocs for the relevant classes: https://jersey.java.net/documentation/latest/filters-and-interceptors.html
I am getting XML responses from a service which responds with XML content, but lacks a "Content-Type: application/xml" header. Probably a better approach would be to register MessageBodyReaders, but the above approach works while I'm playing around with that service's API.
I don't have an answer to your real question, but I think I see how you can get that workers instance if you want to try to create a new response in your filter.
The "workers" object that you need appears to be a singleton. If you can get hold of your com.sun.jersey.api.client.Client instance, you can retrieve the workers object. In my case, the Jersey client code is in a unit test which subclassed JerseyTest. JerseyTest defines a method "client()" which returns the Client object. I added the following test code (well not exactly but close):
MessageBodyWorkers workers = client().getMessageBodyWorkers();
Then I set a breakpoint in the constructor of ClientResponse (this is the original ClientResponse returned by Jersey. I have not attempted to clone it because I don't need to for my test). The workers passed to the constructor was the same instance. So, even though you can not get the workers object from the response object, you should be able to get it elsewhere.
Guido's answer provides the insight required to create a new ClientResponse object and return it instead. For reasons that I've not yet bothered to track down, creating a new InboundHeaders, adding all the existing headers to it, and then modifying the single header in question still fails with an UnsupportedOperationException. As such, to re-write the headers, we iterate over the original headers and build the correct set iteratively:
final Client client = Client.create(clientConfig);
client.addFilter(new ClientFilter()
{
#Override
public ClientResponse handle(ClientRequest cr) throws ClientHandlerException
{
final ClientResponse response = getNext().handle(cr);
final InBoundHeaders headers = new InBoundHeaders();
for (String header : response.getHeaders().keySet())
{
if (header.equals(HttpHeaders.CONTENT_TYPE))
{
headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}
else
{
headers.put(header, headers.get(header));
}
}
return new ClientResponse(response.getStatus(),
headers,
response.getEntityInputStream(),
client.getMessageBodyWorkers());
}
}
In Jersey 2, you should use a ClientResponseFilter. Then you can just call responseContext.getHeaders().putSingle(...).
Under Java 8 you can do it with a lambda:
client.register((ClientResponseFilter) (requestContext, responseContext) ->
responseContext.getHeaders().putSingle("Content-Type", "application/json"));
If you want to re-use an existing filter instance, just register it on the Client instead of on the ClientConfig.
Old way (Jersey-1.9):
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter(username, password));
New way (Jersey-2.3):
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import org.glassfish.jersey.client.filter.HttpBasicAuthFilter;
Client client = ClientBuilder.newClient();
client.register(new HttpBasicAuthFilter(username, password));
That's not the best solution, but it may help you to migrate.

Jersey: Print the actual request

How can I view the actual request that Jersey generates and sends to the server? I am having issues with a particular request and the fellow running the webserver asked to see the full request (with headers and the such).
If you're just using Jersey Client API, LoggingFilter (client filter) should help you:
Client client = Client.create();
client.addFilter(new LoggingFilter(System.out));
WebResource webResource = client.resource("http://localhost:9998/");
ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON)
.get(ClientResponse.class);
Otherwise, you can again log both request and response on server using other LoggingFilter (container filter).
Since Jersey 2.23, there's a LoggingFeature you could use. The following is a bit simplified example, please note that you can register the feature on WebTarget as well.
Logger logger = Logger.getLogger(getClass().getName());
Feature feature = new LoggingFeature(logger, Level.INFO, null, null);
Client client = ClientBuilder.newBuilder()
.register(feature)
.build();
Response response = client.target("https://www.google.com")
.queryParam("q", "Hello, World!")
.request().get();
JavaDoc of LoggingFeature says that the request "and/or" the response is logged lol. On my machine, both are logged.
#ivan.cikic's answer is for Jersey 1.x. Here's how you do it in Jersey 2.x:
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.json.JSONException;
import org.json.JSONObject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
...
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
client.register(new LoggingFilter());
This is irrelevant but I just have to complain: The new LoggingFilter is really annoying because it forces you to use Java Util Logging. It would be better if it gave me control over the logger. Seems like a step backwards in design.
All these answers are pretty close but they lack the setting to log the request and response body. At least with Jersey 2.30.1 this is how I accomplish logging the request and response including their respective bodies:
import javax.ws.rs.client.ClientBuilder;
import org.glassfish.jersey.logging.LoggingFeature;
import java.util.logging.Level;
import java.util.logging.Logger;
Logger logger = Logger.getLogger("LoggingFeature");
logger.setLevel(Level.ALL);
ClientBuilder.newClient()
.target("https://www.example.com")
.register(new LoggingFeature(
logger,
Level.ALL,
LoggingFeature.Verbosity.PAYLOAD_ANY,
8192))
.request()
.get();
Technically the Level.All and 8192 values could be null. I just provide them here to be concise.

Categories