Multiple resouceBean configuration in CXF using Spring

I am using CXF RS 2.5.1 with Spring 3.0.6-RELEASE. I would like to have several implementation classes for "one endpoint". I see that this problem has been reported and fixed https://issues.apache.org/jira/browse/CXF-2439 , however, when I try to do this, CXF just selects the first resource class from jaxrs: the serviceBeans tag. For another request, I see this message on the tomcat console as "There is no way to find a match / account / balance / transfer". Below is the configuration. Rate 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> 

invoice-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 (import 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 (import skipped)

 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 irregularities / programming standards, as this is just an example application for POC.

+4
source share
4 answers

Therefore, I spend some time on the Internet, but could not find a solution to this problem. There is a note in the documentation that can be used to output the solution.

http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Customselectionbetweenmultipleresources

Therefore, I wrote a special resource comparator, performed the appropriate configuration of jaxrs: server and Eureka! it worked !. Now I have 2 implementation classes mapped to a single resource / address in javax: rs address.

Note that the logic in the custom resource comparator shown below may vary depending on the URL pattern.

Providing the source of all files. Hope this helps someone in the future :)

web.xml

 <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/account-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>CXF Servlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXF Servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> 

account-servlet.xml (applicationContext)

 <beans> <jaxrs:server id="accountService" address="/rest"> <jaxrs:serviceBeans> <ref bean="accountServiceImpl" /> <ref bean="transferServiceImpl" /> </jaxrs:serviceBeans> <jaxrs:resourceComparator> <bean id="accountServiceComparator" class="com.etrade.comparator.AccountServiceComparator"/> </jaxrs:resourceComparator> <jaxrs:extensionMappings> <entry key="xml" value="application/xml" /> </jaxrs:extensionMappings> </jaxrs:server> <bean id="accountServiceImpl" class="com.etrade.service.AccountService" /> <bean id="transferServiceImpl" class="com.etrade.service.TransferService" /> </beans> 

pom.xml

  <modelVersion>4.0.0</modelVersion> <groupId>cxf.rest</groupId> <artifactId>account</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>account Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-bundle-jaxrs</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>3.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.0.5.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <!-- This plugin is needed for the servlet example --> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>7.2.0.v20101020</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.1</version> <executions> <execution><goals><goal>java</goal></goals></execution> </executions> </plugin> </plugins> <finalName>account</finalName> </build> </project> 

User Resource Builder

 package com.etrade.comparator; import java.lang.reflect.Method; import javax.ws.rs.Path; import org.apache.cxf.jaxrs.ext.ResourceComparator; import org.apache.cxf.jaxrs.impl.UriInfoImpl; import org.apache.cxf.jaxrs.model.ClassResourceInfo; import org.apache.cxf.jaxrs.model.OperationResourceInfo; import org.apache.cxf.message.Message; public class AccountServiceComparator implements ResourceComparator{ public int compare(ClassResourceInfo arg0, ClassResourceInfo arg1, Message message) { UriInfoImpl uriInfo = new UriInfoImpl(message); String path = uriInfo.getPath(); String[] pathArray = path.split("/"); String resourceUrlName = pathArray[1]; System.out.println("Path : "+resourceUrlName); Method[] methods = arg0.getServiceClass().getMethods(); int value = 1; String resource = null; for(Method method : methods) { Path annotationPath = method.getAnnotation(javax.ws.rs.Path.class); if(null != annotationPath){ String pathValue = annotationPath.value(); String[] parts = pathValue.split("/"); resource = parts[1]; System.out.println("resource : "+resource); } if(resourceUrlName.contains(resource)){ value = -1; } } return value; } public int compare(OperationResourceInfo arg0, OperationResourceInfo arg1, Message arg2) { return 0; } } 

Implementation classes / beans

AccountService.java

 package com.etrade.service; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Path("/account") @Produces("application/xml") public class AccountService { @GET @Produces("application/xml") @Path("/balance/{id}") public String accountBalance(@PathParam("id") long id) { System.out.println("id : "+id); StringBuilder response = new StringBuilder(256); response.append("<Response>").append("<id>").append(id) .append("</id>").append("<balance>").append("250.00").append("</balance>") .append("</Response>"); return response.toString(); } } 

TransferService.java

 package com.etrade.service; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @Path("/account") @Produces("application/xml") public class TransferService { @GET @Produces("application/xml") @Path("/transfer/{id}") public String accountTransfer(@PathParam("id") long id) { System.out.println("transfer id : "+id); StringBuilder response = new StringBuilder(256); response.append("<Response>").append("<id>").append(id) .append("</id>").append("<transfer>").append("250.00").append("</transfer>") .append("</Response>"); return response.toString(); } } 

Url:

 http://localhost:8080/rest/account/balance/12 http://localhost:8080/rest/transfer/balance/13 
+3
source

I solved this problem by moving the @Path mapping part to the bean service class. In your case:

Balance service

 @Path("/balance") @Produces("application/xml") public class BalanceService { @GET @Path("/{id}") public String getBalance(@PathParam("id") long id) { ... } } 

TransferService

 @Path("/transfer") @Produces("application/xml") public class TransferService { @GET @Path("/{id}") public String getTransfer(@PathParam("id") long id) { ... } } 
+4
source

You might simplify the logic involved in comparing root resources, for example, knowing that the request URI and the name of the root resource class may be sufficient to make a decision, checking individual methods seems complicated. BTW, specialized resource comparators are needed only when root resources and / or individual resource methods can become equal candidates.

0
source

I would solve the problem this way:

  • Removing " Custom Resource Comparator " (no need)

  • Removal:

     <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-bundle-jaxrs</artifactId> <version>2.5.0</version> </dependency> 
  • In account-servlet.xml / applicationContext :

     <beans> <jaxrs:server id="accountService" address="/rest"> <jaxrs:serviceBeans> <ref bean="accountServiceImpl" /> <ref bean="transferServiceImpl" /> </jaxrs:serviceBeans> </jaxrs:server> </beans> 
  • In beans / class :

     ... @Context("accountServiceImpl") @Path("/account") public class Accountservice{ ... 

And it's all.:)

0
source

Source: https://habr.com/ru/post/1401670/


All Articles