51장. 엔터티 지원

초록

Apache CXF 런타임은 MIME 유형과 Java 개체 중에서 상자에서 제한된 수의 매핑을 지원합니다. 개발자는 사용자 정의 독자와 작성자를 구현하여 매핑을 확장할 수 있습니다. 사용자 지정 독자 및 작성자는 시작 시 런타임에 등록됩니다.

51.1. 개요

런타임은 JAX-RS MessageBodyReader 및 MessageBodyWriter 구현을 사용하여 HTTP 메시지와 Java 메시지 간에 데이터를 직렬화하고 역직렬화합니다. 독자와 작성자는 처리할 수 있는 MIME 유형을 제한할 수 있습니다.

런타임에서는 여러 공통 매핑에 대한 독자 및 작성자를 제공합니다. 애플리케이션에 더 많은 고급 매핑이 필요한 경우 개발자는 MessageBodyReader 인터페이스 및/또는 MessageBodyWriter 인터페이스의 사용자 지정 구현을 제공할 수 있습니다. 사용자 지정 독자 및 작성자는 애플리케이션이 시작될 때 런타임으로 등록됩니다.

51.2. 기본적으로 지원되는 유형

표 51.1. “기본적으로 지원되는 엔터티 매핑” Apache CXF에서 제공하는 엔터티 매핑을 상자에서 나열합니다.

표 51.1. 기본적으로 지원되는 엔터티 매핑

Java TypeMIME 유형

기본 유형

text/plain

java.lang.Number

text/plain

byte[]

*/*

java.lang.String

*/*

java.io.InputStream

*/*

java.io.Reader

*/*

java.io.File

*/*

javax.activation.DataSource

*/*

javax.xml.transform.Source

text/xml,application/xml,application/\*+xml

javax.xml.bind.JAXBElement

text/xml,application/xml,application/\*+xml

JAXB 주석이 있는 오브젝트

text/xml,application/xml,application/\*+xml

javax.ws.rs.core.MultivaluedMap<String, String>

application/x-www-form-urlencoded [a]

javax.ws.rs.core.StreamingOutput

*/* [b]

[a] 이 매핑은 HTML 양식 데이터를 처리하는 데 사용됩니다.
[b] 이 매핑은 소비자에게 데이터를 반환하는 경우에만 지원됩니다.

51.3. 사용자 정의 리더

사용자 지정 엔터티 리더는 들어오는 HTTP 요청을 서비스의 구현이 조작할 수 있는 Java 유형으로 매핑해야 합니다. javax.ws.rs.ext.MessageBodyReader 인터페이스를 구현합니다.

예 51.1. “메시지 리더 인터페이스” 에 표시된 인터페이스에는 구현이 필요한 두 가지 방법이 있습니다.

예 51.1. 메시지 리더 인터페이스

package javax.ws.rs.ext;

public interface MessageBodyReader<T>
{
  public boolean isReadable(java.lang.Class<?> type,
                            java.lang.reflect.Type genericType,
                            java.lang.annotation.Annotation[] annotations,
                            javax.ws.rs.core.MediaType mediaType);

  public T readFrom(java.lang.Class<T> type,
                    java.lang.reflect.Type genericType,
                    java.lang.annotation.Annotation[] annotations,
                    javax.ws.rs.core.MediaType mediaType,
                    javax.ws.rs.core.MultivaluedMap<String, String> httpHeaders,
                    java.io.InputStream entityStream)
  throws java.io.IOException, WebApplicationException;
}
isReadable()

isReadable() 메서드는 reader가 데이터 스트림을 읽고 적절한 유형의 엔터티 표현을 생성할 수 있는지 여부를 결정합니다. 독자가 적절한 유형의 엔터티를 만들 수 있는 경우 메서드는 true를 반환합니다.If the reader can create the proper type of entity the method returns true.

표 51.2. “판독기가 엔터티를 생성할 수 있는지 확인하는 데 사용되는 매개변수” isReadable() 메서드의 매개 변수에 대해 설명합니다.

표 51.2. 판독기가 엔터티를 생성할 수 있는지 확인하는 데 사용되는 매개변수

매개변수유형설명

type

Class<T>

엔터티를 저장하는 데 사용되는 개체의 실제 Java 클래스를 지정합니다.

genericType

유형

엔터티를 저장하는 데 사용되는 개체의 Java 유형을 지정합니다. 예를 들어 메시지 본문을 메서드 매개 변수로 변환하려는 경우 값은 Method.getGenericParameterTypes() 메서드에서 반환된 메서드 매개 변수의 유형입니다.

annotations

annotation[]

엔터티를 저장하도록 생성된 오브젝트 선언에 대한 주석 목록을 지정합니다. 예를 들어 메시지 본문을 메서드 매개 변수로 변환하는 경우 Method.getParameterAnnotations() 메서드에서 반환된 해당 매개 변수의 주석이 됩니다.

mediaType

MediatType

HTTP 엔터티의 MIME 형식을 지정합니다.

readFrom()

readFrom() 메서드는 HTTP 엔터티를 읽고 원하는 Java 오브젝트에 포함합니다. 읽기가 성공하면 메서드가 엔터티를 포함하는 생성된 Java 개체를 반환합니다. 입력 스트림을 읽을 때 오류가 발생하면 메서드가 IOException 예외를 throw해야 합니다.If an error occurs when reading the input stream the method should throw an IOException exception. HTTP 오류 응답이 필요한 오류가 발생하면 HTTP 응답이 있는 WebApplicationException을 throw해야 합니다.

표 51.3. “엔터티를 읽는 데 사용되는 매개변수” readFrom() 메서드의 매개 변수에 대해 설명합니다.

표 51.3. 엔터티를 읽는 데 사용되는 매개변수

매개변수유형설명

type

Class<T>

엔터티를 저장하는 데 사용되는 개체의 실제 Java 클래스를 지정합니다.

genericType

유형

엔터티를 저장하는 데 사용되는 개체의 Java 유형을 지정합니다. 예를 들어 메시지 본문을 메서드 매개 변수로 변환하려는 경우 값은 Method.getGenericParameterTypes() 메서드에서 반환된 메서드 매개 변수의 유형입니다.

annotations

annotation[]

엔터티를 저장하도록 생성된 오브젝트 선언에 대한 주석 목록을 지정합니다. 예를 들어 메시지 본문을 메서드 매개 변수로 변환하는 경우 Method.getParameterAnnotations() 메서드에서 반환된 해당 매개 변수의 주석이 됩니다.

mediaType

MediatType

HTTP 엔터티의 MIME 형식을 지정합니다.

httpHeaders

MultivaluedMap<String, String>

엔터티와 연결된 HTTP 메시지 헤더를 지정합니다.

entityStream

InputStream

HTTP 엔터티를 포함하는 입력 스트림을 지정합니다.

중요

이 메서드는 입력 스트림을 닫지 않아야 합니다.

MessageBodyReader 구현을 엔터티 리더로 사용하려면 javax.ws.rs.ext.Provider 주석으로 장식해야 합니다. @Provider 주석은 제공된 구현에서 추가 기능을 제공하는 런타임을 경고합니다. 구현도 “독자 및 작성자 등록” 에 설명된 대로 런타임에 등록되어 있어야 합니다.

기본적으로 사용자 지정 엔터티 공급자는 모든 MIME 유형을 처리합니다. 사용자 지정 엔티티 리더가 javax.ws.rs.Consumes 주석을 사용하여 처리할 MIME 유형을 제한할 수 있습니다. @Consumes 주석은 사용자 정의 엔터티 공급자가 읽는 쉼표로 구분된 MIME 유형 목록을 지정합니다. 엔터티가 지정된 MIME 유형이 아닌 경우 엔터티 공급자가 가능한 리더로 선택되지 않습니다.

예 51.2. “XML 소스 엔티티 리더” 엔터티 리더는 XML 엔터티를 사용하여 소스 오브젝트에 저장합니다.

예 51.2. XML 소스 엔티티 리더

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.apache.cxf.jaxrs.ext.xml.XMLSource;

@Provider
@Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" })
public class SourceProvider implements MessageBodyReader<Object>
{
  public boolean isReadable(Class<?> type,
                            Type genericType,
                            Annotation[] annotations,
                            MediaType mt)
  {
    return Source.class.isAssignableFrom(type) || XMLSource.class.isAssignableFrom(type);
  }

  public Object readFrom(Class<Object> source,
                         Type genericType,
                         Annotation[] annotations,
                         MediaType mediaType,
                         MultivaluedMap<String, String> httpHeaders,
                         InputStream is)
  throws IOException
  {
    if (DOMSource.class.isAssignableFrom(source))
    {
      Document doc = null;
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder;
      try
      {
        builder = factory.newDocumentBuilder();
        doc = builder.parse(is);
      }
      catch (Exception e)
      {
        IOException ioex = new IOException("Problem creating a Source object");
        ioex.setStackTrace(e.getStackTrace());
        throw ioex;
       }

       return new DOMSource(doc);
    }
    else if (StreamSource.class.isAssignableFrom(source) || Source.class.isAssignableFrom(source))
    {
      return new StreamSource(is);
    }
    else if (XMLSource.class.isAssignableFrom(source))
    {
      return new XMLSource(is);
    }

    throw new IOException("Unrecognized source");
  }
}

51.4. 사용자 정의 작성자

사용자 지정 엔티티 작성자는 Java 유형을 HTTP 엔티티에 매핑합니다. javax.ws.rs.ext.MessageBodyWriter 인터페이스를 구현합니다.

예 51.3. “메시지 작성기 인터페이스” 에 표시된 인터페이스에는 구현이 필요한 세 가지 방법이 있습니다.

예 51.3. 메시지 작성기 인터페이스

package javax.ws.rs.ext;

public interface MessageBodyWriter<T>
{
  public boolean isWriteable(java.lang.Class<?> type,
                             java.lang.reflect.Type genericType,
                             java.lang.annotation.Annotation[] annotations,
                             javax.ws.rs.core.MediaType mediaType);

  public long getSize(T t,
                      java.lang.Class<?> type,
                      java.lang.reflect.Type genericType,
                      java.lang.annotation.Annotation[] annotations,
                      javax.ws.rs.core.MediaType mediaType);

  public void writeTo(T t,
                      java.lang.Class<?> type,
                      java.lang.reflect.Type genericType,
                      java.lang.annotation.Annotation[] annotations,
                      javax.ws.rs.core.MediaType mediaType,
                      javax.ws.rs.core.MultivaluedMap<String, Object> httpHeaders,
                      java.io.OutputStream entityStream)
  throws java.io.IOException, WebApplicationException;
}
isWriteable()

isWriteable() 메서드는 엔터티 작성자가 Java 유형을 적절한 엔터티 유형에 매핑할 수 있는지 여부를 결정합니다. 작성자가 매핑을 수행할 수 있는 경우 메서드는 true 를 반환합니다.

표 51.4. “엔터티를 읽는 데 사용되는 매개변수” isWritable() 메서드의 매개 변수에 대해 설명합니다.

표 51.4. 엔터티를 읽는 데 사용되는 매개변수

매개변수유형설명

type

Class<T>

작성 중인 개체의 Java 클래스를 지정합니다.

genericType

유형

리소스 메서드 반환 유형의 리플렉션 또는 반환된 인스턴스의 검사를 통해 얻은 Java 유형의 개체를 지정합니다. 48.4절. “일반 유형 정보가 있는 엔터티 반환” 에 설명된 GenericEntity 클래스는 이 값을 제어하는 지원을 제공합니다.

annotations

annotation[]

엔터티를 반환하는 메서드의 주석 목록을 지정합니다.

mediaType

MediatType

HTTP 엔터티의 MIME 형식을 지정합니다.

getSize()

getSize() 메서드는 writeTo() 전에 호출됩니다. 기록되는 엔터티의 길이(▂ 바이트)를 반환합니다.Returns the length, in bytes, of the entity being written. 양수 값이 반환되면 값은 HTTP 메시지의 Content-Length 헤더에 기록됩니다.

표 51.5. “엔터티를 읽는 데 사용되는 매개변수” getSize() 메서드의 매개 변수에 대해 설명합니다.

표 51.5. 엔터티를 읽는 데 사용되는 매개변수

매개변수유형설명

t

generic

작성 중인 인스턴스를 지정합니다.

type

Class<T>

작성 중인 개체의 Java 클래스를 지정합니다.

genericType

유형

리소스 메서드 반환 유형의 리플렉션 또는 반환된 인스턴스의 검사를 통해 얻은 Java 유형의 개체를 지정합니다. 48.4절. “일반 유형 정보가 있는 엔터티 반환” 에 설명된 GenericEntity 클래스는 이 값을 제어하는 지원을 제공합니다.

annotations

annotation[]

엔터티를 반환하는 메서드의 주석 목록을 지정합니다.

mediaType

MediatType

HTTP 엔터티의 MIME 형식을 지정합니다.

writeTo()

writeTo() 메서드는 Java 개체를 원하는 엔터티 유형으로 변환하고 해당 엔터티를 출력 스트림에 씁니다. 출력 스트림에 엔터티를 작성할 때 오류가 발생하면 메서드가 IOException 예외를 throw해야 합니다.If an error occurs when writing the entity to the output stream the method should throw an IOException exception. HTTP 오류 응답이 필요한 오류가 발생하면 HTTP 응답이 있는 WebApplicationException을 throw해야 합니다.

표 51.6. “엔터티를 읽는 데 사용되는 매개변수” writeTo() 메서드의 매개 변수에 대해 설명합니다.

표 51.6. 엔터티를 읽는 데 사용되는 매개변수

매개변수유형설명

t

generic

작성 중인 인스턴스를 지정합니다.

type

Class<T>

작성 중인 개체의 Java 클래스를 지정합니다.

genericType

유형

리소스 메서드 반환 유형의 리플렉션 또는 반환된 인스턴스의 검사를 통해 얻은 Java 유형의 개체를 지정합니다. 48.4절. “일반 유형 정보가 있는 엔터티 반환” 에 설명된 GenericEntity 클래스는 이 값을 제어하는 지원을 제공합니다.

annotations

annotation[]

엔터티를 반환하는 메서드의 주석 목록을 지정합니다.

mediaType

MediatType

HTTP 엔터티의 MIME 형식을 지정합니다.

httpHeaders

MultivaluedMap<String, Object>

엔터티와 연결된 HTTP 응답 헤더를 지정합니다.

entityStream

OutputStream

엔터티가 작성되는 출력 스트림을 지정합니다.

MessageBodyWriter 구현을 엔터티 작성자로 사용하려면 javax.ws.rs.ext.Provider 주석으로 장식해야 합니다. @Provider 주석은 제공된 구현에서 추가 기능을 제공하는 런타임을 경고합니다. 구현도 “독자 및 작성자 등록” 에 설명된 대로 런타임에 등록되어 있어야 합니다.

기본적으로 사용자 지정 엔터티 공급자는 모든 MIME 유형을 처리합니다. 사용자 지정 엔터티 작성자가 javax.ws.rs. inspector 주석을 사용하여 처리할 MIME 유형을 제한할 수 있습니다. @Produces 주석은 사용자 지정 엔터티 공급자가 생성하는 MIME 유형의 쉼표로 구분된 목록을 지정합니다. 엔터티가 지정된 MIME 유형이 아닌 경우 엔터티 공급자가 가능한 작성기로 선택되지 않습니다.If an entity is not of a specified MIME type, the entity provider will not be selected as a possible writer.

예 51.4. “XML 소스 엔터티 작성기” 소스 오브젝트를 가져와 XML 엔터티를 생성하는 엔터티 작성기를 표시합니다.

예 51.4. XML 소스 엔터티 작성기

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import org.apache.cxf.jaxrs.ext.xml.XMLSource;

@Provider
@Produces({"application/xml", "application/*+xml", "text/xml" })
public class SourceProvider implements MessageBodyWriter<Source>
{

  public boolean isWriteable(Class<?> type,
                             Type genericType,
                             Annotation[] annotations,
                             MediaType mt)
  {
    return Source.class.isAssignableFrom(type);
  }

  public void writeTo(Source source,
                      Class<?> clazz,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediatype,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream os)
  throws IOException
  {
    StreamResult result = new StreamResult(os);
    TransformerFactory tf = TransformerFactory.newInstance();
    try
    {
      Transformer t = tf.newTransformer();
      t.transform(source, result);
    }
    catch (TransformerException te)
    {
      te.printStackTrace();
      throw new WebApplicationException(te);
    }
  }

  public long getSize(Source source,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mt)
  {
    return -1;
  }
}

51.5. 독자 및 작성자 등록

JAX-RS 애플리케이션에서 사용자 지정 엔터티 공급자를 사용하려면 사용자 지정 공급자를 런타임으로 등록해야 합니다. 공급자는 애플리케이션 구성 파일의 jaxrs:providers 요소 또는 JAXRSServerFactoryBean 클래스를 사용하여 런타임에 등록됩니다.

jaxrs:providers 요소는 jaxrs:server 요소의 하위이며 빈 요소 목록 포함합니다. 각 빈 요소는 하나의 엔터티 공급자를 정의합니다.

예 51.5. “런타임을 사용하여 엔터티 공급자 등록” 사용자 지정 엔터티 공급자 집합을 사용하도록 구성된 JAX-RS 서버를 표시합니다.

예 51.5. 런타임을 사용하여 엔터티 공급자 등록

<beans ...>
  <jaxrs:server id="customerService" address="/">
    ...
    <jaxrs:providers>
      <bean id="isProvider" class="com.bar.providers.InputStreamProvider"/>
      <bean id="longProvider" class="com.bar.providers.LongProvider"/>
    </jaxrs:providers>
  </jaxrs:server>
</beans>

JAXRSServerFactoryBean 클래스는 구성 API에 대한 액세스를 제공하는 Apache CXF 확장입니다. 애플리케이션에 인스턴스화된 엔터티 공급자를 추가할 수 있는 setProvider() 메서드가 있습니다. 예 51.6. “엔터티 공급자를 프로그래밍 방식으로 등록” 엔터티 공급자를 프로그래밍 방식으로 등록하기 위한 코드를 보여줍니다.

예 51.6. 엔터티 공급자를 프로그래밍 방식으로 등록

import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
...
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
...
SourceProvider provider = new SourceProvider();
sf.setProvider(provider);
...