Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can anyone point me to a working example Camel route using a cxfrs client/producer?

I am having trouble getting my Camel route to successfully POST a message to an existing RESTful web service. I have tried all the examples in the camel cxf package but none of them produce a web service call (they are consumers). I would love to find a working example for this so I can step through the CxfRsProducer execution to hopefully discover why my route is not posting correctly to the web service.

Here is my RouteBuilder's configuration:

public void configure()
{
    //errorHandler(deadLetterChannel(String.format("file:%s/../errors", sourceFolder)).useOriginalMessage().retriesExhaustedLogLevel(LoggingLevel.DEBUG));
    errorHandler(loggingErrorHandler());

    /*
     * JMS to WS route for some of the events broadcast to the jms topic
     */
    Endpoint eventTopic = getContext().getEndpoint(String.format("activemq:topic:%s?clientId=%s&durableSubscriptionName=%s", eventTopicName, durableClientId, durableSubscriptionName));

    from(eventTopic)            // listening on the jms topic
    .process(eventProcessor)    // translate event into a Notifications object (JAX-RS annotated class)
    .choice()                   // gracefully end the route if there is no translator for the event type
    .when(header("hasTranslator").isEqualTo(false)).stop() // no translator stops the route
    .otherwise()                // send the notification to the web service
    .to("cxfrs:bean:rsClient"); 

}

Here is the rsClientBean:

    <cxf:rsClient id="rsClient" 
              address="http://localhost/ws"
              serviceClass="com.foo.notifications.NotificationsResource"
              loggingFeatureEnabled="true" />

I'm pretty new to REST and I don't really understand what the serviceClass does for the rsClient because it looks to me like the definition of the exposed web service on the server.

The NotificationsResource class:

@Path("/notifications/")
public class NotificationManagerResource
{
    // NOTE: The instance member variables will not be available to the
    // Camel Exchange. They must be used as method parameters for them to
    // be made available
    @Context
    private UriInfo uriInfo;

    public NotificationManagerResource()
    {
    }

    @POST
    public Response postNotification(Notifications notifications)
    {
        return null;
    }

}

The processor creates a Notifications object to put in the exechange message body:

private class EventProcessor implements Processor
{
    @Override
    public void process(Exchange exchange) throws Exception
    {
        Message in = exchange.getIn();

        IEvent event = (IEvent) in.getBody();
        Notifications notifications = null;

        in.setHeader("hasTranslator", false);
        in.setHeader("Content-Type", "application/xml");
        in.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, false);
        // I've tried using the HTTP API as 'true', and that results in a 405 error instead of the null ptr.


        INotificationTranslator translator = findTranslator(event);

        if (translator != null)
        {
            notifications = translator.build(event);
            in.setHeader("hasTranslator", true);
        }

        // replace the IEvent in the body with the translation
        in.setBody(notifications);

        exchange.setOut(in);
    }

}

The Notifications class is annotated with JAXB for serialization

@XmlRootElement(name = "ArrayOfnotification")
@XmlType
public class Notifications
{
    private List<Notification> notifications = new ArrayList<>();

    @XmlElement(name="notification")
    public List<Notification> getNotifications()
    {
        return notifications;
    }

    public void setNotifications(List<Notification> notifications)
    {
        this.notifications = notifications;
    }

    public void addNotification(Notification notification)
    {
        this.notifications.add(notification);
    }    
}

The error that is returned from the web service:

Exchange
---------------------------------------------------------------------------------------------------------------------------------------
Exchange[
    Id                  ID-PWY-EHANSEN-01-62376-1407805689371-0-50
    ExchangePattern     InOnly
    Headers             {breadcrumbId=ID:EHANSEN-01-62388-1407805714469-3:1:1:1:47, CamelCxfRsUsingHttpAPI=false, CamelRedelivered=false, CamelRedeliveryCounter=0, Content-Type=application/xml, hasTranslator=true, JMSCorrelationID=null, JMSDeliveryMode=2, JMSDestination=topic://SysManEvents, JMSExpiration=1407805812574, JMSMessageID=ID:EHANSEN-01-62388-1407805714469-3:1:1:1:47, JMSPriority=4, JMSRedelivered=false, JMSReplyTo=null, JMSTimestamp=1407805782574, JMSType=null, JMSXGroupID=null, JMSXUserID=null}
    BodyType            com.ehansen.notification.types.v2.Notifications
    Body                <?xml version="1.0" encoding="UTF-8"?><ArrayOfnotification xmlns="http://schemas.datacontract.org/2004/07/ehansen.Notifications.Dto">   <notification>      <causeType>EVENT_NAME</causeType>      <causeValue>DeviceEvent</causeValue>      <details>         <notificationDetail>            <name>BUSY</name>            <value>false</value>            <unit>boolean</unit>         </notificationDetail>         <notificationDetail>            <name>DESCRIPTION</name>            <value>Software Computer UPS Unit</value>            <unit>name</unit>         </notificationDetail>         <notificationDetail>            <name>DEVICE_NUMBER</name>            <value>1</value>            <unit>number</unit>         </notificationDetail>         <notificationDetail>            <name>DEVICE_SUB_TYPE</name>            <value>1</value>            <unit>type</unit>         </notificationDetail>         <notificationDetail>            <name>DEVICE_TYPE</name>            <value>UPS</value>            <unit>type</unit>         </notificationDetail>         <notificationDetail>            <name>FAULTED</name>            <value>false</value>            <unit>boolean</unit>         </notificationDetail>         <notificationDetail>            <name>RESPONDING</name>            <value>true</value>            <unit>boolean</unit>         </notificationDetail>         <notificationDetail>            <name>STORAGE_UNIT_NUMBER</name>            <value>1</value>            <unit>number</unit>         </notificationDetail>      </details>      <sourceType>DEVICE_ID</sourceType>      <sourceValue>1:UPS:1</sourceValue>      <time>2014-08-11T18:09:42.571-07:00</time>   </notification></ArrayOfnotification>
]

Stacktrace
---------------------------------------------------------------------------------------------------------------------------------------
java.lang.NullPointerException
    at java.lang.Class.searchMethods(Class.java:2670)
    at java.lang.Class.getMethod0(Class.java:2694)
    at java.lang.Class.getMethod(Class.java:1622)
    at org.apache.camel.component.cxf.jaxrs.CxfRsProducer.findRightMethod(CxfRsProducer.java:266)
    at org.apache.camel.component.cxf.jaxrs.CxfRsProducer.invokeProxyClient(CxfRsProducer.java:222)
    at org.apache.camel.component.cxf.jaxrs.CxfRsProducer.process(CxfRsProducer.java:90)
    at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
    at org.apache.camel.processor.SendProcessor$2.doInAsyncProducer(SendProcessor.java:143)
    at org.apache.camel.impl.ProducerCache.doInAsyncProducer(ProducerCache.java:307)
    at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:138)

It is the methodName parameter in the following method from CxfRsProducer class that is null... so I assume there is something about my rsClient that is not configured correctly.

private Method findRightMethod(List<Class<?>> resourceClasses, String methodName, Class<?>[] parameterTypes) throws NoSuchMethodException {
    Method answer = null;
    for (Class<?> clazz : resourceClasses) {
        try {
            answer = clazz.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException ex) {
            // keep looking 
        } catch (SecurityException ex) {
            // keep looking
        }
        if (answer != null) {
            return answer;
        }
    }
    throw new NoSuchMethodException("Cannot find method with name: " + methodName + " having parameters: " + arrayToString(parameterTypes));
}

Thanks for any help anyone can provide!

like image 657
HoopItUp Avatar asked Oct 31 '22 18:10

HoopItUp


1 Answers

The serviceClass is a JAX-RS annotated Java class that defines the operations of a REST web service.

When configuring a CXF REST client, you must specify and address and a serviceClass. By inspecting the annotations found on the serviceClass, the CXF client proxy knows which REST operations are supposed to be available on the REST service published on the specified address.

So in your case, you need to add in.setHeader.setHeader(CxfConstants.OPERATION_NAME, "postNotification"); to the EventProcessor to tell camel which method of the service class you want to call.

like image 119
Ricardo Veguilla Avatar answered Nov 12 '22 23:11

Ricardo Veguilla