JAX-RPC 웹 서비스 데이터 타입
본 장에서는 JAX-RPC 웹 서비스에 관한 여러 가지 데이터 타입에 대해서 설명한다.
표준 Java/XML 데이터 타입 매핑(type mapping)과 사용자 정의 클래스들에서 웹 서비스 파라미터로 사용할 JAX-RPC의 value 타입에 대해서 설명한다. 그 다음으로 출력 또는 입/출력 파라미터들을 위한 JAX-RPC의 Holder 클래스를 설명하고, 마지막으로 에러 정보를 웹 서비스 클라이언트로 어떻게 전송하는지를 설명한다.
1. 개요
다음은 웹 서비스와 웹 서비스 클라이언트에서 사용하는 데이터 타입의 특징이다.
-
Java와 XML 타입 매핑
XML 인스턴스를 Java 객체로 직렬화 또는 역직렬화((de)serialize)하기 위해서는 XML의 타입과 Java 클래스들의 타입들과의 타입 매핑이 필요하다. JEUS 웹 서비스는 JAX-RPC 스펙에 설명된 Java와 표준 XML 타입 매핑을 따르고 있다.
-
JAX-RPC Value 타입 사용
JAX-RPC Value 타입은 웹 서비스의 요청이나 응답에서 값으로써 전달될 수 있는 타입을 말한다. JAX-RPC Value 타입은 일반적으로 사용자 정의 JavaBeans 컴포넌트로 표현된다. JEUS 웹 서비스는 사용자가 작성한 JavaBeans 타입을 웹 서비스의 파라미터와 반환 값으로서 사용할 수 있도록 하고 있다.
-
입출력(In/Out) 파라미터로서 Holder의 사용
입출력(In/Out) 파라미터는 웹 서비스를 실행할 때 입력은 물론 출력에서 사용되는 파라미터이다. 웹 서비스가 여러 출력값들을 반환해야 할 경우 입/출력 파라미터를 사용할 수 있다. JEUS 웹 서비스는 입/출력 파라미터를 표준 JAX-RPC Holder 클래스를 사용하여 지원하고 있다.
-
SOAP Fault로서의 Exception 사용
웹 서비스 실행 중 어떤 에러가 발생하게 되면 웹 서비스 클라이언트에게 그 내용이 전달되어야 한다. SOAP 표준에서는 이런 목적을 위한 SOAP Fault를 정의하고 있다. JEUS 웹 서비스는 표준 SOAP Fault를 지원한다.
2. Java와 XML 타입 매핑
지금까지 하나의 타입만(String만)을 파라미터와 반환값으로 사용하는 웹 서비스 예제를 보았다.
JEUS 웹 서비스는 Java 타입을 XML 또는 WSDL 정의로 매핑한다. 예를 들어 JEUS 웹 서비스는 java.lang.String 클래스를 XML xsd:string 데이터 타입으로 매핑한다.
본 절에서는 JEUS 웹 서비스에서 지원하는 데이터 타입들의 종류와 어떤 데이터 타입이 JEUS 웹 서비스에서 사용되기 위해 필요한 요구 사항인지에 대해서 설명한다.
애플리케이션 개발자들은 Java와 XML 타입의 매핑 과정에 대해서 자세히 알아야 할 필요는 없다. 그러나 모든 Java 클래스가 파라미터와 반환 타입으로 사용될 수 있는 것은 아님에 주의한다. |
2.1. 내장 타입 매핑
다음은 Java와 XML의 데이터 형과 JEUS 웹 서비스의 내장 타입 매핑(Built-in Type Mapping)을 정리한 표이다.
XML 데이터 타입 | Java 데이터 타입 |
---|---|
xsd:string |
java.lang.String |
xsd:boolean |
boolean, java.lang.Boolean * |
xsd:double |
double, java.lang.Double * |
xsd:float |
float, java.lang.Float * |
xsd:int |
int, java.lang.Integer |
xsd:integer |
java.math.BigInteger |
xsd:long |
long, java.lang.Long * |
xsd:short |
short, java.lang.Short * |
xsd:byte |
byte, java.lang.Byte |
xsd:Decimal |
java.math.BigDecimal |
xsd:base64Binary |
byte[ ] |
xsd:hexBinary |
byte[ ] |
xsd:QName |
javax.xml.rpc.namespace.QName |
xsd:dateTime |
java.util.Calendar |
xsd:gYearMonth |
java.util.Calendar |
xsd:gYear |
java.util.Calendar |
xsd:gMonthDay |
java.util.Calendar |
xsd:anyURI |
java.net.URI (JDK 1.4 or over) / java.lang.String (JDK 1.4) |
xsd:duration |
java.lang.String |
xsd:name |
java.lang.String |
xsd:NCName |
java.lang.String |
xsd:NMTOKEN |
java.lang.String |
xsd:nomalizedString |
java.lang.String |
xsd:time |
java.util.Calendar |
xsd:token |
java.lang.String |
xsd:unsignedByte |
short |
xsd:unsignedLong |
java.math.BigInteger |
xsd:unsignedInt |
long |
xsd:unsignedShort |
int |
SOAP-ENC:base64 |
byte[ ] |
SOAP-ENC:string |
java.lang.String |
SOAP-ENC:boolean |
boolean, java.lang.Boolean * |
SOAP-ENC:double |
double, java.lang.Double * |
SOAP-ENC:float |
float, java.lang.Float * |
SOAP-ENC:int |
int, java.lang.Integer * |
SOAP-ENC:long |
long, java.lang.Long * |
SOAP-ENC:short |
short, java.lang.Short * |
SOAP-ENC:byte |
byte, java.lang.Byte * |
SOAP-ENC:interger |
java.math.BigInteger |
SOAP-ENC:decimal |
java.math.BigDecimal |
SOAP-ENC:Array |
java.math.BigDecimal |
'xsd' 접두어는 XML namespace URI(http://www.w3.org/2001/XMLSchema)를 나타낸다. 'SOAP-ENC' 접두어는 XML namespace URI(http://schemas.xmlsoap.org/soap/encoding)를 나타낸다. 만약 WSDL에서 어떤 객체가 null이 될 수 있다고 정의되어 있다면 서비스 호출자는 xsd:nil을 데이터로 보내거나 받을 때 사용할 수 있다. 그리고 Java primitive 타입은 Wrapper 클래스로 교체된다. 위의 표에서 Java 타입 뒤에 붙은 "*"는 이것을 나타낸다. |
DII 클라이언트에서는 어떤 타입을 지정할 때, XML의 QName을 사용할 수 있다.
String NS_XSD = “http://www.w3.org/2001/XMLSchema”; String XSD_DATETIME = new QName(NS_XSD, “dateTime”); call.addParameter(“arg1”, XSD_DATETIME, PARAM_MODE_IN);
2.2. 배열
JEUS 웹 서비스는 JAX-RPC 타입에서 정의한 배열들을 지원한다. 예를 들어 int[ ]와 String[ ]이나 다차원 배열인 java.math.BigDecimal[ ][ ]도 지원한다.
2.3. 사용자 정의 타입 : JAX-RPC Value Type
JEUS 웹 서비스는 애플리케이션을 위해 작성한 모든 사용자 정의 타입을 지원한다. JAX-RPC 스펙에는 이러한 클래스들을 Value Type이라고 한다.
JEUS 웹 서비스에서 이것을 지원하기 위해서는 사용자 정의 클래스들은 다음 규칙을 따라야 한다.
-
파라미터 없는 public default 생성자를 가져야 한다.
-
직접 또는 간접적으로 java.rmi.Remote를 구현해서는 안 된다.
-
멤버 필드들의 타입은 JEUS 웹 서비스가 지원하는 타입이어야 한다.
클래스는 public, private 또는 protected 필드들을 포함할 수 있다. 웹 서비스 호출 중 전달되는 값을 위해서 필드는 다음 조건을 충족해야 한다.
-
public 필드는 final이나 transient가 될 수 없다.
-
public 필드가 아닌 것은 관련된 getter와 setter 메소드를 가져야 한다.
위의 규칙을 따르는 JavaBeans 컴포넌트 또한 지원된다.
3. JAX-RPC Value 타입의 사용
사용자 정의 타입 : JAX-RPC Value Type에서 설명한 규칙들을 따르는 사용자 정의형들은 웹 서비스의 파라미터나 반환 타입으로써 사용이 가능하다. 이런 종류의 사용자 정의 타입은 JAX-RPC Value 타입이라고 한다.
본 절에서는 JAX-RPC Value 타입을 사용하는 예제를 설명한다. CalcService는 2개의 숫자와 하나의 연산자를 받아서 결과를 숫자로 넘겨주는 예제이다. 그리고 CalcService를 위한 웹 서비스 클라이언트를 작성한다.
3.1. JAX-RPC Value 타입을 사용하는 웹 서비스 생성
다음은 CalcService 소스 코드인 Calculator.java 코드이다.
package calc; public class Calculator implements CalculatorIF { public Calculator() { } public double calc(CalcData data) { String op = data.getOp(); double num1 = data.getNum1(); double num2 = data.getNum2(); double ret = -9999.0; if (op.equals("plus")) { ret = num1 + num2; } else if (op.equals("minus")) { ret = num1 - num2; } else if (op.equals("mult")) { ret = num1 * num2; } else if (op.equals("div")) { if (num2 != 0) ret = num1 / num2; } return ret; } }
calc() 메소드는 CalcData형을 인자로 받고, 에러가 발생하면 –9999.0을 반환한다. 이후에 설명하는 예제에서는 좀 더 확장된 에러 처리방법을 설명할 것이다.
다음은 CalcData.java의 소스 코드이다.
package calc; public class CalcData { private double num1; private double num2; private String op; public CalcData() { } public double getNum1() { return num1; } public double getNum2() { return num2; } public String getOp() { return op; } public void setNum1(double n) { num1 = n; } public void setNum2(double n) { num2 = n; } public void setOp(String s) { op = s; } }
CalcData 클래스는 JAX-RPC Value Type 요구 사항을 따르고 있다. 이것은 CalcData의 인스턴스는 값으로서 전달될 수 있음을 의미한다.
다음은 Service Endpoint Interface 파일 CalculatorIF.java 코드이다.
package calc; import java.rmi.Remote; import java.rmi.RemoteException; public interface CalculatorIF extends Remote { public double calc(CalcData data) throws RemoteException; }
이 파일들을 컴파일하기 위해서 다음과 같이 명령어를 수행한다.
$ ant compile
위 명령은 결과 클래스 파일들을 build 디렉터리 아래에 옮겨놓을 것이다.
배치 가능한 EAR 파일을 생성하기 위해서는 다음 명령을 수행한다.
ant wsear
웹 서비스 모듈을 배치하면 다음과 같은 주소로 서비스에 접근할 수 있다.
http://localhost:8088/Calculator1Service/Calculator1Service?wsdl
3.2. JAX-RPC Value 타입을 사용하는 웹 서비스 클라이언트 생성
다음의 명령을 수행하면 CalcService 웹 서비스를 위한 프록시 클라이언트를 생성한다.
ant wsdl2java
생성된 Stub 코드의 패키지 이름은 com.test.calc로 가정한다.
다음은 클라이언트 프로그램의 소스 코드이다.
import com.test.calc.*; import javax.xml.rpc.soap.SOAPFaultException; public class CalcClient { public static void main(String[] args) { CalcClient calc = new CalcClient(); if (args.length != 3) { System.out.println("usage: java CalcClient num1 op num2"); System.out.println(" where op is one of " + "'plus', 'minus', 'mult', 'div'"); System.exit(1); } try { calc.run(args); } catch (SOAPFaultException e) { System.err.println("faultcode = " + e.getFaultCode()); System.err.println("faultString = " + e.getFaultString()); } catch (Exception e) { System.err.println(e.toString()); e.printStackTrace(); } } public void run(String[] args) throws Exception { CalculatorIF port = new Calculator1Service_Impl().getCalculatorIFPort(); CalcData data = new CalcData(); data.setNum1((new Double(args[0])).doubleValue()); data.setNum2((new Double(args[2])).doubleValue()); data.setOp(args[1]); double ret = port.calc(data); System.out.println(ret); } }
클라이언트 코드 구현이 완료되면 다음 명령을 통해 클라이언트 코드와 Stub 코드를 컴파일한다.
$ ant build
클라이언트를 실행하기 위해서 다음과 같이 Ant Task를 실행한다.
$ ant runclient
성공하면 다음과 같은 결과가 출력된다.
2.0
4. Holder 클래스
웹 서비스가 여러 개의 값을 반환하기를 원한다면 JAX-RPC Value 타입을 정의하거나 출력 또는 입/출력 파라미터를 하나 이상 지정해야 한다. Holder 클래스는 출력 또는 입/출력 파라미터로 사용되는 Helper 클래스이다.
4.1. 내장 Holder 클래스
Holder Class | Java Data Type |
---|---|
javax.xml.rpc.holders.BooleanHolder |
boolean |
javax.xml.rpc.holders.ByteHolder |
byte |
javax.xml.rpc.holders.ShortHolder |
short |
javax.xml.rpc.holders.IntHolder |
int |
javax.xml.rpc.holders.LongHolder |
long |
javax.xml.rpc.holders.FloatHolder |
float |
javax.xml.rpc.holders.DoubleHolder |
double |
javax.xml.rpc.holders.BigDecimalHolder |
java.math.BigDecimal |
javax.xml.rpc.holders.BigIntegerHolder |
java.math.BigInteger |
javax.xml.rpc.holders.ByteArrayHolder |
byte[ ] |
javax.xml.rpc.holders.CalendarHolder |
java.util.Calendar |
javax.xml.rpc.holders.QNameHolder |
javax.xml.namespace.QName |
javax.xml.rpc.holders.StringHolder |
java.lang.String |
각 Holder 클래스들이 가지고 있는 값을 액세스하기 위해 value 필드를 사용한다.
다음은 Caculator.java를 수정한 소스이다.
package calc; import javax.xml.rpc.holders.DoubleHolder; public class Calculator implements CalculatorIF { public Calculator() { } public void calc(CalcData data, DoubleHolder result){ String op = data.getOp(); double num1 = data.getNum1(); double num2 = data.getNum2(); double ret = -9999.0; if (op.equals("plus")) { ret = num1 + num2; } else if (op.equals("minus")) { ret = num1 - num2; } else if (op.equals("mult")) { ret = num1 * num2; } else if (op.equals("div")) { if (num2 != 0) ret = num1 / num2; } result.value = ret; } }
소스 코드는 내장 Holder 클래스인 javax.xml.rpc.holders.DoubleHolder를 import하였다.
import javax.xml.rpc.holders.DoubleHolder;
calc() 메소드의 signature 또한 수정되었다. 리턴 타입은 void이고, 두 번째 파라미터로 Holder 객체를 받는다.
public void calc(CalcData data, DoubleHolder result){
. . .
}
Holder 객체가 가진 객체 값을 접근하기 위해서는 Holder 클래스의 value 필드를 이용한다.
result.value = ret;
웹 서비스를 위한 웹 서비스 클라이언트를 생성하기 전에 이 웹 서비스를 패키지로 생성하고 배치해야 한다. 또한 클라이언트 프로그램 CalcClient.java도 수정되어야 한다.
다음은 클라이언트 소스 코드 CalcClient.java의 run() 메소드의 내용이다.
public void run(String[] args) throws Exception { CalculatorIF port = new CalcService_Impl().getCalculatorIFPort(); CalcData data = new CalcData(); data.setNum1((new Double(args[0])).doubleValue()); data.setNum2((new Double(args[2])).doubleValue()); data.setOp(args[1]); DoubleHolder ret = new DoubleHolder(); port.calc(data, ret); System.out.println(ret.value); }
CalcClient.java를 컴파일하기 전에 프록시 소스 코드를 다시 생성하는 것을 잊지 않도록 주의한다. |
4.2. 사용자 정의 타입을 위한 Holder 클래스 작성
다음은 사용자가 작성한 클래스의 Holder 클래스를 작성하는 과정이다.
-
javax.xml.rpc.holders.Holder 인터페이스를 구현한다.
-
사용자가 작성한 클래스를 TypeHolder라고 명명한다.
Type은 Holder 객체가 가지게 될 클래스의 이름이다. 예를 들어 CalcData 클래스를 위한 Holder를 작성하기 위해서는 Holder 클래스의 이름은 CalcDataHolder가 된다.
-
작성한 Holder 클래스를 public으로 선언한다.
-
value라는 이름을 가진 public 필드를 생성한다. 이 데이터 타입은 Holder 클래스의 타입과 같다.
-
value 필드를 기본값으로 초기화하는 파라미터 없는 default 생성자를 생성한다.
-
파라미터 값으로 value 필드를 설정하는 생성자를 생성한다.
JAX-RPC Value 타입의 Holder 클래스를 사용하는 방법을 설명하기 위해서 CalcService 예제를 수정한다. 다음 예제에 클래스 CalcData는 계산 결과값을 멤버로 포함한다. private 변수인 result와 getter/setter 메소드를 멤버로 추가하였다.
package calc; public class CalcData { private double num1; private double num2; private String op; private double result; public CalcData() { } public double getNum1() { return num1; } public double getNum2() { return num2; } public String getOp() { return op; } public double getResult() { return result; } public void setNum1(double n) { num1 = n; } public void setNum2(double n) { num2 = n; } public void setOp(String s) { op = s; } public void setResult(double n) { result = n; } }
CalcData의 Holder 클래스는 CalcDataHolder이다. CalcDataHolder 클래스는 데이터 타입이 CalcData인 public의 value 필드와 value 필드를 초기화하는 2개의 생성자를 가지고 있다.
package calc; import javax.xml.rpc.holders.Holder; public class CalcDataHolder implements Holder { public CalcData value; public CalcDataHolder() { } public CalcDataHolder(CalcData value) { this.value = value; } }
Holder 클래스를 사용하는 Calculator.java 소스 코드는 다음과 같다. calc() 메소드는 데이터 타입이 CalcDataHolder인 파라미터를 지정하고 있다. Holder 객체가 가지고 있는 객체를 접근하기 위해 public의 value 필드를 사용하고 있다.
package calc; public class Calculator implements CalculatorIF { public Calculator() { } public void calc(CalcDataHolder calcData) { CalcData data = calcData.value; String op = data.getOp(); double num1 = data.getNum1(); double num2 = data.getNum2(); double ret = -9999.0; if (op.equals("plus")) { ret = num1 + num2; } else if (op.equals("minus")) { ret = num1 - num2; } else if (op.equals("mult")) { ret = num1 * num2; } else if (op.equals("div")) { if (num2 != 0) ret = num1 / num2; } data.setResult(ret); // The following line is not necessary in this case. // But we will use it to show the usage of holder class. calcData.value = data; } }
다음은 클라이언트 소스 코드의 내용이다. Ant wsdl2java 명령어 또한 WSDL 문서로부터 클라이언트를 위한 Holder 클래스들을 생성한다. Ant wsdl2java 명령어로부터 생성된 Holder 클래스들은 사용자가 작성한 것과는 다르다는 것을 유념한다.
import com.test.calc.*; // generated by ant process-wsdl import com.test.calc.holders.*; // generated by ant process-wsdl public class CalcClient { public static void main(String[] args) { CalcClient calc = new CalcClient(); if (args.length != 3) { System.out.println("usage: java CalcClient num1 op num2"); System.out.println(" where op is one of " + "'plus', 'minus', 'mult', 'div'"); System.exit(1); } try { calc.run(args); } catch (Exception e) { System.err.println(e.toString()); e.printStackTrace(); } } public void run(String[] args) throws Exception { CalculatorIF port = new Calculator3Service_Impl().getCalculatorIFPort(); CalcData data = new CalcData(); CalcDataHolder dataHolder = new CalcDataHolder(data); data.setNum1((new Double(args[0])).doubleValue()); data.setNum2((new Double(args[2])).doubleValue()); data.setOp(args[1]); port.calc(dataHolder); System.out.println(dataHolder.value.getResult()); } }
5. Exception과 SOAP Fault
JEUS 웹 서비스에서 java.rmi.RemoteException 또는 java.lang.Exception 클래스를 상속한 클래스를 SOAP Fault로 사용할 수 있다. SOAP Fault는 SOAP 메시지를 통해 에러나 상태 정보를 보내기 위해 사용된다. 보다 자세한 내용은 SOAP 1.1 스펙의 "4.4 SOAP Fault"을 참고한다.
Exception 상태를 웹 서비스 애플리케이션의 클라이언트로 전송하려면 웹 서비스에서 java.rmi.RemoteException이나 사용자 정의 Exception을 발생하도록 지정한다. 발생한 Exception은 자동으로 SOAP Fault로 감싸져서 SOAP 메시지의 body에 포함되어 웹 서비스 클라이언트로 전송된다.
CalcService 예제에서 에러 상황이 발생할 경우 –9999.0 값을 반환값으로 사용하였던 소스 코드는 다음과 같이 변경할 수 있다.
package calc; import java.rmi.RemoteException; public class Calculator { public Calculator() { } public double calc(CalcData data) throws RemoteException, DevideByZeroException { String op = data.getOp(); double num1 = data.getNum1(); double num2 = data.getNum2(); double ret = 0; if (op.equals("plus")) { ret = num1 + num2; } else if (op.equals("minus")) { ret = num1 - num2; } else if (op.equals("mult")) { ret = num1 * num2; } else if (op.equals("div")) { if (num2 != 0) ret = num1 / num2; else throw new DevideByZeroException("divide by zero"); } else { throw new RemoteException("invalid opertion : " + op); } return ret; } }
calc() 메소드는 알려지지 않은 연산자가 지정되거나 나누기 값이 0일 때 java.rmi.RemoteException을 발생하도록 만들었다. 웹 서비스 클라이언트 프로그램에서 java.rmi.RemoteException 또는 java.lang.Exception을 잡을 때 사용할 수 있다.
6. MIME 타입을 DataHandler 타입으로 매핑
웹 서비스 클라이언트는 Attachment를 SOAP 메시지에 첨부하여 전송할 수 있다. JAX-RPC 스펙은 Attachment의 MIME 타입에 상응하는 Java 타입 매핑을 정의하고 있지만, 경우에 따라 웹 서비스 클라이언트가 MIME 타입에 관계없이 항상 javax.activation.DataHandler 타입으로 매핑하여 사용할 수도 있다.
본 절에서는 WSDL-to-Java 매핑 툴을 사용하여 MIME 타입을 항상 DataHandler 타입으로 매핑하는 방법을 설명한다.
6.1. Wsdl2java에서 dataHandlerOnly 옵션 사용
다음과 같이 MIME part를 가진 웹 서비스의 WSDL이 있다.
<message name="submission"> <part name="title" type="xsd:string" /> <part name="price" type="xsd:float" /> <part name="attachment" type="xsd:hexBinary" /> </message> . . . <operation name="submit"> . . . <input> . . . <mime:part> <mime:content part="attachment" type="application/xml" /> </mime:part> . . . </input> . . . </operation>
기본적으로 이러한 WSDL을 가지고 wsdl2java 툴을 사용하여 SEI를 생성하면 다음과 같이 MIME 타입 "application/xml"은 javax.xml.transform.Source 타입으로 매핑된다.
public interface SubmitBook extends java.rmi.Remote { public String submit(String title, float price, javax.xml.transform.Source attachment) throws java.rmi.RemoteException; }
Ant Task, wsdl2java에서 attribute, dataHandlerOnly="true"로 설정하거나 Command Line 툴에서 –datahandleronly 옵션을 사용하면, 다음과 같이 MIME 타입에 상관없이 part는 항상 javax.activation.DataHandler 타입으로 매핑된다.
public interface SubmitBook extends java.rmi.Remote { public String submit(String title, float price, javax.activation.DataHandler attachment) throws java.rmi.RemoteException; }
따라서 웹 서비스 클라이언트 개발자는 DataHandler 타입으로 Attachment를 송부해야 한다.
다음은 DataHandler 타입을 사용한 웹 서비스 클라이언트 예제이다.
// Creates a FileInputStream from the specified path name FileInputStream inputStream = new FileInputStream(new File("attachment/book.xml")); DataHandler dataHandler = new DataHandler(inputStream, "application/xml"); // Get a Service port SubmitBook port = new SubmitBookService_Impl().getSubmitBookPort(); String result = port.submit("Sample for a option: datahandleronly", 12.34f, dataHandler); System.out.println("response = " + result);
7. Doc/Literal에서 데이터 바인딩을 사용하지 않기
JAX-RPC 스펙에는 XML 타입에 대한 Java 타입 매핑을 정의하고 있다. 그러나 WSDL의 타입에 의해 매핑된 Java 타입을 사용하기 보다는 SOAPElement를 직접 구성하여 메시지를 전송하는 것이 더욱 편리할 수도 있다.
본 절에서는 WSDL-to-Java 매핑 툴을 사용하여 XML 타입에 상관없이 javax.xml.soap.SOAPElement를 사용하는 방법을 설명한다.
7.1. Wsdl2java에서 noDataBinding 옵션 사용
다음과 같은 Document/Literal로 기술된 WSDL이 있다.
<definitions name="BookQuoteService" ... >
<types>
<xsd:schema targetNamespace="...">
<xsd:complexType name="Book">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="isbn" type="xsd:string" />
<xsd:element name="authors" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Book" type="mh:Book" />
<xsd:element name="Result" type="xsd:float" />
</xsd:schema>
</types>
<message name="getBookPriceRequest">
<part name="book" element="mh:Book" />
</message>
<message name="getBookPriceResponse">
<part name="result" element="mh:Result" />
</message>
. . .
<binding name="BookServiceSoapBinding" type="mh:BookQuote">
<soap:binding style="document" ... />
<operation name="getBookPrice">
<input>
<soap:body use="literal" ... />
</input>
<output>
<soap:body use="literal" ... />
</output>
</operation>
</binding>
. . .
</definitions>
이 WSDL로부터 wsdl2java로 생성한 SEI는 다음과 같이 Object 타입의 Input 파라미터를 갖는다.
public interface BookQuote extends java.rmi.Remote { public float getBookPrice(sample.nodatabinding.stub.Book book) throws java.rmi.RemoteException; }
만약 wsdl2java로 생성한 SEI를 생성할 때 Ant Task, wsdl2java에서 attribute, noDataBinding="true"로 설정하거나, Command Line 툴에서 –nodatabinding 옵션을 사용하면 다음과 같이 XML 타입에 상관없이 Input 파라미터 및 Return value 타입은 javax.xml.soap.SOAPElement가 된다.
public interface BookQuote extends java.rmi.Remote { public javax.xml.soap.SOAPElement getBookPrice(javax.xml.soap.SOAPElement book) throws java.rmi.RemoteException; }
wsdl2java의 nodatabinding 옵셥은 Document/Literal의 WSDL에서만 유효하다. 이 경우 웹 서비스 클라이언트 개발자는 SOAPElement 타입으로 메시지를 직접 구성해야 한다.
다음은 SOAPElement 타입을 사용한 웹 서비스 클라이언트 예제이다.
// Creates a FileDataSource from the specified path name SOAPFactory factroy = SOAPFactory.newInstance(); // Create a SOAPElement object SOAPElement book = factroy.createElement( "Book", "mh", "http://www.tmaxsoft.com/j2eews/BookQuote"); SOAPElement title = factroy.createElement("title"); title.addTextNode("Sample for a option: nodatabinding"); book.addChildElement(title); SOAPElement isbn = factroy.createElement("isbn"); isbn.addTextNode("123-456-789"); book.addChildElement(isbn); SOAPElement authors = factroy.createElement("authors"); authors.addTextNode("TmaxSoft Co., Ltd."); book.addChildElement(authors); // Get a Service port BookQuote port = new BookQuoteService_Impl().getBookQuotePort(); SOAPElement price = port.getBookPrice(book); System.out.println("price = " + price.getValue());