Converting COM request to web service request

Since its introduction, web service has become a standard mechanism for integration between applications. However, there are many applications written in languages that do not provide a strong support for web service - for example Visual C++ V6.0 or Visual Basic V6.0. One approach to enable these applications is to have an adaptor that can convert a request into a web service call. This tutorial describes how to develop this adaptor implemented as a DCOM server using C#. It basically receives a COM request and forward the request to a web service endpoint. It also provides steps on how to make a COM request in C++ and shows steps on how to implement a Java web service. In short, it demonstrates how web service interoperability on the .NET and JEE platform can be achieved. C# code is developed using Microsoft Visual Studio, while the web services is developed using Rational Application Developer V7.5 and deployed on the WebSphere Application Server V7.0 test environment.

Kok Sing Khong (khongks@sg.ibm.com), IT Architect, IBM China

Kok Sing KhongKok Sing is an IT Architect with Application Innovation Services, IBM Singapore. He is well-versed in various programming languages like C/C++, Java and C# and development platform like JEE and .NET. He holds a Master of Technology (Software Engineering) and a IBM Certified IT Specialist.



12 October 2010

Also available in

Before you start

About this tutorial

This tutorial details the following steps:

  1. Develop a Java-based web service using Rational Application Developer V7.5
  2. Develop a COM server using C# using Visual Studio 2008
  3. Develop a COM client using C; that calls the COM server
  4. Enable to COM to be accessible remotely (DCOM)

Prerequisites

To develop the code, you need to following software:

  • Microsoft Visual Studio 2008
  • IBM Rational Application Developer for WebSphere Application Server V7.5

You can also choose to use IBM Rational Software Architect for WebSphere Application Server V7.5.

System requirements

To run the examples in this tutorial, you need a Intel machine running Microsoft Windows XP or above, with at least 2GB of RAM and 300MB of of free disk space.


Tutorial

Develop a Java-based web service using Rational Application Developer V7.5

The objective of this step is to create a Java-based web service using Rational Application Developer V7.5.

  1. Switch to Java EE perspective.
  2. Create an enterprise application; select File | New | Enterprise Application Project. Enter SimpleCalculatorEAR as the Project Name and click Finish.
Figure 1. New EAR application project wizard
New EAR application project wizard
  1. Create a dynamic web project; select File | New | Dynamic Web Project. Enter SimpleCalculatorWEB as the Project Name and select SimpleCalculatorEAR as the EAR Project Name.
  2. Create a package com.ibm.sample.
  3. Create a simple Plain Old Java Object (POJO) with a method intended to be exposed as a web service; right click on com.ibm.samplepackage and select Java | Class. Give the Name as SimpleCalculator. A file SimpleCalculator.java will be created.
Listing 1. SimpleCalculator.java
package com.ibm.sample;

public class SimpleCalculator {

  public String compute(String expression) throws NumberFormatException {
    if(expression.indexOf('+') != -1) {
      int index = expression.indexOf('+');			
      String operand1 = expression.substring(0, index).trim();
      String operand2 = expression.substring(index+1).trim();
      
      double opr1 = Double.parseDouble(operand1);
      double opr2 = Double.parseDouble(operand2);
      return Double.toString(opr1 + opr2);
    } else if(expression.indexOf('-') != -1) {
      int index = expression.indexOf('-');			
      String operand1 = expression.substring(0, index).trim();
      String operand2 = expression.substring(index+1).trim();
      
      double opr1 = Double.parseDouble(operand1);
      double opr2 = Double.parseDouble(operand2);
      return Double.toString(opr1 - opr2); 
    } else if(expression.indexOf('*') != -1) {
      int index = expression.indexOf('*');			
      String operand1 = expression.substring(0, index).trim();
      String operand2 = expression.substring(index+1).trim();
      
      double opr1 = Double.parseDouble(operand1);
      double opr2 = Double.parseDouble(operand2);
      return Double.toString(opr1 * opr2);
    } else if(expression.indexOf('/') != -1) {			
      int index = expression.indexOf('/');			
      String operand1 = expression.substring(0, index).trim();
      String operand2 = expression.substring(index+1).trim();
      
      double opr1 = Double.parseDouble(operand1);
      double opr2 = Double.parseDouble(operand2);
      return Double.toString(opr1 / opr2); 
    } else {
      throw new NumberFormatException("Invalid operand");
    }
  }
  
}
  1. Create a web service based on the class SimpleCalculator; right click on the class and select Web Services | Create Web service.
  2. Select the Server implementation to point to the class and click Next.
Figure 2. New web services
New web service
  1. Ensure that the option Enable SOAP 1.2 Binding and Generate WSDL file into the project is checked. Also ensure that the option Generate WSDL file into the project is checked. The reason is to ensure compatibility with the generatd code with .NET. then click Finish. Click Ignore All to use SOAP 1.2 Binding. There you have it, you have created a web service!
Figure 3. WebSphere JAX-WS Bottom Up Web Service Configuration
WebSphere JAX-WS Bottom Up Web Service Configuration
  1. Change the binding/tranport as follows.
Listing 2. WSDL Binding
<soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http">
  1. Important: After the WSDL file is generated, ensure that the soapAction attribute of the operation element is specified correctly.
Listing 3. WSDL SOAP Action
<operation name="setImage">
<soap12:operation soapAction="http://sample.ibm.com/setImage"/>
  1. Run the application on WebSphere Application Server (if not already running); right click on the enterprise applicationSimpleCalculatorEAR and select Run As | Run On Server.
  2. There are two generated files; SimpleCalculatorService.wsdl and SimpleCalculatorService_schema1.xsd.
Listing 4. SimpleCalculatorService.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net.
        RI's version is JAX-WS RI 2.1.1 in JDK 6. -->
<definitions name="SimpleCalculatorService" 
                targetNamespace="http://sample.ibm.com/"
                xmlns="http://schemas.xmlsoap.org/wsdl/"
                xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
                xmlns:tns="http://sample.ibm.com/"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <types>
    <xsd:schema>
      <xsd:import namespace="http://sample.ibm.com/"
                     schemaLocation="SimpleCalculatorService_schema1.xsd"/>
    </xsd:schema>
  </types>
  <message name="compute">
    <part element="tns:compute" name="parameters"/>
  </message>
  <message name="computeResponse">
    <part element="tns:computeResponse" name="parameters"/>
  </message>
  <message name="NumberFormatException">
    <part element="tns:NumberFormatException" name="fault"/>
  </message>
  <portType name="SimpleCalculatorDelegate">
    <operation name="compute">
      <input message="tns:compute"/>
      <output message="tns:computeResponse"/>
      <fault message="tns:NumberFormatException" name="NumberFormatException"/>
    </operation>
  </portType>
  <binding name="SimpleCalculatorPortBinding" type="tns:SimpleCalculatorDelegate">
    <soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="compute">
      <soap12:operation soapAction=""/>
      <input>
        <soap12:body use="literal"/>
      </input>
      <output>
        <soap12:body use="literal"/>
      </output>
      <fault name="NumberFormatException">
        <soap12:fault name="NumberFormatException" use="literal"/>
      </fault>
    </operation>
  </binding>
  <service name="SimpleCalculatorService">
    <port binding="tns:SimpleCalculatorPortBinding" name="SimpleCalculatorPort">
      <soap12:address
          location="http://localhost:9080/SimpleCalculatorWEB/SimpleCalculatorService"/>
    </port>
  </service>
</definitions>
Listing 5. SimpleCalculatorService_schema1.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://sample.ibm.com/"
           version="1.0"
           xmlns:tns="http://sample.ibm.com/"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="NumberFormatException" type="tns:NumberFormatException"/>
  <xs:element name="compute" type="tns:compute"/>
  <xs:element name="computeResponse" type="tns:computeResponse"/>
  <xs:complexType name="compute">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="computeResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="NumberFormatException">
    <xs:sequence>
      <xs:element minOccurs="0" name="message" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Create a COM Server using C#

This objective of this exercise is to create a C# based COM Server that communicates with a Java-based web service running on WebSphere Application Server.

  1. Create a Class Library Project. Select Other languages | Visual C# | Windows | Class Library and give the project Name as SimpleCalculator and Location as c:\developerworks.
Figure 4. New C# class library
New C# class library
  1. Rename the generated Class1.cs to SimpleCalculator.
  2. Change the namespace to Developerworks.
  3. Add a line using System.Runtime.InteropServices;.
  4. Create an interface ISimpleCalculator. Note: To create Guid; select Tools | Create GUID .
  5. Create a class SimpleCalculator.
  6. Press F7 to compile.
Listing 6. SimpleCalculator.cs
[Guid("92F0C680-445E-4ee0-A1FB-DA273B21133C")]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Developerworks
{
  [Guid("F8CE3E7B-A155-4e43-AD2F-24BEAC6A6DF6")]
  public interface ISimpleCalculator
  {
    string Compute(string expression);
  }
  
  [Guid("88588196-BDF7-420c-A282-B7877569214F")]
  public class SimpleCalculator : ISimpleCalculator
  {
    public string Compute(string expression)
    {
      return "";
    }
  }
}
  1. Make assemby COM-Visible; double click on the Properties of the project SimpleCalculator in the SolutionExplorer. In the Application tab, click on the Assembly Information button. In the Assembly Information dialog box, check option Make assembly COM-Visible. Ctrl-S to save.
Figure 5. Assembly Information - Application
Assembly Information - Application
  1. Register for COM interoperability; In the same Properties panel, go to the Build tab, check option Regiser for COM interop. Ctrl-S to save. Make sure you do it for both Release and Debug configurations.
Figure 6. Assembly Information - Build
Assembly Information - Build
  1. There are two ways to generate a C# adapter class that communicates with a web service.
    1. Use wsdl.exe. Generate a C# class via the location URL of the web service. Go to Tools | Visual Studio 2008 Command Prompt. Ensure that the web service is running. Note: The value of location URL can be obtained from the WSDL of the web service. WSDL http://hostname:9080/CalculatorWEB/CalculatorService?WSDL. 1 file is generated SimpleCalculatorService.cs
    2. Use svcutil.exe. Generate two file C# file and configuration file based on the wsdl and xsd files. Go to Tools | Visual Studio 2008 Command Prompt. Copy the wsdl and xsd files into the directory. I recommend option (b) as it is more flexible. svcutil SimpleCalculatorService.wsdl *.xsd /config:SimpleCalculator.config. 2 files are generated SimpleCalculatorService.cs and SimpleCalculator.config
  2. Add the generated C# class file SimpleCalculatorService.cs and SimpleCalculator.config files into the project SimpleCalculator
  3. To resolve compile errors, add the references System.Web.Services, System.ServiceModel and System.Runtime.Serialization; right click References | App Reference item within the project. Press F7 to compile.
Figure 7. Add Reference
Add Reference
  1. Implement the class; calling the web services method. Note: You also need to insert this code to point to the correct configuration file.
Listing 7. SimpleCalculator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Developerworks
{
  [Guid("F8CE3E7B-A155-4e43-AD2F-24BEAC6A6DF6")]
  public interface ISimpleCalculator
  {
    string Compute(string expression);
  }
  [Guid("88588196-BDF7-420c-A282-B7877569214F")]
  public class SimpleCalculator :  ISimpleCalculator
  {
    private SimpleCalculatorDelegateClient client = null;
    public SimpleCalculator()
    {
      AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", 
"C:\\developerworks\\SimpleCalculator\\SimpleCalculator\\SimpleCalculator.config");

      client = new SimpleCalculatorDelegateClient();
    }
    
    public string Compute(string expression)
    {
      return client.compute(expression);
    }
  }
}
  1. Generate *.tlb to be used by the client and register the DCOM server; run the following regasm SimpleCalculator.dll /tlb:SimpleCalculator.tlb in the Visual Studio 2008 Command Prompt.

Create a COM client in C++

The objective of this exercise is to create a C++ based COM Client that communicates with the COM Server that we have built.

  1. Add a New Project within the SimpleCalculator solution; select Visual C++ | Win32 | Win32 Console Application. Give a Name as CalculatorClient.
Figure 8. Add new C++ project
Add New Project
  1. Select Empty project in Additional options and click Finish.
Figure 9. Application Settings
Application Settings
  1. Add a C++ file; right click on Source File and Add New Item. Type in the name CalculatorClient.cpp
Figure 10. Add new C++ file
Add New Project
  1. Implement CalculatorClient.cpp as follows.
Listing 8. CalculatorClient.cpp
// CalculatorClient.cpp
// Build with "cl CalculatorClient.cpp"
// arguments: friend

#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
#include <atlconv.h>

#pragma warning (disable: 4278)

// To use managed-code servers like the C# server, 
//we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only
#import "..\SimpleCalculator\bin\Release\SimpleCalculator.tlb" no_namespace named_guids

int main(int argc, char* argv[])
{
	ISimpleCalculator* pSimpleCalculator = NULL;
	int retval = 1;

	// Initialize COM and create an instance of the InterfaceImplementation class:
	CoInitialize(NULL);
	HRESULT hr = CoCreateInstance(CLSID_SimpleCalculator,
					NULL, 
					CLSCTX_SERVER,
					IID_ISimpleCalculator,
					reinterpret_cast<void**>(&pSimpleCalculator));

	if (FAILED(hr))
	{
		printf("Couldn't create the instance!... 0x%x\n", hr);
	}
	else
	{	  
		// ATL conversion macros uses local vars - they are allocated here
		USES_CONVERSION;
		char* answer = W2A(pSimpleCalculator->Compute("4+3").GetBSTR());
		// The new string are allocated on the stack.
		printf("Answer: %s\n", answer);
		pSimpleCalculator->Release();
		pSimpleCalculator = NULL;
	}

	// Be a good citizen and clean up COM:
	CoUninitialize();
	return retval;
}
  1. To test the code; go to the directory .\SimpleCalculator\Release and run CalculatorClient.exe.

Enable COM to be accessible remotely (DCOM)

The objective of this exercise is to configure the COM server as DCOM and change the client code to call a remote server.

  1. Configure a COM object to be launched from a Remote Server. Use a tool OleView. Go to Object Classes | .NET Category | Developerworks.SimpleCalculator.
  2. Click on the Implementation | InprocServer and check the Use Surrogate Process checkbox so that the COM dll can be launched by the system provided surrogate executable during the DCOM activation.
Figure 11. Use surrogate process
OleView
  1. You need to change the client code to use CoCreateInstanceEx to specify the remote server and use the option CLSCTX_REMOTE_SERVER. Note: the value of serverInfo.pwzName is the host name where the web service is running on.
Listing 9. CalculatorClient.cpp (DCOM)
// CalculatorClient.cpp
// Build with "cl CalculatorClient.cpp"
// arguments: friend

#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
#include <atlconv.h>

#pragma warning (disable: 4278)

// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only
#import "..\SimpleCalculator\bin\Release\SimpleCalculator.tlb" no_namespace named_guids

int main(int argc, char* argv[])
{
  ISimpleCalculator *pSimpleCalculator = NULL;
  int retval = 1;

  // Initialize COM and create an instance of the InterfaceImplementation class:
  CoInitialize(NULL);
  COSERVERINFO serverInfo;
  ZeroMemory(&serverInfo, sizeof(COSERVERINFO));

  COAUTHINFO authInfo;
  ZeroMemory(&authInfo, sizeof(COAUTHINFO));

  // Set up the NULL security information
  authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_NONE;
  authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT;
  authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE;
  authInfo.dwCapabilities = EOAC_NONE;
  authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
  authInfo.pAuthIdentityData = NULL;
  authInfo.pwszServerPrincName = NULL;

  serverInfo.pwszName = L"\\\\khongkstpad";
  serverInfo.pAuthInfo = &authInfo;
  serverInfo.dwReserved1 = 0;
  serverInfo.dwReserved2 = 0;

  MULTI_QI qi = {&IID_ISimpleCalculator, NULL, S_OK};
  HRESULT hr = CoCreateInstanceEx(
	    CLSID_SimpleCalculator, 
		NULL,
		CLSCTX_REMOTE_SERVER,
		&serverInfo,
		1,
		&qi);
  if (FAILED(hr))
  {
    printf("Couldn't create the instance!... 0x%x\n", hr);
  }
  else
  {	  
    pSimpleCalculator = (ISimpleCalculator*)qi.pItf;
    // ATL conversion macros uses local vars - they are allocated here
    USES_CONVERSION;
    // The new string are allocated on the stack
    char* answer = W2A(pSimpleCalculator->Compute("4+3").GetBSTR());
    printf("Answer: %s\n", answer);
    pSimpleCalculator->Release();
    pSimpleCalculator = NULL;
  }
  // Be a good citizen and clean up COM:
  CoUninitialize();
  return retval;
}

Downloads

DescriptionNameSize
Project interchange file for Java web serviceSimpleCalculator.zip13KB
Solution zip file for DCOM server and clientSimpleCalculator.NET.zip538KB

Resources

Learn

  • IBM developerWorks - Web services and SOA has articles, tutorials has other technical resources for web services and SOA.
  • Visual C# Developer Center is a Microsoft site where you could get various information on Visual C#; which includes sample codes, C# language reference and learning tutorials.
  • .NET Framework Developer Center is a Microsoft site that contains technical resources on Windows Communication Foundation (WCF). It is a part of the .NET Framework that provides a unified programming model for rapidly building service-oriented applications that communicate across the web and the enterprise.
  • Microsoft has an MSDN site that contains COM and DCOM technical resources. It has articles on the COM/DCOM concepts and developer APIs. Click to this "MSDN link"

Get products and technologies

Discuss

  • Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into SOA and web services on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=550163
ArticleTitle=Converting COM request to web service request
publish-date=10122010