HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

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:

  1. Write a Java class and compile it. The class must have public members and methods.
  2. Register the class using JavaReg.
DCOM Servers
Once you create a COM object, you need a server to serve the objects up to the world. A COM server is a container that holds COM objects. In essence, it's a class, a dynamic link library or an executable file that contains COM classes, which in the case of Java relates to Java classes. The COM server has the ability to turn classes into objects. The server implements the IClassFactory interface, which provides a standard way to request having a COM class instantiated into a COM object.

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:

  1. In-process servers execute in the same process (DLL, OCX, INPROC) context as the client. Local Java classes exported as COM objects run in this mode unless they're using a surrogate process that they need to run remotely.
  2. Local servers run in a separate process (EXE) from their clients but still on the same machine. Basically, local servers run over LPC.
  3. Remote servers execute in a separate process on a remote machine. The clients use RPC to talk to the remote server. However, a client doesn't have to know that the server is remote. This may be completely transparent to the client. Java classes exported as COM objects can be run in this mode only if they use a surrogate process, which can be the default surrogate process provided by the COM library. This process is covered in the code examples.
The COM library supplies three ways for COM clients to attach to a remote server and request a COM object:
  1. The server name is associated with the ProgID of the object in the Windows Registry.
  2. An explicit parameter is passed to CoCreateInstanceEx, CoGetInstanceFromFile, CoGetInstanceFromStorage or CoGetClassObject specifying the server name.
  3. Monikers are used.

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.

  1. If OLEVIEW is on your path, type OLEVIEW at the DOS prompt. Otherwise, find it and execute it.
  2. Select View, ExpertMode.
  3. Select Object, CoCreateInstanceFlags, CLSCTX_INPROC_SERVER.
  4. Expand the node labeled Grouped by Component.
  5. Expand the Java Classes mode under Grouped by Component.
  6. Find the class you created in the previous exercise (HelloCOM).
  7. Click this node once.
The CLSCTX_INPROC_SERVER component won't run as a remote server. Local and remote servers run in separate processes. Inproc is associated with a DLL, so when the class is registered it's associated with the DLL that runs Java classes (namely, msjava.dll).

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:

  • The inproc server is listed as msjava.dll.
  • The threading model is specified as Both.
  • The Java class associated with this COM object is HelloCOM.
  • The ProgID is Acme.HelloCOM.

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:

  • OLEVIEW takes the ProgID and finds the CLSID (remember, the CLSID is a 128-bit GUID that identifies our COM class) for this COM class.
  • OLEVIEW calls CoCreateInstance, passing it the CLSID and the CLSCTX_INPROC_SERVER.
  • The COM library loads msjava.dll and negotiates with its IFactory to get a COM object representing our Java class. See Figure 1.

Figure 1
Figure 1:

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:

  • IUnknown: Interface negotiation and lifecycle management
  • IDispatch: Dynamic invocation for scripting languages
  • IMarshal: Custom marshaling
You should be familiar with these interfaces from the earlier discussion of the architecture in Part One of this series. From there we can ascertain that Microsoft has defined some kind of custom marshaler to marshal parameters to methods and return types between processes and machines.

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()
Dim helloCOM As Object
Dim count As Integer

Set helloCOM = CreateObject("Acme.HelloCOM")
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:

  • CreateObject takes the ProgID and finds the CLSID for this COM class in the system Registry.
  • CreateObject then calls CoCreateInstance, passing it the CLSID and the CLSCTX_INPROC_SERVER, which tells CoCreateInstance to create this object as an in-process COM server.
  • The COM library loads msjava.dll and negotiates with IUnknown to get an IFactory interface.
  • The COM library then uses the IFactory to get a COM object representing the Java class. (Actually, at this point IFactory returns an IDispatch that represents the interface to the COM object.) See Figure 1.

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 int count = 0;

public String getHello()
{
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:

  1. At the DOS prompt, type DCOMCNFG (or launch it any way you prefer; it's in the Windows \System32 directory).
  2. Select Java Class: HelloDCOM in the Applications tab's Application list box.
  3. Double-click Java Class: HelloDCOM or press the Properties button.
  4. Another dialog box pops up called Java Class: HelloDCOM.

    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.

  5. Go to the Security tab.
  6. Select the Use Custom Access permission.
  7. Press the Edit button to edit the access permissions.
  8. Add the user name with the Add button. (Press Show Users in the Add Users and Groups dialog box.)
  9. Set their permissions to Allow Access in the type of permission.
  10. Select the Use Custom Launch permission.
  11. Press the Edit button to edit the access permissions.
  12. Add the user name with the Add button. (Press Show Users in the Add Users and Groups dialog box.)
  13. Set their permissions to Allow Access in the type of permission.

Configuring the Client
Here are the steps needed to configure the client:

  1. Run the JavaReg tool on the client machine. The following line is entered as a continuous line at the DOS prompt without a line break:

    C:\jsdk>javareg /register /class:HelloDCOM /progid:My.HelloDCOM
    /clsid:{CLSID} /remote:servername

  2. Set the CLSID to the 128-bit CLSID associated with this class. You can get this value by looking it up in OLEVIEW.
  3. Set the server name to the name of your remote machine. Here's an example of what it might look like:

    javareg /register /class:HelloDCOM /progid:My.HelloDCOM
    /clsid:{064BEED0-62FC-11D2-A9AF-00A0C9564732} /remote:azdeals08

  4. Next, you can use OLEVIEW on the client to ensure that you can connect to the remote server. This step should be familiar to you by now. It's the third time we've used OLEVIEW. Go through the steps from the second exercise with OLEVIEW and note the differences between HelloDCOM on the client and HelloDCOM on the server.
Demonstrating Access
From your favorite Automation controller scripting language, add a button to a form called Command1 and then add the code shown below, which demonstrates helloDCOM Class Access.

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:

  • CreateObject takes the ProgID and finds the CLSID for this COM class in the system Registry. The Visual Basic runtime notices that the object being requested is a remote object.
  • CreateObject then calls CoCreateInstance, passing it the CLSID, which tells CoCreateInstance to create this object as a remote process COM server.
  • The local machine's SCM contacts the remote machine's SCM.
  • The remote machine's SCM uses the COM library to load msjava.dll in a surrogate process and then negotiates with IUnknown to get an Ifactory interface.
  • The COM library then uses the IFactory to get a COM object representing the Java class. (Actually, at this point, IFactory returns an IDispatch that represents the interface to the COM object.)
  • The IDispatch reference gets marshaled back to the local machine so that the VB application (the COM client) can begin making calls. See Figure 2.

Figure 2
Figure 2:

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.

Author Bio
Rick Hightower, a senior software engineer at LookSmart, a category-based Web directory, has been writing software for a decade, from embedded systems to factory automation solutions. Rick recently worked at Intel's Enterprise Architecture Lab, where he researched emerging middleware and component technologies.
He can be reached at: [email protected]

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.