Camel: Bean Proxy to CXF Endpoint - java

I'm currently trying to get familiar with Servicemix, Camel, CXF, etc. and have basically the same question as somebody had four years ago here:
How do I convert my BeanInvocation object in camel to a message body and headers?
Unfortunately, the answer there don't help me much. As one of the answers mentions: all examples on the Camel website concern themselves with sending something to a bean from CXF.
I have a bean proxy endpoint that I'm using in a POJO, injected via
#Produce(uri ="direct:start")
MyService producer; //public interface example.MyService { void myMethod(MyObject o);}
When I use another bean endpoint at the other end, implementing a consumer for that interface, this all works fine. What I now would like to do is to use camel-cxf to consume a web service implementing that interface instead. I created a cxfEndpoint via:
<cxf:cxfEndpoint id="cxfEndpoint"
address="http://localhost:8080/MyService/services/MyService"
wsdlURL="http://localhost:8080/MyService/services/MyService?wsdl"
serviceName="s:MyService"
serviceClass="example.MyService"
endpointName="s:MyService"
xmlns:s="http://example" />
What I'm now basically trying to do is, in a RouteBuilder:
from( "direct:start" ).to( "cxf:bean:cxfEndpoint" );
but get an Exception, when trying invoke something on the proxy object:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Part
{http://example}o should be of type example.MyObject, not
org.apache.camel.component.bean.BeanInvocation
From what I understand, the Spring proxy object generates a BeanInvocation object that can be consumed by another bean endpoint, and I have to transform this into a way the cxf can generate a SOAP request out of it (or is there some automatic conversion?).
But I'm kind of stuck doing that:
I tried soap marshalling as described at http://camel.apache.org/soap.html or writing my own Processor, but I'm not even sure if I just failed, or if that's not how it's supposed to work. I also tried to set the cxfEndpoint into the different message modes without success.
Any pointers what I should be generally doing would be greatly appreciated!

So after a week of trial and error, I found that the answer is quite simple. If the cxfEndpoint is set to POJO mode (the default), the solution is to just grab the invocation parameters and stuff them into the message body instead:
from( "direct:start" ).process( new Processor() {
#Override
public void process( Exchange e) throws Exception {
final BeanInvocation bi = e.getIn().getBody( BeanInvocation.class );
e.getIn().setBody( bi.getArgs() );
}
} ).to( "cxf:bean:cxfEndpoint" )
I guess this could be done more elegantly somehow though.

Related

Unable to create MockEndpoint for camel-timer

I am using Camel Timer component to read blobs from Azure storage container. A route is created which will poll for blobs every 10secs and is processed by the CloudBlobProcessor.
from("timer://testRoute?fixedRate=true&period=10s")
.to("azure-blob://storageAccountName/storageContainerName?credentials=#credentials")
.to(CloudBlobProcessor)
.to("mock:result");
I want to write a testcase by creating a mock endpoint something like this
MockEndpoint timerMockEndpoint = context.getEndpoint("timer://testRoute?fixedRate=true&period=10s", MockEndpoint.class);
But, I receive a below exception while creating the above mock endpoint.
java.lang.IllegalArgumentException: The endpoint is not of type:
class org.apache.camel.component.mock.MockEndpoint but is: org.apache.camel.component.timer.TimerEndpoint
Below is the code where I am trying to skip sending to the original endpoint
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("timer://testRoute?fixedRate=true&period=10s").skipSendToOriginalEndpoint()
.log("Original Batch Endpoint skipped")
.to("azure-blob://*")
.to(CloudBlobProcessor).to("mock:result");
from("timer://testRoute?fixedRate=true&period=10s").to("mock:result");
}
};
}
What I understand, we're trying to solve two different problems here:
MockEndpoint != TimerEndpoint
Interceptions
Answer to the first one is simple: MockEndpoints follow syntax mock:name. TimerEndpoint is a different endpoint and a totally different object. I don't know what you're aiming to do with the MockEndpoint here, but we just can't technically have a TimerEndpoint object as a MockEndpoint object. Why? Because that's how object oriented programming and Java work.
Let's take a look on the second problem. I've less than a year experience with Camel and I've only used interception once last year, but I hope I can guide you to some helpful direction.
The point of interception is to say "don't do that, do this instead". In this use case, it seems that we're only trying to skip sending a request to azure-blob endpoint. I'd try intercepting azure-blob://storageAccountName/storageContainerName?credentials=#credentials.
So instead of your interception, I'd try writing an interception like this:
interceptSendToEndpoint("azure-blob://storageAccountName/storageContainerName?credentials=#credentials")
.skipSendToOriginalEndpoint()
.log("Intercepted!");
In this case, instead of sending the request to azure-blob we intercept that request. We're telling Camel to skip the send to original endpoint, which means nothing will be sent to azure-blob://storageAccountName/storageContainerName?credentials=#credentials. Instead, we'll log "Intercepted!".

Spring Remoting with AMQP - Client is not seeing the beans exposed from Server

I'm trying to run example from http://www.baeldung.com/spring-remoting-amqp, even when I set up the connection to the dedicated vhost to my RabbitMQ broker, I can only send the request from client (I see it in RabbitMQ UI), but I never get the answer from the server.
The server seems to bean the service (the returning Impl class) with getBeanDefinitionNames(), but I definitly do not see those beans on the client side. I use annotations to set up beans, not the .xml file.
So the question is - why my client is not seeing the Server beans, I discover it more a less in following way:
#Autowired
private ApplicationContext appContext;
public GetResponse get(String id) {
Service service = appContext.getBean(Service.class);
System.out.println(service.ping());
return new GetResponse();
}
The answer which I get on the level of webservice is:
{
"timestamp": "2018-02-01T10:09:00.809Z",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.remoting.RemoteProxyFailureException",
"message": "No reply received from 'toString' with arguments '[]' - perhaps a timeout in the template?",
"path": "/v3/app/r"
}
Service:
public interface Service extends Serializable{
String ping();
}
Service Impl:
public class ServiceImpl implements Service {
#Override
public String ping() {
System.out.println("ponged");
return "pong";
}
#Override
public String toString() {
return "to string";
}
EDITED + BOUNTY
In the link you can find extracted modules which I want to connect together. I suppose that it is still about 'not seeing' the beans from one module in the second one.
The action can be trigerd with GET http://localhost:8081/v3/app/u The RabbitMQ settings has to be adjusted to your set-up.
https://bitbucket.org/herbatnic/springremotingexample/overview
I think you shouldn't set the routing key in your client, in amqpFactoryBean (and the one you set seems invalid):
https://bitbucket.org/herbatnic/springremotingexample/src/b1f08a5398889525a0b1a439b9bb4943f345ffd1/Mod1/src/main/java/simpleremoting/mod1/messaging/Caller.java?at=master&fileviewer=file-view-default
Did you try to run their example?
https://github.com/eugenp/tutorials/tree/master/spring-remoting/remoting-amqp
Just stumbled upon this question 3 years later.. trying to run the Baeldung example!
I tried debugging the issue and as far as I can tell, something internal in the AMQP implementation of spring remoting is not using the correct Routing Key when sending the client message, meaning the payload arrives at the broker and is never put into the queue for processing, we then timeout after 5s (default) on the client.
I tried the other answer by Syl to remove the routingKey however it doesn't seem to allow us to create a binding without one, and even when creating a binding directly on the broker management page (without a routing key) it doesn't route the messages.
I have not managed to make the example work, however I found a blog post on fatalerrors.org that shows a custom implementation of the AmqpProxyFactoryBean and it has custom handling for the routing key, this one works.
I've create this gist with the example that is working for me in case the blog post above goes under.
One other thing to note is that on the Baeldung example they are using a DirectExchange, while here we are using a TopicExchange.

Camel CXF Soap Client calling web service with multiple input parameters

I'm using Camel and have generated code from a WSDL using CXF. I generated a client stub and the implementation appears like this:
SetDeviceDetailsv4 port = ss.getSetDeviceDetailsv4Port();
com.vodafone.gdsp.ws.SetDeviceDetailsv4_Type _setDeviceDetailsv4_parameters = null;
com.vodafone.gdsp.ws.GdspHeader _setDeviceDetailsv4_gdspHeader = null;
com.vodafone.gdsp.ws.SetDeviceDetailsv4Response _setDeviceDetailsv4__return = port.setDeviceDetailsv4(_setDeviceDetailsv4_parameters, _setDeviceDetailsv4_gdspHeader);
System.out.println("setDeviceDetailsv4.result=" + _setDeviceDetailsv4__return);
As you one can see, the port takes two parameters and returns the response, which I want to delegate back to my Camel Route. What's the best way to implement this in Camel? I already have my CXF Enpoint defined, I'm just struggling with the DSL Routing part of it. Should I add a processor like what is found in this link? Apache Camel and web services
Thanks
You can use jax-ws client (implement as bean) and use it in camel DSL. JAX-WS client bean definition takes service class/interface and allow you configure additional properties like SSL config & etc. In route, we can use it as bean. It takes JAXB generated Request object (WSDL request object) as input and returns the JAXB generated Response Object (WSDL response object). To convert you pojo to JAXB classes, Dozer framework can be used or custom mapping can be also used.
Jax-WS client is also flexible to take XML as request and response. In that case, properties need to be set as DATAFORMAT as PAYLOAD.
I'm not sure if this is the correct way to do it but I added both of my "input" objects as a Camel Header, then I wrote a processor that grabbed what I needed and put the two objects that the service call needed as parameters.
public void process(Exchange exchange) throws Exception {
Message inMessage = exchange.getIn();
gdspHeader = inMessage.getHeader(GDSP_HEADER, com.vodafone.gdsp.ws.GdspHeader.class);
commModule = inMessage.getHeader(COMM_MODULE_HEADER, resmed.hi.ngcs.datastore.model.CommModule.class);
SetDeviceDetailsv4_Type deviceDetails = createSetDeviceDetailsv4(commModule);
List<Object> params = new ArrayList<>();
params.add(deviceDetails);
params.add(gdspHeader);
inMessage.setBody(params);
}
`

Im using camel proxy how can i send headers in interface as parameter

Im using camel proxy to call an endpoint in camel say direct:say.
public interface xyz{
public void sayhello(String body,??????);
}
??? i want to set headers or send headers can any one help with an example in binding interface.
Thanks
Saitsh
You should have a look at http://camel.apache.org/parameter-binding-annotations.html. One nifty example:
public void sayhello(#Header("user") String user, #Body String body, Exchange exchange) {
exchange.getIn().setBody(body + "MyBean");
}
Beside that following annotations are available:
#Headers to bind to the Map of the inbound message headers
#OutHeaders to bind to the Map of the outbound message headers
Very quick leads...
look at http://camel.apache.org/bean and http://camel.apache.org/bean-binding.html
JndiContext context = new JndiContext();
context.bind("xyz", new XyzImp());
CamelContext camelContext = new DefaultCamelContext(context);
then you can call
to("bean:xyz?method=sayhello(${body}, ${headers})")
or you can add annotation to you interface
sayhello(#Body String body,#Headers Map headers);
and then
to("bean:xyz?method=sayhello(*, *)")
or
to("bean:xyz?method=sayhello")
should be enough...
The BIG question is, how do you instantiate your xyz interface? Is it singleton or you need a fresh instance for each message or one per thread? But that would be different question :)
Camel 2.16+
Use native proxy parameter binding as described here: http://camel.apache.org/using-camelproxy.html ("What is send on the Message" section).
Previous versions
There no standard way of parameter binding for Camel route proxy. Only one String argument can be bound to exchange body.
But you can create custom implementation of InvocationHandler which will extend Camel core AbstractCamelInvocationHandler class and provide desired parameter binding by overriding of invokeWithbody(...) method.
Check Camel ProxyUtil.createProxyObject(...) method to understand how proxy object is initiated.

How to initialize Jersey with a a specific resource instance with specific MessgeBodyReaders/Writers?

I'm trying to start Jersey on a preconfigured port/url with a preconfigured resource instance. I can't quite figure out how to correctly do it.
Here is a snippet of a code. Help me, please, fill in the blanks:
#Component
#PerRequest
#Path("/svc")
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class MyService
{
// This piece is known
}
public class JSONMessageBodyWriter implements MessageBodyWriter<Object>
{
// This piece is known
}
public class XMLMessageBodyWriter implements MessageBodyWriter<Object>
{
// This piece is known
}
// This is where I need help
MyService service = new MyService();
...
HttpHandler handler = ???
...
HttpServer server = ???
server.createContext("/services", handler);
...
server.start();
In the snippet above, I'm trying to expose the MyService via the http://localhost:8080/services/svc url. If the JSONMessageBodyWriter and the XMLMessageBodyWriter will be plugged in - the service will work vis XML and JSON accordingly.
If you know how to do this on Jetty or Grizzly, let me know too. Can Spring help here?
Jersey itself provides an entire set of examples, and specifically the simplest helloworld example shows how to start a server on a port to either just run it or test against in JUnits. If you look at that, you'll get example for how to setup and start a server.
Now, on configuring MessageBodyReaders and MessageBodyWriters as part of a jersey application, you'll find that this is covered by the JAX-RS spec itself (which jersey implements). First off, your reader and writer need the #Provider annotation. Additionally, the reader should get the #Consumes annotation, and the writer should get the #Produces annotation, so you can specify what mime-type(s) they consume and produce, respectively.
Next is activating them. The helloworld example above won't show that, because it doesn't use custom readers or writers (another example might, I didn't look). So instead of supplying the package to find resources (as they do; you'll know what I speak of when you see the helloworld example), you'll code a subclass of Application, where you specify your resource class and the reader/writer classes. With the reader and writer you have the option of specifying either a class (that you return from getClasses), or providing an already created instance yourself (that you return from getSingletons).
Finally, specify the name of your Application sub-class as the value of init-parameter "javax.ws.rs.Application". The init-params can be passed to GrizzlyWebContainerFactory.create (again, you'll see this used in the example) when you start the server.
Hope this helps.

Categories

Resources