How to mock rest-easy's asynchronous HTTP request? - java

Is there an offical way to mock a rest-easy asynchronous HTTP request?
The sample code:
#GET
#Path("test")
public void test(#Suspended final AsyncResponse response) {
Thread t = new Thread() {
#Override
public void run()
{
try {
Response jaxrs = Response.ok("basic").type(MediaType.APPLICATION_JSON).build();
response.resume(jaxrs);
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
t.start();
}
I offen mock rest-easy's request this way:
#Test
public void test() throws Exception {
/**
* mock
*/
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(action);
MockHttpRequest request = MockHttpRequest.get("/hello/test");
request.addFormHeader("X-FORWARDED-FOR", "122.122.122.122");
MockHttpResponse response = new MockHttpResponse();
/**
* call
*/
dispatcher.invoke(request, response);
/**
* verify
*/
System.out.println("receive content:"+response.getContentAsString());
}
BUT it dosn't work. I got a BadRequestException during the unit test.
What's the right way to mock a rest-easy asynchronous HTTP request?

By reading rest-easy's source code, I finally find a way to work around with asynchronous HTTP request :
#Test
public void test() throws Exception {
/**
* mock
*/
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(action);
MockHttpRequest request = MockHttpRequest.get("/hello/test");
request.addFormHeader("X-FORWARDED-FOR", "122.122.122.122");
MockHttpResponse response = new MockHttpResponse();
// Add following two lines !!!
SynchronousExecutionContext synchronousExecutionContext = new SynchronousExecutionContext((SynchronousDispatcher)dispatcher, request, response );
request.setAsynchronousContext(synchronousExecutionContext);
/**
* call
*/
dispatcher.invoke(request, response);
/**
* verify
*/
System.out.println("receive content:"+response.getContentAsString());
}
The output of response is correct !!!

Related

How to write junit of init()?

Hi I am a beginner in junit's and I got stumbled upon the junit of init() method which I defined in my servlet.
Here is my servlet.
public class EmailSendingServlet extends HttpServlet{
private static final long serialVersionUID = -7796409155466523414L;
/**
* Creates an Email Model Object
*/
Email emailMessage = new Email();
/**
* Overrides the init constructor of servlet
*
*/
public void init() {
ServletContext context = getServletContext();
emailMessage.setHostName(context.getInitParameter("host"));
emailMessage.setPortName(context.getInitParameter("port"));
}
/**
* Overrides the Service method of Generic Servlet
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
emailMessage.setFrom(request.getParameter("from"));
emailMessage.setRecipient(request.getParameterValues("recipients"));
emailMessage.setSubject(request.getParameter("subject"));
emailMessage.setBody(request.getParameter("body"));
emailMessage.setFile(request.getParameterValues("file"));
String resultMessage = "";
try {
EmailUtility.sendEmail(emailMessage);
resultMessage = "The Email was sent successfully";
request.setAttribute("message", resultMessage);
response.setContentType("text/html");
RequestDispatcher view = request.getRequestDispatcher("/Result.jsp");
view.forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
and given below is the test case of my servlet class:
#RunWith(PowerMockRunner.class)
public class EmailSendingServletTest extends Mockito
{
#Test
public void TestEmailSendingServlet() {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);
when(request.getParameter("from")).thenReturn("robi#robi.com");
String[] recipients = new String [3];
recipients[0] = "abc#abc.com";
recipients[1] = "xyz#xyz.com";
recipients[2] = "qwe#qwe.com";
when(request.getParameterValues("recipients")).thenReturn(recipients);
when(request.getParameter("subject")).thenReturn("Test Mail");
when(request.getParameter("body")).thenReturn("This is Body");
String[] files = new String[1];
files[0] = "C:\\Users\\asara3\\Documents\\Architecture.jpg";
when(request.getParameterValues("file")).thenReturn(files);
when(request.getRequestDispatcher("/Result.jsp")).thenReturn(requestDispatcher);
try {
new EmailSendingServlet().doPost(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
I am bit confused to stub the context.getInitParameter("host") in my test case ? Any help guys ?
You could use the spy() method of Mockito to mock the behavior of the getServletContext() method.
For example add this in your setup() method or in the constructor of the unit test class :
public class EmailSendingServletTest {
...
private EmailSendingServlet emailSendingServlet;
private EmailSendingServlet emailSendingServletSpy;
...
public EmailSendingServletTest(){
emailSendingServlet = new EmailSendingServlet();
emailSendingServletSpy = Mockito.spy(emailSendingServlet);
}
}
Then you may mock the getServletContext() method like that :
Mockito.doReturn(yourMockedServletContext).when(emailSendingServletSpy.getServletContext());
Generally I avoid spying (that mocks the object under test) but in the case of third-party dependency as servlet, it is an acceptable case as refactoring is not able or else it forces us to write no standard changes in the way the third-party dependency is used. Which is often undesirable too.

How to make jetty ErrorHandler display error in json format in java

I have the jetty server started this way by the sample code below where I have written my own errorHandler class. Through some few research, I got some information here. But it seems not enough to get me what I want. The name of the class that I set to be called by the server is HandleClient.
So if error 404 occurs, it display {"message":"HTTP error 404"} . the program works fine anyway But the response is in text/plain format.
My Problem is: How can I configure the class to format and display the error in MIME MediaType: application/json.
I have had sleepless nights on this. Will be very grateful to all helps.
public class MVCPattern{
public MVCPattern(){
}
public static void main(String args[]) throws JSONException, IOException{
MVCPattern mvcPattern = new MVCPattern();
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
Server jettyServer = new Server(9000);
jettyServer.setHandler(context);
context.setErrorHandler(new ErrorHandler());
// default error handler for resources out of "context" scope
jettyServer.addBean(new ErrorHandler());
ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
HandleClient.class.getCanonicalName() );
try {
jettyServer.start();
jettyServer.join();
} catch (Exception ex) {
Logger.getLogger(HandleClient.class.getName()).log(Level.SEVERE, null, ex);
} finally {
jettyServer.destroy();
}
}
/**
* Dummy error handler that disables any error pages or jetty related messages and returns our
* ERROR status JSON with plain HTTP status instead. All original error messages (from our code) are preserved
* as they are not handled by this code.
*/
static class ErrorHandler extends ErrorPageErrorHandler {
#Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter()
.append("{\"message\":\"HTTP error ")
.append(String.valueOf(response.getStatus()))
.append("\"}");
}
}
}
You should check the Accept header in the HttpServletRequest.getHeader("Accept") to first see if the client can accept that type.
Then use HttpServletResponse.setContentType("text/json") to set the content type for the response.
Or, if you are using Jetty 9.3.11.v20160721 (or newer) you can override the ErrorHandler.generateAcceptableResponse(Request baseRequest, HttpServletRequest request, HttpServletResponse response, int code, String message, String mimeType) method and handle it accordingly.
See: https://github.com/eclipse/jetty.project/blob/jetty-9.3.11.v20160721/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java#L196-L226 for example on its use.
This is not a big deal.
By default Jetty uses ErrorPageErrorHandler, you can check Jetty's WebAppContext () constructors, the default one looks like this (jetty 9.4.19.v20190610):
public WebAppContext()
{
this(null,null,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
}
https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
You can extend ErrorPageErrorHandler and write to response errors in JSON.
Example:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.igorkhromov.dto.Errors;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
public class ErrorHandler extends ErrorPageErrorHandler {
/*
Messages to error made based on:
Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
https://tools.ietf.org/html/rfc7231
*/
private static final String ERROR_404_MESSAGE = "Target resource not found";
private static final String ERROR_501_MESSAGE = "Server functionality to process request is not implemented";
private static final String ERROR_502_MESSAGE = "Server cannot proxy request";
private static final String ERROR_503_MESSAGE = "Server is currently unable to handle the request";
private static final String ERROR_504_MESSAGE = "Server did not receive a timely response from an upstream server";
private static final String ERROR_UNEXPECTED_MESSAGE = "Unexpected error occurs";
private static final ObjectMapper MAPPER = new ObjectMapper();
#Override
protected void generateAcceptableResponse(Request baseRequest, HttpServletRequest request, HttpServletResponse response, int code, String message, String mimeType)
throws IOException
{
baseRequest.setHandled(true);
Writer writer = getAcceptableWriter(baseRequest, request, response);
if (null != writer) {
response.setContentType(MimeTypes.Type.APPLICATION_JSON.asString());
response.setStatus(code);
handleErrorPage(request, writer, code, message);
}
}
#Override
protected Writer getAcceptableWriter(Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException
{
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
return response.getWriter();
}
#Override
protected void writeErrorPage(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks)
throws IOException
{
try {
writer.write(MAPPER.writeValueAsString(new Errors(getMessage(code))));
}
catch (Exception e) {
// Log if needed
}
}
private boolean isRestRequest(HttpServletRequest request) {
return request.getServletPath().startsWith("/api/");
}
private String getMessage(int code) {
switch (code) {
case 404 : return ERROR_404_MESSAGE;
case 501 : return ERROR_501_MESSAGE;
case 502 : return ERROR_502_MESSAGE;
case 503 : return ERROR_503_MESSAGE;
case 504 : return ERROR_504_MESSAGE;
default : return ERROR_UNEXPECTED_MESSAGE;
}
}
}
Code in my GitHub repo:https://github.com/xrom888/blog_jetty-display-errors-in-json
Custom ErrorHandler: https://github.com/xrom888/blog_jetty-display-errors-in-json/blob/master/src/main/java/com/igorkhromov/ErrorHandler.java
All code under the MIT license, enjoy )).

Making multiple asynchronous HTTP 2.0 requests with okhttp3

I am trying to use okhttp (3.4.1) to implement a HTTP 2.0 based client. Part of my requirement is to implement multiple asynchronous HTTP requests to different URLs with a callback to handle the response at a later time.
However, with my current implementation, I see that I cannot get all my asynchronous requests to use the same TCP connection unless I make a blocking HTTP request from my main thread at the beginning.
I understand that the enque() method used for asynchronous calls engages the dispatcher which seems to spawn a new thread for each of the requests.
Here is my code snippet:
OkHttpClient client = new OkHttpClient();
My async Get Request method looks as follows:
public void AsyncGet(String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
new Thread() {
#Override
public void run() {
/* Some code */
}
}.start();
}
});
}
My synchronous Get Request is as follows:
public Response SyncGet(String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
Response response = call.execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
return response;
}
Making a call sequence like the following triggers 2 TCP connections to the server.
AsyncGet(Url);
AsyncGet(Url2);
However, a call sequence like the following makes use of the same TCP connection.
SyncGet(Url);
AsyncGet(Url);
AsyncGet(Url2);
I have not debugged this but, it looks like OkHttp forces us to make a blocking HTTP request on the main thread first to possibly obtain the TCP connection context and then share that with other threads? Or, am I missing something?
You can call async to and set sector to each call later on based on the sector you can distinguish the response of call. Try this code; I hope it will help you!
Create a separate class for api call:
public class RestApi {
protected RestApiResponse serverResponse;
protected Context c;
public RestApi(RestApiResponse serverResponse, Context c) {
this.serverResponse = serverResponse;
this.c = c;
}
public void postDataWithoutAuth(String url,String sector) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.e("response", call.request().body().toString());
serverResponse.onErrorLoaded(call.request().body().toString(), sector);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
serverResponse.onResponseLoaded(response, sector);
}
});
}
}
Then create an interface for callback
public interface RestApiResponse {
public void onResponseLoaded(Response response, String sector);
public void onErrorLoaded(String response, String sector);
}
and access it from your Activity like this
RestApi apicall = new RestApi(this, getBaseContext());
apicall.postDataWithoutAuthUrlEncoded("ur url","your sector");

Jersey async request handling with programmatical resource registration

We used
org.glassfish.jersey.server.model.ResourceMethod$Builder
to register the method and the handler.
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod);
methodBuilder.produces(restContext.getProduceContent()).handledBy(inflector);
methodBuilder.consumes(restContext.getConsumeContent()).handledBy(inflector);
The handler class implements the org.glassfish.jersey.process.Inflector<ContainerRequestContext, Response>
public class CommonMethodInflector implements Inflector<ContainerRequestContext, Response>
{
#Override
public Response apply(ContainerRequestContext request)
{
//sync bloc
//using resqest object we do processing in different maner
incRestFeRequestCounters(request.getMethod());
Response response = processIncoming(request);`enter code here`
}
}
Could you please help us in creating the async handler.
our requirement in short:
At runtime only we know the http method and other resources to register.
So, we can not use annotations for resource & httpMethod registration.
We need only programmatic resource registration.
In handler We need the request object so that we can access what method and what json body in it.
we need to make the async response as we are doing huge operation in the processing request.
The first thing you need to do is to suspend the resource method:
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod)
.suspend(AsyncResponse.NO_TIMEOUT, TimeUnit.Seconds);
Then you have few choices how to process the request in async mode:
"Arbitrary" handler class
builder.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(MyHandler.class, MyHandler.class.getMethod("handle", AsyncResponse.class));
and
public class MyHandler {
public void handle(final #Suspended AsyncResponse response) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
}
}
Inflector class
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
// Can be registered only as a class otherwise the
// #Context injection would be out of request scope.
.handledBy(MyAsyncInflector.class);
and
public class MyAsyncInflector implements Inflector<ContainerRequestContext, Response> {
#Context
private AsyncResponse response;
#Override
public Response apply(final ContainerRequestContext context) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
}
Annonymous Inflector
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(new Inflector<ContainerRequestContext, Response>() {
#Inject
private javax.inject.Provider<AsyncResponse> responseProvider;
#Override
public Response apply(final ContainerRequestContext context) {
// Make sure we're in request scope.
final AsyncResponse response = responseProvider.get();
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
});

Restlet Client :: how to add filters?

I suffering of a lack of documentation on the use of Restlet at the client side.
I am getting a resource on server via a ClientResource:
new ClientResource(url).get();
But the server can return an ETag header. To handle this I want to save the ETag when returned and send it back to the server when using the same url.
Currently I am doing it like this:
ClientResource clientResource = new ClientResource(url);
addEtag(url, clientResource); // add the cached ETag to the query if any
clientResource.get();
saveEtag(url, clientResource); // cache the ETag if any
I would like to do this using the Restlet framework. I am searching for days wihtout understanding the missing link.
I can extend an application, overwrite the createOutboundRoot() method and return a filter:
public class RestLetClient extends Application {
private Client client;
// instantiation of the client and other things here
#Override
public Restlet createOutboundRoot() {
return new Filter(getContext(), client){
#Override
protected int beforeHandle(Request request, Response response) {
addEtag(request);
return super.doHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtag(request, reponse);
return super.afterHandle(request, response);
}
};
}
}
BUT how can I use this filtering around the Restlet client from my business code?
EDIT
The best I could get to work until now is this:
Request request = new Request(Method.GET, uri);
//the filter created in original post
filter.handle(request).getEntity();
This works but it is not integrated in the framework. What I am achieving to do is at the client side what is only documented for the server side. On the server you would do:
public class ServerApplication extends Application {
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(GET_URL, GetResource.class);
return router;
}
}
and then start the server. The application will the be triggered on the reception of a GET request on the url.
What is the equivalent on the client side? How can I trigger a Client Application? If I have an Application running at the client side I can nicely add filters where they belong in a REST application
EDIT 2
When trying to run my client within an Application I get the error: The filter org.restlet.engine.application.RangeFilter#f372a7a was executed without a next Restlet attached to it.
Here is how I am getting the error. I have a class extending Application that is called from a JUnit test:
public class RestLetClient extends Application {
private final Client client;
Logger logger = LoggerFactory.getLogger(getClass());
public RestLetClient() {
this.client = new Client(Protocol.HTTP);
}
public Representation get(final String uri) throws Exception {
Request request = new Request(Method.GET, uri);
Response response = handle(request);
return response.getEntity();
}
#Override
public Restlet createOutboundRoot() {
return new Filter(getContext(), client) {
#Override
protected int beforeHandle(Request request, Response response) {
addEtagFilter(request);
return super.beforeHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtagFilter(request, response);
super.afterHandle(request, response);
}
};
}
private void saveEtagFilter(Request request, Response response) {
logger.debug("saving etag");
}
private void addEtagFilter(Request request) {
logger.debug("adding etag");
}
}
and the unit with a single test method:
public class RestLetClientTest {
public static final String URL = "http://localhost:8123/resource";
private RestLetClient instance;
private Server server;
#Before
public void setUp() throws Exception {
server = new Server(Protocol.HTTP, 8123, new TestApplication());
server.start();
instance = new RestLetClient();
instance.start();
}
#After
public void tearDown() throws Exception {
instance.stop();
}
#Test
public void testGet() throws Exception {
Representation representation = instance.get(URL);
System.out.println(representation.getText());
}
private class TestApplication extends Application {
#Override
public Restlet createInboundRoot() {
return new Router().attach(RestLetClientTest.URL, GetResource.class);
}
}
private class GetResource extends ServerResource {
#Get
public Representation getResource() {
return new StringRepresentation("hello world");
}
}
}
What am I doing wrong?
I had a much nicer answer from a colleague. I post it here for the documentation.
The solution is to use a ClientResource, a Filter and a Client.
The Filter becomes the next() of the ClientResource and the Client the next() of the Filter.
public class ETagFilter extends Filter {
#Override
protected int beforeHandle(Request request, Response response) {
addEtag(request);
return super.beforeHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtag(request, reponse);
super.afterHandle(request, response);
}
}
public class RestLetClient extends Application {
public Representation get(final String uri) throws Exception {
Client client = new Client(Protocol.HTTP);
ETagFilter eTagFilter = new ETagFilter();
clientResource = new ClientResource(uri);
clientResource.setNext(eTagFilter);
eTagFilter.setNext(client);
return clientResource.get(halMediaType);
}
}
For info. In my OP I was trying to transform code meant for server side into client side. That approach was wrong. My colleague pointed that the approach is much more like the use Apache HttpClient for similar needs
To have a client working you need to take the Application out of the picture since it is Server oriented according to the javadoc.
What you need is a Component, a ClientRouter and a custom ClientRoute.
Component manage connectors. A Restlet Client is a Connector.
ClientRouter dispatches to client connectors.
ClientRoute extends Filter allowing to add filters around your client handeling.
My solution:
The Component
public class RestLetComponent extends Component {
public RestLetComponent(Client client) {
getClients().add(client);
}
}
The ClientRouter
public class RestLetClientRouter extends ClientRouter {
public RestLetClientRouter(final Client client) {
super(new RestLetComponent(client));
ClientRoute clientRoute = new RestLetClientRoute(this, client);
//forcing to use only our custom route
getRoutes().clear();
getRoutes().add(clientRoute);
}
public Representation get(final String uri) throws Exception {
Request request = new Request(Method.GET, uri);
Response response = handle(request);
return response.getEntity();
}
}
And the custom ClientRoute that will add the filters
public class RestLetClientRoute extends ClientRoute {
Logger logger = LoggerFactory.getLogger(getClass());
public RestLetClientRoute(Router router, Client client) {
super(router, client);
}
//the filters
#Override
protected int beforeHandle(Request request, Response response) {
addEtagFilter(request);
return super.beforeHandle(request, response);
}
#Override
protected int doHandle(Request request, Response response) {
logger.debug("handling request: " + request.getMethod() + " - " + request.getResourceRef());
return super.doHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtagFilter(request, response);
super.afterHandle(request, response);
}
private void saveEtagFilter(Request request, Response response) {
logger.debug("saving etag");
}
private void addEtagFilter(Request request) {
logger.debug("adding etag");
}
}
And last but not least, I apologize to the Restlet authors, the documentation is there. I was reading the Restlet in Action book but the answer is in the very well documented javadoc.

Categories

Resources