I'm using CXF RS 2.5.1 with Spring 3.0.6-RELEASE. I would like to have multiple implementation classes for "a single endpoint". I see that this issue was reported and fixed https://issues.apache.org/jira/browse/CXF-2439, however, when I try to do it, CXF just selects the first resource class from jaxrs:serviceBeans tag. For the other request, I see this message on tomcat console as "No operation matching request path /account/rest/transfer is found". Below is the configuration. Appreciate any input.
web.xml
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:account-servlet.xml</param-value>
</context-param>
<servlet>
<servlet-name>CXF Servlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXF Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
account-servlet.xml
<jaxrs:server id="accountService" address="/rest">
<jaxrs:serviceBeans>
<ref bean="transferService" />
<ref bean="balanceService"/>
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
</jaxrs:server>
<bean id="transferService" class="com.mycompany.service.TransferService"/>
<bean id="balanceService" class="com.mycompany.service.BalanceService"/>
BalanceService.java (imports omitted)
package com.mycompany.service;
@Path("/")
@Produces("application/xml")
public class BalanceService{
@GET
@Path("/balance")
public String getBalance() {
StringBuilder response = new StringBuilder(128);
response.append("<Balance>")
.append("<amount>").append("250.00").append("</amount>")
.append("</Balance>");
return response.toString();
}
}
TransferService.java (imports omitted)
package com.mycompany.service;
@Path("/")
@Produces("application/xml")
public class TransferService {
@GET
@Path("/transfer")
public String getTransfer() {
StringBuilder response = new StringBuilder(128);
response.append("<Transfer>")
.append("<amount>").append("350.00").append("</amount>")
.append("</Transfer>");
return response.toString();
}
}
Please ignore any programming irregularities/standards as it's just a sample app for the POC.
I'd solve the problem this way:
Removing a "Custom Resource Comparator" (there's no need to)
Removing this:
In the account-servlet.xml/applicationContext:
In the beans/class implementation:
And that's all. :)
I solved this problem by moving part of the @Path mapping to the service bean class. In your case:
BalanceService
TransferService
So I spend some time searching the internet but found no solution to this problem. There is a note written in the documentation that can be used to deduce the solution.
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Customselectionbetweenmultipleresources
Hence I wrote a custom resource comparator, did the appropriate jaxrs:server configuration and Eureka! it worked!. Now, I have 2 implementation classes mapped to a single resource/address in javax:rs address.
Please be advised that logic in custom resource comparator shown below may vary based on the URL pattern.
Providing source of all the files. Hope that this will help someone in future :)
web.xml
account-servlet.xml (applicationContext)
pom.xml
Custom Resource comparator
Implementation classes/beans
AccountService.java
TransferService.java
URLs:
You can probably simplify the logic to do with comparing the root resources, example, knowing the request URI and the name of the root resource class can be sufficient to make the decision, checking the individual methods looks complicated. BTW, the custom resource comparators are only needed when either root resources and/or individual resource methods can become the equal candidates