Why do we use <cxf:rsServer> as opposed to a plain <jaxrs:server> when using the CXF-RS component?
Asked Answered
L

2

8

As a follow-up to this question, I'm still a bit confused about how to properly use the CXF-RS component.

I'm confused why we need the <cxf:rsServer> tag for specifying CXF-RS endpoints (or is there even such a concept?), when I can use the <jaxrs:server> tag perfectly fine.

Here's my configuration XML for both Camel and CXF:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xmlns:camel="http://camel.apache.org/schema/spring"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd      
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
        http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.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">

    <jaxrs:server id="userService" address="/users">
        <jaxrs:serviceBeans>
            <bean class="com.example.UserServiceNoop" />
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
        </jaxrs:providers>
    </jaxrs:server>
    
    <bean id="user" class="org.apache.camel.component.direct.DirectComponent" />
    
    <camel:camelContext id="someCamelContext">
        <camel:route id="userServiceRoute">
            <camel:from uri="cxfrs:bean:userService" />
            <camel:routingSlip>
                <camel:simple>user:${header.operationName}</camel:simple>
            </camel:routingSlip>
        </camel:route>

        <camel:route id="userServiceRetrieveUser">
            <from uri="user:retrieveUser" />
            <!-- Assume this is going to a useful Processor -->
        
        </camel:route>  
    </camel:camelContext>
</beans>

UserService.java:

package com.example;

/* a bunch of imports... */

public interface UserService {
    @GET
    @Path(value="/{user.id}")
    @Produces({MediaType.APPLICATION_JSON})
    public User retrieveUser(
        @PathParam("user.id") Integer id
    );
}

UserServiceNoop.java

package com.example;

/* a bunch of imports ... */

public class UserServiceNoop implements UserService 
{
    @Override
    public User retrieveUser(Integer id) {
        throw new RuntimeException();
    }
}

In this example, I'm not using any <cxf:rsServer> tag, yet it works fine. I know it goes through the CXF-RS component, because when I run the application, it doesn't throw any RuntimeExceptions, which is the expected behavior when using CXF-RS (the method implementation in the service class will not be called).

Am I missing something by not using this tag?

Lycaonia answered 29/1, 2014 at 19:48 Comment(0)
B
1

As the other answer says, the cxf:rsServer is mainly used to be processed by a Camel route as in the jaxrs:server the processing of the request is done by a classic controller.

For example:

  1. Classic JAXRS server:

You will declare a classic Bean Rest (Controller) and inject a Service inside.

Sample of an XML config (extract):

<jaxrs:server id="deviceServiceSvcV1" address="/device/v1">
    <jaxrs:serviceBeans>
        <ref component-id="deviceServiceRest" />
    </jaxrs:serviceBeans>
    <!-- and other providers, interceptors, etc... here --> 
</jaxrs:server>

<!-- Service bean -->
<bean id="deviceServiceRest" class="org.mycomp.device.rest.v1.ws.api.DeviceServiceRest">
    <property name="deviceService" ref="deviceService" />
</bean>

The Controller class will process the request / response in a classic way (e.g. calling an injected service).

  1. Camel route with cxf:rsServer

Sample of an XML config (extract):

<cxf:rsServer id="rsServer" address="/device/v1"
    serviceClass="org.mycomp.device.rest.v1.ws.api.DeviceServiceRest">
    <cxf:properties>
        <!-- whatever here -->
    </cxf:properties>
    <!-- and other interceptors, etc... here -->         
</cxf:rsServer>

and in the classes:

@Produces({ MediaType.APPLICATION_XML })
@Path("/")
public class DeviceServiceRest {

    @GET
    public Response listDevicess( 
            @QueryParam("model") String model,
            @QueryParam("sid") String sid,
    ) {
        return null; // never used
    }

    @GET
    @Path("{id}")
    public Response getDeviceById(
            @PathParam("id") String id,
            @QueryParam("model") String model,
            @QueryParam("sid") String sid
    ){               
        return null; // never used
    }
}

The REST Controller has empty methods (returning null) but I think the latest camel-cxf supports now an Interface which is more elegant than having methods returning null. Now, the request processing can be implemented by a Camel Route like this:

from("cxfrs:bean:rsServer?synchronous=true")
    .routeId("cxf-device-rest-v1")
    .process( new CheckAuthenticationProcessor())
    .choice()
        .when(header("operationName").isEqualTo("listDevice"))
            .setHeader("backenOperation").constant("list")
            .setHeader("backendResource").constant("device")
            
        .endChoice()
        .when(header("operationName").isEqualTo("getDeviceById"))
            .setHeader("backenOperation").constant("retrieve")
            .setHeader("backendResource").constant("device")
        .endChoice()
    .end()
    .bean("requestProcessor")
    .to(InOut, backendEndpoint)
    .process(checkResponseStatusCode())
    .bean(new HttpResponseProcessor())
;

And you can also control the request / response processing as you want from the route.

These are two different kind of implementing a REST API (server side) but in my opinion this is a bit old school as modern framework like spring-boot does not need any of these.

I found the second way a bit too much overkill as I like Camel for integration purpose but using it for a REST API could be subject to discussion. One use-case I can see is a HTTP REST Web-Service for asynchronous processing, the service responding 202 Accepted and the Camel Route making an integration of the request in asynchronous mode especially when a specific Camel Component can be easily used instead of a complex class (or any need of the EIP patterns).

Blarney answered 27/11, 2020 at 12:7 Comment(0)
S
0

You use cxf:reserver tag when you want to use CXF endpoint as the consumer of something. Say for example in a complex Apache Camel route or in Spring integration. is used when you are the provider of the endpoint serving requests.

Serval answered 3/7, 2014 at 19:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.