I'm trying to use the Splitter app to split a JSON array e.g. [{...},{...}] into multiple messages {...} {...}. With input contentType=application/json (per the docs) Spring Cloud is surfacing an exception from Jackson:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token at [Source: [B#163b1945; line: 1, column: 1]
Unit tests showed I have the correct pattern for the split to work:
splitter.expression=#jsonPath(payload,'$.[*]')
This worked for me in Spring XD 1.3. How should Spring Cloud (or Splitter) be configured to handle this case? Input and output are both Kafka strings (no headers).
If the messages to the splitter come from non Spring Cloud Stream applications, then you would need to set --spring.cloud.stream.bindings.<inputChannelName>.consumer.headerMode=raw. While the application/json contentType messages have jackson exception, this would at least get the text/plain contentType messages evaluated against the expression appropriately.
Related
Need to post a byte message to solace queue using Jmeter. I have tried in following manner might be am incorrect but tried with following:
Use JMSPublisher sampler
create jndi.properties file and put in jmeter/lib
jndi.properties
java.naming.factory.initial = com.solacesystems.jndi.SolJNDIInitialContextFactory
java.naming.provider.url = smf://<remote IP and port>
java.naming.security.principal=<username>
java.naming.security.credentials=<password>
Solace_JMS_VPN=<VPN Name>
in JMSPublisher sampler (in GUI)
Connection Factory = connectionFactory
Destination = (Queue Name )
Message Type (radio button---Byte message)
Content encoding -- RAW
in text area ---> (Byte message)
Note : I have used actual values of IP/port/username/port/queuename/bytemessage, cannot share those. Soljms jar is available in lib folder too.
getting error :
Response message: javax.naming.NamingException: JNDI lookup failed - 503: Service Unavailable [Root exception is (null) com.solacesystems.jcsmp.JCSMPErrorResponseException: 503: Service Unavailable]
Though it is working perfectly fine when did with java spring boot. There used properties files in place of JNDI.
It would be great if anyone can guide me , please do not give activeMQ JNDI am actively looking for posting on solace queue or create connection to solace appliances through Jmeter.
I don't think you should be putting your Byte message into the textarea as it accepts either plain text or an XStream object, consider providing your payload via binary file(s) instead
If you're capable of sending the message using Java code you should be able to replicate the same using:
JMeter's JSR223 Sampler with Groovy language (Java syntax will work)
Or JUnit Request sampler if you need "strict" java
Trying to run producer.py from solace-samples-amqp-qpid-proton-python with payload of python dict
Message(id=(self.sent+1), body={'sequence':(self.sent+1)})
Get following error
Reject message: 1 Remote disposition:
Condition('amqp:not-implemented', 'unsupported AMQP value type:
TOK_MAP_START')
Get similar error when trying to send integer value in body - TOK_TYPE_INT
Does solace support only Strings over AMQP?
Solace message brokers support amqp-value message sections containing values of types null, string, binary, symbol, or uuid. (https://docs.solace.com/Open-APIs-Protocols/AMQP/AMQP-Protocol-Conformance.htm#Sec3-2-8)
This is done in order to preserve maximum message inter-operability.
Any published message using a language specific semantic can only be consumed using the same semantic. I.e. if you publish with Python dict, you can only decode using Python dict, so if you are using a MQTT or REST consumer, it will not be able to decode the message.
The best option is to use a cross-language serialization library, which will make it easier for future expansions. For example, you might decide to add an REST consumer to in future, which can decode the data using the cross-language serialization library.
I'm building a contract-first SOAP web service with spring-ws 2.2.3. My XML Schema uses extensions, leading to xsi:type= attributes in the XML requests. Since some respones can be very large (30MB), I'm using a AxiomSoapMessageFactory instead of the default SaajSoapMessageFactory, as suggested in the Spring WS docs. I validate incoming requests with a PayloadValidatingInterceptor:
PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
interceptor.setSchema(new ClassPathResource("format-service.xsd"));
interceptor.setValidateRequest(true);
interceptor.setValidateResponse(false);
My problem is that I get spurious validation errors, depending on where in the XML the namespace is declared: if it's declared in the payload, then everything works fine:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Header/>
<soapenv:Body>
<sch:formatRequest xmlns:sch="http://example.com/springws/extension/schema">
<sch:value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="sch:currencyType">
<sch:amount>1000</sch:amount>
<sch:currency>EUR</sch:currency>
</sch:value>
</sch:formatRequest>
</soapenv:Body>
</soapenv:Envelope>
But as soon as I move the sch namespace declaration up to the Envelope:
<soapenv:Envelope xmlns:sch="http://example.com/springws/extension/schema" ...>
validation fails and I receive a SOAP Fault:
<faultcode>soapenv:Client</faultcode>
<faultstring xml:lang="en">Validation error</faultstring>
<detail>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">UndeclaredPrefix: Cannot resolve 'sch:currencyType' as a QName: the prefix 'sch' is not declared.</spring-ws:ValidationError>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-elt.4.1: The value 'sch:currencyType' of attribute 'http://www.w3.org/2001/XMLSchema-instance,type' of element 'sch:value' is not a valid QName.</spring-ws:ValidationError>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-type.2: The type definition cannot be abstract for element sch:value.</spring-ws:ValidationError>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">UndeclaredPrefix: Cannot resolve 'sch:currencyType' as a QName: the prefix 'sch' is not declared.</spring-ws:ValidationError>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-attribute.3: The value 'sch:currencyType' of attribute 'xsi:type' on element 'sch:value' is not valid with respect to its type, 'QName'.</spring-ws:ValidationError>
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-complex-type.2.1: Element 'sch:value' must have no character or element information item [children], because the type's content type is empty.</spring-ws:ValidationError>
</detail>
It appears like a bug in the AxiomSoapMessageFactory / Axiom implementation to me (namespace context is lost), as both requests validate fine when using the SaajSoapMessageFactory. Note that the validation only fails on the xsi:type= attribute. The same namespace is recognized correctly for elements.
I cannot use SaajSoapMessageFactory due to high memory consumption for large responses. I found similar problems described in the Spring forum and on SO, but no solution. Thanks for help!
The reason is that Spring-WS uses OMContainer#getXMLStreamReader(). Instead it should use OMContainer#getXMLStreamReader(boolean, OMXMLStreamReaderConfiguration) and set the preserveNamespaceContext property to true in the OMXMLStreamReaderConfiguration object.
You should file a bug for Spring-WS, or even better, fix the issue and submit a pull request.
Problem
I have added support for http compression in our self-hosted OWIN/Katana Web API OData 4 service but I do not see how to support compression in the .NET client. I'm using OData libraries v6.5.0 and I need to support compression/decompression in the client (OData v4 Client Code Generator). I am using Deflate encoding for the compression via an ActionFilter. Everything compresses correctly on the server as confirmed via Fiddler but I do not know how to configure the client to support this now that the OData client uses the Request and Response Pipelines instead of the now defunct WritingRequest and RecievingResponse events that once supported this very scenario.
Attempts
By experimentation I found that I can hook into the ReceivingResponse event on my DataServiceContext and then call ReceivingResponseEventArgs.ResponseMessage.GetStream() but I don't know what to do to overwrite the message content correctly. If I CopyTo() on the stream, I get a null reference exception at Microsoft.OData.Core.ODataMessageReader.DetectPayloadKind(). I presume this is because the stream was read to the end and the position needs to be set back to zero but I cannot do that because the stream also throws an exception when setting the position back because it says it does not support seeking. I presume this is simply due to the stream being read-only. Even if I could copy the stream to decompress it successfully, how do I modify the response message content with the decompressed content? I don't see any hooks for this at all in the RequestPipeline or ResponsePipeline. To clarify, I want to decompress the response message content and then set it for the materialization that occurs soon after, how might I do that? Extra credit for how to also send compressed requests to the OData service. Thanks!
OData client use the HTTPWebRequest and HTTPWebReponse, which supports the compression well. Try setting the AutomaticDecompression of HTTPWebRequest to Deflate or GZip, in SendingRequest2 pipeline event, like this:
private void OnSendingRequest_(object sender, SendingRequest2EventArgs args)
{
if (!args.IsBatchPart) // The request message is not HttpWebRequestMessage in batch part.
{
HTTPWebRequest request = ((HttpWebRequestMessage)args.RequestMessage).HttpWebRequest;
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
}
Then in response, HTTPWebResponse will decompress the stream automatically, before the materialization work.
JAX-RS offers a wonderful way to specify content types in #Produces, and the framework will automatically determine the best content type from the client's HTTP Accept header and, wonder of wonders, even convert your object to that type (e.g. XML using JAXB or JSON using Jackson) when returning information to the caller.
My (work) client, as clients often do, made a simple job more difficult by requesting I specify the content type by the extension in the URL, e.g. api/widgets.json. This would force me to have various getWidgetsXXX() methods, one with #Produces("application/json"), another with #Produces("application/xml"), etc.
But I'm using Apache CXF and I was delighted to find that I could configure CXF to map various extensions to content types using the jaxrs.extensions init parameter!
<!-- registers extension mappings -->
<init-param>
<param-name>jaxrs.extensions</param-name>
<param-value>
xml=application/xml
json=application/json
</param-value>
</init-param>
But I can find absolutely no documentation on how this works in the real world. I naively thought I could just annotate a method with a path with an extension and it would mimic the Accepts header:
#Path("/widgets.{extension}")
#GET
#Produces({ "application/json", "application/xml" })
public List<Widget> getWidgets();
So I call it using api/widgets.json, and it returns XML! Which is particularly odd, because JAX-RS specifies that the default content type is the first one listed.
Where can I find out how to use CXF extension content type mapping?
P.S. I am not using Spring.
Adding the following in your <jaxrs:server> works:
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
Source: http://cxf.apache.org/docs/jax-rs.html#JAX-RS-Debugging
Don't know whether that help you or not but I was also facing the same issue to introduce something like that in my JAX-RS services. I achieved this functionality using JAX-RS_Content_Negotiation Following location has details about it.
https://docs.jboss.org/resteasy/docs/3.0.6.Final/userguide/html/JAX-RS_Content_Negotiation.html
You just have to map your media types with the values which you want
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
<param-value>
html : text/html, json : application/json, xml :
application/xml
</param-value>
</context-param>
#GET
#Path("/second/{param}")
#Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response printStudent(#PathParam("param") String msg) {
}
now i can access my services like that and response is according the extension which i put at the end
http://localhost:8080/RESTfulExample/rest/message/second/bill.json
you can put .xml OR .json at the end of the url and service will generate response accordingly.
In your situation, I'd declare that the method #Produces the content type */* (i.e., a full wildcard) and then do the content negotiation myself. You'd probably be looking at a method signature like this:
#javax.ws.rs.GET
#javax.ws.rs.Path("{filename}")
#javax.ws.rs.Produces("*/*")
javax.ws.rs.core.Response getDirectoryOrFileContents(
#javax.ws.rs.PathParam("filename") String filename,
#javax.ws.rs.core.Context javax.ws.rs.core.HttpHeaders headers);
That gives you access to both the desired filename — one way to guess the media type to deliver — and the full set of HTTP headers (hint: use headers.getAcceptableMediaTypes()), which give the other way. How to balance the two is likely to be “interesting”. (The code I've got to do it is very specific to my app's internal model, so isn't likely to be useful to you.) You then return the result by constructing a Response, which gives you quite close control over what the client gets back.
Yes, this is more work than letting CXF handle all this for you (it normally generates a lot of boilerplate to do all of this stuff) but in a complex case you'll be glad of the control.
The extension mimics the Accept header as you guessed. However you must not declare the extenstion in the #Path annotation:
#Path("/widgets")
#GET
#Produces({ "application/json", "application/xml" })
public List<Widget> getWidgets();
You can then call widgets.xml or widgets.json