5

我无法让我的 Camel 路由成功地将消息发布到现有的 RESTful Web 服务。我已经尝试了骆驼 cxf 包中的所有示例,但它们都没有产生 Web 服务调用(它们是消费者)。我很想为此找到一个可行的示例,这样我就可以逐步执行 CxfRsProducer 以希望发现我的路线未正确发布到 Web 服务的原因。

这是我的 RouteBuilder 的配置:

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"); 

}

这是 rsClientBean:

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

我对 REST 很陌生,我并不真正了解 serviceClass 对 rsClient 的作用,因为它在我看来就像服务器上公开的 Web 服务的定义。

NotificationsResource 类:

@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;
    }

}

处理器创建一个 Notifications 对象以放入交换消息正文中:

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);
    }

}

Notifications 类使用 JAXB 注解以进行序列化

@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);
    }    
}

从 Web 服务返回的错误:

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)

它是来自 CxfRsProducer 类的以下方法中的 methodName 参数为空......所以我假设我的 rsClient 有一些配置不正确。

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));
}

感谢任何人都可以提供的任何帮助!

4

2 回答 2

2

serviceClass 是一个带有JAX-RS注释的 Java 类,它定义了 REST Web 服务的操作。

配置 CXF REST 客户端时,您必须指定地址和 serviceClass。通过检查在 serviceClass 上找到的注释,CXF 客户端代理知道哪些 REST 操作应该在指定地址上发布的 REST 服务上可用。

因此,在您的情况下,您需要添加in.setHeader.setHeader(CxfConstants.OPERATION_NAME, "postNotification");到 EventProcessor 以告诉骆驼您要调用的服务类的哪个方法。

于 2014-08-12T03:26:44.573 回答
0

那好吧。这是骆驼配置xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://w3.org/2001/XMLSchema-instance"
   xmlns:cxf="http://camel.apache.org/schema/cxf"
   xmlns:jaxrs="http://cxf.apache.org/jaxrs"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://camel.apache.org/schema/cxf
   http://camel.apache.org/schema/cxf/camel-cxf.xsd
   http://cxf.apache.org/jaxrs
   http://cxf.apache.org/schemas/jaxrs.xsd
   http://camel.apache.org/schema/spring
   http://camel.apache.org/schema/spring/camel-spring.xsd
>

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="helloBean" class="com.examples.camel.cxf.rest.resource.HelloWorldResource" />

<cxf:rsServer id="helloServer" address="/helloapp" loggingFeatureEnabled="true">
 <cxf:serviceBeans>
  <ref bean="helloBean" />
 </cxf:serviceBeans>
 <cxf:providers>
  <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
 </cxf:providers>
</cxf:rsServer>

<camelContext id="context" xmlns="http://camel.apache.org/schema/spring">
 <route>
  <from uri="cxfrs:bean:helloServer />
   <log message="Processing CXF route....http method ${header.CamelHttpMethod}" />
   <log message="Processing CXF route....path is ${header.CamelHttpPath}" />
   <log message="Processing CXF route....body is ${body}" />
   <choice>
    <when>
    <simple>${header.operationName} == 'sayHello'</simple>
    <to uri="direct:invokeSayHello" />
  </when>
  <when>
    <simple>${header.operationName} == 'greet'</simple>
    <to uri="direct:invokeGreet" />
  </when>
</choice>
</route>

 <route id="invokeSayHello">
  <from uri="direct:invokeSayHello" />
    <bean ref="helloBean" method="sayHello" />
 </route>
 <route id="invokeGreet">
   <from uri="direct:invokeGreet" />
     <bean ref="helloBean" method="greet" />
 </route>
</camelContext>

</beans>

实际的资源实现类如下所示。包 com.examples.camel.cxf.rest.resource;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

public class HelloWorldResource implements HelloWorldIntf
{
public Response greet() {

   return Response.status(Status.OK).
             entity("Hi There!!").
                build();
}

public Response sayHello(String input) {
   Hello hello = new Hello();
   hello.setHello("Hello");
   hello.setName("Default User");

    if(input != null)
       hello.setName(input);

   return Response.
             status(Status.OK).
               entity(hello).
                 build();
}
}

class Hello {
   private String hello;
   private String name;

   public String getHello() { return hello; }
   public void setHello(String hello) { this.hello = hello; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }
}

您不需要提供 , 和 cxf:rsServer> 。仅标签就足以处理 Web 服务请求和调用路由。

如果您同时拥有两者,然后调用前者将无助于您执行路线。对于要调用的路由,请求必须到达由 发布的地址。

希望这可以帮助。

于 2015-07-22T15:56:11.013 回答