www.teamdev.com

This Question is Answered

1 "helpful" answer available (2 pts)
9 Replies Last post: Aug 14, 2008 6:21 PM by Serge Piletsky  
Click to view Fabricio Sanchez's profile   6 posts since
Aug 7, 2008

Aug 7, 2008 1:40 PM

Lost Beginner - Exposing Java Objects to COM

Hi There,

I'm very new to all things COM and comfyj, so please bear with me.
I've read the documentation and posts on this forum, but I think I am lacking some basic knowledge which I hope you will be able to kindly help me with.

For example, I have two classes in Java: Manager.class and Player.class defined simply as:
public class Player {
    protected String name;
    protected int age;
    protected boolean injured;
 
    public Player( String name, int age, boolean injured ) {
        this.name = name;
        this.age = age;
        this.injured = injured;
    }
 
    public String getName() { return name; }
    public void setName(String n) { this.name = n; }
 
    public boolean isInjured() { return injured; }
    public void setInjured(boolean b) { this.injured = b; }
 
    public int getAge() { return age; }
    public void setAge(int age) { this.age - age; } 
}
 
 
public class Manager {
    protected Vector<Player> players = new Vector<Player>();
    protected String managerName = "John";
    protected boolean active = true;
    protected int age = 30;
 
    public Manager() {
        players.add( new Player("Player 1", 30, false) );
        players.add( new Player("Player 2", 30, true) );
    }
 
    public Vector<Player> getPlayers() { return players; }
 
    public String getManagerName() { return managerName; }
    public void setManagerName(String n) { this.managerName = n; }
 
    public boolean isActive() { return active; }
    public void setActive(boolean b) { this.active = b; }
 
    public int getAge() { return age; }
    public void setAge(int age) { this.age - age; } 
}


What is the correct way to expose both those classes so that they could be accessed through the com interface?
I know that I can return Strings as com.jniwrapper.win32.automation.types.BStr, how about Vector,int, boolean, etc...?

If I define a ManagerComServer, is it possible to have access to my Player class through the COM interface also?
I would like to mimic something like:
Manager manager = new Manager();
String mName = manager.getManagerName();
Player player = manager.getPlayers().get(0);
String pName = player.getName();
int pAge = player.getAge();
player.setInjured(false);
boolean pInjured = player.isInjured();


Thank you for any help or pointers to help,

Best regards,

Fabricio.
Click to view Serge Piletsky's profile TeamDev Ltd. 632 posts since
Apr 24, 2006
3. Aug 11, 2008 9:01 PM in response to: Fabricio Sanchez
Re: Lost Beginner - Exposing Java Objects to COM
Hi Fabricio,

I am sorry for the delay in replaying to you.

Your initial steps are correct. Yes, IManager COM interface is required and it should be derived from IDispatch COM interface. Also, no additional methods (to construct a Manager object) in IManager interface are required:
public interface IManager extends IDispatch {
    public BStr getManagerName();
    public void setManagerName(BStr n);
 
    public VariantBool isActive();
    public void setActive(VariantBool b);
 
    public Int getAge();
    public void setAge(Int age); 
}

In order to implement the public Vector<Player> getPlayers() method in COM server you can use standard IEnumUnknown or IEnumVariant COM interfaces as the return type or via SafeArray object, for example:
public interface IManager extends IDispatch {
    public IEnumUnknown getPlayers();
}

In this case method should return the IEnumUnknown COM interface to your COM object (Java COM implementation of IEnumUnknown interface) that contains the Player objects, represented by IPlayer COM interface.

And of course, the most simplest way is to return that collection of objects via SafeArray:
public interface IManager extends IDispatch {
    public SafeArray getPlayers();
}

You have also correctly implemented the ManagerComServer COM server. Though you could skip the implementation of IPersist COM interface, which was used in the example only for reference, to demonstrate that Java COM server actually can implement any number of standard COM interfaces as well as custom ones:
public class ManagerComServer extends DispatchComServer implements IManager {
    public static final CLSID COM_SERVER_CLSID = new CLSID("{D1F3EAED-780E-46cd-B504-3919FFEAE887}");
    public static final String PROG_ID = "comfyj.test.1";
    public static final String VERSION_INDEPENDENT_PROG_ID = "comfyj.test";
    public static final String COM_SERVER_DESCRIPTION = "Java COM Server Test.";
    ....
}


This server can be registered in the system using the ServerManager application or via its API. All these steps are described in ComfyJ programmers Guide: http://www.teamdev.com/downloads/comfyj/docs/ComfyJ-PGuide.html#AEN607 After registering this server any COM application can create and use it.

There are several requirements for creating such Java COM server application correctly:
1) It should be built using a certain Java language level in the mind. This means that if you build it using JDK 5 or 6 then it this application cannot be started by earlier JDKs. That's possible for example, when you do not specify the certain JDK while registering Java COM server and JDK 1.4 is set as default in the system;
2) All required license files should be located in the \META-INF sub folder of the JAR file;
3) Native JNIWrapper library (jniwrap.dll and/or jniwrap64.dll) should be located in the root of this file;
4) All depended libraries should be specified in the manifest of this JAR file;

Therefore this application JAR file should be only JAR which you register by ServerManager. Such approach helps to minimize the length of command line which starts Java COM server, because there is such restriction in COM.

And that NullPointerException exception could indicate that server could not be created. In any case you should use the following approach in order to verify that:
  IDispatch server = new IDispatchImpl(CLSID.createFromProgID("comfyj.test"), ClsCtx.LOCAL_SERVER);
  if (server.isNull()) {
    // server object was not created successfully
  } else {
    // otherwise you can continue working with it
  }

Also, you have mentioned that (in your further comments) that all that methods (getProgId(), getVersionIndependentProgId() and getComServerDescription()) were not included to the IManager interface. They definitely should be included to the dispatch interface if you plane to invoke them in a client application. But that's quite strange that code could even get to that point. That's because there should be exception (DISP_E_UNKNOWNNAME) thrown if you try to call a method which does not exist in a server.

One additional hint for debugging Java COM servers: when registering a server please use java.exe, but not javaw.exe. When you specify java.exe the Java console should be displayed and thus you can see any exception that could occur in the server application.

You can try the sample Java COM server application which is available on our site: ftp://ftp.teamdev.com/demo/JavaCOMServerSample.zip. To register this example you just need to unpack the archive to some folder (e.g. C:\Server\) and register the server.jar using the ServerManager and specify Java 6 executable. I have made this example to demonstrate how to create simple ActiveX components using ComfyJ. And after registering this example application you can even embed the SimpleSwingActiveX component into various OLE containers. Also, note that this example contains the latest builds of ComfyJ and JNIWrapper libraries and there are numerous fixes in it since version 2.4.

Please let me know if you have any further questions.

Regards,
-Serge
Click to view Serge Piletsky's profile TeamDev Ltd. 632 posts since
Apr 24, 2006
5. Aug 12, 2008 5:29 PM in response to: Fabricio Sanchez
Re: Lost Beginner - Exposing Java Objects to COM
Hi Fabricio,

Yes, IPlayer must extend IDispatch interface, otherwise its methods will not be registered in the Java COM server. Also, supposing that getPlayers() method returns SafeArray object, the getNumberOfPlayers() method is not necessary, because SafeArray object (descriptor of an array) already contains the number of elements.

Without the sample it's difficult to say why that NullPointer exception occurs. I have tried to recreate such issue in my environment, but the method which returns a SafeArray object worked on a client without any problem. Can you please check which versions of ComfyJ and JNIWrapper are used by your server application? Please make sure that this a latest ones, which are available in a sample application that I have provided before. Also, you can download it as separate archive from the following link: ftp://ftp.teamdev.com/updates/comfyj-2.4.902.zip

But if the problem persists please send me a sample application that reproduces the problem. I will investigate it and let you know the solution.

-Serge
Click to view Serge Piletsky's profile TeamDev Ltd. 632 posts since
Apr 24, 2006
7. Aug 13, 2008 10:44 PM in response to: Fabricio Sanchez
Re: Lost Beginner - Exposing Java Objects to COM
Hi Fabricio,

I have found the cause of that problem. It turned out that getPlayers() of ManagerComServer class was incorrectly implemented. I have made the required changes and that started working correctly. Also, I have simplified some classes a little bit.

Please try this update and let me know the results.

-Serge
Attachments:
Click to view Serge Piletsky's profile TeamDev Ltd. 632 posts since
Apr 24, 2006
9. Aug 14, 2008 6:21 PM in response to: Fabricio Sanchez
Re: Lost Beginner - Exposing Java Objects to COM
Hi Fabricio,

Yes, your understanding is quite correct.

I just would like to clarify why I have added these IPlayerServer and IPlayerImpl classes. First (IPlayerServer) is the COM wrapper class for the Player object which is an upper COM layer for the corresponding Java class. It's actually the similar to ManagerComServer class that implements IManager dispatch interface and encapsulates the working with Manager object. The second IPlayerImpl was added for the convenience. Actually I could even use the IDispatch interface instead, but then in order to invoke its methods I would need to create an Automation object. So, I hope that should explain a little bit the changes that I have made. But if you still have unanswered questions please let me know.

Also, I would like to mention that in one of the future versions of ComfyJ we are going to introduce new feature that will simplify the exposing of already created Java objects as COM objects. This means that you will not need to create any additional Java COM wrappers by your own because ComfyJ will do that for you. And I hope that should greatly simply the usage of API.

-Serge