Best practice when using rest clients in a servlet container - java

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?

Related

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.

java web services - request, acknowledgment and response

I have to develop a Java web service that get a request and sends immediately an acknowledgment (synchronous), so that far, it is simple.
Next, the web service has to do multiple checks on the request, then send a response according to that (synchronous too, because i don't have a callback endpoint from the client).
The problem is that i can send the ackowledgment, and i launch the multiple checks in another thread, but when the checks are done, the client already recieved his response, and i can't send another one.
Here's what i did for now:
#WebService
public class Configuration {
#Resource WebServiceContext context;
#WebMethod
public ReqAckType configure(#XmlElement(required = true) #WebParam(name = "reqType")
ReqType req) {
ReqAckType ack = new ReqAckType();
ack.setReceptionTime(Calendar.getInstance());
ChecksScheduler cs = ChecksScheduler.getInstance();
Checks checks = cs.schedule(req);
ack.setInternalId(checks.getId());
return ack;
}
}
If anyone can help me figure out how to send two separate message (ack and response), knowing that i have to send them separately and the checks take too much time (it's because of that, that i have to send and ack), i would be thankful.
I am using Oracle Fusion Middleware (Weblogic, JDeveloper, ..)

Consuming a RESTful WebService passing a JSON object as request body

I've defined a RESTful WebService (by using RESTEasy on JBoss AS 7) that consumes a JSON data stream.
#PUT
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response consumeJSON(Student student) {
String output = student.toString();
// Do something...
return Response.status(200).entity(output).build();
}
How can I call my WS from another Spring-based webapp, by properly using the RestTemplate, mapping a Java Object to JSON and passing it as request body?
Note: I'm asking about Spring with the aim to investigate the facilities provided by the framework. I well know that it is possible to do that by defining manually the request body.
Cheers, V.
In the client application, you can create an interface with the same signature as the one you expose on the server side, and the same path.
Then, in the spring configuration file, you can use the RESTeasy client API to generate a proxy connecting to the exposed webservice.
In the client application, it would look like this :
SimpleClient.java
#PUT
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response consumeJSON(Student student);
Config.java
#Bean
public SimpleClient getSimpleClient(){
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://example.com/base/uri");
ResteasyWebTarget rtarget = (ResteasyWebTarget)target;
SimpleClient simple = rtarget.proxy(SimpleClient.class);
return simple;
}
Then, in the place where you want to invoke this web service, you inject it with Spring and you can call the method. RESTeasy will search for the webservice matching with with your client (according to the path and the request type) and will create a connection.
Launcher.java
#Resource
private SimpleClient simpleClient;
public void sendMessage(Student student) {
simpleClient.consumeJSON(student);
}
Docs on the RESTesay client API : http://docs.jboss.org/resteasy/docs/3.0.7.Final/userguide/html/RESTEasy_Client_Framework.html
Hope this was helpfull.

Different web service object after each call

I am new with Java EE and SOAP. I have tried to create a simple web service application and its client (environment: NetBeans 7.2.1 IDE, GlassFish Server 3.1, Java 1.6).
Web service code:
package simplews;
import javax.jws.*;
#WebService(serviceName = "SimpleWebService")
public class SimpleWebService {
String something = null;
#WebMethod(operationName = "setSomething")
#Oneway
public void setSomething(#WebParam(name = "smth") String smth) {
something = smth;
}
#WebMethod(operationName = "getSomething")
public String getSomething() {
return something;
}
}
Client application code:
package simpleclientapp;
import simplews.*;
public class SimpleClientApp {
public static void main(String[] args) {
SimpleWebService_Service service = new SimpleWebService_Service();
SimpleWebService port = service.getSimpleWebServicePort();
port.setSomething("trololo");
String smth = port.getSomething();
System.out.println(smth);
}
}
Unfortunately, the client application printed out null. After short investigation I have realised, that on the server side a new SimpleWebService object is created for each client call (sounds like stateless approach).
What is wrong here? Why the client port does not refer to the same WS object for each call?
Web services are stateless by nature. In order to keep state between requests, you have to persist the data (in a file,database etc.).
You're right, JAX-WS web services are stateless by default and you can't rely on something thatviolates this premise. Follow a different approach in storing such values. You can read this doc Java TM API for XML Web Services (JAX-WS) Stateful Web Service with JAX-WS RI, if you really want to follow the direction in your post.

Integrate Jersey with RMI

I have a Java Jersey project, where I am running an application. On the other hand I have a project were there is a RMI application how can I put this two to work together. In other words how can I intergrate RMI/IIOP into a Jersey application.
I was thinking of something like this:
#Path("/items")
public class ItemsResource {
#Context
UriInfo uriInfo;
#Context
Request request;
Stuber s = new Stuber();
#GET
public Response get() throws RemoteException {
}
Were I have an external class in the Jersey Project that will work as a client to connect with the RMI/IIOP
public class Stuber {
Context ic;
iRemoteLogic logic;
public Stuber() {
super();
Object objref;
try {
ic = new InitialContext();
objref = ic.lookup("LogicService");
System.out.println("Client: Obtained a ref. to Hello server.");
logic = (iRemoteLogic) PortableRemoteObject.narrow(
objref, iRemoteLogic.class);
What should I add to the Stuber class to be able to work as an RMI/IIOP client?
Thanks :)
NOTE: I followed this tutorial for the RMI/IIOP
You would need to provide somewhere an implementation of iRemoteService that is exported via RMI/IIOP (i.e. PortableRemoteObject), and register it via JNDI as LogicService. I doubt the latter is going to work: surely you will need to provide a protocol and host to JNDI.

Categories

Resources