How to close a proxied RESTEasy WebTarget - java

Given the following RESTEasy snippet
UriBuilder FULL_PATH = UriBuilder.fromPath("http://127.0.0.1:8080/movies");
ResteasyClient client = (ResteasyClient)ClientBuilder.newClient();
ResteasyWebTarget target = client.target(FULL_PATH);
MoviesResource proxy = target.proxy(MoviesResource.class);
Movie movie = proxy.movieById(someId);
I could/can/should close WebTarget or Client to free resources I allocated*.
However, what do I do if the creation of the proxy object and everything it depends on is handed off to a separate class like so:
public class Foo {
private UriBuilder FULL_PATH = UriBuilder.fromPath("http://127.0.0.1:8080/movies");
public MoviesResource getMoviesApi() {
ResteasyClient client = (ResteasyClient)ClientBuilder.newClient();
ResteasyWebTarget target = client.target(FULL_PATH);
return target.proxy(MoviesResource.class);
}
}
...
MoviesResource proxy = myFoo.getMoviesApi();
Movie movie = proxy.movieById(someId);
// A) how to clean up here?
How do I close the WebTarget or Client or their underlying resources? I can't get access to either of them through the proxied instance.
If I do not close them I get this dreaded warning
RESTEASY004687: Closing a class org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine instance for you. Please close clients yourself.
Hence, RESTEasy does clean up for me but that's of course not what I should rely on.
*there are discussions in a number of places eg. Reusing JAX RS Client in multi-threaded environment (with resteasy) and Is JAX-RS Client Thread Safe about what is reusable and what not

Related

Best practice when using rest clients in a servlet container

I am working on a project where we have a big portal running in a tomcat container. Every time the user logs into the portal, some information is retrieved by invoking a rest client. The code looks something like this:
public class RestClient {
private static final String REST_URI = "http://url.tld/to/login/api";
private Client client;
public Client getClient() {
if (client == null) {
client = ClientBuilder.newClient();
}
return client;
}
public LoginData getLoginData(int userId) {
return client
.target(REST_URI)
.path(String.valueOf(userId))
.request(MediaType.APPLICATION_JSON)
.get(LoginData.class);
}
}
I am using Apache CXF as my jax-rs client implementation. As far as I know the client object should be thread safe, so that shouldn't be a problem. But imagine 10000 users are opening the website simultaneously and for each request the above code is executed. Do I get a performance problem? Do I need more than just one client object, maybe a client pool?

Quarkus: request to URL given as string

I want to make an HTTP request with Quarkus. However, the target URL is not known at compilation time, it will be composed from different parts at runtime.
Quarkus provides a way to build static REST clients like this:
#Path("/v2")
#RegisterRestClient
public interface CountriesService {
#GET
#Path("/name/{name}")
#Produces("application/json")
Set<Country> getByName(#PathParam String name);
}
However, I am loking for something like the Python requests package:
url = 'https://stackoverflow.com/questions/ask'
response = requests.get(url)
I am building the application in Kotlin, so all Java and Kotlin libraries should work.
What should I use?
With the MP REST Client defined in an interface, you can use the programmatic client creation API:
CountriesService remoteApi = RestClientBuilder.newBuilder()
.baseUri("created at runtime url")
.build(CountriesService.class);
remoteApi.getByName("");

Set up proxy data to Spring SOAP web service template

I'm working on an application where I have to consume a SOAP webservice and convert it to Rest based webservices. I followed the tutorial from Spring team and was capable to generate pojos, but when I try to make the call using the webServiceTemplate I have an error that the host is not recognized which is basically because I'm behind a proxy in our company.
The technology stack I'm using is Spring boot with web module and spring-ws-core, and I would like to know how to set up my proxy data in the webServiceTemplate.
Thanks
Try to setup the template according this answer.
Afterwards you should be able to set it within your class extending WebServiceGatewaySupport using
setWebServiceTemplate(WebServiceTemplate webServiceTemplate)
After a lot of research, I came up with a programmatic solution. Once you defined your SOAP client that will extend WebServiceGatewaySupport class, I created a configuration class (annotated with #configuration that will declare a bean of my SOAP client. In this method, I used the following code to setup my proxy information and thus I was able to consume my web service:
#Bean
public CommerceSoapClient commerceSoapClient(Jaxb2Marshaller marshaller) {
CommerceSoapClient commerceService = new CommerceSoapClient();
//Setup proxy
HttpClientBuilder builder = HttpClientBuilder.create();
builder.addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor());
HttpHost proxy = new HttpHost("127.0.0.1", 8080);
builder.setProxy(proxy);
CloseableHttpClient httpClient = builder.build();
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender(httpClient);
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMessageSender(messageSender);
webServiceTemplate.setDefaultUri("http://your webservice address path");
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.setMarshaller(marshaller);
commerceService.setDefaultUri("https://your webservice address path");
commerceService.setWebServiceTemplate(webServiceTemplate);
return commerceService;
}

Java Wiremock - mock external server during testing

Suppose the application is dependent on a REST service on a external server, http://otherserver.com. For testing, I would like to simulate the external rest call (via Wiremock) within a JUnit environment. Starting a seperate server consumes time and is not easy. Working with WiremockRule looks the right direction. Creating simulation controllers is not an elegant way as Wiremock is available.
E.g. get( "http://otherserver.com/service3/");
PS: of course I know that I can simulate a REST call via Mockito.
Simulating localhost with Wiremock is easy. How can I use that code to simulate other servers and services? I copied parts from the popular Baeldung examples.
public class WireMockDemo {
#Rule
public WireMockRule wireMockRule = new WireMockRule();
#Test
public void wireMockTestJunitOtherServer() {
try {
// **this does not work...**
configureFor("otherserver.com", 8080);
stubFor(get(urlPathMatching("/service2/.*"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("\"testing-library\": \"WireMock\"")));
// Test via simple client
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://otherserver:8080/service2/test");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);
System.out.println( "Response = " + stringResponse);
// Test via JUnit
verify(getRequestedFor(urlEqualTo("/service2/wiremock")));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("\"testing-library\": \"WireMock\"", stringResponse);
} catch( Exception e) {
e.printStackTrace();
}
}
// Support methods
private String convertHttpResponseToString(HttpResponse httpResponse) throws IOException {
InputStream inputStream = httpResponse.getEntity().getContent();
return convertInputStreamToString(inputStream);
}
private String convertInputStreamToString(InputStream inputStream) {
Scanner scanner = new Scanner(inputStream, "UTF-8");
String string = scanner.useDelimiter("\\Z").next();
scanner.close();
return string;
}
}
Your application code should not have the http://otherserver.com hardcoded, it should be configurable. When running normally it should point to http://otherserver.com, when running in test mode it should be pointed to http://localhost:<port> where <port> is where you have started your Wiremock server (preferably dynamic to avoid port clashes)
TL; DR:
No, you cannot.
What WireMock does, is to establish a Jetty server simulating a remote server you need to send request to. However, it does not change your hosts file or DNS mapping and automatically "redirect" your real request for remote server to localhost. In tests you still need to send request to localhost.
What you can do, if you are using Spring Boot, is to create two application.yml file(or another properties file) in main and test package, with same structure of keys, but the value in src/main/resources/application.yml is the real URL you request(like http://example.com/api), and that in src/test/resources/application.yml you put localhost/api.
By the way, to clarify, MockMvc is not for simulation of external 3rd party server request that your application depends on, but requests sent to the endpoints of your application. In MockMvc tests, your application is who receives the request, but in WireMock tests, your applications sends request.
Some working example:
// configure static, class-level rule for all tests, like #BeforeClass.
// this launches a Jetty server with configurations
#ClassRule
public static WireMockClassRule classRule = new WireMockClassRule(options().
port(80).httpsPort(443));
// Divide #ClassRule and #Rule,
// to bypass JUnit limitation of "#Rule cannot be static"
#Rule
public WireMockClassRule rule = classRule;
#Test
public void shouldReturnGivenJson() throws Exception {
// stubFor() also works; givenThat() is more TDD-ish
givenThat(post(urlEqualTo("/service2/test")) // <----- note here: without host name
.willReturn(WireMock.aResponse()
.withStatus(HttpStatus.OK.value())
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
.withBody("{\"status\":\"up\"}")));
// .... your connection here
I suggest to begin with urlEqualTo(), without messing around with regex. Then you progress to urlMatching().
Also, use org.apache.http.util.EntityUtils to get content from the response. This is the official, built-in way to process the response. And, use a ResponseHandler because it will consume() the response without manually cleaning the resources.
Check HttpClient documentation for more details.

Interceptor for {}WebClient has thrown exception, unwinding now Could not send Message

I am trying to access sample Rest method using Webtarget code in Websphere Liberty profile deployed as war and getting following exception.
[WARNING ] Interceptor for {https://www.google.com}WebClient has thrown exception, unwinding now
Could not send Message.
Its working when directly run with java main method.
#GET
#Produces("text/plain")
#Path("/hello")
public Response healthCheck() {
ClientConfig configuration = new ClientConfig();
configuration = configuration.property(ClientProperties.CONNECT_TIMEOUT, 30000);
configuration = configuration.property(ClientProperties.READ_TIMEOUT, 30000);
configuration = configuration.property(ClientProperties.PROXY_URI, "http://xxx.xxx.com:8080");
configuration.connectorProvider(new ApacheConnectorProvider());
Client client = ClientBuilder.newClient(configuration);
WebTarget target = client.target(
"https://www.google.com");
String content = target.request().get(String.class);
System.out.println(content);
}
Any help is appreciated? Its simple task but taking lot of time.
The ClientConfig and ClientProperties types are specific to Jersey. While you might have them in your application, they will almost certain conflict with WebSphere's JAX-RS implementation based on CXF. If you post the full logs, I may be able to confirm that.
Try using the JAX-RS spec API types instead of the Jersey types - and use the IBM properties (unfortunately, these properties are not portable) like this:
#GET
#Produces("text/plain")
#Path("/hello")
public Response healthCheck() {
Client client = ClientBuilder.newBuilder()
.property("com.ibm.ws.jaxrs.client.connection.timeout", 30000)
.property("com.ibm.ws.jaxrs.client.receive.timeout", 30000)
.property("com.ibm.ws.jaxrs.client.proxy.host", "xxx.xxx.com")
.property("com.ibm.ws.jaxrs.client.proxy.port", "8080")
.build();
WebTarget target = client.target(
"https://www.google.com");
String content = target.request().get(String.class);
System.out.println(content);
return Response.ok(content).build();
}
Hope this helps, Andy

Categories

Resources