Microsoft MVP성태의 닷넷 이야기
Java: 8. 닷넷 개발자가 구현해 본 자바 웹 서비스 (2) [링크 복사], [링크+제목 복사],
조회: 29240
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 106  107  108  109  110  111  112  113  114  [115]  116  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11049정성태9/24/201620980오류 유형: 357. 윈도우 백업 시 오류 - 0x81000037
11048정성태9/24/201622019VC++: 100. 전역 변수 유형별 실행 파일 크기 차이점
11047정성태9/21/201625815기타: 61. algospot.com - 양자화(Quantization) 문제 [2]파일 다운로드1
11046정성태9/15/201627475개발 환경 구성: 298. Windows 10 - bash 실행 시 시작 디렉터리 자동 변경
11045정성태9/15/201620132Windows: 119. Windows 10 - bash 명령어 창을 실행했는데 바로 닫히는 경우
11044정성태9/15/201620382VS.NET IDE: 112. Visual Studio 확장 - 편집 화면 내에서 링크를 누르면 외부 웹 브라우저에서 열기
11043정성태9/15/201621809.NET Framework: 606. .NET 스레드 콜 스택 덤프 (7) - ClrMD(Microsoft.Diagnostics.Runtime)를 이용한 방법 [1]파일 다운로드1
11042정성태9/14/201619965오류 유형: 356. Unknown custom metadata item kind: 6
11041정성태9/10/201619434.NET Framework: 605. CLR4 보안 - yield 구문 내에서 SecurityCritical 메서드 사용 불가 - 2번째 이야기
11040정성태9/10/201626739.NET Framework: 604. C# Windows Forms - Drag & Drop 예제 코드 [2]파일 다운로드1
11039정성태9/9/201623227오류 유형: 355. Visual Studio 빌드 오류 - error CS0122: '__ComObject' is inaccessible due to its protection level
11038정성태9/9/201625077VC++: 99. 서로 다른 프로세스에서 WM_DROPFILES 메시지를 전송하는 방법파일 다운로드1
11037정성태9/8/201628311.NET Framework: 603. socket - shutdown 호출이 필요한 사례파일 다운로드1
11036정성태8/29/201624787개발 환경 구성: 297. 소스 코드가 없는 닷넷 어셈블리를 디버깅할 때 지역 변숫값을 확인하는 방법
11035정성태8/29/201620420오류 유형: 354. .NET Reflector - PDB 생성 화면에서 "Clear Store"를 하면 "Index and length must refer to a location within the string" 예외 발생
11034정성태8/25/201624438개발 환경 구성: 296. .NET Core 프로젝트를 NuGet Gallery에 배포하는 방법 [2]
11033정성태8/24/201622337오류 유형: 353. coreclr 빌드 시 error C3249: illegal statement or sub-expression for 'constexpr' function
11032정성태8/23/201621555개발 환경 구성: 295. 최신의 Visual C++ 컴파일러 도구를 사용하는 방법 [1]
11031정성태8/23/201617788오류 유형: 352. Error encountered while pushing to the remote repository: Response status code does not indicate success: 403 (Forbidden).
11030정성태8/23/201620341VS.NET IDE: 111. Team Explorer - 추가한 Git Remote 저장소가 Branch에 보이지 않는 경우
11029정성태8/18/201627473.NET Framework: 602. Process.Start의 cmd.exe에서 stdin만 redirect 하는 방법 [1]파일 다운로드1
11028정성태8/15/201621525오류 유형: 351. Octave 설치 시 JRE 경로 문제
11027정성태8/15/201622599.NET Framework: 601. ElementHost 컨트롤의 메모리 누수 현상
11026정성태8/13/201623578Math: 19. 행렬 연산으로 본 해밍코드
11025정성태8/12/201622292개발 환경 구성: 294. .NET Core 프로젝트에서 "Copy to Output Directory" 처리 [1]
11024정성태8/12/201621606오류 유형: 350. "nProtect GameMon" 실행 중에는 Visual Studio 디버깅이 안됩니다! [1]
... 106  107  108  109  110  111  112  113  114  [115]  116  117  118  119  120  ...