2011-08-25 4 views
6

Tôi mới ở các dịch vụ an toàn và có khởi đầu khá tốt, cho đến khi tôi quyết định chơi với một số đối tượng phức tạp. Vấn đề tôi đang nhấn là về việc unmarshalling một đối tượng đến máy chủ (tạo đối tượng từ XML ở phía máy chủ).CXF Đối tượng phức tạp dịch vụ không ổn định không hoạt động

Dưới đây là mẫu (đại diện) triển khai dịch vụ của tôi.

Đây là loại dữ liệu "đối tượng phức tạp" của tôi.

package data; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class ComplexType { 
    private long id; 
    private String name; 
    private Boolean isRegistered; 


    public ComplexType() { 
     super(); 
    } 
    public ComplexType(long id, String name, Boolean isRegistered) { 
     super(); 
     this.id = id; 
     this.name = name; 
     this.isRegistered = isRegistered; 
    } 
    public long getId() { 
     return id; 
    } 
    public void setId(long id) { 
     this.id = id; 
    } 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    public Boolean getIsRegistered() { 
     return isRegistered; 
    } 
    public void setIsRegistered(Boolean isRegistered) { 
     this.isRegistered = isRegistered; 
    } 
} 

Đây là dịch vụ của tôi API

package api; 

import javax.ws.rs.FormParam; 
import javax.ws.rs.GET; 
import javax.ws.rs.POST; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 

import data.ComplexType; 


public interface Service { 
    @GET 
    @Path("/nummembers") 
    int getNumElements(); 

    @GET 
    @Path("/member/{id}") 
    ComplexType getMember(@PathParam("id") long id); 

    @POST 
    @Path("/member") 
    boolean addMember(@FormParam("member") ComplexType member); 
} 

Và đây là việc thực hiện các dịch vụ:

package impl; 

import java.util.HashMap; 
import java.util.Map; 

import data.ComplexType; 
import api.Service; 

public class ServiceImpl implements Service { 
    Map<Long, ComplexType> data; 

    public ServiceImpl() { 
     System.out.println("TestApp Starting"); 
     data = new HashMap<Long, ComplexType>(); 
    } 

    @Override 
    public int getNumElements() { 
     return data.size(); 
    } 

    @Override 
    public ComplexType getMember(long id) { 
     if (data.containsKey(id)) { 
      return data.get(id); 
     } 
     ComplexType ct = 
      new ComplexType(id, "NAME" + new Long(id).toString(), (id % 2 == 1)); 
     data.put(id, ct); 
     return ct; 
    } 

    @Override 
    public boolean addMember(ComplexType member) { 
     int preSize = data.size(); 
     data.put(member.getId(), member); 
     return preSize < data.size(); // True if added 
    } 
} 

Vì vậy, khi tôi gọi getNumElements() không có vấn đề. Khi tôi gọi số getMember(long id), tôi nhận được một "Kiểu chữ" được sắp xếp theo hàng loạt. Khi tôi serialize một loại phức tạp và vượt qua nó như FormParam để addMember(ComplexType member), tôi luôn luôn có được Parameter Class data.ComplexType has no constructor with single String parameter, static valueOf(String) or fromString(String) methods

tôi đã cố gắng để cung cấp cung cấp tùy chỉnh của tôi, bằng cách viết các lớp sau đây:

package impl; 

import javax.ws.rs.ext.ContextResolver; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 

import data.ComplexType; 

public class JaxbXmlContextResolver implements ContextResolver<Object> { 
    private static final Class<?>[] classes = new Class[] {ComplexType.class}; 
    private static final JAXBContext context = initContext(); 

    public static JAXBContext initContext() { 
     JAXBContext context = null; 
     try { 
      context = JAXBContext.newInstance(classes); 
     } catch (JAXBException e) { 
      throw new RuntimeException(e); 
     } 
     return context; 
    } 

    @Override 
    public Object getContext(Class<?> arg0) { 
     return context; 
    } 
} 

Và đối với phần còn lại của cấu hình , đây là web.xml của tôi:

<?xml version="1.0"?> 
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
"http://java.sun.com/dtd/web-app_2_3.dtd"> 

<web-app> 
    <display-name>TestApp</display-name> 

    <context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>classpath:cxf.xml</param-value> 
    </context-param> 

    <context-param> 
    <param-name>log4jConfigLocation</param-name> 
    <param-value>classpath:log4j.properties</param-value> 
    </context-param> 

    <listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 

    <servlet> 
    <servlet-name>CXFServlet</servlet-name> 
    <display-name>CXF Servlet</display-name> 
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> 
    <load-on-startup>1</load-on-startup> 
    </servlet> 

    <servlet-mapping> 
    <servlet-name>CXFServlet</servlet-name> 
    <url-pattern>/services/*</url-pattern> 
    </servlet-mapping> 
</web-app> 

Và cxf.xml nó đề cập đến:

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

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

    <bean id="myservice" class="impl.ServiceImpl" /> 
    <bean id="jaxbXmlProvider" class="impl.JaxbXmlContextResolver" /> 

    <jaxrs:server id="connectionService" address="/" > 
    <jaxrs:serviceBeans> 
     <ref bean="myservice" /> 
    </jaxrs:serviceBeans> 
    <jaxrs:extensionMappings> 
     <entry key="xml" value="application/xml" /> 
    </jaxrs:extensionMappings> 
    <jaxrs:providers> 
     <ref bean="jaxbXmlProvider" /> 
    </jaxrs:providers> 
    </jaxrs:server> 
</beans> 

Và để có đầy đủ, đây là tệp pom.xml tôi đang sử dụng để tạo ứng dụng.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 

    <properties> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    </properties> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>TESTAPP</groupId> 
    <artifactId>testApp</artifactId> 
    <packaging>war</packaging> 
    <version>1.0</version> 
    <name>Test Application</name> 
    <url>http://www.mycompany.com</url> 

    <dependencies> 
    <dependency> 
     <groupId>log4j</groupId> 
     <artifactId>log4j</artifactId> 
     <version>1.2.16</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-orm</artifactId> 
     <version>3.0.5.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.apache.cxf</groupId> 
     <artifactId>cxf-rt-frontend-jaxrs</artifactId> 
     <version>2.4.1</version> 
     <exclusions> 
     <exclusion> 
      <groupId>wsdl4j</groupId> 
      <artifactId>wsdl4j</artifactId> 
     </exclusion> 
     </exclusions> 
    </dependency> 
    <dependency> 
     <groupId>com.thoughtworks.xstream</groupId> 
     <artifactId>xstream</artifactId> 
     <version>1.3.1</version> 
    </dependency> 
    <dependency> 
     <groupId>net.sf.kxml</groupId> 
     <artifactId>kxml2</artifactId> 
     <version>2.2.2</version> 
    </dependency> 
    <dependency> 
     <groupId>javax.xml.bind</groupId> 
     <artifactId>jaxb-api</artifactId> 
     <version>2.2.1</version> 
    </dependency> 

    </dependencies> 

    <build> 
    <plugins> 
     <plugin> 
     <artifactId>maven-compiler-plugin</artifactId> 
     <version>2.3.2</version> 
     <configuration> 
      <source>1.6</source> 
      <target>1.6</target> 
     </configuration> 
     </plugin> 
    </plugins> 
    </build> 
</project> 

Bất kỳ trợ giúp (giải pháp, con trỏ hoặc bất kỳ hướng nào) sẽ được đánh giá cao.

Ồ, tôi đang sử dụng cxf 2.4.1 và Spring 3.0.5.RELEASE. Đây là bản sao chính xác của ứng dụng được triển khai của tôi.

Cảm ơn.

+0

Theo: http://cxf.547215.n5.nabble.com/MessageBodyReader-not-picked-up-td564496.html params được đánh dấu @FormParam không được xử lý bởi người đọc tin nhắn, và yêu cầu một ParameterHandler cụ thể. Vì vậy, tôi đã viết một trình xử lý tùy chỉnh và nó hoạt động tốt. Nó làm phiền tôi rằng tôi phải viết một xử lý cho mọi obj tôi mong đợi để nhận được trong một hình thức. Nếu có một giải pháp, tôi sẽ đánh giá cao nó. Ngoài ra, một trong những lo ngại của tôi là, tôi muốn hỗ trợ cả xml và json trong dịch vụ của tôi. Tôi không chắc làm thế nào tôi có thể viết hai xử lý cho cùng một obj, hoặc cho dù tôi nên viết xử lý của tôi để xử lý cả hai định dạng. –

Trả lời

0

Triển khai hàm tạo mặc định không có đối số và một đối số có đối số cho từng thuộc tính trong số ba thuộc tính. Điều đó sẽ làm cho JAXB hạnh phúc.

+0

Cảm ơn. Nhưng điều đó không hiệu quả. Tôi thực sự có cả hai nhà xây dựng, nhưng quên thêm chúng vào mẫu đơn giản của tôi. –

1

Như đã nêu tại http://cxf.547215.n5.nabble.com/MessageBodyReader-not-picked-up-td564496.html, hóa ra tôi cần một ParameterHandler. Vì tôi có một tập hợp lớn các đối tượng trong ứng dụng của mình nên tôi không muốn tạo ParameterHandler riêng lẻ <> cho mỗi ứng dụng, vì vậy tôi đã thực hiện một thay đổi nhỏ:

Sử dụng kỹ thuật được mô tả tại JAXB inheritance, unmarshal to subclass of marshaled class, tôi đã tạo loại cơ sở "BaseType "tất cả các đối tượng dữ liệu API (TypeA, TypeB, ...) đều được kế thừa.

public class XmlParamHandler implements ParameterHandler<BaseType> { 
    private static final Logger log = LoggerFactory.getLogger(XmlParamHandler.class); 
    private static final JAXBContext jaxbContext = initContext(); 


    private static JAXBContext initContext() throws JAXBException { 
     Class<?>[] classes = new Class[] { 
      com.mycompany.BaseType.class, 
      com.mycompany.TypeA.class, 
      com.mycompany.TypeB.class, 
      com.mycompany.TypeC.class, 
      com.mycompany.TypeD.class, 
      com.mycompany.TypeE.class, 
      com.mycompany.TypeF.class, 
     }; 
     JAXBContext context = JAXBContext.newInstance(classes); 
     return context; 
    } 

    public static <T> T valueOf(String str) throws JAXBException { 
     if (str == null) { 
      return null; 
     } 
     Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
     StringReader sr = new StringReader(str); 
     @SuppressWarnings("unchecked") 
     T request = (T) unmarshaller.unmarshal(sr); 
     return request; 
    } 

    @Override 
    public BaseType fromString(String s) { 
     BaseType ct = null; 
     try { 
      return valueOf(s); 
     } catch (JAXBException e) { 
      return null; 
     } 
    } 
} 
0

Có một chút kỳ lạ khi chuyển đối tượng "XML-marshallable" phức tạp dưới dạng @FormParam. @FormParam được thiết kế chủ yếu cho các loại đơn giản hoặc nguyên thủy như chuỗi, số nguyên, v.v.

Một trong những điều tốt đẹp của JAX-RS là nó sẽ tự động sửa đổi/bỏ đối tượng cho bạn là bạn cho biết loại phương tiện nào cần sử dụng .Vì vậy, bạn có thể muốn xác định điểm cuối của bạn như (thêm @Consumes và không có chú thích về thành viên):

@POST 
@Path("/member") 
@Consumes({ MediaType.APPLICATION_XML }) 
boolean addMember(ComplexType member); 

Và POST đại diện XML của một thành viên là cơ quan yêu cầu chứ không phải là một tham số hình thức và thiết lập tiêu đề Content-Type của yêu cầu của bạn đến application/xml. Sau đó, CXF sẽ tự động đọc và phân tích cú pháp phần thân yêu cầu thành một thể hiện của ComplexType và chuyển nó vào phương thức của bạn.

Bạn có thể sử dụng curl cho điều đó. Tạo một tệp XML với đối tượng được tuần tự hóa (member.xml) và chạy:

curl -k --request POST --header "Content-Type: application/xml" --data @member.xml https://localhost:8080/path/to/service