If using JSON Object to send and receive over RabbitMQ is this the cleanest way to send and receive? All that conversion seems inefficient.
Sending Code
JSONObject messageJSON = new JSONObject();
messageJSON.put("messageId", "testId");
messageJSON.put("NodeId", "testNode");
template.convertAndSend("TEST-EXCHANGE",
"routing.test", messageJSON.toJSONString()
.getBytes());
Receive Code
public class Listener implements MessageListener {
#Override
public void onMessage(Message message) {
String recmessage = new String(message.getBody());
JSONObject obj = (JSONObject) JSONValue.parse(recmessage);
System.out
.println("Message Received " + (String) obj.get("messageId"));
}
}
Solution From Answer given
You need to add the Dependency of Jackson. Below is maven:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
Add to the Spring Config
<bean id="amqpCorrectionTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="messageConverter">
<bean
class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter">
</bean>
</property>
</bean>
Add to the message Listener
Jackson2JsonMessageConverter jmc = new Jackson2JsonMessageConverter();
JSONObject obj = (JSONObject) jmc.fromMessage(message);
Sending Code
Only send the JSON Object passed in.
template.convertAndSend("TEST-EXCHANGE",
"routing.test", messageJSON);
This wrong question: the AMQP protocol (as any other wire-based) gets deal just with bytes, so it's up to your application how to convert to byte[] and back (if it is Java everywhere, of course).
From other side it would be better to your configuration based on the out-of-the- box converters. For example Jackson2JsonMessageConverter.
See more information in the Documentation.
Related
I am sending JMS messages with XML payloads and some custom headers contentType with values text/json and text/xml using Jmeter.
My Spring integration configuration looks like this:
<jms:message-driven-channel-adapter channel="jmsInChannel" destination-name="queue.demo" connection-factory="jmsConnectionFactory1" />
<int:channel id="jmsInChannel" />
<int:header-value-router input-channel="jmsInChannel" header-name="contentType" default-output-channel="nullChannel">
<int:mapping value="text/json" channel="jsonTransformerChannel" />
<int:mapping value="text/xml" channel="xmlTransformerChannel" />
</int:header-value-router>
Up to this point, everything works perfectly, the messages are successfully routed to their respective transformers.
My problem is, when it's an XML payload I first used a JAXB Unmarshaller along with <ixml:unmarshalling-transofmer ../> provided by http://www.springframework.org/schema/integration/xml.
I could get the payload, but it couldn't process the message afterwards as a JMS message, it became a pure POJO. So I lost the headers, and I couldn't use <int: ../> components without serializing the POJO, which is not what I wanted to achieve.
I have found a work-around, where I defined my own Unmarshalling bean in java like so:
<int:channel id="xmlTransformerChannel" />
<int:transformer input-channel="xmlTransformerChannel" ref="xmlMsgToCustomerPojoTransformer" output-channel="enrichInChannel" />
method:
#SuppressWarnings("rawtypes")
public Message transform(String message) {
logger.info("Message Received \r\n" + message);
try {
MyModel myModel = (MyModel) unmarshaller.unmarshal(new StreamSource(new StringReader(message)));
return MessageBuilder.withPayload(myModel).build();
} catch (XmlMappingException e) {
return MessageBuilder.withPayload(e).build();
} catch (Exception e) {
return MessageBuilder.withPayload(e).build();
}
}
I could successfully process the message as a Spring integration Message, but I lost the original custom JMS headers.
In contrast, all I had to do to transform the json payloads and keep the Message format AND preserving my custom headers is this xml configuration:
<int:channel id="jsonTransformerChannel" />
<int:json-to-object-transformer input-channel="jsonTransformerChannel" output-channel="enrichInChannel" type="com.alrawas.ig5.MyModel" />
My question is, how to keep the original custom JMS headers after unmarshalling the xml payload?
UPDATE:
I did try to write the xml transformer this way, taking Message as input instead of only string, it did not throw exception in this method, but it did later in the routing phase
public Message<?> transform(Message<String> message) {
logger.info("Message Received \r\n" + message);
try {
MyModel myModel = (MyModel) unmarshaller.unmarshal(new StreamSource(new StringReader(message.getPayload())));
return (Message<MyModel>) MessageBuilder.withPayload(myModel).copyHeaders(message.getHeaders()).build();
} catch (XmlMappingException e) {
return MessageBuilder.withPayload(e).build();
} catch (Exception e) {
return MessageBuilder.withPayload(e).build();
}
}
I ran into a problem in a component I use later in this flow:
<int:object-to-json-transformer input-channel="outr" output-channel="outch" />
<int:router method="route" input-channel="outch" default-output-channel="nullChannel">
<bean class="com.alrawas.ig5.MyCustomRouter" />
</int:router>
In my route method threw Cannot cast String to com.alrawas.ig5.MyModel Exception:
public class MyCustomRouter {
public String route(Message<MyModel> myModel) {
Integer tenNumber = myModel.getPayload().getNumber(); // <-- Cast Exception here
System.out.println(myModel);
return (tenNumber % 10 == 0) ? "stayLocal" : "goRemote";
}
}
This Cast Exception only happens after unmarshalling xml, JSON payloads work fine without losing headers or throwing cast exceptions.
UPDATE:
Check my answer below: contentType was not really a custom header
When you develop custom transformer, you need to keep in mind that returning a Message<?> leads you to a fully control over its content.
When transformer function returns a Message, it doesn't populate any request headers.
So, your public Message transform(String message) { must expect a Message as an input and you need to copy all the headers from the request message to reply message. There are appropriate methods on the MessageBuilder.
On the other hand it is fully unclear why do you need to return a Message here since everything in the Spring Integration is going to be wrapped to Message before sending to the output channel.
The take:
The ClassCastException was happening later in the last router, was because I named my custom header contentType. Which was a default jms header used internally. When I changed its value to text/xml, that last router String route(Message<MyModel> myModel) was trying to cast the json into MyModel, and it failed because the header was no more application/json like it should be, it was text/xml. Which caused the ClassCastException.
So I got rid of the custom xml unmarshalling logic bean. I renamed my custom header. And used <ixml:unmarshalling-transformer ../>.
It worked using xml configuration without additional custom java beans.
I have this soap handler:
#Slf4j
public class ResponseInterceptor implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage message = context.getMessage();
ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
String strMsg = new String(out.toByteArray());
} catch (SOAPException | IOException e) {
e.printStackTrace();
}
return false;
}
But I handle Requests. Is there a similar way to handle Responses?
EDIT:
I have next task: I need handle all RAW responces from SOAP service, filter it, and send to apache kafka. I do not want to have unmarshaling operation and I want send RAW responce to kafka
EDIT2:
I write interceptor:
#Slf4j
public class ResponseInterceptor extends AbstractPhaseInterceptor<Message> {
public ResponseInterceptor() {
super(Phase.PRE_UNMARSHAL);
}
#Override
public void handleMessage(Message message) throws Fault {
try {
SOAPMessage soapMessage = message.getContent(SOAPMessage.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
soapMessage.writeTo(out);
String strMsg = new String(out.toByteArray());
message.getInterceptorChain().abort();
} catch (SOAPException | IOException e) {
e.printStackTrace();
}
}
}
But if I call message.getInterceptorChain().abort(); I get exception in service. But I need just brake this responce and not delivery to web service
CXF interceptors are not "in and of themselves" linked to requests or responses, for at least two reasons :
Many interceptors can work on both sides (e.g. logging the soap payload)
There is a symetry of what is to be done on the request/response side of things, with respect to the client/server nature of the app.
So the way CXF works is that interceptors are bound to "chains", which CXF creates and manages at runtime, and which account for all combinations of the above : IN, OUT, IN_FAULT, OUT_FAULT. You can read all about them here.
If your current interceptor handles "requests" that means one of two things :
If your application is a server, then, your interceptor is bound to the "IN" chain
If your application is a client, then, your interceptor is bound to the "OUT" chain
If you want to handle responses as well as requests, you need to find how/where your custom interceptors are bound to a chain, which is usually in the configuration file in CXF (see : "writing and configuring interceptors" in the abose link).
Many people use CXF with Spring configuration, and therefore, add interceptors at the whole CXF (bus) level like so :
<bean id="MyInterceptor" class="demo.interceptor.MyInterceptor"/>
<!-- We are adding the interceptors to the bus as we will have only one endpoint/service/bus. -->
<cxf:bus>
<cxf:inInterceptors>
<ref bean="MyInterceptor"/>
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="MyInterceptor"/>
</cxf:outInterceptors>
</cxf:bus>
But it can also be done at the enpoint level.
Further reading : How to catch any exceptions in a SOAP webservice method?
Accessing the contents before (un)marshalling
I could expand a lot, but I suggest you look at the org.apache.cxf.interceptor.LoggingInInterceptor (or the respective one for "Out" messages), they are as good an example as you can see of "how to access the raw content without breaking anything".
I am getting 400 Http response when i am passing the invalid json format,
I would like to return the custom json message instead of this , can any one advise how to do in Spring 4.1 ?
Handling Execption using ControllerAdvice,but it is not working.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler({org.springframework.http.converter.HttpMessageNotReadableException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public String resolveException() {
return "error";
}
}
spring-config.xml is given below
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- Renders JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Given below Json request and response from WebSphere application server (7.0).
Request 1: Empty json request : {}
Response Status Code: 400 Bad Request
Response Message : Json request contains invalid data:null
Request 2:Invalid format of Json Request : {"data":,"name":"java"}
Response Status Code: 400 Bad Request
Response or Exception message :
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unexpected character (',' (code 44)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: com.ibm.ws.webcontainer.srt.http.HttpInputStream#8f308f3; line: 5, column: 57]
Similar question like below link
Using Spring MVC, accepting POST requests with bad JSON leads to a default 400 error code server page being returned
You can attempt to map the exception this way. This code will return a 400 status, but you can change the return the same way as is the link you posted
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleJsonMappingException(JsonMappingException ex) {}
Finally i have handle the exception via Servlet Filter with HttpServletRequestWrapper.
Step 1: Add the filter
Step 2: Get the request body from Customize HttpServletRequestWrapper class
Step 3: Convert request body json string to java object using JSON API
Step 4: Chain the request/response
Step 5: Catch exception / and update the HttpServlet Response
Using below reference.
Filter Example
HttpServletRequestWrapper Example
String to Json Object
With the help of this approach i can handle 400/405/415 Http Errors.
You may try this, in your pom.xml add dependency:
<!-- Need this for json to/from object -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
this will convert your java objects to JSON automatically when you return them. like you can write a class for response:
public class Response {
private int responseCode;
private String responseMessage;
//as many fields as you like
public Response (int responseCode, String responseMessage) {
this.responseCode = responseCode;
this.responseMessage = responseMessage;
} }
then you can return any java objects and they will be received as JSON,
#RequestMapping(value="/someMethod", method=RequestMethod.POST)
public #ResponseBody Response someMethod(#RequestBody Parameters param) {
return new Response(404, "your error message");
}
I'm implementing a SOAP client that interacts with some legacy SOAP services.
All of SOAP body requests have the same format, like this:
<soap:Body>
<execute>
<msg>
</msg>
</execute>
</soap:Body>
As content, the msg element supports a list of any XML tags, so I can send any type of element inside msg: orders, customer, contacts, etc..
All of the requests also have the same action name.
Due to the restrictions/aspects above, if I use spring's PayloadRootAnnotationMethodEndpointMapping, since the root element in the soap body is the same for all the requests, each request will fall in the same method of my Endpoint. If I use spring's SoapActionAnnotationMethodEndpointMapping, since the action is the same for every requests, all of them will fall in the same method again.
The only thing I've got different in the requests, is the requesting URI.
It changes by the name of the operation that I'm invoking. Like:
http://host:port/services/Operation1?Wsdl or
http://host:port/services/Operation2?Wsdl
My idea was to have an endpoint for each type of requests. For example: all request related to products would fit into a "ProductsEndpoint".
I wanted to create a custom endpoint mapping that would extend springs AbstractEndpointMapping. In my implementation I would decide which endpoint to call based on the URI.
But how can I register my custom endpoint mapping in the spring's endpoint mapping chain?
Best regards
If anyone has a request like mine explained above, here goes what I've decided to do...
I've create a class MultipleMarshallersPayloadMethodProcessor extending spring org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor. This is the class responsible for marshalling and unmarshalling arguments. In this class I've defined a java.util.map that will associate a given URL to a specific Marshaller. If the current URL request isn't a key in the map, it would use the default Marshaller supplied by the MarshallingPayloadMethodProcessor class.
To register the class as a spring bean:
<bean id="marshallingPayloadMethodProcessor"
class="br.com.tim.fiber.middleware.services.server.helpers.MultipleMarshallersPayloadMethodProcessor">
<constructor-arg ref="defaultMarshaller" />
<property name="otherMarshallers">
<map>
<entry key="/Operation1?wsdl" value-ref="operation1Marshaller"></entry>
<entry key="/Operation2?wsdl" value-ref="operation2Marshaller"></entry>
</map>
</property>
</bean>
And an example of a marshaller:
<bean id="operation1Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>com.app.dtos.operation1</value>
<value>com.app.dtos.common</value>
</list>
</property>
</bean>
With this setup I was able to marshall and unmarshall any request according to the URL. Then I've used the Facade design pattern to create a SOAP Endpoint with a single method that receives all the requests. That method just inspects the URL and delegates to specific endpoints.
#Endpoint
public class FacadeEndpoint {
private static final String NAMESPACE_URI = "http://my.namespace.com/services";
#Autowired
private RequesEndpointURLExtractor requestUrlExtractor;
#Autowired
private OrdersEndpoint ordersEndpoint;
#SuppressWarnings("unchecked")
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "execute")
#ResponsePayload
public ExecuteResponse dispatch(#RequestPayload Execute request) {
String serviceURL = this.requestUrlExtractor.getCurrentURL();
ExecuteResponse response = null;
if (serviceURL.equals(ServiceRequestsEndpoint.CREATE_ENDPOINT_URI)) {
Operation1DTO serviceRequest = (Operation1DTO) request.getMsg().getAnies().get(0);
}
...
}
The RequestEnpointURLExtractor is just a spring bean that extracts the full URL from the request.
#Component
public class RequesEndpointURLExtractor {
public String getCurrentURL() {
TransportContext ctx = TransportContextHolder.getTransportContext();
HttpServletRequest httpServletRequest = ((HttpServletConnection) ctx.getConnection()).getHttpServletRequest();
String pathInfo = httpServletRequest.getPathInfo();
String queryString = httpServletRequest.getQueryString();
return pathInfo + "?" + queryString;
}
}
I could create a custom annotation that would have a URL associated, and use that annotation on methods that would process the request with the URL configured. That would be a cleaner solution instead of the if/else if ladder that I have.
But, since this was only for a simple mockup server, the if/else if ladder isn't such a big deal.
I have followed a mash up of a couple tutorials and neither too succesfully!
I am trying to get Apache CXF and WS-Security to call back to my Spring Security authenticator. All is close to working but at tne moment I have a problem getting the password to give Spring security out of the WS-call back.
The Handler below gets galled but pc.getPassword() is null. I want this to be the password sent in Soap so I can pass it to spring
public class ServerPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
pc.setPassword( pc.getPassword() );
}
My interceptor is set up as so
<bean id="wsAuthenticationInterceptor" class="com.olympus.viewtheworld.server.security.auth.WSAuthenticationInInterceptor">
<constructor-arg index="0">
<map key-type="java.lang.String" value-type="java.lang.Object">
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass" value="com.olympus.viewtheworld.server.security.auth.ServerPasswordCallback" />
</map>
</constructor-arg>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<jaxws:endpoint id="secureHelloService"
implementor="#secureHelloServiceImpl"
implementorClass="com.olympus.viewtheworld.server.service.Impl.SecureHelloServiceImpl"
address="/SoapService/secure/hello">
<jaxws:serviceFactory>
<ref bean="jaxws-and-aegis-service-factory" />
</jaxws:serviceFactory>
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>
<ref bean="wsAuthenticationInterceptor" />
</jaxws:inInterceptors>
</jaxws:endpoint>
And the soap request I am sending out of SoapUI is
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://test/">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>rob2</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">passwordxx</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<test:hello>
<!--Optional:-->
<hello>asdf</hello>
</test:hello>
</soapenv:Body>
</soapenv:Envelope>
Version wise it is Spring 3.1 and CXF 2.7.0
What do I need to do to see "passwordxx" in the ServerPasswordCallback class? Is it the Soap request, the config or just wrong?!
Cheers,
Rob
It appears from the documentation on org.apache.ws.security.handler.WSHandlerConstants.PW_CALLBACK_CLASS that the method should instead call pc.setPassword with the stored password to compare the user provided password against as argument, instead of the user provided password itself:
This tag refers to the CallbackHandler implementation class used to
obtain passwords. The value of this tag must be the class name of a
javax.security.auth.callback.CallbackHandler instance. The callback function
javax.security.auth.callback.CallbackHandler.handle(javax.security.auth.callback.Callback[])
gets an array of org.apache.ws.security.WSPasswordCallback objects.
Only the first entry of the array is used. This object contains the
username/keyname as identifier. The callback handler must set the
password or key associated with this identifier before it returns. The
application may set this parameter using the following method:
call.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, "PWCallbackClass");
I'm using a org.apache.ws.security.validate.Validator to check the validity of the supplied password, and setting the Spring security context there:
#Bean(name = "wssforjInInterceptor")
public WSS4JInInterceptor wssforjInInterceptor() {
// Configure how we ask for username and password
Map<String, Object> props = new HashMap<>();
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// Password callback
props.put(WSHandlerConstants.PW_CALLBACK_REF, passwordCallbackHandler());
// Validator registration
Map<QName, Object> validators = new HashMap<>();
String WSS_WSSECURITY_SECEXT_1_0_XSD = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
QName qName = new QName(WSS_WSSECURITY_SECEXT_1_0_XSD, WSHandlerConstants.USERNAME_TOKEN, "");
validators.put(qName, usernameTokenValidator());
props.put(WSS4JInInterceptor.VALIDATOR_MAP, validators);
WSS4JInInterceptor wss4jInInterceptor = new WSS4JInInterceptor(props);
return wss4jInInterceptor;
}
I'm not sure whether that approach is any better or worse (I'd appreciate some feedback on this), but perhaps it's useful for the next person to come across this issue. There appears to be a lack of decent up to date documentation on how to integrate Spring security and CXF.
Ok so this is not the ideal solution and hopefully a better answer will come along a bit later down the line, most likely when I have some more time to look at this.
I use a regex to inspect the full packet and pull out the password field. Code is below. Will update later when I work out the right way to do this as this is defintley not it!
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
String mydata= pc.getRequestData().getMsgContext().toString();
Pattern pattern = Pattern.compile("<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">(.*?)<");
Matcher matcher = pattern.matcher(mydata);
if (matcher.find())
{
pc.setPassword( matcher.group(1) );
}