Part One of this article appeared in the July issue of JDJ (Vol. 4, issue 7)
How can Java classes be used as scriptable components? DCOM, like CORBA, provides both static and dynamic invocation of objects. DCOM uses type library to provide metadata to do the dynamic invocation and introspection similar to CORBA's interface repository or Java's introspection mechanism.
IDispatch, a standard COM interface that supports Automation (late binding), is great for scripting languages such as Perl, Python, VBScript, JScript and so on. The Microsoft JVM implements IDispatch automatically for all Java objects. Microsoft refers to this feature as AutoIDispatch. Before AutoIDispatch, Java programmers had to write their own IDL files. Now all we have to do is create a Java class. Any public methods or member variables are automatically exposed via Automation. Anything that makes my job easier, I like.
Java programmers don't need to create interface definition files (IDL/ODL) to create COM objects. In addition, special tools like IDL compilers aren't necessary when doing Java COM development.
To implement a COM object in Java using AutoIDispatch, follow these steps:
You won't actually need to create servers; they can be created via MIDL (Microsoft IDL compiler) or you can use the default one provided by the Microsoft Java SDK. In addition, COM clients don't need to deal with IClassFactory directly because the COM library handles this when you call CoCreateInstance or the extended version, CoCreateInstanceEx.
The COM client asks the COM library for a given class via a CLSID. The CLSID can be looked up in the registry. The COM library goes to the registry and looks up the CLSID, then instantiates a server, which must provide an IClassFactory interface so that the COM server can create the object on behalf of the COM library. For the COM client to ask the COM library for a class, the COM client has to find the CLSID in the registry. Therefore, the server must register a CLSID for every COM object it's able to create. Here are the three types of COM servers:
Newer clients that are DCOM savvy have the flexibility to specify the server they want to connect to, which is essential with some applications. The newer servers can specify the server name as a parameter to CoCreateInstanceEx.
Creating a Java COM Object
This code example assumes you have Microsoft's Java SDK 3.1 or higher. The Microsoft Java SDK is freely downloadable from its Web site. If you don't have it, get it from www.microsoft.com/java. Once you've downloaded it, follow the install instructions closely.
For the first code example we're going to create a simple COM server, then register it in the system Registry so other programs can find it.
In your favorite editor, enter the code to create the HelloCOM COM Program class HelloCOM:
{
public int count = 0;
public String getHello()
{
count ++;
return "Hello from COM " + count;
}
Save this in a file as HelloCOM.java. Now you need to compile it. Use the Microsoft compiler; from the command line, type:
C:\jsdk>jvc HelloCOM.java
Next, you need to register it with JavaReg by typing the command exactly as shown in the following command line. If successful, you should get a dialog box.
C:\jsdk>javareg /register /class:HelloCOM /progid:Acme.HelloCOM
JavaREG is a tool for registering Java classes as COM components in the system Registry. This tool also enables you to configure the COM classes you create to execute remotely.
You now need to copy the HelloCOM.class file to \java\lib in the Windows directory. You'll need to substitute drives and directories as needed:
C:\jsdk>copy HelloCOM.class c:\winnt\java\lib\HelloCOM.class
That's it! You just created your first COM object. Remember, the only difference between COM and DCOM, from the programmer's perspective, is a few Registry settings and the length of the wire. Let's do some poking around and see this firsthand.
Exploring COM with OLEVIEW
OLEVIEW, the OLE/COM object viewer, is a development, administration and testing tool. Let's work with it to get a feel for how HelloCOM is configured in the registry.
Notice that the CLSID and AppID are listed under the Registry tab on the right side. When we executed JavaREG earlier, we used the following for the class and ProgID arguments:
/class:HelloCOM /progid:Acme.HelloCOM
Notice how the names map to this ProgID.
Now let's look at the information we can glean from the Registry tab:
Let's test this COM object. When you double-click the HelloCOM node, OLEVIEW will try to instantiate the COM class associated with the CLSID listed (an easy way to test if everything is set up). The following events will happen when you double-click this node:
The bottom line is that OLEVIEW is actually loading a live version of our class. This is a powerful tool for testing COM classes. Next, notice that the node has been expanded to show all the interfaces that this COM class supports. Notice also that it supports the following interfaces:
Scripting the HelloCOM Object
We're going to use the COM server you just created with Visual Basic. This code example assumes you have some form of scripting language that works as an Automation controller. If you don't, don't worry about it. An example in the book mentioned at the end of this article shows you how to create an Automation controller in Java without a type library, i.e., using raw IDispatch.
I chose Visual Basic for this example because many people know how to use it, and it's widely available. If you can't find a scripting language that suits your fancy, don't sweat it. Just skip the scripting section of this example.
Any Automation-capable scripting language will do. You can use LotusScript (which comes with Notes and Lotus 1-2-3), VBScript, and Visual Basic for Applications (VBA) (which comes with Word and Excel). Two of my favorite Automation-capable scripting languages are Perl and Python. Both provide examples for doing Automation. They're freely available at www.perl.org and www.python.org, respectively.
From your favorite Automation controller scripting language, add a button to a form called Command1. Then add the following code.
Private Sub Command1_Click()
Set helloCOM = CreateObject("Acme.HelloCOM")
Dim helloCOM As Object
Dim count As Integer
MsgBox helloCOM.getHello
count = helloCOM.count
End Sub
Notice that we use Visual Basic's CreateObject to instantiate the COM class into an object. Also notice that we pass CreateObject the ProgID of the COM class. You can probably guess what Visual Basic is doing underneath, but let's recap to make sure:
Now let's consider what happens when the code example makes the following call:
MsgBox helloCOM.getHello
Here we're taking what helloCOM.getHello returns to use as a parameter to MsgBox. MsgBox is just a Visual Basic method that pops up a message box with a string that you give it. Underneath, Visual Basic is calling the IDispatch.Invoke method with the name of the method we defined in the Java class getHello. IDispatch.Invoke passes back a return type of variant. A variant, as you'll remember from Part One, can hold any type of value. Visual Basic then works with the variant to see what it contains (in this case, a string).
You may wonder why you're being prepped with all this background information. AutoIDispatch doesn't provide a type library. If it did, you could use JActiveX (a tool from the Microsoft Java SDK that wraps COM objects into Java classes) to create wrapper functions around the COM class you created (which we'll show you how to do in later lessons). Without JActiveX, creating wrapper classes can be a little tough.
Creating a Java DCOM Object
In this exercise, a duplicate of the first, we're going to create a remote COM object. We're going to use a different class name from the HelloCOM Java class -HelloDCOM -so you can compare the Registry settings of HelloDCOM with those of HelloCOM in the next exercise. As you did in the first example, using your favorite editor, enter the code below to create the HelloDCOM DCOM object.
class HelloDCOM
public String getHello()
{
public int count = 0;
{
count ++;
return "Hello from COM" + count;
}
}
Also, as before, you need to compile. Use the Microsoft compiler as follows:
C:\jsdk>jvc HelloCOM.java
Next, you need to register it with JavaReg:
C:\jsdk>javareg /register /class:HelloDCOM /progid:My.HelloDCOM /surrogate
The only real difference between this step and what you did in the first exercise is the addition of the /surrogate parameter, which is essential for doing remote objects. The JavaReg /surrogate parameter allows the system-provided surrogate process to wrap the msjava.dll file in a process. This is needed -otherwise the DLL would have to run inproc, which can't be used with remote objects.
Again, as before, you need to copy this HelloDCOM.class file to the Windows directory:
C:\jsdk>copy HelloDCOM.class d:\winnt\java\lib\HelloDCOM.class
To test this setup, let's run the OLEVIEW program from the second exercise. Go to the HelloCOM class and try to instantiate. This will test to see if everything is working okay.
From OLEVIEW select View.ExpertMode and also set the Object.CoCreateInstance flags to CLSCTX_LOCAL_SERVER and CLSCTX_REMOTE_SERVER. It's essential that CLSCTX_INPROC_SERVER not be selected.
Expand the Java Classes node, then the Class HelloDCOM node. If the node opens, the COM class you created was instantiated to a COM object. This essentially tests that everything is running okay. Now compare all the parameters and settings in the Registry with the settings to HelloCOM. Go through the steps from the second exercise with HelloDCOM.
To actually use the COM object remotely, you're going to need to familiarize yourself with another tool: DCOM Configuration (or DCOMCNFG). DCOMCNFG is included with DCOM for Windows 95 and Windows NT with Service Pack 2 (SP2) or Service Pack 3. You can use it to set application properties, such as security and location.
On the computer running the client application, you must also specify the location of the server application that will be accessed or started. For the server application you'll specify the user account that has permission to access and start the application to the client computer.
Configuring the Server
Here are the steps needed to configure the server:
Ensure that the words DLLSurrogate appear next to the application type in the General tab. This is essential for remote operation of Java classes, as mentioned previously.
Configuring the Client
Here are the steps needed to configure the client:
C:\jsdk>javareg /register /class:HelloDCOM /progid:My.HelloDCOM
/clsid:{CLSID} /remote:servername
javareg /register /class:HelloDCOM /progid:My.HelloDCOM
/clsid:{064BEED0-62FC-11D2-A9AF-00A0C9564732} /remote:azdeals08
Private Sub Command1_Click()
Dim helloDCOM As Object
Dim count As Integer
Set helloDCOM = CreateObject("My.HelloDCOM")
MsgBox helloDCOM.getHello
count = helloDCOM.count
MsgBox helloDCOM.getHello
count = helloDCOM.count
End Sub
We now pass the name of the COM class's ProgID to CreateObject. The Visual Basic runtime library initiates a similar process, as described in previous exercises. Notice that we use Visual Basic's CreateObject to instantiate the DCOM class into an object. Also notice that again we pass CreateObject the ProgID of the COM class. By this point you should be able to guess what Visual Basic might be doing underneath, but let's recap one more time:
Conclusion
You know how to create a local COM server and a remote COM server. You know how to test both a local and a remote COM server with OLEVIEW. You know how to configure a COM server to be a remote server with JavaReg and DCOMCNFG. If you read this article and the previous one, you should have a feel for the difference in the architectures of DCOM, CORBA and RMI.
Reference
In the book Java Distributed Objects by Bill McCarty and Luke Cassady-Dorion, the subject of Java DCOM programming is covered in more detail with more examples. The book also covers RMI as well as CORBA (with an emphasis on CORBA). I wrote chapter 20 on DCOM, which covers Java and DCOM in greater detail, how to create callbacks in DCOM and how to use JActiveX to create Java wrappers around existing COM components. Also, I have an example that uses late bound calls using IDispatch.