Microsoft MVP성태의 닷넷 이야기
Java: 8. 닷넷 개발자가 구현해 본 자바 웹 서비스 (2) [링크 복사], [링크+제목 복사],
조회: 21645
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)

닷넷 개발자가 구현해 본 자바 웹 서비스 (2)


지난번의 경우,

닷넷 개발자가 구현해 본 자바 웹 서비스 (1)
; https://www.sysnet.pe.kr/2/0/1130

개발 환경 구성하고 어떻게든 예제를 돌리느라 바빴는데요. 이제 여유를 가지고 잠시 살펴보면.

우선, 닷넷에 생성된 프록시 코드의 차이점을 파악해 보겠습니다.

(WCF를 제외하고) 닷넷의 웹 서비스인 asmx 서비스에 대해 프록시 코드를 만들면 보통 다음과 같은 식으로 메서드에 SoapDocumentMethod 특성이 부여됩니다.

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/Echo", 
    RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/",
    Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]

public string Echo(string text) {
    object[] results = this.Invoke("Echo", new object[] {text};
    return ((string)(results[0]));
}


반면, 자바 쪽 웹 메서드를 호출하는 프록시 코드에는 SoapRpcMethod 특성이 부여되었습니다.

[System.Web.Services.Protocols.SoapRpcMethodAttribute("", RequestNamespace="http://DefaultNamespace", 
        ResponseNamespace="http://localhost:8080/axis/services/HelloService")]
[return: System.Xml.Serialization.SoapElementAttribute("EchoReturn")]

public string Echo(string text) {
    object[] results = this.Invoke("Echo", new object[] {text});
    return ((string)(results[0]));
}

닷넷의 asmx는 기본적으로 Document 스타일임을 알 수 있는데, 물론 RPC 스타일로 변경하는 것도 가능합니다. 방법도 아주 쉬운데요, 그냥 아래와 같이 asmx 파일 안의 웹 메서드 정의를 포함하고 있는 클래스에 SoapRpcService 특성만 지정해 주면 됩니다. (또는 메서드 단위로 SoapRpcMethod 특성을 지정할 수 있습니다.)

[SoapRpcService()] 
public class WebService1 : System.Web.Services.WebService
{
    [WebMethod]
    public string HelloWorld()
    {
        return "Hello World";
    }
}

참고로, Document/literal, Rpc/literal, Document/literal wrapped 방식이 있는데, 이에 대해서는 다음의 글이 도움이 될 것입니다. (WS-I Basic Profile에서 encoded 사용은 더 이상 허용하지 않기 때문에 그 방식은 잊어버리시는 것도 좋겠습니다.)

Which style of WSDL should I use?
; http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/

마이크로소프트의 경우, 정확히는 Document/literal wrapped 스타일의 SOAP 구현을 하고 있으며 지난번 구현했던 Axis 예제는 Rpc/Encoded 방식의 웹 서비스를 구현한 예제였던 것임을 알 수 있습니다.




자바 예제를 좀 더 확장해 볼까요? 기본 타입 값이 아닌 Complex 타입을 반환하도록 다음과 같이 수정해 보겠습니다.

===== HelloService.java =====
public class HelloService {

    public String Echo(String text)
    {
        return "Hello " + text;
    } 
    
    public Person EchoPerson(String name)
    {
        Person person = new Person();
        person.name = name;
        person.age = 5;
        
        return person;
    }
}

===== Person.java =====
public class Person {
    public String name;
    public int age;
}

노출되어야 할 메서드가 추가되었으므로 deploy.wsdd 파일 역시 변경 사항을 반영시켜줍니다.

<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/" 
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

  <service name="HelloService" provider="java:RPC">
    <parameter name="className" value="HelloService"/>
    <parameter name="allowedMethods" value="*"/>
  </service>

</deployment>

Axis에 배포해주고, 프록시를 업데이트 한 후 닷넷 클라이언트 프로그램을 실행하면 이번엔 다음과 같이 오류가 발생합니다.

Unhandled Exception: System.InvalidOperationException: Response is not well-formed XML. ---> System.Xml.XmlException: Root element is missing.
   at System.Xml.XmlTextReaderImpl.Throw(Exception e)
   at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   at System.Xml.XmlTextReaderImpl.Read()
   at System.Xml.XmlTextReader.Read()
   at System.Xml.XmlReader.MoveToContent()
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   --- End of inner exception stack trace ---
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at ConsoleApplication1.localhost.HelloServiceService.EchoPerson(String name) in D:\...[생략]...\ConsoleApplication1\Web References\localhost\Reference.cs:line 114
   at ConsoleApplication1.Program.Main(String[] args) in D:\...[생략]...\ConsoleApplication1\Program.cs:line 14
Press any key to continue . . .

이 순간의 네트워크를 가로채 보면 단서가 있을까요?

======= 요청 =======
POST http://192.168.50.132:8080/axis/services/HelloService HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.235)
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Host: 192.168.0.132:8080
Content-Length: 610
Expect: 100-continue
Connection: Keep-Alive

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://localhost:8080/axis/services/HelloService" xmlns:types="http://localhost:8080/axis/services/HelloService/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><q1:EchoPerson xmlns:q1="http://DefaultNamespace"><name xsi:type="xsd:string">test1</name></q1:EchoPerson></soap:Body></soap:Envelope>

======= 응답 =======
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Date: Thu, 22 Sep 2011 02:07:52 GMT
Content-Length: 0

불친절한 Axis군요. 가타부타 아무런 오류 메시지도 없이 200 OK를 보내주다니. ^^;

원인은 책을 찾아보고 나서야 알았습니다. 자바는 RPC 스타일의 경우 클래스 타입에 대해서 get/set 메서드에 대한 정의를 별도로 추가해야 한다는 것입니다. 따라서, Person 클래스는 다음과 같은 코드들이 부가적으로 작성됩니다.

public class Person {

    public String name;
    public int age;
    
    public String getName()
    {
        return name;
    }
    
    public void setName(String name)
    {
        this.name = name;
    }
    
    public int getAge()
    {
        return age;
    }
    
    public void setAge(int age)
    {
        this.age = age;
    }
}

이뿐만이 아니라, 이에 대한 매핑을 wsdd 파일에도 지정해 주어야 합니다.

<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/" 
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

  <service name="HelloService" provider="java:RPC">
    <parameter name="className" value="HelloService"/>
    <parameter name="allowedMethods" value="*"/>
    <!--parameter name="wsdlServicePort" value="Echo"/-->
  </service>

  <beanMapping qname="ns:Person" xmlns:ns="http://test.com" languageSpecificType="java:Person">  
  </beanMapping>

</deployment>

오호~~~ 이거 불편함이 장난 아닌데요. ^^;

암튼, 이렇게 하고 다시 AdminService로 배포해 준 다음 닷넷 클라이언트 측의 프록시 코드를 업데이트 하고 실행해 주면 정상적으로 메서드 수행 결과값을 반환받을 수 있습니다.

여기까지 해서, 자바 Axis의 Rpc/encoded 방식으로 구현된 웹 서비스를 살펴보는 것이 끝났습니다.




원래는, 한 단계 더 나아가서 Axis로 Document 스타일의 SOAP 서비스를 실습해 보려고 했는데요. 닷넷 개발자로써는 도저히 현실적이지 않은 이유로 인해 포기했습니다.

왜냐하면, Axis의 Document 스타일은 Message라는 것에 너무 충실한 나머지 다음과 같은 식의 signature만 하용이 되기 때문입니다.

Service Styles - RPC, Document, Wrapped, and Message
 - Document / Wrapped services
; http://ws.apache.org/axis/java/user-guide.html#ServiceStylesRPCDocumentWrappedAndMessage

public Element [] method(Element [] bodies); 
public SOAPBodyElement [] method (SOAPBodyElement [] bodies); 
public Document method(Document body); 
public void method(SOAPEnvelope req, SOAPEnvelope resp); 

느낌이 오시죠? 말 그대로 XML 문서를 수작업으로 구성해서 메서드에 전달하고, 다시 반환값을 XML 파서로 select해 가면서 값을 받아와야 하는 구조입니다.

이렇게 구현된 Axis 웹 서비스를 닷넷에서 WSDL 코드 생성기를 거치면 다음과 같은 식으로 나옵니다.

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", 
    Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("getTimeReturn", Namespace="http://localhost:8080/axis/services/TimeService")]

public object portfolio([System.Xml.Serialization.XmlElementAttribute("getTime", Namespace="http://DefaultNamespace")] object getTime1) {
    object[] results = this.Invoke("getTime", new object[] {
                getTime1});
    return ((object)(results[0]));
}

그렇습니다. object 인자에 object 반환값의 메서드로 asmx에서는 SoapParameterStyle.Bare 유형이 바로 Axis의 Document 스타일이었던 것입니다.




위의 실습을 해보고 나니, 개인적으로 설마 자바 개발자들이 Axis를 가지고 웹 서비스를 만들고 있을 거라는 생각은 들지 않더군요. 아마도 좀 더 나은 상용 웹 서비스 기반 도구가 있지 않을까 싶은데... 제가 거기까진 조사를 못했습니다.

어느 정도까지 자바의 상용 웹 서비스 도구들이 편할지는 알 수 없지만, 일단 이 정도만 봐서는 적어도 웹 서비스 환경만큼은 역시 마이크로소프트 제품만한 것이 없는 것 같습니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...
NoWriterDateCnt.TitleFile(s)
13285정성태3/14/20233936Windows: 227. Win32 C/C++ - Dialog Procedure를 재정의하는 방법파일 다운로드1
13284정성태3/13/20234157Windows: 226. Win32 C/C++ - Dialog에서 값을 반환하는 방법파일 다운로드1
13283정성태3/12/20233679오류 유형: 852. 파이썬 - TypeError: coercing to Unicode: need string or buffer, NoneType found
13282정성태3/12/20233995Linux: 58. WSL - nohup 옵션이 필요한 경우
13281정성태3/12/20233953Windows: 225. 윈도우 바탕화면의 아이콘들이 넓게 퍼지는 경우 [2]
13280정성태3/9/20234716개발 환경 구성: 670. WSL 2에서 호스팅 중인 TCP 서버를 외부에서 접근하는 방법
13279정성태3/9/20234226오류 유형: 851. 파이썬 ModuleNotFoundError: No module named '_cffi_backend'
13278정성태3/8/20234232개발 환경 구성: 669. WSL 2의 (init이 아닌) systemd 지원 [1]
13277정성태3/6/20234853개발 환경 구성: 668. 코드 사인용 인증서 신청 및 적용 방법(예: Digicert)
13276정성태3/5/20234534.NET Framework: 2102. C# 11 - ref struct/ref field를 위해 새롭게 도입된 scoped 예약어
13275정성태3/3/20234842.NET Framework: 2101. C# 11의 ref 필드 설명
13274정성태3/2/20234410.NET Framework: 2100. C# - ref 필드로 ref struct 타입을 허용하지 않는 이유
13273정성태2/28/20234174.NET Framework: 2099. C# - 관리 포인터로서의 ref 예약어 의미
13272정성태2/27/20234392오류 유형: 850. SSMS - mdf 파일을 Attach 시킬 때 Operating system error 5: "5(Access is denied.)" 에러
13271정성태2/25/20234337오류 유형: 849. Sql Server Configuration Manager가 시작 메뉴에 없는 경우
13270정성태2/24/20233909.NET Framework: 2098. dotnet build에 /p 옵션을 적용 시 유의점
13269정성태2/23/20234523스크립트: 46. 파이썬 - uvicorn의 콘솔 출력을 UDP로 전송
13268정성태2/22/20235052개발 환경 구성: 667. WSL 2 내부에서 열고 있는 UDP 서버를 호스트 측에서 접속하는 방법
13267정성태2/21/20234950.NET Framework: 2097. C# - 비동기 소켓 사용 시 메모리 해제가 finalizer 단계에서 발생하는 사례파일 다운로드1
13266정성태2/20/20234591오류 유형: 848. .NET Core/5+ - Process terminated. Couldn't find a valid ICU package installed on the system
13265정성태2/18/20234493.NET Framework: 2096. .NET Core/5+ - PublishSingleFile 유형에 대한 runtimeconfig.json 설정
13264정성태2/17/20236041스크립트: 45. 파이썬 - uvicorn 사용자 정의 Logger 작성
13263정성태2/16/20234194개발 환경 구성: 666. 최신 버전의 ilasm.exe/ildasm.exe 사용하는 방법
13262정성태2/15/20235235디버깅 기술: 191. dnSpy를 이용한 (소스 코드가 없는) 닷넷 응용 프로그램 디버깅 방법 [1]
13261정성태2/15/20234526Windows: 224. Visual Studio - 영문 폰트가 Fullwidth Latin Character로 바뀌는 문제
13260정성태2/14/20234326오류 유형: 847. ilasm.exe 컴파일 오류 - error : syntax error at token '-' in ... -inf
1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...