Developer Scenarios  

Creating and Consuming Custom C++/COM Components

 

The ArcGIS Java API provides Java proxies for ArcObjects COM components, allowing ArcObjects to be accessed through Java.

It is sometimes useful to extend ArcObjects using custom COM components, and to programmatically access the extensions through Java. This can be useful in the following cases:

  1. Boosting performance of ArcGIS Server Java. Extending ArcGIS Server with custom COM components that make heavy use of fine-grained ArcObjects method calls. Having a COM component on the ArcGIS Server tier perform the multiple fine-grained ArcObjects method calls leads to a performance boost as multiple round-trip calls from the web-tier to the ArcGIS Server over the network are replaced by a single call to the custom COM component, which in turn makes multiple in-process calls.
  2. Achieving extremely performant ArcGIS Engine applications that make heavy use of fine-grained ArcObjects method calls. Writing and calling a coarse-grained custom COM component that subsumes the many fine-grained ArcObjects calls would heavily reduce the interoperability overhead between Java and ArcObjects in an ArcGIS Engine application, thus improving performance.
  3. Integrating legacy or third party components written in a native, COM compliant language, into a GIS application. These components may offer functionality orthogonal to the GIS domain but may need to feature in the workflow of a GIS application, working alongside ArcObjects.

 

The following information provides steps on how to create and consume a custom COM component. You should be familiar with ArcObjects and writing a C++/COM component before continuing. The steps include:

 

 

Writing an ArcGIS Engine extension in C++/COM

We present a developer scenario that demonstrates the writing of a custom coarse-grained COM component that performs the task of calculating the total area of all polygon features in a feature class. A Java application then uses the custom coarse-grained component to execute the task at the expense of just one method call rather than performing several interop-calls between Java and ArcObjects.

The three steps involved in building and consuming the extension are as follows:

  1. Writing the extension in C++/COM
  2. Generating Java proxies for the extension using proxygen
  3. Consuming the extension from a Java Engine application.

Writing the extension

The ArcGIS Engine extension sample demonstrated here is essentially a COM component that implements one interface that exposes a method called "CalculateTotalArea". This sample can be found in the ARCGISHOME\java\samples\data\proxygen folder. A snippet of the .idl file is shown below.

import "oaidl.idl";
import "ocidl.idl";	

[
	uuid(3760A0D0-A56C-4C19-A3ED-9D3DF225632D),
	version(1.0),
	helpstring("ArcGISExtension 1.0 Type Library")
]
library ARCGISEXTENSIONLib
{
	importlib("stdole32.tlb");
	importlib("stdole2.tlb");
	importlib("C:\ArcGIS\com\esriGeoDatabase.olb");

	[
		object,
		uuid(B9CC08B8-98F4-4EBD-A267-A0750BDE9A8E),
	
		helpstring("IAGSExtension Interface"),
		pointer_default(unique)
	]
	interface IAGSExtension : IUnknown
	{
		[helpstring("method CalculateTotalArea")] HRESULT CalculateTotalArea([in] 				
		IFeatureClass *fc, [out, retval] double *result);
	};

	[
		uuid(EE2820F3-F9F1-4826-9C22-20CFC7D9950A),
		helpstring("AGSExtension Class")
	]
	coclass AGSExtension
	{
		[default] interface IAGSExtension;
	};
};

 

Note that the idl file "imports" the "esriGeodatabase.olb" type library to obtain the definition of the IFeatureClass interface which the "CalculateTotalArea" method receives as input. The definition of the method which performs the task of calculating feature areas is described below.

 

STDMETHODIMP CAGSExtension::CalculateTotalArea(IFeatureClass *pFeatureClass, double *result)
{		
  esriGeometryType type;
  pFeatureClass->get_ShapeType(&type);
  if(type != esriGeometryPolygon) 
    return 0;

  long numFeatures = 0;
  pFeatureClass->FeatureCount(NULL, &numFeatures);

  IFeature* pFeature = 0;
  IGeometry *pShape = 0;
  IArea *pArea = 0;
  for(int i=0; i<numFeatures; i++)
  {
    pFeatureClass->GetFeature(i,&pFeature);  
    pFeature->get_Shape(&pShape);    
    pShape->QueryInterface(&pArea);    
    double area = 0;
    pArea->get_Area(&area);
    *result += area;
    pFeature->Release();
    pShape->Release();
    pArea->Release();    
  }  
  return S_OK;
}

  

 

The above COM component was built using the Visual Studio 6 IDE. It should be minimal effort to port the project to later versions of the Visual Studio development environment.

 

Generating Java proxies using proxygen

The next step, after writing and building the custom COM extension would be to generate Java proxies for the extension using proxygen. proxygen uses the type library information, produced while building the custom COM component, to generate Java proxies. As described in the proxygen Tool Instructions, proxygen needs to be supplied with a text file that contains information in the following format.

<COM type library location>, <java package name>, <java proxies’ location>

The text file used to generate proxies for the custom COM extension demonstrated in this scenario, contains the following information.

C:\proxygen\ArcGISExtension\ArcGISExtension.tlb, agsextension, C:\proxygen\ArcGISExtension\java\agsextension

Run the proxygen tool from the console, supplied with the text file.

 

You will receive a "Success" message if the Java proxies were generated successfully.

Consuming the extension from a Java application

Once Java proxies for the custom COM component are generated successfully, they need to be added to an existing Java ArcGIS Engine project within an IDE so the custom component could be consumed. The following Java code snippet shows how this is performed:

 

	// Initialize the engine and perform licensing
  EngineInitializer.initializeEngine();				
  AoInitialize ao = new AoInitialize();
  ao.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);		
				
	// Open a shape file workspace and obtain a feature class 
  IWorkspaceFactory shpFileWSFactory = new ShapefileWorkspaceFactory();			
  IFeatureWorkspace shpFileWS = (IFeatureWorkspace)shpFileWSFactory.openFromFile("C:/Data/World", 0);    
  IFeatureClass featureClass = shpFileWS.openFeatureClass("Country");			
	
	// Consume the custom extension for calculating the total area of all features
  AGSExtension agsExtension = new AGSExtension();
  double totalArea = agsExtension.calculateTotalArea(featureClass);
  System.out.println("The total area of all features is " + totalArea);

 

The sample application calculated the total area of 3140 polygon features representing all the counties in the United States. An 18% improvement in the running time resulted when the application used the custom extension to perform the task as compared to not using it and making all fine-grained calls through the interoperability layer.

Although not demonstrated here, writing a custom extension to the ArcGIS Server and consuming it through a Java client over the network would be similar in nature to the steps involved in extending ArcGIS Engine.

 

Summary

This docuement has explained the motivations for writing a custom coarse-grained C++/COM extension to ArcGIS Engine/Server to then be consumed from an ArcGIS Engine/Server Java application. We demonstrate the steps necessary to achieve this, starting first with writing the extension itself, then using proxygen as a tool to generate Java proxies for the extension and finally using the extension from an ArcGIS Engine Java application.

Note: Before using the samples within this document, please change file paths in the source code to appropriate ones on your machine.