-
Notifications
You must be signed in to change notification settings - Fork 6
The Least You Need to Know about COM
The wiki on COM explains, among many other things, that COM (Component Object Model) is an older Microsoft techonology that encompasses OLE, OLE Automation, ActiveX, COM+ and DCOM. Microsoft.NET is not based on COM, but for the most part it supports it seamlessly. Scriptom lets you easily integrate these technologies into your Java/Groovy project.
Despite the fact that Visual Basic versions up through 6 were abstractions on top of COM, it's not at all simple. I won't go into the details here (the wiki does a better job than I could at explaining it all). The important point to remember is that COM is fundamentally a C/C++ based technology that was developed using pre-Java ideas of object orientation. To be specific, C/C++ apps are generally responsible for cleaning up allocated memory, and the COM threading models differ somewhat from Java.
Because C++/COM and Java aren't quite compatible, it is possible to get Scriptom to randomly crash your JVM (core-dump), use up all available memory, peg the CPU at 100% for hours on end, or prevent your Java process from exiting. If you use it correctly, though, you'll find that Scriptom is rock-solid stable.
This article is going to explain, in very simple terms, some of the differences between COM and Java, and how to deal with the issues they present in the simplest way possible.
Sorry about this, but you have to manage COM thread apartment contexts. The good news is that this is not nearly as onerous as it sounds. You can do this using Jacob directly (giving you a lot of flexibility). Of course, this article is about the simple, safe ways to use Scriptom, so we'll leave the advanced Jacob topics for another.
Scriptom provides an even simpler and safer way that handles 95% of the cases you are likely to run into. It also tends to produce the most stable applications, making it ideal for use on the server side. All you have to do is wrap your code in a Groovy closure, like this:
When you wrap your Scriptom code using .inApartment, the thread apartment is initialized if needed, and all COM resources are released when you are done. You can do this over and over again in a thread, and you can also nest calls to .inApartment, allowing you to use it safely with other code (this addresses an issue with Jacob). You'll see this pattern time and time again in our examples and test code.
The main side-effect of this is that you have to define all your ActiveXObjects within the apartment scope (the closure). In case that doesn't work for your purposes, you can freely use Jacob to manage your COM thread contexts. However, you can't safely mix and match the Scriptom thread management with Jacob calls in the same thread.
It should come as no surprise that COM strings, numbers, and objects are not interchangable with Java strings, numbers, and objects (though Scriptom does a pretty good job of making them look as if they are). Every time we exchange data with a COM library, the Java data-type needs to be converted to and from a form that is suitable for COM. That form just happens to be Microsoft's (pseudo) universal data type: the Variant. Without going too deeply into the murky details, a Variant can store various forms of numbers, strings, and objects. You don't normally need to worry about this because Scriptom handles all the conversion details for you.
What you need to know about Variants is that they can't be completely reclaimed by standard Java garbage collection. There is a little "residue" that Jacob has to hold on to even after you are finished using the Variant. This is necessary to prevent random JVM crashes (it's a C/C++/JNI thing). Over a large number of calls, this residue can fill all the memory on your server.
Don't panic! It isn't a memory leak. When you exit the .inApartment closure, the COM context is released and all the memory is reclaimed.
There is one more thing you should be aware of. The clean-up process gets much slower if you make a very large number (millions) of COM calls before cleaning up. So for a long running thread, you are better off breaking up the apartment contexts rather than having one context for the whole thread.
In short:
- Use an .inApartment closure around your COM code (more specifically, any code that uses ActiveXObject).
- Break .inApartment closures into reasonably-sized chunks