CXF Не работает служба восстановления сложных объектов. - PullRequest
6 голосов
/ 25 августа 2011

Я новичок в отдыхающих службах, и у меня было относительно хорошее начало, пока я не решил поиграть с некоторыми сложными объектами.Проблема, с которой я сталкиваюсь, заключается в том, чтобы демонтировать объект, поступающий на сервер (создание объекта из XML на стороне сервера).

Ниже приведен пример (пример) реализации службы.

Здесьмой тип данных "сложный объект".

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

Это 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);
}

И это реализация сервиса:

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

Поэтому, когда я звоню getNumElements(), проблем нет.Когда я звоню getMember(long id), я получаю сериализованный "ComplexType" просто отлично.Когда я сериализирую сложный тип и передаю его как FormParam в addMember(ComplexType member), я всегда получаю Parameter Class data.ComplexType has no constructor with single String parameter, static valueOf(String) or fromString(String) methods

Я пытался предоставить своего пользовательского провайдера, написав следующий класс:

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

А для остальных конфигураций вот мой web.xml:

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

И файл cxf.xml, к которому он относится:

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

А для полноты здесьpom.xml Я использую для создания приложения.

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

Любая помощь (решения, указатели или любые другие направления) будет принята с благодарностью.

О, я использую cxf2.4.1 и Spring 3.0.5. ВЫПУСК.Это точная копия моего развернутого приложения.

Спасибо.

Ответы [ 3 ]

1 голос
/ 30 августа 2011

Как было указано при http://cxf.547215.n5.nabble.com/MessageBodyReader-not-picked-up-td564496.html,, оказалось, что мне нужен ParameterHandler.Поскольку в моем приложении был большой набор объектов, я не хотел создавать отдельный ParameterHandler <> для каждого, поэтому я внес небольшое изменение:

Используя технику, описанную в JAXB-наследование, unmarshalдля подкласса маршалированного класса я создал базовый тип "BaseType", который унаследовал все объекты данных API (TypeA, TypeB, ...).

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 голосов
/ 01 августа 2013

Немного странно передавать сложный объект "XML-marshallable" как @FormParam. @FormParam предназначены в основном для простых или примитивных типов, таких как строки, целые числа и т. Д.

Одной из приятных особенностей JAX-RS является то, что он будет автоматически маршалировать / демаршировать объекты для вас, если вы скажете ему, какой тип носителя использовать. Таким образом, вы можете определить свои конечные точки как (добавление @Consumes и отсутствие аннотации на элементе):

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

И POST XML-представление члена в виде тела запроса вместо параметра формы и установите заголовок Content-Type вашего запроса равным application/xml. Затем CXF автоматически прочитает и проанализирует тело запроса для экземпляра ComplexType и передаст его вашему методу.

Вы можете использовать curl для этого. Создайте файл XML с сериализованным объектом (member.xml) и запустите:

curl -k --request POST --header "Content-Type: application/xml" --data @member.xml https://localhost:8080/path/to/service
0 голосов
/ 25 августа 2011

Реализуйте конструктор по умолчанию без аргументов и один с аргументом для каждого из трех свойств. Это должно сделать JAXB счастливым.

...