Below is my routing for a sample web service in camel and cxf.
from("cxf:http://localhost:9000/sampleService?serviceClass=com.sample.CommonIntf")
.id("wsProxy")
.bean(MyBean.class)
Simply I am passing input pojo object to bean. And inside bean I am setting the ws response. Here is the bean class.
#Handler
public SOut handle(SInput sin){
SOut s = new SOut();
s.setName(sin.getName());
s.setSurName("aa");
return s;
}
However althoug I can see input object is converted and delivered the handler method soap response is empty.
Here is my web service signature.
public interface CommonIntf{
SOut sampleMethod(SInput input);
}
My question is although my handler returns response, why response soap is empty ?
I think, you just not set exchange output body (request-reply pattern).
Try to modify your route like this:
from("cxf:http://localhost:9000/sampleService?serviceClass=com.sample.CommonIntf")
.id("wsProxy")
.to("bean:MyBean?method=handle");
MyBean class must be registered in bundle context.
<bean id="MyBean" class="com.sample.MyBean"/>
Try the following , define your CXF endpoint
[as per http://camel.apache.org/schema/cxf/ ] in a endpoint definition bean, in this refer the service class,
and refer the same id (for example wsCxfId)in the Camel route.
So the route will be as follows:
from("cxf:bean:wsCxfId")
.id("wsProxy")
.to("bean:MyBean?method=handle");
Hope this helps.
Related
I'm writing a POC for Quarkus. I'm using this quick start guide to build a REST client. The REST service I'll be integrating with is third party. Here is a simple example of my current implementation:
#Path("/v1")
#RegisterRestClient
public class EmployeeApi {
#POST
#Path("/employees")
ApiResponse createEmployee(#RequestBody Employee employee)
}
This works fine. The issue I'm having is that the third party API will, depending on success / failure, return a response body. In the scenario it does fail, it provides details in the response body (ApiResponse) on why it was unsuccessful. When it succeeds, it returns nothing. This causes Quarkus to throw the following exception:
javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type application/octet-stream and type com.test.app.ApiResponse
I've tried to wrap ApiResponse in an Optional type but does not solve the problem. I see absolutely nothing in Quarkus / RESTEasy documentation that would indicate a work-around.
I'm wondering if I should be using javax.ws.rs.core.Response instead.
The problem is JaxRS tries to fit ApiResponse to a default return type being application/octet-stream
You should make sure to specify explicitly that you're returning application/json
This is possible using #Produces(APPLICATION_JSON) on top of your service.
Here is the correct code snippet
#Path("/v1")
#RegisterRestClient
public class EmployeeApi {
#POST
#Path("/employees")
#Produces(APPLICATION_JSON)
ApiResponse createEmployee(#RequestBody Employee employee)
}
I have rest controller with request mapping as follows:
#PostMapping(value = "fpl/generate/{legIdentifier:.+}"
My camel route is defined as from("direct:/fpl/generate/").
The controller calls web service, web service calls FluentEndpointInvoker class which calls route defined above.
public class FluentEndpointInvoker {
#EndpointInject(uri = BASE_ENDPOINT_URI)
private FluentProducerTemplate producer;
#Value("${server.servlet.context-path}")
private String contextRoot;
public <T, R> T request(Class<T> type, R request, HttpHeaders headers) {
return producer.withProcessor(exchange -> {
exchange.getIn().setBody(request, request.getClass());
headers.forEach((key, value) -> exchange.getIn().setHeader(key, value));
String endpoint = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()
.getRequestURI();
exchange.getIn().setHeader(ROUTE_ENDPOINT_HEADER, "direct:".concat(endpoint.replaceFirst(contextRoot, "")));
}).request(type);
}
}
The endpoint that is generated is something like direct:///fpl/generate/LH.1234.30Jun2016.FRA.BOG.X1. How can I configured wildcards in camel route so that endpoint can get called
Well, if you are not forced to use an endpoint with the full URI, you could simplify the case.
Instead of creating a dynamic endpoint, you could send all messages to the endpoint direct:///fpl/generate and set the full request URI as header on the message.
That way you have a simple route endpoint to use and the URI header to make decisions etc based on the full URI.
I'm using the Camel component Vertx: http://camel.apache.org/vertx.html
The headers set in Camel are not sent into the eventBus address:
from("direct:inbound")
.setHeader("myHeader").constant("myHeaderValue")
.to("vertx:inboundAddress");
inboundAddress does not receive the header "myHeader".
I've checked the code of the VertxProducer in Vertx Component and it actually only send the body.Is there any reason why the headers are not sent to the eventBus?
Btw, the documentation on GitHub is much more up to date: https://github.com/apache/camel/blob/master/components/camel-vertx/src/main/docs/vertx-component.adoc
I agree that the vertx headers should be accessible via camel. If someone sees an official way it is supported I would love to know. Until then there is an easy workaround though. Instead of passing just the body in the message pass both the headers and the body as the message body and both will be accessible.
JsonObject message = new JsonObject();
message.put("body", routingContext.getBodyAsJson());
DeliveryOptions d = new DeliveryOptions()
.addHeader("myheader", "myheadervalue")
.addHeader("h2", "h2value");
message.put("deliveryOptions", d.toJson());
vertx.eventBus().send("myVertxEndpoint",
message,...
Later in Camel, you could call a method similar to the following as part of your route to have the Vertx headers put inside the camel headers.
public void setVertxHeaders(#Body JsonObject jsonObject, Exchange exchange ) {
exchange.getIn().getHeaders()
.putAll(jsonObject.getJsonObject("deliveryOptions").getJsonObject("headers")
.getMap());
}
You're setting the headers incorrectly.
Instead of
.setHeader("myHeader").constant("myHeaderValue")
Do
.setHeader("myHeader", constant("myHeaderValue"))
See their test suite for more references:
https://github.com/apache/camel/blob/3505e718db48cc0d8be5b47f6c4030c958d93a5f/camel-core/src/test/java/org/apache/camel/component/bean/BeanWithAnnotationInheritedTest.java
I am using the recipientList tag in my route as shown below. The bean findCallbackUrl will return a String which is my destination address. i.e i will be making a POST request to this endpoint . For example i may have a server listening at http://localhost:8080/acceptcallbacks .
<recipientList>
<method ref="findCallbackUrl"/>
</recipientList>
So when the String(destination) returned from the findCallbackUrl bean is jetty:http://localhost:8080/acceptcallback , the POST works fine.
However when some of the option mentioned in the Camel-jetty component are used so issues are seen. when the destination returned is jetty:http://localhost:8080/acceptcallbacks?enableJmx=false or jetty:http://localhost:8080/acceptcallbacks?disableStreamCache=false the POST works fine. However if the returned string is jetty:http://localhost:8080/acceptcallbacks?chunked=false , the call becomes a GET request .
Not sure what is happening here. If the Camel-jetty options are not to be used in the way they have been used above , for options enableJmx or disableStreamCache or some other options , the resulting destination URL should have been http://localhost:8080/acceptcallbacks?enableJmx=false which is a GET request.
Can chunked=false be used for producer as well as consumer endpoints or is it only for consumer endpoint?
Selection of http method in jetty depends on In Body of camel exchange, or on CamelHttpMethod header.
Look at source code of class org.apache.camel.component.http.helper.HttpHelper, method createMethod:
public static HttpMethods createMethod(Exchange exchange, HttpEndpoint endpoint, boolean hasPayload) throws URISyntaxException {
// compute what method to use either GET or POST
HttpMethods answer;
HttpMethods m = exchange.getIn().getHeader(Exchange.HTTP_METHOD, HttpMethods.class);
if (m != null) {
// always use what end-user provides in a header
answer = m;
} else if (hasPayload) {
// use POST if we have payload
answer = HttpMethods.POST;
} else {
// fallback to GET
answer = HttpMethods.GET;
}
return answer;
}
Now look at the call of this method in class org.apache.camel.component.jetty.JettyHttpProducer:
HttpMethods methodToUse = HttpHelper.createMethod(exchange, getEndpoint(), exchange.getIn().getBody() != null);
IMHO options don't matter. Check exchange.getIn().getBody() before request to HTTP, or you can reset header CamelHttpMethod.
I am developing JAX-RS Rest service using Apache CXF. After deploying it to Tomcat 7 server, if I type the URL http://localhost:8080/Rest/rest?_wadl it shows me the WADL. but if I enter the URL http://localhost:8080/Rest/rest/retrieve it gives me 404 error.
In above URLs: Rest is the name of my project
/rest is the url-pattern for my CXFServlet which is specified in web.xml
/ is the address of jaxrs:server which is specified in beans.xml
retrieve is the path of service which is specified in my interface with #Path annotation.
(My apologies: I can't provide the XML documents referred to above.)
I think this is a CXF bug which get the incorrect base URL for restful web services.
The class "org.apache.cxf.transport.servlet.ServletController" invokes the method "getBaseURL" of the class "org.apache.cxf.transport.servlet.BaseUrlHelper".
It gets the base URL from request URL, and it ignores the parameters part.
This is correct for SOAP web servcies, because SOAP web services URL is just like: http://host:port/basepath?para=a. Unfortunately, for restful web services, the URL is just like http://host:port/basepath/method/parameter. The correct base URL should be http://host:port/basepath, but actually, the BaseUrlHelper gives you http://host:port/basepath/method/parameter. It just gives the URL before "?". It's why the result is correct when you access http://localhost:8080/Rest/rest?_wadl, in this case, it gives the correct base URL http://localhost:8080/Rest.
If you access http://localhost:8080/Rest/rest?_wadl at first then you access http://localhost:8080/Rest/rest/retrieve, it would be correct. Because, CXF set the base URL as the address of EndpointInfo only at the first time. It means, you MUST access the correct base URL at the first time! :(
The solution is: override the method "getBaseURL(HttpServletRequest request)" of "org.apache.cxf.transport.servlet.ServletController", let it return correct base URL.
For example, step1: extends the ServletController.
public class RestfulServletController extends ServletController {
private final String basePath;
public RestfulServletController(DestinationRegistry destinationRegistry, ServletConfig config,
HttpServlet serviceListGenerator, String basePath) {
super(destinationRegistry, config, serviceListGenerator);
this.basePath = basePath;
}
#Override
protected String getBaseURL(HttpServletRequest request) {
// Fixed the bug of BaseUrlHelper.getBaseURL(request) for restful service.
String reqPrefix = request.getRequestURL().toString();
int idx = reqPrefix.indexOf(basePath);
return reqPrefix.substring(0, idx + basePath.length());
}
}
step2: extends CXFNonSpringServlet and use the RestfulServletController in the subclass
public class RestfulCXFServlet extends CXFNonSpringServlet {
... ...
private ServletController createServletController(ServletConfig servletConfig) {
HttpServlet serviceListGeneratorServlet = new ServiceListGeneratorServlet(destinationRegistry, bus);
ServletController newController = new RestfulServletController(destinationRegistry, servletConfig,
serviceListGeneratorServlet, basePath);
return newController;
}
}
step3: instead of CXFNonSpringServlet , you use the derived class RestfulServletController.
Don't forget, you should config the "basePath" as /Rest/rest.
Hope this can help you.