JAX-RPC 웹 서비스 호출

본 장에서는 JAX-RPC 웹 서비스를 호출하기 위한 여러 가지 클라이언트를 작성하고 호출하는 방법에 대해 설명한다.

1. JAX-RPC 웹 서비스 호출(Java SE 클라이언트)

웹 서비스 호출은 웹 서비스를 사용하기 위한 클라이언트 응용 프로그램이 수행하는 동작을 의미한다. 클라이언트 응용 프로그램은 Java 또는 .NET과 같은 여러 다양한 기술을 이용하여 JEUS에 배치된 웹 서비스를 호출할 수 있다.

웹 서비스 클라이언트는 웹 서비스에게 서비스를 요청하는 프로그램이다. JEUS JAX-RPC 웹 서비스를 사용하여 2가지 타입의 웹 서비스 클라이언트를 생성할 수 있다.

1.1. Stub 클라이언트

Stub 클라이언트는 특정 웹 서비스의 WSDL로부터 생성된 로컬 Stub의 메소드를 호출한다. Stub 객체는 원격의 웹 서비스와 상호작용을 담당한다.

본 절에서는 JAX-RPC 웹 서비스 생성과 배치에서 이미 작성했던 DocLitEchoService 웹 서비스의 메소드를 호출하는 웹 서비스 클라이언트 프로그램을 작성하는 방법을 설명한다.

1.1.1. WSDL로부터 웹 서비스 Stub 생성

웹 서비스 Stub 소스 코드를 생성하기 위해서 wsdl2java Ant Task나 wsdl2java 명령어를 사용한다.

Ant Task를 사용하는 경우 build.xml의 코드는 다음과 같다.

웹 서비스 Stub 생성 : <build.xml>
...
<target name="-pre-compile">
    <antcall target="wsdl2java">
        <param name="wsdl2java.option"
         value="-gen:client -d ${build.classes.dir}
         -package echo -compile -verbose ${src.conf}/DocLitEchoService.wsdl" />
    </antcall>
</target>
...

wsdl2java Task에 대한 더 자세한 설명은 JEUS Reference 안내서의 wsdl2java를 참고한다.

다음과 같은 명령어를 사용하여 웹 서비스 Stub 소스 코드를 생성할 수 있다.

wsclient$ ant -pre-compile

위의 과정이 성공했다면 웹 서비스 Stub 소스 코드가 생성된다. Stub 소스 코드는 wsdl2java Task의 destDir 속성으로 지정한 디렉터리 아래에 컴파일된다.

wsclient/build/classes/echo/
                         |
                         +-- DocLitEchoService.java
                         |
                         +-- DocLitEchoService_Impl.java
                         |
                         +-- Echo.java
                         |
                         +-- ... </screen>

다음은 생성된 클래스에 대한 설명이다.

클래스 설명

Echo.java

portType 인터페이스 클래스이다.

Echo_Stub.java

portType 인터페이스 클래스의 Stub 클래스이다.

DocLitEchoService.java

서비스 인터페이스 클래스이다.

DocLitEchoService_Impl.java

서비스 구현 클래스이다.

*.java

기타 생성 클래스이다.

생성된 Java 파일 또는 Java 파일 안의 메소드의 이름은 웹 서비스의 WSDL로부터 매핑된다. WSDL에 대응하는 Java 이름 매핑은 다음과 같다.

WSDL element 매핑

<service>

서비스 인터페이스와 구현 Java 클래스에 매핑된다.

<service>의 name 속성값이 인터페이스의 이름이다. 구현 파일은 <service>의 이름 뒤에 '_Impl’이 붙는다.

<port>

<service>의 서비스 인터페이스와 구현 클래스 내의 메소드에 매핑된다. 메소드의 이름은 "get + <port>의 name 속성값"으로 이루어진다.

<portType>

웹 서비스 operation들을 위한 Java 인터페이스와 구현 Java 클래스에 매핑된다. Java 파일 이름은 <portType>의 name 속성을 사용해서 생성된다. 구현 클래스의 Java 파일명은 <portType>의 name 속성값에 '_Stub’이 붙은 형태이다.

1.1.2. 웹 서비스 클라이언트 작성

본 절에서는 웹 서비스 클라이언트의 작성을 위해서 위에서 생성한 4개의 클래스의 사용방법에 대해서 설명한다.

  1. 웹 서비스 객체 생성

    원격 웹 서비스를 위해 서비스 구현 객체를 생성한다. 예제에서 서비스 구현 클래스는 DocLitEchoService_Impl이다.

    다음은 웹 서비스 객체 생성을 위한 Java 코드이다.

    DocLitEchoService service = new DocLitEchoService_Impl();
  2. Stub 객체로부터 Port 객체 얻기

    일단 서비스 구현 객체가 생성되었다면 다음 단계로 서비스 구현 객체로부터 Port 객체를 얻는다.

    서비스 인터페이스 클래스는 Port 객체를 얻기 위한 메소드들을 제공한다. 메소드 이름은 "get + WSDL의 <port>의 name 속성값"으로 구성되며, WSDL의 <portType>의 name 속성을 이름으로 가지는 타입이 리턴된다. WSDL Port 이름은 웹 서비스의 WSDL 문서의 <port> 안에 기술되어 있다. <port>는 <service>의 하위 element이다.

    이 예제에서의 WSDL 문서는 다음과 같다.

    <wsdl:portType name=Echo”>
        . . .
    </wsdl:portType>
    . . .
    <wsdl:service name=”DocLitEchoService”>
        <wsdl:port binding=”impl:EchoSoapBinding” name=”EchoPort”>
            <soap:address location=”http://localhost:8088/DocLitEchoServi
    ce/DocLitEchoService”/>
        </wsdl:port>
    </wsdl:service>

    다음은 서비스 인터페이스 클래스인 DocLitEchoService.java의 일부 내용이다.

    <package echo;
    
    public interface DocLitEchoService extends javax.xml.rpc.Service {
        public java.lang.String getEchoPortAddress();
    
        public echo.Echo getEchoPort()
            throws javax.xml.rpc.ServiceException;
    }

    서비스 객체로부터 Port 객체를 얻기 위한 다음의 소스 코드를 구현한다.

    DocLitEchoService service = new DocLitEchoService_Impl();
    Echo port = service.getEchoPort();
  3. Port 객체상의 Operation 실행

    원격 웹 서비스에서 제공하는 Operation들을 위한 Port 객체를 생성하였다면 Port 객체의 메소드들을 호출함으로써 웹 서비스 Operation을 실행할 수 있다. 예제에서 웹 서비스의 Port는 'echoString’이라는 Operation을 제공한다.

    Operation을 실행하기 위한 코드는 다음과 같다.

    DocLitEchoService service = new DocLitEchoService_Impl();
    Echo port = service.getEchoPort();
    String s = port.echoString(“JEUS”);

    또는 다음의 코드로 구현한다.

    Echo port = new DocLitEchoService_Impl().getEchoPort();
    String s = port.echoString(“JEUS”);

다음은 웹 서비스 클라이언트를 구현한 예제로 wsclient/src/j2se 디렉터리에 위치한다고 가정한다.

웹 서비스 클라이언트 예제 : <ProxyClient.java>
package j2se;

import echo.DocLitEchoService_Impl;
import echo.Echo;

public class ProxyClient {
    public static void main(String args[]) {
        ProxyClient client = new ProxyClient();
        client.run();
    }

    public void run() {
        try {
            Echo port = new DocLitEchoService_Impl().getEchoPort();
            String s = port.echoString("JEUS");
            System.out.println("response = " + s);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

1.1.3. 웹 서비스 클라이언트 컴파일

ProxyClient 코드의 컴파일을 위해서 wsclient 디렉터리에서 다음의 명령어를 콘솔에서 실행한다.

JEUS_HOME/samples/webservice/jaxrpc/from_java/doclit/doclit-client$ ant build

다음은 컴파일이 성공한 경우의 컴파일된 클래스이다.

JEUS_HOME/samples/webservice/jaxrpc/from_java/doclit/doclit-client/build/classes/j2s
e/ProxyClient.class

1.1.4. 웹 서비스 클라이언트 실행

웹 서비스 클라이언트가 위치하는 디렉터리로 이동하여 다음의 명령어를 실행한다.

JEUS_HOME/samples/webservice/jaxrpc/from_java/doclit/doclit-client$ ant run

다음은 실행 결과이다.

Response = JEUS

1.2. DII 클라이언트

DII을 사용하면 클라이언트는 실행되기 전까지 Remote Operation의 signature나 웹 서비스의 이름을 몰라도, Remote Operation을 호출할 수 있다.

본 절에서는 DII 클라이언트를 생성하는 방법에 대해서 설명한다.

1.2.1. DII 클라이언트 작성

DII 클라이언트를 작성할 때 JAX-RPC 1.1 API를 사용한다. JEUS JAX-RPC 웹 서비스는 JAX-RPC 1.1 API를 완벽히 지원한다. 본 절에서는 JAX-RPC API의 간단한 사용법만을 설명한다.

JAX-RPC에 관한 자세한 정보는 JAX-RPC 스펙(http://www.jcp.org/en/jsr/detail?id=101)과 Oracle의 JAX-RPC 홈페이지(http://www.oracle.com/technetwork/java/docs-142876.html)를 참고한다.

DII 호출 방법은 RPC 방식의 웹 서비스를 호출하는 데에만 사용될 수 있다. 예제에서는 RPC 방식의 RpcEncEchoService 서비스의 echoString Operation을 호출하는 DII 클라이언트를 작성한다.

DII 클라이언트의 작성 방법은 다음과 같다.

  1. 다음의 문장들을 DII 클라이언트 코드에 추가한다.

    import javax.xml.rpc.Call;
    import javax.xml.rpc.Service;
    import javax.xml.rpc.ServiceFactory;
    import javax.xml.rpc.ParameterMode;
    import javax.xml.namespaceQName;
  2. ServiceFactory와 Service 객체들을 생성한다.

    String targetNamespace = “urn:RpcEncService”;
    String serviceName = “RpcEncService”;
    ServiceFactory factory = ServiceFactory.newInstance();
    Service service = factory.createService(new QName(targetNamespace, serviceName);
  3. Call 객체를 생성하고 설정한다.

    String operationName = “echoString”;
    QName QNAME_XSD_STRING = new QName(“http://www.w3.org/2001/XMLSchema”, “string”);
    Call call = (Call) service.createCall();
    call.setTargetEndpointAddress(endpoint);
    call.setOperationName(new QName(targetNamespace, operationName));
    call.addParameter("String_1", QNAME_XSD_STRING, ParameterMode.IN);
    call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");
    call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY,
                     "http://schemas.xmlsoap.org/soap/encoding/");
    call.setReturnType(QNAME_XSD_STRING);
  4. 최종적으로 Call 객체상에서 웹 서비스 operation을 실행한다.

    String ret = (String)call.invoke(new Object[] { “JEUS” } );

다음은 DII 클라이언트를 구현한 예제로 wsclient/src 디렉터리에 위치한다고 가정한다.

DII 클라이언트 예제 : <DIIClient.java>
package j2se;

import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;

public class DIIClient {
    private static final String NS_XSD =
        "http://www.w3.org/2001/XMLSchema";
    private static final QName QNAME_XSD_STRING = new QName(NS_XSD, "string");

    public void run() {
        try {
            ServiceFactory factory = ServiceFactory.newInstance();
            String endpoint = "http://localhost:8088/" +
                "RpcEncEchoService/RpcEncEchoService";
            String targetNamespace = "urn:RpcEncService";
            Service service = factory.createService(
                new QName(targetNamespace, "RpcEncService"));
            Call call = service.createCall();

            call.setTargetEndpointAddress(endpoint);
            call.setOperationName(
                new QName(targetNamespace, "echoString"));
            call.addParameter("String_1", QNAME_XSD_STRING, ParameterMode.IN);
            call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");
            call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY,
                             "http://schemas.xmlsoap.org/soap/encoding/");
            call.setReturnType(QNAME_XSD_STRING);

            String ret = (String)call.invoke(new Object[] { "JEUS" });
            System.out.println("response = " + ret);
        } catch (Exception e) {
            System.err.println(e.toString());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        DIIClient client = new DIIClient();
        try {
            client.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.2.2. DII 클라이언트 컴파일

DII 클라이언트 코드의 컴파일을 위해서 wsclient 디렉터리로 이동하여 다음의 명령어를 콘솔에서 실행한다.

JEUS_HOME/samples/webservice/jaxrpc/from_java/rpcenc/rpcenc-client$ ant build

컴파일이 성공한 경우 컴파일된 클래스는 다음과 같다.

JEUS_HOME/samples/webservice/jaxrpc/from_java/rpcenc/rpcenc-client/build/
classes/j2se/DIIClient.class

1.2.3. DII 클라이언트 실행

DII 클라이언트가 위치하는 디렉터리로 이동하여 다음의 명령어를 실행한다.

JEUS_HOME/samples/webservice/jaxrpc/from_java/rpcenc/rpcenc-client$ ant run

다음은 실행결과이다.

Response = JEUS

2. JAX-RPC 웹 서비스의 호출(Jakarta EE 클라이언트)

EJB, 서블릿과 같이 JEUS 서버에 배치된 구성 요소로부터 웹 서비스를 호출하는 것은 독립적인(stand-alone) 클라이언트로부터 호출하는 것과 본질적으로는 동일하다. 그러나 현재 Jakarta EE 웹 서비스 스펙에서는 Jakarta EE 웹 서비스 클라이언트의 이식성(Portability)을 위해 프로그래밍 모델을 별도로 정의하고 있고, 이러한 모델을 따라 클라이언트를 작성하는 것을 권장한다.

2.1. Jakarta EE 클라이언트 프로그래밍 모델

웹 서비스는 클라이언트의 입장에서 본다면 클라이언트를 대신하여 비즈니스 로직을 수행하는 메소드의 집합이라 할 수 있다. 클라이언트는 메소드가 로컬에서 수행되는지 원격에서 수행하는지를 구별할 수 없다. 클라이언트는 JAX-RPC 스펙에 정의된 SEI를 사용하여 웹 서비스에 접근한다.

Jakarta EE 클라이언트는 JAX-RPC에 정의된 서비스 인터페이스를 구현한 서비스 객체에 접근하기 위해서 JNDI를 사용한다. 서비스 객체는 클라이언트가 SEI를 구현한 Stub이나 프록시를 얻어오기 위해 사용하는 Factory이다. 서비스 인터페이스는 JAX-RPC에 정의된 javax.xml.rpc.Service 인터페이스이거나, 이를 상속하여 생성된 서비스 인터페이스이다.

클라이언트 개발자는 SEI와 서비스 인터페이스를 얻어오는 것으로부터 시작하며 이들은 JAX-RPC에 정의된 WSDL → Java 매핑 법칙에 의해 생성된다. 클라이언트 개발자는 Stub을 개발 시점에 생성하지 않을 것을 권장하며, 개발 시점에는 Stub이 아닌 인터페이스를 사용할 수 있다. Stub은 클라이언트 모듈을 배치하는 시점에 클라이언트가 구동되는 환경에 맞게 자동으로 생성될 것이다. 웹 서비스의 JNDI-lookup은 논리적인 이름에 의해 이루어지며, 이 이름은 클라이언트 DD에 정의된다.

2.2. Jakarta EE 클라이언트 프로그래밍 절차

Jakarta EE 클라이언트 프로그래밍의 기본적인 절차는 다음과 같다.

  1. JNDI Lookup을 통한 서비스(인터페이스)의 획득한다.

    클라이언트 개발자는 서비스의 레퍼런스로 사용되는 논리적인 서비스의 JNDI 이름을 클라이언트의 DD에 정의해야 한다. 필수 요구 사항은 아니지만 모든 서비스의 논리적인 레퍼런스 이름을 JNDI Name Space의 service라는 서브 컨텍스트 아래에 구성하는 것을 권장한다.

    컨테이너는 서비스 인터페이스의 구현을 클라이언트 환경 컨텍스트(java:comp/env) 하위에 서비스 레퍼런스의 논리적인 이름으로 바인딩시켜야 한다.

    InitialContext jndiContext = new InitialContext();
    Service service = (Service)jndiContext.lookup("java:comp/env/service/DocLitEchoService");

    위의 예제에서는 서비스 인터페이스 javax.xml.rpc.Service가 web.xml의 <service-ref> 하위 element인 <service-interface>에 설정되어 있고, JNDI 이름은 <service-ref-name>에 설정되어 서로 바인딩된다.

    JAX-RPC에서는 ServiceFactory라는 클래스에서 서비스를 생성할 수 있다. 하지만, Jakarta EE 응용 프로그램에서는 ServiceFactory를 사용하는 것을 권장하지 않는다. Jakarta EE 클라이언트는 항상 JNDI 서비스 Lookup을 통해 서비스를 가져와야 한다.

  2. 서비스 인터페이스의 API를 이용한 Stub 생성 또는 Call 객체를 생성한다.

    서비스 인터페이스는 클라이언트가 서비스 포트에 접근하기 위해서 Stub과 동적 프록시 혹은 DII Call 객체를 생성하려고 할 때 사용된다. 클라이언트 개발자는 응용 프로그램이 사용하는 서비스 인터페이스의 타입을 명시적으로 클라이언트 DD(web.xml)에 선언해야 한다.

    다음은 서비스 인터페이스의 타입과 JNDI 이름이 설정된 web.xml의 예이다.

    서비스 인터페이스의 타입과 JNDI 이름 설정 예제 : <web.xml>
    <?xml version="1.0"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
        <servlet>
            <servlet-name>jsp_helloClient</servlet-name>
            <jsp-file>/helloClient.jsp</jsp-file>
            <load-on-startup>0</load-on-startup>
        </servlet>
        <service-ref>
            <service-ref-name>
                service/DocLitEchoService
            </service-ref-name>
            <service-interface>
                javax.xml.rpc.Service
            </service-interface>
            <wsdl-file>
                WEB-INF/wsdl/DocLitEchoService.wsdl
            </wsdl-file>
            <jaxrpc-mapping-file>
                WEB-INF/DocLitEchoService-mapping.xml
            </jaxrpc-mapping-file>
            <port-component-ref>
                <service-endpoint-interface>
                    echo.Echo
                </service-endpoint-interface>
            </port-component-ref>
        </service-ref>
    </web-app>
  3. Stub이나 Call 객체를 이용한 웹 서비스를 호출한다.

    • Stub 객체를 이용한 호출

      클라이언트는 서비스 Lookup을 통하여 가져온 서비스 인터페이스의 다음과 같은 함수들을 이용해서 Stub과 동적인 프록시를 생성할 수 있다.

      java.rmi.Remote getPort(QName portName, Class serviceEndpointInterface)
          throws ServiceException;
      java.rmi.Remote getPort(java.lang.Class serviceEndpointInterface)
          throws ServiceException;
    • Call 객체를 이용한 호출

      클라이언트는 JNDI Lookup을 통하여 얻어온 서비스 인터페이스의 DII 메소드를 사용하여 Call 객체를 가져올 수 있다.

      다음은 동적 포트에 접근하기 한 API이다.

      Call createCall() throws ServiceException;
      Call createCall()(QName portName) throws ServiceException;
      Call createCall(QName portName, String operationName)
          throws ServiceException;
      Call createCall(QName portName, QName operationName)
          throws ServiceException;
      Call[] getCalls(QName portName) throws ServiceException;

2.3. Jakarta EE 클라이언트의 작성과 예제

Jakarta EE 클라이언트는 WSDL을 사용하거나 사용하지 않고도 작성할 수 있다. 클라이언트는 JSP, 서블릿, EJB 등 여러 형태일 수 있다. 단, 앞에서 제시한 프로그래밍 모델에 부합하기만 하면 된다.

본 절에서는 간단한 JSP 형태의 Jakarta EE 클라이언트 작성 예제를 통해 작성법을 설명한다.

2.3.1. WSDL을 사용한 서비스 호출

WSDL을 사용한 서비스를 호출방법은 다음의 절차를 따른다.

  1. JEUS는 wsdl2java Ant Task와 wsdl2java Command Line 툴을 사용해서 Jakarta EE 클라이언트 Portable Artifact와 JAX-RPC 매핑 파일을 생성한다. wsdl2java Task에 대한 보다 자세한 설명은 JEUS Reference 안내서의 wsdl2java를 참고한다.

    Ant Task를 사용하는 build.xml의 예는 다음과 같다.

    WSDL을 사용한 서비스 호출 예제 : <build.xml>
        <target name="do-package-war">
            ...
            <antcall target="wsdl2java">
                <param name="wsdl2java.option"
                       value="-import:client
           -d ${build.war.dir}/WEB-INF/classes -package echo
           -outputmapping ${build.war.dir}/WEB-INF/DocLitEchoService-mapping.xml
           -compile ${src.web}/WEB-INF/wsdl/DocLitEchoService.wsdl" />
            </antcall>
            ...
        </target>

    ant build 명령을 수행하고 나면, Jakarta EE 클라이언트 Portable Artifact와 JAX-RPC 매핑 파일이 생성된다.

  2. 클라이언트 프로그램을 작성한다.

    WSDL을 가지고 클라이언트를 개발하는 경우에는 다음과 같은 java.xml.rpc.Service 인터페이스 메소드를 이용하여 Stub이나 Call 객체들을 가져올 수 있다.

    java.rmi.Remote getPort(QName portName, Class serviceEndpointInterface)
        throws ServiceException;
    java.rmi.Remote getPort(java.lang.Class serviceEndpointInterface)
        throws ServiceException;
    Call createCall() throws ServiceException;
    Call createCall()(QName portName) throws ServiceException;
    Call createCall(QName portName, String operationName)
        throws ServiceException;
    Call createCall(QName portName, QName operationName)
        throws ServiceException;
    Call[] getCalls(QName portName) throws ServiceException;

    다음은 위의 메소드 중 동적인 프록시(Dynamic Proxy)를 생성하여 웹 서비스를 호출하도록 JSP로 작성된 Jakarta EE 클라이언트의 예이다.

    Jakarta EE 클라이언트의 예 : <helloClient.jsp>
    <%@ page language="java" %>
    <%@ page import="javax.naming.*" %>
    <%@ page import="javax.rmi.*" %>
    <%@ page import="java.rmi.RemoteException" %>
    <%@ page import="java.util.*" %>
    
    <%@ page import="javax.naming.InitialContext" %>
    <%@ page import="javax.xml.rpc.Service" %>
    
    <%@ page import="echo.*" %>
    <%@ page errorPage="/error.html" %>
    
    <%! String msgToSend = "msg_sent_by_jspClient";
        String ret=null;
        String exceptionString="";
    %>
    <%
            try {
                InitialContext jndiContext = new InitialContext();
                Service service = (Service)jndiContext.lookup(
                        "java:comp/env/service/DocLitEchoService");
                java.rmi.Remote port = service.getPort(Echo.class);
                Echo echoPort = (Echo)port;
                ret = echoPort.echoString(msgToSend);
            } catch (Exception e) {
                exceptionString = e.toString();
                e.printStackTrace();
            }
    %>
    <%= "Result is "+ret+"......"
    %>
  3. 앞서 제시한 예제에 대한 표준 DD(web.xml) 파일을 작성한다.

    표준 DD 파일 : <web.xml>
    <?xml version="1.0"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
        <servlet>
            <servlet-name>jsp_helloClient</servlet-name>
            <jsp-file>/helloClient.jsp</jsp-file>
            <load-on-startup>0</load-on-startup>
        </servlet>
        <service-ref>
            <service-ref-name>
                service/DocLitEchoService
            </service-ref-name>
            <service-interface>
                javax.xml.rpc.Service
            </service-interface>
            <wsdl-file>
                WEB-INF/wsdl/DocLitEchoService.wsdl
            </wsdl-file>
            <jaxrpc-mapping-file>
                WEB-INF/DocLitEchoService-mapping.xml
            </jaxrpc-mapping-file>
            <port-component-ref>
                <service-endpoint-interface>
                    echo.Echo
                </service-endpoint-interface>
            </port-component-ref>
        </service-ref>
    </web-app>

    web.xml의 <service-ref-name>에 JSP 클라이언트에서 JNDI Lookup으로 찾아오는 서비스 이름을 설정한다.

    <service-interface>에 서비스 인터페이스가 javax.xml.rpc.Service 인터페이스임이 설정되어 있다. 이 인터페이스의 구현은 <wsdl-file>에 설정된 WSDL 파일의 정보를 가지고 클라이언트가 JEUS에 배치될 때 생성된다. 이때 필요한 Java 패키지와 타입에 관한 정보는 <jaxrpc-mapping-file>에 설정된 JAX-RPC 매핑 파일에서 가져온다.

    다음은 앞에 제시한 예제의 JEUS 웹 DD(jeus-web-dd.xml)이다.

    JEUS 웹 DD 파일 : <jeus-web-dd.xml>
    <?xml version="1.0" encoding="UTF-8"?>
    <jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
        <service-ref>
            <service-client>
                <service-ref-name>
                    service/DocLitEchoService
                </service-ref-name>
                <port-info>
                    <wsdl-port xmlns:ns1="urn:DocLitService">
                        ns1:Echo
                    </wsdl-port>
                    <stub-property>
                        <name>
                            javax.xml.rpc.service.endpoint.address
                        </name>
                        <value>
                            http://localhost:8088/DocLitEchoService/DocLitEchoService
                        </value>
                    </stub-property>
                </port-info>
            </service-client>
        </service-ref>
    </jeus-web-dd>

2.3.2. WSDL을 사용하지 않고 서비스 호출

WS-I라는 표준 기구는 웹 서비스의 상호 호환성을 위해 Basic Profile을 정의하고 이를 준수할 것을 요구하고 있다. 현재 나와있는 Basic Profile 1.0은 WSDL이 항상 접근 가능하게 공개되기를 요구한다.

Basic Profile을 준수하지 않는 서비스에 접근하려면 WSDL이 항상 공개되어 있다고 볼 수 없다. 이 경우 WSDL과 무관하게 웹 서비스를 호출할 수 있는데, DII이 바로 그 방법이다. 하지만 이는 결국 WSDL에 포함된 오퍼레이션 스타일이나 오퍼레이션 이름과 같은 상세를 Call 객체에 제공해야만 가능하다.

WSDL이 클라이언트 DD에 정의되어 있지 않다면 클라이언트 개발자는 다음과 같은 서비스 인터페이스의 메소드를 통해서 Call 객체를 생성해야 한다.

Call createCall() throws ServiceException;

다음은 Call 객체를 이용하여 웹 서비스를 호출하는 JSP Jakarta EE 클라이언트의 예이다.

웹 서비스를 호출하는 JSP Jakarta EE 클라이언트 : <helloClient.jsp>
<%@ page language="java" %>
<%@ page import="javax.naming.*" %>
<%@ page import="javax.rmi.*" %>
<%@ page import="java.rmi.RemoteException" %>
<%@ page import="java.util.*" %>

<%@ page import="javax.naming.InitialContext" %>
<%@ page import="javax.xml.rpc.Service" %>
<%@ page import="javax.xml.rpc.Call" %>
<%@ page import="javax.xml.namespace.QName" %>
<%@ page import="javax.xml.rpc.ParameterMode" %>

<%@ page errorPage="/error.html" %>

<%! String msgToSend = "msg_sent_by_jspClient";
    String ret=null;
    String exceptionString="";
%>

<%
  try {
        InitialContext jndiContext = new InitialContext();
        Service service = (Service)jndiContext.lookup(
            "java:comp/env/service/RpcEncEchoService");

        String targetNamespace = "urn:RpcEncService";
        QName operationName = new QName(targetNamespace,"echoString");
        Call call = service.createCall();
        call.setOperationName(operationName);
        call.addParameter("String_1",
            new QName("http://www.w3.org/2001/XMLSchema", "string"),ParameterMode.IN);

        call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");
        call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY,
            "http://schemas.xmlsoap.org/soap/encoding/");

        call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema","string"));
        call.setTargetEndpointAddress("http://localhost:8088/" +
            "RpcEncEchoService/RpcEncEchoService");

        ret = (String)call.invoke(new Object[]{msgToSend});
    } catch (Exception e) {
        exceptionString = e.toString();
        e.printStackTrace();
    }
%>
<%= "Result is "+ret+"......"
%>

위 예제에서 제시한 대로 Call 객체를 생성한 후 오퍼레이션 이름과 오퍼레이션이 갖는 파라미터들을 추가하게 되며, 이때 추가적으로 오퍼레이션의 스타일과 인코딩 방식을 설정할 수도 있다.

설정 방법은 다음과 같다.

call.setProperty(Call.OPERATION_STYLE_PROPERTY, “rpc”);
call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY, “http://schemas.xmlsoap.org/soap/encoding/”);

이전에 설명한 예제의 표준 DD(web.xml)는 다음과 같이 구성되어 있다.

표준 DD 파일 : <web.xml>
<?xml version="1.0"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
    <servlet>
        <servlet-name>jsp_helloClient</servlet-name>
        <jsp-file>/helloClient.jsp</jsp-file>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <service-ref>
        <service-ref-name>
            service/DocLitEchoService2
        </service-ref-name>
        <service-interface>
            javax.xml.rpc.Service
        </service-interface>
    </service-ref>
</web-app>

<service-ref-name>에 JSP 클라이언트에서 JNDI Lookup으로 찾아오는 서비스 이름을 설정한다. <service-interface>에 서비스 인터페이스가 javax.xml.rpc.Service 인터페이스임이 설정되어 있다. WSDL이 없이 웹 서비스를 호출할 때에는 <jaxrpc-mapping-file>을 이용한 JAX-RPC 매핑 파일의 설정을 사용할 수 없다.

2.3.3. 웹 서비스 클라이언트의 패키징

Jakarta EE 웹 서비스 클라이언트는 하나의 Jakarta EE 컴포넌트이기 때문에 컴포넌트 고유의 패키징 방식을 따른다.

다음은 JSP 클라이언트의 패키징 방식에 대한 설명이다.

WAR
 |
 +-- JSP Client
 |
 +-- WEB-INF
      |
      +-- web.xml (서블릿 DD)
      |
      +-- jeus-web-dd.xml (JEUS Specific DD)
      |
      +-- Jax-rpc mapping 파일
      |
      +-- wsdl
      |    |
      |    +-- wsdl 파일
      |
      +-- classes
           |
           +-- Java 클래스 콤포넌트 (SEI, Service Interface..)

위와 같이 패키징 한 Jakarta EE 클라이언트를 배치하고 JSP를 호출하면 웹 서비스를 호출할 수 있게 된다.

JAX-RPC 매핑 파일과 WSDL 파일이 webservices.xml 파일에 <wsdl-file>과 <jax-rpc-mapping-file>로 기술한 위치에 존재한다면 반드시 위의 구조를 따르지 않아도 무방하다.