Multiple resouceBean configuration in CXF using Spring

穿精又带淫゛_ 提交于 2019-12-18 17:32:38

问题


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.


回答1:


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

BalanceService

@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) {
       ...      
    }
}



回答2:


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

<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>

Custom Resource comparator

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


}

URLs:

http://localhost:8080/rest/account/balance/12
http://localhost:8080/rest/transfer/balance/13



回答3:


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




回答4:


I'd solve the problem this way:

  • Removing a "Custom Resource Comparator" (there's no need to)

  • Removing this:

    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-bundle-jaxrs</artifactId>
        <version>2.5.0</version>
    </dependency> 
    
  • In the 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 the beans/class implementation:

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

And that's all. :)



来源:https://stackoverflow.com/questions/9725770/multiple-resoucebean-configuration-in-cxf-using-spring

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!