Java/COM integration

Bruce Eckel's Thinking in Java Contents | Prev | Next

COM (formerly known as OLE) is the Microsoft Component Object Model, the foundation of all ActiveX technologies. These include ActiveX Controls, Automation, and ActiveX Documents. But COM is much more; it’s a specification (and a partial implementation) for developing component objects that can interoperate using dedicated features of the operating system. In practice, all of the new software developed for Win32 systems has some relationship with COM – the operating system exposes some of its features via COM objects. Third-party components can be COM, and you can create and register your own COM components. In one way or another, if you want to write Win32 code, you’ll have to deal with COM. Here, I’ll just recap the fundamentals of COM programming, and I’ll assume that you are familiar with the concept of a COM server (any COM object that can expose services to COM clients) and a COM client (a COM object that uses the services provided by a COM server). This section kept things simple; the tools are actually much more powerful, and you can use them in a more sophisticated way. But this requires a deep knowledge of COM, which is beyond the scope of this appendix. If you’re interested in this powerful but platform-dependent feature, you should investigate COM and the Microsoft documentation on Java/COM integration. For more information, Dale Rogerson’s “Inside COM” (Microsoft Press, 1997) is an excellent book.

Since COM is the architectural heart of all the new Win32 applications, being able to use, or to expose, COM services from Java code can be important. The Java/COM integration is no doubt one of the most interesting features of the Microsoft Java compiler and virtual machine. Java and COM are so similar in their models that the integration is conceptually straightforward and technically seamless – there’s almost no special code to write in order to access COM. Most the details are handled by the compiler and/or by the virtual machine. The effect is that the COM objects are seen as ordinary Java objects by the Java programmer, and COM clients can use COM servers implemented in Java just like any other COM server. Again, I use the generic term COM, but by extension this means that you can implement an ActiveX Automation server in Java, or you can use an ActiveX Control in your Java programs.

The most notable similarities between Java and COM revolve around the relationship between COM interfaces and the Java interface keyword. This is a near-perfect match because:

  • A COM object exposes interfaces (and only interfaces).
  • A COM interface has no implementation; the COM object exposing an interface is responsible for its implementation.
  • A COM interface is a description of a group of functions semantically related; no data is exposed.
  • A COM class groups together COM interfaces. A Java class can implement an arbitrary number of Java interfaces
  • COM has a reference object model; the programmer never “has” an object, just references to one or more of its interfaces. Java has a reference object model as well – a reference to an object can be cast to a reference to one of its interfaces.
  • The lifetime in memory of a COM object is determined by the number of clients using the object; when this count goes to zero, the object deletes itself from memory. In Java, the lifetime of an object is also determined by the number of clients. When there are no more references to that object, the object is a candidate to be released by the garbage collector.
This tight mapping between Java and COM not only allows the Java programmer to easily access COM features, but it also makes Java an interesting language for writing COM code. COM is language-independent, but the de facto languages for COM development are C++ and Visual Basic. Compared to Java, C++ is much more powerful for COM development and generates much more efficient code, but it’s hard to use. Visual Basic is much easier than Java, but it’s too far from the underlying operating system, and its object model does not map very well to COM. Java is an excellent compromise between the two.

Let’s take a look at some of the keys points of COM development that you need to know to write Java/COM clients and servers.

COM Fundamentals

  • Let objects call services into other objects.
  • Allow new types of objects, or upgrades to existing ones, to be seamlessly plugged into the environment.
The first point is exactly what object-oriented programming is about: you have a client object that makes requests to a server object. In this case, the terms “client” and “server” are used in a generic way, and not to refer to some particular hardware configuration. With any object-oriented language, the first goal is easy to achieve if your application is a monolithic piece of code that implements both the server object code and the client object code. If you make changes to the way client and the server objects interface with each other, you simply compile and link again. When you restart your application, it uses a new version of the components.

The situation is completely different when your application is made up of component objects that are not under your control – you don’t control their source code and they can evolve separately from your application. This is exactly the case, for example, when you use a third-party ActiveX Control in your application. The control is installed in your system, and your application is able, at runtime, to locate the server code, activate the object, link to it, and use it. Later, you can install a newer version of the control, and your application should still be able to run; in the worst case, it should gracefully report an error condition, such as “Control not found,” without hanging up.

In these scenarios, your components are implemented in separate executable code files: DLLs or EXEs. If the server object is implemented in a separate executable code file, you need a standard, operating system supplied method to activate these objects. Of course, in your code you do not want to use the physical name and location of the DLL or EXE, because these might change; you want some identifier maintained by the operating system. Also, your application needs a description of the services exposed by the server. This is exactly what I’ll cover in the next two sections.

GUIDs and the Registry Type Libraries Function return codes in COM: HRESULT

MS Java/COM Integration

The Microsoft Java compiler, Virtual Machine, and tools make life a lot easier for the Java/COM programmer than it is for the C++/COM programmer. The compiler has special directives and packages for treating Java classes as COM classes, but in most cases, you’ll just rely on the Microsoft JVM support for COM, and on a couple of external tools.

The Microsoft Java Virtual Machine acts as a bridge between COM and Java objects. If you create a Java object as a COM server, your object will still be running inside the JVM. The Microsoft JVM is implemented as a DLL, which exposes COM interfaces to the operating system. Internally, the JVM maps function calls to these COM interfaces to method calls in your Java objects. Of course, the JVM must know which Java class file corresponds to the server executable; it can discover this information because you previously registered the class file in the Windows Registry using Javareg, a utility in the Microsoft Java SDK. Javareg reads a Java class file, generates a corresponding type library and a GUID, and registers the class in the system. Javareg can be used to register remote servers as well, for example, servers that run on a different physical machine.

Developing COM servers in Java

This section shows the process you will apply to the development of ActiveX Controls, Automation Servers, or any other COM-compliant server. The following example implements a simple Automation server that adds integer numbers. You set the value of the addend with the setAddend( ) method, and each time you call the sum( ) method the addend is added to the current result. You retrieve the result with getResult( ) and reset the values with clear( ). The Java class that implements this behavior is straightforward:

public class Adder {
  private int addend;
  private int result;
  public void setAddend(int a) { addend = a; }
  public int getAddend() { return addend; }
  public int getResult() { return result; }
  public void sum() { result += addend;  }
  public void clear() {
    result = 0;
    addend = 0;
  }
}

To use this Java class as a COM object, the Javareg tool is applied to the compiled Adder.class file. This tool has a number of options; in this case we specify the Java class file name (“Adder”), the ProgID we want to put in the Registry for this server (“JavaAdder.Adder.1”), and the name we want for the type library that will be generated (”JavaAdder.tlb”). Since no CLSID is given, Javareg will generate one; if we call Javareg again on the same server, the existing CLSID will be used.

javareg /register
/class:Adder /progid:JavaAdder.Adder.1
/typelib:JavaAdder.tlb

Javareg also registers the new server in the Windows Registry. At this point, you must remember to copy your Adder.class file into the Windows\Java\trustlib directory. For security reasons, related mostly to the use of COM services by applets, your COM server will be activated only if it is installed in the trustlib directory.

Dim Adder As Object
 
Private Sub Form_Load()
    Set Adder = CreateObject("JavaAdder.Adder.1")
    Addend.Text = Adder.getAddend
    Result.Caption = Adder.getResult
End Sub
 
Private Sub SumBtn_Click()
    Adder.setAddend (Addend.Text)
    Adder.Sum
    Result.Caption = Adder.getResult
End Sub
 
Private Sub ClearBtn_Click()
    Adder.Clear
    Addend.Text = Adder.getAddend
    Result.Caption = Adder.getResult
End Sub

Note that this code has no knowledge that the server was implemented in Java.

When you run this program and the CreateObject( ) function is called, the Windows Registry is searched for the specified ProgID. Among the information related to the ProgID is the name of the Java class file, so in response the Java Virtual Machine is started, and the Java object instantiated inside the JVM. From then on, the JVM takes care of the interaction between the client and server code.

Developing COM clients in Java

Now let’s jump to the other side and develop a COM client in Java. This program will call services in a COM server that’s installed on your system. The example is a client for the server we implemented in the previous example. While the code will look familiar to a Java programmer, what happens behind the scenes is quite unusual. This example uses a server that happens to be written in Java but applies to any ActiveX Control, ActiveX Automation server, or ActiveX component installed in your system for which you have a type library.

First, the Jactivex tool is applied to the server’s type library. Jactivex has a number of options and switches, but in its basic form it reads a type library and generates Java source files, which it stores in your windows/Java/trustlib directory. In the example line below, it is applied to the type library that was generated for out COM Automation server.

jactivex /javatlb JavaAdder.tlb

If, after Jactivex has finished, you take a look at your windows/Java/trustlib directory, you’ll find a new subdirectory called javaadder that contains the source files for a new package. This is the Java equivalent of the type library. These files use compiler directives specific to the Microsoft compiler: the @com directives. The reason jactivex generated more than one file is that COM uses more than one entity to describe a COM server (and also because I did not fine-tune the use of MIDL files and the Java/COM tools).

The file named Adder.java is the equivalent of a coclass directive in a MIDL file: it’s the declaration of a COM class. The other files are the Java equivalent of the COM interfaces exposed by the server. These interfaces, such as Adder_DispatchDefault.java, are dispatch interfaces, part of the mechanism of interaction between an Automation controller and an Automation server. The Java/COM integration feature also supports the implementation and use of dual interfaces. IDispatch and dual interfaces are beyond the scope of this appendix.

Below, you can see the client code. The first line just imports the package generated by jactivex. Then an instance of the COM Automation server is created and used, as if it was an ordinary Java class. Notice the typecast on the line where the COM object is instantiated. This is consistent with the COM object model. In COM, the programmer never has a reference to the whole object; instead, the programmer can only have references to one or more of the interfaces implemented in the class.

Instantiating a Java object of the Adder class tells COM to activate the server and to create an instance of this COM object. But then we must specify which interface we want to use, choosing among the ones implemented by the server. This is exactly what the typecast does. The interface used here is the default dispatch interface , the standard interface that an Automation controller uses to communicate with an Automation server (for details, see Inside COM , ibid.). Notice how simple it is to activate the server and select a COM interface:

import javaadder.*;
 
public class JavaClient {
  public static void main(String [] args) {
    Adder_DispatchDefault iAdder =
         (Adder_DispatchDefault) new Adder();
    iAdder.setAddend(3);
    iAdder.sum();
    iAdder.sum();
    iAdder.sum();
    System.out.println(iAdder.getResult());
  }
}

Now you can compile and run the code.

The com.ms.com package
I cannot cover all of these topics here, but I want to point out something about COM exceptions. By convention, virtually all COM functions return an HRESULT value that tells you if the function invocation succeeded or not and why. But if you look at the Java method signature in our server and client code, there no HRESULT. Instead, we use the function return value to get data back from some functions. The virtual machine is translating Java-style function calls into COM-style function calls, even for the return parameter. But what happens inside the virtual machine if one of the functions you call in the server fails at the COM level? In this case, the JVM sees that the HRESULT value indicates a failure and generates a native Java exception of class com.ms.com.ComFailException. In this way, you can handle COM errors using Java exception handling instead of checking function return values.

ActiveX/Beans integration

An interesting result of Java/COM integration is the ActiveX/Beans integration, by which a Java Bean can be hosted by an ActiveX container such as VB or any Microsoft Office product, and an ActiveX Control can be hosted by a Beans container such as Sun’s BeanBox. The Microsoft JVM takes care of the details. An ActiveX Control is just a COM server exposing predefined, required interfaces. A Bean is just a Java class that is compliant with a specific programming style. At the time this was written, however, the integration was not perfect. For example, the virtual machine is not able to map the JavaBeans event model to the COM event model. If you want to handle events from a Bean inside an ActiveX container, the Bean must intercept system events such as mouse actions via low-level techniques, not the standard JavaBeans delegation event model.

A note about native methods and applets

Native methods face the security issue. When your Java code calls a native method, you pass control outside of the virtual machine “sandbox.” The native method has complete access to the operating system. Of course, this is exactly what you want if you write native methods, but it is not acceptable for applets, at least not implicitly. You don’t want an applet, downloaded from a remote Internet server, to be free to play with the file system and other critical areas of your machine unless you allow it to do so. To prevent this situation with J/Direct, RNI, and COM integration, only trusted Java code has permission to make native method calls. Different conditions must be met depending on the feature the applet is trying to use. For example, an applet that uses J/Direct must be digitally signed to indicate full trust. At the time of this writing, not all of these security mechanisms are implemented (in the Microsoft SDK for Java, beta 2), so keep an eye on the documentation as new versions become available.



Comments

  • Good Article

    Posted by Muhannad on 09/15/2012 06:52pm

    Good day, The article is informative, thanks, but I ran into some issues while registering the java class, the error is "unable to create the type library for the specified java class", I have googled it & did not get any helpful results. javareg /register /class:COMServer /codebase:"C:\_tests_\com" /progid:"jasperTest.COMServer.1" /typelib:"C:\_tests_\com\javaCOMServer.tlb" if executed without the typelib switch, it successfully registers & creates an entry in registry for "jasperTest.COMServer.1" but when you try to instantiate the object (I tested it with Python): from comtypes.client import CreateObject as CO obj = CO("jasperTest.COMServer.1") #trail 1 #trial 2 with the generated class ID obj = CO("{8B97341B-8B6D-4C10-B0CC-B111191CD645}") both fails with the underlying windows call (ole32.CoCreateInstance) [the specified module cannot be found]. any suggestions? thanks in advance.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • "Security" is the number one issue holding business leaders back from the cloud. But does the reality match the perception? Keeping data close to home, on premises, makes business and IT leaders feel inherently more secure. But the truth is, cloud solutions can offer companies real, tangible security advantages. Before you assume that on-site is the only way to keep data safe, it's worth taking a comprehensive approach to evaluating risks. Doing so can lead to big benefits.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds