I'm trying to build simple Camel route to public webservice,
http://wsf.cdyne.com/WeatherWS/Weather.asmx
I'm using latest Camel with Spring, CXF, SOAP and Java Config Modules.
Here's my CxfEndpoint :
#Bean(name = "testCxfBean")
public CxfEndpoint createTestEndpoint() throws ClassNotFoundException {
CxfEndpoint endpoint = new CxfEndpoint();
endpoint.setAddress("http://wsf.cdyne.com/WeatherWS/Weather.asmx");
endpoint.setWsdlURL("http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL");
endpoint.setEndpointNameString("WeatherSoap");
endpoint.setServiceNameString("Weather");
return endpoint;
}
And here is my route :
#Component
public class TestCXFRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
SoapJaxbDataFormat dataFormat = new SoapJaxbDataFormat("com.cdyne.ws.weatherws", new ServiceInterfaceStrategy(
WeatherSoap.class, true));
from("timer:testTimer?period=5000").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody(new GetWeatherInformation());
}
}).setHeader("operationName", constant("GetWeatherInformation")).marshal(dataFormat)
.to("cxf:testCxfBean?serviceClass=com.cdyne.ws.weatherws.WeatherSoap").log("level:info");
}
}
What I'm trying to do here is query weather information every 5 seconds, using GetWeatherInformation operation, marshalling relevant object, and simply logging result.
However I'm getting such error :
Exchange[
Id ID-darkstar-dev-39021-1423838783518-0-4
ExchangePattern InOnly
Headers {breadcrumbId=ID-darkstar-dev-39021-1423838783518-0-3, CamelRedelivered=false, CamelRedeliveryCounter=0, firedTime=Fri Feb 13 15:46:31 CET 2015, operationName=GetWeatherInformation}
BodyType byte[]
Body <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Envelope xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://ws.cdyne.com/WeatherWS/"> <ns2:Body> <ns3:GetWeatherInformation/> </ns2:Body></ns2:Envelope>
]
Stacktrace
---------------------------------------------------------------------------------------------------------------------------------------
java.lang.IllegalArgumentException: Get the wrong parameter size to invoke the out service, Expect size 0, Parameter size 252. Please check if the message body matches the CXFEndpoint POJO Dataformat request.
What am I doing wrong ?
Related
In my Springboot Camel application I'm using the Kafka integration to produce message on a topic.
I compared message produced by Camel Kafka integration with message produced by Kafka Spring integration and I noticed one little difference:
in OffsetExplorer, messages produced by Spring Kafka uses an Header "spring_json_header_types" with the class of all other headers, and header Long or Integer are correctly showed and serialized.
With Camel instead, if I set an header not String, in OffsetExplorer it is represented with the square char.
I did my research and found that Spring Kafka uses the DefaultKafkaHeaderMapper to create this header, and this help the inbound process to know how to deserialize the other header.
How can I use it in my Camel Kafka Producer?
Here my code:
#Component
public class KafkaProducerRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:myTimer?period=10000")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Map<String, Object> headers = new HashMap<>();
headers.put("TIMER_COUNTER_1", exchange.getProperty(Exchange.TIMER_COUNTER, Long.class));
headers.put("TIMER_COUNTER_2", String.valueOf(exchange.getProperty(Exchange.TIMER_COUNTER, Long.class)));
exchange.getIn().setHeaders(headers);
}
})
.to("kafka:topic1?brokers=localhost:9092");
}
}
Trying to handle an empty SOAP message with Spring Web Services but failing.
So, I have a request to provide an endpoint for a sort of a PING method. Basically the SOAP messages I can handle look like this:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:def="http://www.something.com/edf/services/defaultservice">
<soapenv:Header/>
<soapenv:Body>
<def:ServiceReqType>
<transactionId>1111</transactionId>
<subscriberId>2222</subscriberId>
</def:ServiceReqType>
</soapenv:Body>
</soapenv:Envelope>
and that I can handle with an endpoint that is handling ServiceReqType.
But the PING looks like this:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body/>
</soapenv:Envelope>
That I can't handle, Spring logs a Can't handle [SaajSoapMessage].
What I need to return is the exactly same message.
I understand there that there is a type/class missing that I would provide to #PayloadRoot.
So I am wondering what would an endpoint specification be for this empty bodied request?
Just for reference here is my endpoint for handling the ServiceReqType:
#PayloadRoot(namespace = NAMESPACE_EDF, localPart = "ServiceReqType")
#ResponsePayload
public ServiceRespType serviceResponse(#RequestPayload ServiceReqType request) {
LOGGER.debug("-----> ServiceReqType:{}", request);
return reqProcessor.process(request);
}
UPDATE 1:
So I tried with implementing the interceptor in the following way:
public class CustomEndpointInterceptor implements EndpointInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomEndpointInterceptor.class);
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("---> Message context: {}", messageContext.toString());
LOGGER.info("---> Message endpoint: {}", endpoint.toString());
return false;
}
#Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("handleResponse");
return false;
}
#Override
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
LOGGER.info("handleFault");
return false;
}
#Override
public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
LOGGER.info("afterCompletion");
}
}
and then in the WebServiceConfiguration class I added this:
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new CustomEndpointInterceptor());
super.addInterceptors(interceptors);
}
But it still does not work. What I get is that the interceptor is called when SOAP message has a BODY but when it is sent a Ping that is BODYless then I get again the same error message and interceptor is not called. So it seems I must find an interceptor that is further up the chain...
UPDATE 2:
Here is how WSDL file looks like for this Ping...
<message name="PingRequest"/>
<message name="PingResponse"/>
There is nothing as a part inside of these...
For comparison this is how ServiceReqType looks:
<message name="ServiceReqType">
<part name="body" element="tns:ServiceReqTypeDefinition"/>
</message>
and then the ServiceReqTypeDefinition is defined in an accompanying xsd file.
UPDATE 3:
So, found the reason why interceptors won't work on this type of message :-/
Below code is from MessageDispatcher line 234.
// No triggering of interceptors if no endpoint is found
if (endpointNotFoundLogger.isWarnEnabled()) {
endpointNotFoundLogger.warn("No endpoint mapping found for [" + messageContext.getRequest() + "]");
}
throw ex;
So it's on purpose, now I need to find how to handle these incomplete or incorrect SOAP messages... any ideas because rewriting Dispatcher doesn't feel like the right way.
UPDATE 4
So, I managed to intercept the payload, the one that is invalid, by extending AbstractEndpointMapping class and then overriding getInternalEndpoint gives me the possibility to evaluate the message and see if it is this empty Ping request I have been trying to process.
I think this would all be easily solved if I knew how to define a defaultEndpoint because I see that in case SOAP message is not recognised, so no endpoint mapping is found, this defaultEndpoint is used to handle that message.
I noticed that when working with XML specification of Beans then there is a property to define the endpoint as a default one, but how to do it when using annotations?
Another way is to just create an endpoint and then return it from getInternalEndpoint so spring can handle processing, I guess, but I don't know yet how to create an endpoint object... working on that now.
p.s. This documentation mentions defaultEndpoint but not how to set it up in a non XML defining Bean way.
So, in the end I had to create a custom exception handler. Something like this:
public class CustomEndpointNotFoundException extends Exception {
public CustomEndpointNotFoundException(String message) {
super(message);
}
}
then:
#Component
#Order(Ordered.LOWEST_PRECEDENCE)
public class NoEndpointFoundEndpointMapping implements EndpointMapping {
#Override
public EndpointInvocationChain getEndpoint(MessageContext messageContext) throws Exception {
throw new CustomEndpointNotFoundException("");
}
}
and finally:
#Component
public class CustomEndpointExceptionResolver implements EndpointExceptionResolver {
#Override
public boolean resolveException(MessageContext messageContext, Object endpoint, Exception ex) {
if (messageIsPing()) {
return true;
}
return false;
}
}
So basically I am handling it as an error. It's not really ideal solution IMHO, I will still see if I can intercept the endpoint resolution, or define a default endpoint.
I'm new to Camel and am trying to get a response from a Netty4 route using a POST request. I'd like to send a JSON and return a string extracted from the body.
My rest setup is as follows:
public class Server extends RouteBuilder {
#Override
public void configure() {
String listenAddress = "0.0.0.0";
int listenPort = 8080;
restConfiguration()
.component("netty4-http")
.scheme("http")
.host(listenAddress)
.dataFormatProperty("prettyPrint", "true")
.bindingMode(RestBindingMode.auto)
.port(listenPort);
rest("/")
.post()
.consumes("application/json; charset=UTF-8")
.to("direct:post");
}
}
Within my Camel route I'd like to send the message back using:
#Component
public class RestRoute extends RouteBuilder {
#Autowired
CamelContext context;
#Override
public void configure() {
from("direct:post")
.log("New Request")
.streamCaching()
.setHeader(Exchange.HTTP_METHOD,constant(org.apache.camel.component.http4.HttpMethods.POST))
.setBody().jsonpath("$.Text") // extract text from JSON
.to("http4://0.0.0.0:8080?bridgeEndpoint=true");
However I get the following error: org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://0.0.0.0:8080 with statusCode: 500
I'd appreciate some help!
Oh you should not send the message back, this happens automatic when the routing ends, then the message at that point is used as the response message for the rest.
So remove
.to("http4://0.0.0.0:8080?bridgeEndpoint=true");
I am new to Apache camel. I am trying to create routes to call multiple rest APIs and aggregate the response into one.
But for some reason, the JSON request that I am creating does not reach the rest endpoint.
During debug, I see that Exchange objects do have the values that I have set and get converted into byte array and on the other side, rest API recieves empty objects.
I am working on a Spring boot project and I have tried different ways of marshalling the request to JSON including Gson and Jackson. None of which seem to work.
Please assist.
from("direct:oneResponse")
.multicast(new MyAggregationStrategy()).parallelProcessing()
.to("direct:rest1call", "direct:rest2call")
.end();
from("direct:rest1call")
.routeId("rest1call")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody(<<valid json>>); //json values as required for the rest call.
}
})
.to("http4://localhost:5555/mock/rest1call")
.setProperty("route", simple("routeId"))
.unmarshal(new JacksonDataFormat(Rest1Response.class));
from("direct:rest2call")
.routeId("rest2call")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody(<<valid json>>); //json values as required for the rest call.
}
})
.to("http4://localhost:5555/mock/rest2call")
.setProperty("route", simple("routeId"))
.unmarshal(new JacksonDataFormat(Rest2Response.class));
Can you try to create a processor and specify all the headers and body in it?
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getOut().setHeader(Exchange.HTTP_METHOD, HttpMethod.POST);
exchange.getOut().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getOut().setHeader("Accept", "application/json");
/* this is one way, string representation of json, but maybe you can try to build Model and fill that model with data */
exchange.getIn().setBody(<<valid json>>); //json values as required for the rest call.
}
})
if you decide to go with models, use marshaling after processor just to be sure your data is converted to JSON.
.marshal(yourDataFormat)
Try GsonDataFormat it works pretty good for me.
I am trying to save a value on the Camel Exchange between a Request - Response invocation against a QPID endpoint.
You can see from my code that I set a Header (and Property) before i invoke the Endpoint. Upon return the same Header and Property Values are null.
I basically want to keep a track of the fileName and filePath so that I can write the results into the same location
Really struggling with this.
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Value;
public class ProcessingRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
//#formatter:off
from("file:/home/molko/in/?recursive=true&include=.*.txt")
.log("File read from disk : ${file:name}")
.doTry()
.setHeader("JMSReplyTo", constant("response-1"; {create:always, node:{type:queue}}"))
.setHeader("JMSCorrelationID", constant(java.util.UUID.randomUUID().toString()))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
final String fileParent = exchange.getIn().getHeader("CamelFileParent", String.class);
final String endPath = fileParent.substring(fileParent.lastIndexOf('/') + 1);
exchange.getIn().setHeader("endPath", endPath);
exchange.setProperty("endPath", endPath);
}
})
.to(amqp:request-1;{node:{type:queue}}?preserveMessageQos=true?exchangePattern=InOut")
.doCatch(Exception.class)
.log("Failed : ${file:name}")
.log("${exception.stacktrace}")
.stop();
from("amqp:response-1; {create:always, node:{type:queue}}")
.log("Received from qpid broker : ${date:now}")
.doTry()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
byte[] response = exchange.getIn().getBody(byte[].class);
System.out.println("properties : " + exchange.getProperties());
System.out.println("headers : " + exchange.getIn().getHeaders());
}
})
.to("file:/home/molko/out")
.doCatch(Exception.class)
.log("Failed from qpid brokre : ${date:now}")
.log("${exception.stacktrace}")
.stop();
//#formatter:on
}
}
includeAllJMSXProperties is probably what you are looking for ,
Camel 2.11.2/2.12: Whether to include all JMSXxxx properties when
mapping from JMS to Camel Message. When set to true properties such as
JMSXAppID, and JMSXUserID etc will be included. Note: If you are using
a custom headerFilterStrategy then this option does not apply.
Source : https://camel.apache.org/jms.html