Citrus Framework async HTTP test scenario - asynchronous

I have async test scenario. I call my SUT (System Under Test) and it returns acknowledge response. Next the proper response is returned asynchronously. I configured my mock server that is designated to receive callback response as below:
<citrus-http:server id="receiveCallbackMockService"
port="${server.port}"
auto-start="true"
timeout="10000"
endpoint-adapter="dispatchingEndpointAdapter" />
<citrus:dispatching-endpoint-adapter id="dispatchingEndpointAdapter"
mapping-key-extractor="mappingKeyExtractor"
mapping-strategy="mappingStrategy"/>
<bean id="mappingKeyExtractor" class="com.consol.citrus.endpoint.adapter.mapping.HeaderMappingKeyExtractor">
<property name="headerName" value="#{T(com.consol.citrus.http.message.HttpMessageHeaders).HTTP_REQUEST_URI}"/>
</bean>
<bean id="mappingStrategy"
class="com.consol.citrus.endpoint.adapter.mapping.SimpleMappingStrategy">
<property name="adapterMappings">
<map>
<entry key="/callback" value-ref="responseAdapter"/>
</map>
</property>
</bean>
<citrus:static-response-adapter id="responseAdapter">
</citrus:static-response-adapter>
Then I use Java DSL code that should receive expected payload on callback
receive(receiveCallbackMockService)
.payload(new ClassPathResource("/async/callbackExpectedRequest01.xml"));
but I get such an exception when executing a test:
14:25:43,516 ERROR citrus.Citrus| TEST FAILED HELLO_ASYNC_01: output 039 <com.mycompany.myproject> Nested exception is:
com.consol.citrus.exceptions.CitrusRuntimeException: Unable to create endpoint for static endpoint adapter type 'class com.consol.citrus.endpoint.adapter.RequestDispatchingEndpoint
Adapter'
at com.consol.citrus.endpoint.adapter.StaticEndpointAdapter.getEndpoint(StaticEndpointAdapter.java:35)
at com.consol.citrus.server.AbstractServer.createConsumer(AbstractServer.java:200)
at com.consol.citrus.actions.ReceiveMessageAction.receive(ReceiveMessageAction.java:146)
at com.consol.citrus.actions.ReceiveMessageAction.doExecute(ReceiveMessageAction.java:125)
at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:42)
at com.consol.citrus.dsl.actions.DelegatingTestAction.doExecute(DelegatingTestAction.java:54)
at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:42)
at com.consol.citrus.TestCase.executeAction(TestCase.java:214)
at com.consol.citrus.TestCase.doExecute(TestCase.java:142)
at com.consol.citrus.actions.AbstractTestAction.execute(AbstractTestAction.java:42)
at com.consol.citrus.Citrus.run(Citrus.java:254)
at com.consol.citrus.dsl.testng.TestNGCitrusTest.invokeTestMethod(TestNGCitrusTest.java:124)
at com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner.invokeTestMethod(TestNGCitrusTestDesigner.java:73)
at com.consol.citrus.dsl.testng.TestNGCitrusTest.run(TestNGCitrusTest.java:100)
at com.consol.citrus.dsl.testng.TestNGCitrusTest.run(TestNGCitrusTest.java:58)
at org.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:209)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:639)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:820)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1128)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
at org.testng.TestRunner.privateRun(TestRunner.java:782)
at org.testng.TestRunner.run(TestRunner.java:632)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:366)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:361)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:319)
at org.testng.SuiteRunner.run(SuiteRunner.java:268)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1244)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
at org.testng.TestNG.run(TestNG.java:1064)
at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:132)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:224)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:113)
at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:146)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:286)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:240)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:121)
How should I configure Citrus HTTP Mock Server to receive callback requests (and validate it with expected request payload) in async test scenario?

You are using static-response-adapter and receive() operations on the Citrus server component at the same time. This is not working. The static response adapter will always jump in and the receive operation in your test never gets any request.
Please remove the dispatching response adapter in combination with the static response adapter if you want to receive requests from that server component in a test case.

Just found some time to share my solution. First of all - there is a post (http://citrus.895196.n3.nabble.com/Citrus-to-act-as-a-mock-server-td4023066.html) telling us, that "What you need is a standalone simulator that waits for incoming requests and sends pre-defined response messages. This is not the primary focus of Citrus as a test framework.". But I found a workaround:
1) Use global variable as the flag telling us, did async request arrive from SUT:
Spring context:
<bean id="globalVariables" class="com.consol.citrus.variables.GlobalVariables"></bean>
Java code:
#Autowired
private GlobalVariables globalVariables
and before start waiting for a SUT async callback request set the variable (flag)
globalVariables.getVariables().put("SUTcallbackStatus", "waiting");
2) extend IteratingConditionExpression - override the evaluate method as below:
#Override
public boolean evaluate(int index, TestContext context) {
if(index > threshold){
testToFail.fail("The async message didn't arrived to the mock server.");
return false;
}
if(globalVariables.getVariables().get("SUTcallbackStatus").equals("received")){
return false;
}
return true;
}
The testToFail and threshold variables from above code should be set from the Test class:
myIteratingConditionExpression.setThreshold(50);
myIteratingConditionExpression.setTestToFail(this);
It allows you to control how long test should wait for the response (threshold*0.5sec) and allows you to point the test, which should be failed in case of not receiving callback response in assumed period.
3) Extend the StaticResponseEndpointAdapter - override below method
#Override
public Message handleMessageInternal(Message message) {
globalVariables.getVariables().put("SUTcallbackStatus", "received");
return super.handleMessageInternal(message);
}
and use this adapter as your mock service adapter:
<citrus-http:server id="asyncResponseMockService"
port="${server.callback.port}"
auto-start="true"
timeout="10000"
endpoint-adapter="myResponseAdapter" />
4) Then, in your test class - to start waiting for an async callback request - just use iterate action with custom iteratingConditionExpression:
iterate(sleep(0.5)).condition(myIteratingConditionExpression);
This do the job for me :)

Related

Spring Cloud Stream Kafka : How to access recordMetadata of kafka headers after producing a message to a kafka topic

I want get the offset and partition information after i produce a message to kafka topic.
I read through spring cloud stream kafka binding document and found that that can be achieved by fecthing RECORD_METADATA kafka header.
From Spring documentation: (https://cloud.spring.io/spring-cloud-static/spring-cloud-stream-binder-kafka/3.0.0.RELEASE/reference/html/spring-cloud-stream-binder-kafka.html#kafka-producer-properties)
recordMetadataChannel
The bean name of a MessageChannel to which successful send results should be sent; the bean must exist in the application context. The message sent to the channel is the sent message (after conversion, if any) with an additional header KafkaHeaders.RECORD_METADATA. The header contains a RecordMetadata object provided by the Kafka client; it includes the partition and offset where the record was written in the topic.
ResultMetadata meta = sendResultMsg.getHeaders().get(KafkaHeaders.RECORD_METADATA, RecordMetadata.class)
I have configured my output topic bean name as message channel in the property file
spring.cloud.stream.kafka.bindings.acknowledgement-out.producer.record-metadata-channel = acknowledgement-out
my customized interface and producer class as below:
public interface OutputAcknowledgement {
#Output("acknowledgement-out")
MessageChannel output();
}
Producer class:
#EnableBinding(OutputAcknowledgement.class)
public class AcknowledgementProducer {
#Autowired
OutputAcknowledgement outputAcknowledgement;
public Boolean produce(Acknowledgement acknowledgement) {
Message msg = MessageBuilder.withPayload(acknowledgement).build();
boolean val = outputAcknowledgement.output().send(msg);
RecordMetadata recordMetadata = msg.getHeaders().get(KafkaHeaders.RECORD_METADATA, RecordMetadata.class);
Getting null for recordMetadata.
Please suggest whether my approach is correct?
You're getting null because it doesn't exist in that message object at the time you're accessing it. According to the docs the metadata is only provided after successful publish. See this answer on how to get record metadata by providing a handler/consumer for the record metadata channel.

How to response with a success JSON format after completing a transaction in corda

Hi everyone i am working on a project in which i need to send a response in JSON format to the CLI that the Transaction have completed let me give you an example.Consider that i have stated a flow Start ExampleFlow pojo: {iouValue: 7}, otherParty: "O=PartyB,L=London,C=GB" and the result will be Starting
Generating transaction based on new IOU.
Verifying contract constraints.
Signing transaction with our private key.
Gathering the counter party's signature.
Collecting signatures from counterparties.
Verifying collected signatures.
Obtaining notary signature and recording transaction.
Broadcasting transaction to participants
Done
Flow completed with result: SignedTransaction(id=F95406D901209BA77396C1A4D375585C6E051414EE22BE441FC02E5AE147A050)
but what i want is that their should be a JSON format result not all of it but something like this
{response: success }
i just want some success response in JSON format
i am using IOU project
thanks
You can achieve that by establishing an RPC connection with your node; call the flow, then return the JSON object.
There are a couple of approaches that you can follow, and I recommend that you go through the samples repository https://github.com/corda/samples to explore them:
Create a webserver (SpringBoot application) that server REST API's that call your flows and return a JSON object: https://github.com/corda/samples/tree/release-V4/spring-webserver
Create a simple Java app that establishes an RPC connection with your node and serves as a client to call a certain method/flow: https://github.com/corda/samples/blob/release-V4/cordapp-example/clients/src/main/java/com/example/server/JavaClientRpc.java
If you follow the webserver sample, you can add a method to your controller that does something like:
#GetMapping(value = "/my-api", produces = MediaType.APPLICATION_JSON_VALUE)
private ResponseEntity<YourObject> getSomething() {
// Some code that calls your flow and returns YourObject.
return ResponseEntity.ok().body(YourObject);
}
so i got the answer what u need to do is add this dependency in client build.gradle
cordaCompile "net.corda:corda-jackson:$corda_release_version"
after that you just need to implement this code snip
String json = "";
try {
ObjectMapper mapper = JacksonSupport.createNonRpcMapper();
json = mapper.writeValueAsString(results);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return json;
result can be any datatype you want to convert to json

Customize HTTP codes and error message in JBoss AS 7

can anyone tell me how i can customize http codes and reasonphrase in JBoss AS 7?
basically i have a REST webservice that returns a nonstandard status code '499' with reasonphrase 'app error'
In standalone.xml, I set the org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER to true under systemproperties, but AS still overrides the HTTP error message.
There seems to be a mistake in JBoss documentation, the correct property name is:
org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER
So in the standalone you should have something like this:
<system-properties>
<property name="org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER" value="true"/>
</system-properties>
I assume that the REST service is interpretted using RestEasy.
That provides a nice feature of injecting a HTTP response object using #Context:
The #Context annotation allows you to inject instances of javax.ws.rs.core.HttpHeaders, javax.ws.rs.core.UriInfo, javax.ws.rs.core.Request, javax.servlet.HttpServletRequest, javax.servlet.HttpServletResponse, javax.servlet.ServletConfig, javax.servlet.ServletContext, and javax.ws.rs.core.SecurityContext objects.
#Path("/")
public class MyService {
#Context org.jboss.resteasy.spi.HttpResponse response;
#GET #Path("/") public void myMethod(){
response.sendError(499, "The file was censored by NSA.")
}
}
But maybe you should rather consider using a proprietary HTTP header:
response.getOutputHeaders().putSingle("X-MyApp-Error",
"499 Our server is down and admin is on holiday. MaƱana.");

Gracefully handle expired HttpSession in Spring WebFlow

(From SpringSource forum.)
When the HttpSession has expired and the user re-submits a page in the flow, he/she is sent back to the beginning of the flow. All I want to add to this behavior is a message explaining why it occurred. "You were inactive, so you have been restarted..."
What's the easiest/best-practice way to do this?
The default behavior, in FlowHandlerAdapter.defaultHandleException(), "attempts to start a new execution of the ended or expired flow".
It looks like a WebFlow way to handle this would be to provide a FlowHandler with a handleException() method that checks for an instanceof NoSuchFlowExecutionException, then do something like construct a redirect URL or place something on Session scope that can later be removed once utilized.
Due to the way WebFlow uses redirects, I don't think any other scopes would allow such a flag or message to be used later when the new flow's view renders.
However, simply detecting a new Session in an Interceptor or even a Filter would seem to be just as effective. Which is what I ended up doing in my previous investigation of this, as documented in the referenced forum thread. I was just hoping for something prettier.
Also, by the time the new flow begins, a new Session ID has already been created, so there's no way to initially detect this condition from within the flow.xml.
Sample filter logic:
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
log.info("Expired Session ID: " + request.getRequestedSessionId());
response.sendRedirect("sessionExpired");
}
else {
chain.doFilter(request, response);
}
Sample Interceptor:
public class SessionExpiredInterceptor extends HandlerInterceptorAdapter
{
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
response.sendRedirect("sessionExpired");
return false;
}
return true;
}
}
Step 1:
FlowController has a default handlerAdapter. To customize session exceptions you are required to write your own custom handler adapter and register it with the flow controller bean as below:
<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
.
.<property name="flowHandlerAdapter" ref="customFlowHandlerAdapter"/>
.
</bean>
<bean id="customFlowHandlerAdapter" class="gov.mo.courts.pbw.adapters.CustomFlowHandlerAdapter"
p:flowExecutor-ref="flowExecutor"/>
Step 2:
CustomFlowHandlerAdapter
In this class override defaultHandleException method. this is the method that webflow invokes in case of exceptions and re-initializes session. please note, new session has already been created till this point. Only the exception type will tell you at this point that the previous session timed out.
public class PbwFlowHandlerAdapter extends FlowHandlerAdapter{
protected void defaultHandleException(String flowId, FlowException e,
HttpServletRequest request, HttpServletResponse response)
throws IOException {
if(e instanceof NoSuchFlowExecutionException){
if(e.getCause() instanceof NoSuchConversationException){
//"use newly created session object within request object to save your customized message."
}
}
super.defaultHandleException(flowId, e, request, response);
}
The first view page of your app should be able to show this message.
<%
if (session.getAttribute(YOUR_CUSTOM_MSG_KEY) != null) {
%>
<p class="errormessage">
<%=session.getAttribute(YOUR_CUSTOM_MSG_KEY)%>
</p>
<%
//once the message has been shown, remove it from the session
//as a new session has already been started at this time
session.removeAttribute(YOUR_CUSTOM_MSG_KEY);
}
%>
Hope this helps.
From the Jira request I had opened, WebFlow developer says,
Custom FlowHandler is the extension point for an expired session. You
can redirect to the flow and append a query param, something like
restarted=true, and then in the flow declare an <input
name="restarted" type="boolean" value="flashScope.restarted" />.
I haven't been able to confirm, but wanted to share the information.

Handling Exceptions like ServletRequestBindingException in Spring rather than Servlet Container

I am using springmvc for a REST project and whenever the client calls a rest resource with the wrong HTTP method, a servletrequestbindingexception is thrown. I cannot handle these exceptions with a #ExceptionHandler in the controller, as it happens not within the handler method but in the spring mapping layer.
Currently I declared a web.xml exception handling, this works:
<error-page>
<exception-type>org.springframework.web.bind.ServletRequestBindingException</exception-type>
<location>/servletRequestBindingException.jsp</location>
</error-page>
<error-page>
<error-code>405</error-code>
<location>/methodNotSupported.jsp</location>
</error-page>
I'd rather use spring exception handling though. For example I'd like to create a dynamic response based on teh incoming Accept header, so either writing out json or xml for a rest exception for example. The best would be to return an object from this handler that would automatically be converted to json or xml just like a normal dto returned from a handler.
Is there a way to catch these lower level mapping exceptions?
You can't use #ExceptionHandler (since as you say, this is for dealing exceptions thrown from within the handler code), but you can still use the HandlerExceptionResolver framework to do this.
By default, DispatcherServlet registers an instance of DefaultHandlerExceptionResolver:
Default implementation of the HandlerExceptionResolver interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes.
The generation of the HTTP 405 is actually handled in this class, by catching HttpRequestMethodNotSupportedException thrown by the handler-mapping code.
So if you want to handle this exception differently, you can provide your own implementation of HandlerExceptionResolver. It's probably easiest to subclass DefaultHandlerExceptionResolver and override the handleHttpRequestMethodNotSupported method, returning your ModelAndView from there.
Be careful to include the default exception resolvers if you include your own. If you are using annotated #Exception handlers you need to explicitly load these or they will no longer function.
In this case, FooBarHandlerExceptionResolver extends DefaultHandlerExceptionResolver and provides a method that the default resolver doesn't cover. This lets FooBarHandlerExceptionResolver handle class-level exceptions that I couldn't catch with annotated #Exception handler methods.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
<property name="order" value="1"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver" >
<property name="order" value="2"/>
</bean>
<bean class="com.company.package.foo.FooBarHandlerExceptionResolver">
<property name="order" value="3"/>
</bean>
Here is the exception resolver
public class FooBarHandlerExceptionResolver extends DefaultHandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
try {
if (ex instanceof UnsatisfiedServletRequestParameterException) {
return handleUnsatisfiedServletRequestParameter((UnsatisfiedServletRequestParameterException) ex, request, response,
handler);
}else {
super.doResolveException(request,response,handler,ex);
}
}
catch(Exception handlerException){
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
}
return null;
}
protected ModelAndView handleUnsatisfiedServletRequestParameter(UnsatisfiedServletRequestParameterException ex,
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
logger.warn(ex.getMessage());
return new ModelAndView("blank", new ModelMap("reason", ex.getMessage()));
}
}

Resources