This chapter describes SilverStream's COM support. It gives an overview of COM integration with SilverStream and explains how to call COM services directly from SilverStream applications.
This chapter covers the following topics:
In some situations, a SilverStream application needs to be able to call native code. SilverStream applications can use the Java Native Interface (JNI) to call native methods. However, JNI is a low-level interface that requires that you write additional C or C++ code. Writing good JNI code is difficult.To simplify access to native code, SilverStream lets you use either of the following programming techniques:
The remainder of this chapter describes how to invoke the services of COM objects. For information about making native calls to DLLs, see Accessing DLLs Directly.
COM is Microsoft's Component Object Model. COM is a specification that describes how executable components can interoperate in a seamless manner. COM objects are language-independent. They can be implemented in any language that can produce a binary layout that conforms to the COM standard.
The remainder of this section provides a very brief overview of COM. For complete details on COM, see your Microsoft documentation.
The COM architecture builds on object-oriented concepts:
Since COM is object-oriented, it maps easily to the object-oriented concepts inherent in Java.
COM uses the Windows Registry to keep track of information about COM components registered on your system. To identify components, COM uses GUIDs (Globally Unique Identifiers), generated numbers that are guaranteed to be unique at any time and on any computer. A GUID can identify a COM class (in which case it's known as a CLSID) or a COM interface (known as an IID). The COM programmer can also specify a ProgID for a component. A ProgID is a friendly string identifier for a component.
All information about COM components is stored in the HKEY_CLASSES_ROOT branch in the Windows Registry.
The services provided by a COM component are described in a type library. A type library is a binary file that identifies the supported interfaces and method signatures. The type library for a component can be a separate .TLB file, or a resource that is incorporated into the .DLL or .EXE file.
COM functions return a value of the type HRESULT. The HRESULT data type reports errors in a standard format. Since a COM function must return an HRESULT, COM methods cannot use the return value to return data back to the caller. Instead, COM methods must use an [out, retval] parameter to return results.
SilverStream allows the Java programmer to access COM nonvisual objects. It does not provide support for visual controls. In addition, SilverStream does not support events or callbacks; SilverStream classes can invoke the services of COM components, but COM components cannot call back to SilverStream classes.
COM support is built into the Microsoft Virtual Machine. However, because SilverStream 3.0 uses the Sun Virtual Machine, which does not have built-in COM support, SilverStream provides a proprietary mechanism for COM integration. SilverStream's support for COM is very similar to that provided by the Microsoft Virtual Machine.
SilverStream provides a SilverCmd utility called ComGen that exposes COM objects to Java. When you run ComGen against a type library, it generates a set of Java source files that correspond to the classes and interfaces defined in the type library. You can then compile these Java files and use them in your SilverStream code to access one or more COM objects.
ComGen performs many of the same operations as jactivex, a Microsoft tool that reads a type library and generates a set of Java source files that represent the contents of the type library. However, unlike jactivex, ComGen is not tied directly to the Microsoft virtual machine and compiler. This gives the SilverStream developer some flexibility, but also introduces some restrictions. Among the restrictions is the fact that ComGen does not provide support for visual controls. Note that SilverStream COM integration is intended for use on the server; if you use COM objects on a Java form, you need to ensure that the objects are available on all clients that will run the form.
To access a COM object from a SilverStream application, you need to:
To do this, you can run ComGen with the -l switch to generate a list of the type libraries registered on your system. Alternatively, you can use the OLE/COM Object Viewer provided by Microsoft.
ComGen exposes COM objects to Java. When you run ComGen against a type library, it generates a set of Java source files that correspond to the classes and interfaces defined in the type library. You can then compile these Java files and use them in your SilverStream code to access one or more COM objects.
For each CoClass in a type library, ComGen generates a Java class that extends a base class called AgCoClass. This base class does not provide any public methods or fields that you need to access in your code. It implements base class functionality used internally by SilverStream.
For each Interface in a type library, ComGen generates a Java class that extends AgCoInterface. The only public methods associated with this interface that you need to use in your code are equals()
and release()
.
The following example shows how you can run ComGen with the -l switch to identify the type libraries that are available on your system, as shown below:
silvercmd comgen -l
This command produces output that looks like this:
Microsoft ActiveX Data Objects Recordset 1.5 Library
-> C:\Program Files\Common Files\system\ado\msador15.dll
OLE Automation
-> C:\WINNT\System32\stdole32.tlb
Visual Basic For Applications
-> C:\Program Files\Common Files\Microsoft Shared\VBA\VBA332.dll
Visual Basic For Applications
-> C:\WINNT\System32\VBAEN32.OLB
Microsoft Graph 8.0 Object Library
-> C:\Program Files\Microsoft Office\Office\GRAPH8.OLB
Microsoft Excel 5.0 Object Library
-> C:\Program Files\Microsoft Office\Office\XL5EN32.OLB
...
The following command generates Java source files for EventLogger.dll, the library for the KeyTech Windows EventLogger ActiveX control. The -d switch specifies the root directory for the output and the -p switch specifies the target package. ComGen writes all .java files to c:\test\evtlog:
silvercmd comgen -d c:\test -p evtlog c:\EventLogger\EventLogger.dll
The resulting Java classes are created in the evtlog package.
NOTE You can download the KeyTech Windows EventLogger ActiveX control from http://www.keytech.com.au/download.html.
After you use ComGen to generate the Java source files for a COM object, you need to compile the resulting source files, make a JAR file that contains the compiled classes, upload the JAR to the SilverStream Server, and then add the JAR to any business object, form, or page. Once you've completed these steps, you're ready to write Java code in SilverStream to access the COM object.
NOTE If you use COM objects on a form, you need to ensure that the COM objects in question are available on all clients that will run the form.
To access a COM object from a business object, form, or page, you need to:
castFrom()
method on the interface.
Ordinarily, a COM client uses the HRESULT returned by a function to check for errors. This is not the case in the Java context. Classes generated by ComGen detect HRESULTs that signal errors and throw a ComException
. To handle COM exceptions in SilverStream code, you can wrap a COM function call in a try block and specify ComException
as the data type in the exception handler. If the function has a parameter marked as [retval], this parameter is returned by the Java function.
COM uses reference counting to manage the lifecycle of an object. The object is kept in memory as long as the number of clients using the interfaces implemented by this object is greater than zero. When the reference count reaches zero, the object is removed from memory. This rule applies to the SilverStream implementation of the COM bridge. Therefore, every instance of an object representing a COM interface must be released before the actual COM object can be destroyed. During Java garbage collection, every interface is automatically released. However, to exercise more control over when COM objects are destroyed, you can call the release()
function on the COM interface. Calling release()
invalidates a COM object. If you call a function on a COM interface after calling release()
, a RuntimeException is thrown.
Most of the data types used by COM objects map easily to Java data types. When you run ComGen against a type library, it automatically generates code that uses appropriate Java data types. To allow you to work with Variants, SilverStream provides a Variant class. The Variant class acts as a wrapper around the VARIANT data type in COM.
The following code demonstrates the use of a COM object inside a SilverStream function. It uses several methods that are defined in EventLogger.dll, the type library for the KeyTech Windows EventLogger ActiveX control. To see the contents of this type library (including the IDL for each method), you can use the OLE/COM Object Viewer provided by Microsoft.
NOTE You can download the KeyTech Windows EventLogger ActiveX control from http://www.keytech.com.au/download.html.
The SilverStream function readOldestRecord()
performs these operations:
castFrom()
method.getOldestRecordNumber()
function on the IEventLog interface. The IDL for the corresponding COM method is shown below:
HRESULT OldestRecordNumber([out, retval] long* pVal);
ReadRecord()
function, which returns an IEventLogRecord interface. The IDL for the corresponding COM method is shown below:
HRESULT ReadRecord(
[in] long RecordNumber,
[out, retval] IEventLogRecord** pRecord);
getCategoryDescription()
function on the IEventLogRecord interface. The call to getCategoryDescription()
is wrapped in a try block. The IDL for the corresponding COM method is shown below:
HRESULT CategoryDescription([out, retval] BSTR* pVal);
getCategoryDescription()
, along with the event description returned by a call to getEventDescription()
. The IDL for the EventDescription COM method is shown below:
HRESULT EventDescription([out, retval] BSTR* pVal);
Here's the code for the readOldestRecord()
function:
public String readOldestRecord() {
//create an object and cast it to an interface
IEventLog eLog=IEventLog.castFrom(new EventLog());
//perform operation on that interface
int recNumber = eLog.getOldestRecordNumber();
//get another interface
IEventLogRecord logRec=eLog.ReadRecord(recNumber);
String category = null;
try {
category = logRec.getCategoryDescription();
} catch (ComException ex) {
category = "none"; //handle COM errors
}
return "Oldest =" + recNumber + ", category =" +
category + ", " + logRec.getEventDescription();
}
This example demonstrates the use of the SilverStream Variant class. In addition, it shows how to force a COM interface to be released. This example builds on Example 1, and therefore also uses methods that are defined in EventLogger.dll.
The SilverStream function dumpStringInfo()
performs these operations:
castFrom()
method.putSourceName()
function on the IEventSource interface. The IDL for the corresponding COM method is shown below:
HRESULT SourceName([in] BSTR pVal);
ReportInformation()
function on the IEventSource interface, passing in the two Variant objects as parameters. The IDL for the corresponding COM method is shown below:
HRESULT ReportInformation(
[in] long EventId,
[in, optional] VARIANT Strings,
[in, optional] VARIANT Data);
release()
function.
Here's the code for the dumpStringInfo()
function:
public void dumpStringInfo(String info) {
//create an object and cast it to an interface
IEventSource evtSrc = IEventSource.castFrom(new EventSource());
evtSrc.putSourceName("ComGen");
//create and populate two variants
Variant vtStr = new Variant();
vtStr.putString (new Date() + ": " + info);
Variant vtDat = new Variant();
vtDat.putNull();
//pass the variants in as parameters
evtSrc.ReportInformation (666, vtStr, vtDat);
//force the release of the interface
evtSrc.release();
}
To test Example 1 and Example 2 together, you could add code to a SilverStream page that calls the readOldestRecord() and dumpStringInfo() functions:
String desc = readOldestRecord();
dumpStringInfo(desc);
In the Event Viewer, you would see the following output when you ran the page:
If you were to click on the event, you would see this detail information about the event:
The procedure for migrating SilverStream 2.x code that relies on JActiveX to SilverStream 3.0 code that uses ComGen is relatively straightforward. The basic structure of the application does not need to change. To migrate from JActiveX to ComGen, you need to regenerate the supporting Java classes for a COM object and make a few modifications to the Java code that uses the object.
For each COM object you want to use in SilverStream, you need to:
For each SilverStream business object, form, or page that uses the COM object, you need to:
import com.ms.com.*
statement with import com.sssw.rt.com.*
.com.ms.*
from your code.castFrom()
method on the interface.For example, suppose you had the following line of code:
IEventSource evtSrc = (IEventSource) new EventSource();
In this case, the cast operator (IEventSource)
would be replaced with a call to the castFrom ()
method on the IEventSource interface.
IEventSource evtSrc = IEventSource.castFrom(new EventSource());
ComFailException
with ComException
.ComLib.release()
calls with calls to the release()
method on the COM interface.