Borland Inprise Application Server (Delphi variant) Accessing EJBs

Java/J2EE COM Interoperability Products Page

Summary

Having read this article you will have a clear understanding of how easy it is for software written in languages supporting COM to access CORBA objects, including Enterprise JavaBeans ™ (EJBs), hosted in the Borland Inprise Application Server ™ (IAS) running on any platform.

Introduction

Java™ and CORBA provide an excellent foundation for building enterprise software solutions. There is however a whole other world out there, based on the Microsoft™ Component Object Model (COM). This article shows developers how COM-CORBA integration can be seamlessly achieved. It gives an overview of the technology, followed by a simple example showing a Visual Basic™ client accessing an Enterprise JavaBean hosted in IAS.

Overview of the Architecture

In order to access EJBs from Delphi you will use J-Integra®. J-Integra® allows pure Java objects to access COM objects, and it allows languages which support COM to communicate with pure Java objects. J-Integra® achieves this by incorporating a pure Java implementation of Distributed COM (DCOM), the COM network protocol.

Although you are going to use Delphi to access pure Java EJBs, you could in fact use any language which supports COM, such as Visual C++.

At this point I would like to emphasize that this requires no alteration to the EJB or supporting files, the same EJB may be accessed from pure Java and Delphi.

The following diagram illustrates three different interactions between the Delphi client, and Java objects:

Now you have an overview of the architecture, you can try out a working example.

The Step-by-Step example

Prerequisites for the example

The following items are required before you try out the example:

The example is based on the EJB instructors example which is part of the IAS installation. You must have the instructor example working using the standard Java client which is part of the example, before proceeding.

By default the Instructors example is installed in \Inprise\AppServer\examples\ejb\instructors

Introduction

The example is divided into two parts:

  1. The first part describes the steps to be performed on the server machine on which IAS and J-Integra® are installed, which may be any platform supporting IAS.
  2. The second part describes the steps to be performed on the client machine, which must be a Windows machine and must have Delphi installed.
You may of course run both client and server on the same machine.

In the example commands are often entered on the command line, if this is the case then they are displayed using an italic font.

The server machine

  1. Start IAS.

  2. Start the EJB container with the instructors EJB, as suggested in the instructors README file, using the following command:

    vbj com.inprise.ejb.Container ejbcontainer instructors_beans.jar -jns -jts -jdb

  3. Create a new directory and in it create a file called COMtoCORBA.java, with the following contents:

    import javax.naming.*;
    import com.linar.jintegra.Jvm;
    import com.linar.jintegra.AutomationException;
    import com.linar.jintegra.Instanciator;
    
    public class COMtoCORBA {
      public static void main(String[] args) throws Exception {
        Jvm.register("InstructorJvm", new CorbaInstanciator());
        while (true) { // endless loop to keep the bridge alive
          Thread.sleep(10000000);
        }
      }
    
      public Object narrow(Object narrowFrom, String narrowTo)
                                     throws AutomationException {
        try {
          return javax.rmi.PortableRemoteObject.narrow(narrowFrom,
                                                       Class.forName(narrowTo));
        } catch (ClassNotFoundException e) {
          e.printStackTrace();
          throw new AutomationException(e);
        }
      }
    }
    
    class CorbaInstanciator implements Instanciator {
      Context ctx;
    
      CorbaInstanciator() throws NamingException {
        ctx = new InitialContext();
      }
    
      // This method is called by the J-Integra® runtime when a COM client tries
      // to instantiate an object.  We have special handling if the object is a CORBA object
      public Object instanciate(String javaClass) throws AutomationException {
        try {
          try {
            return Class.forName(javaClass).newInstance();
          } catch(Exception e) {}
          return ctx.lookup(javaClass);
        } catch (Throwable t) {
          t.printStackTrace();
          throw new AutomationException(new Exception("Unexpected: " + t));
        }
      }
    }
    

    This is the COMtoCORBA bridge mentioned in the overview.

  4. Make sure that the J-Integra® runtime file jintegra.jar, which is located in the lib directory of your J-Integra® installation is in your CLASSPATH environment variable, and compile the file COMtoCORBA.java using the vbjc command provided in the IAS bin directory:

    vbjc COMtoCORBA.java

  5. To run COMtoCORBA you need to add the instructors_beans.jar file to your CLASSPATH. This file is located with the other instructors files in the IAS installation. Run COMtoCORBA using the vbj command provided in the IAS bin directory:

    vbj -VBJprop JINTEGRA_DCOM_PORT=1333 COMtoCORBA

    The property JINTEGRA_DCOM_PORT specifies a TCP/IP port used by the J-Integra® runtime to receive DCOM requests. It must match the port used in the 'regjvmcmd' command on the client machine, as you will see later.

The client machine

  1. If you do not have J-Integra® installed on your client machine, copy the jintmk.dll, regjvmcmd.exe, and regprogid.exe files from the bin directory of your J-Integra® installation on the server to a directory on the client.

  2. Open a DOS console on the client machine and change directory to the directory into which you copied the three files. Then tell J-Integra® about the location of the JVM, and the name used to access it. You will need to know the TCP/IP host name of the server machine. If it is the same machine as the client machine, then use 'localhost', otherwise use the appropriate host name. Run regjvmcmd as follows:

    regjvmcmd InstructorJvm yourservermachinename[1333]

  3. In the DOS console register three COM ProgIDs which will be used to access Java objects in the JVM:

    regprogid Instructor.Bridge "InstructorJvm:COMtoCORBA"
    regprogid Instructor.Address "InstructorJvm:Address"
    regprogid Instructor.Instructors "InstructorJvm:instructors"

  4. Start Delphi and create a new project. Modify the initial form to look similar to the following:

    Please note:
    - The Id TextBox must have its name property set to "Id"
    - The Name TextBox must have its name property set to "InstructorName"
    - The City TextBox must have its name property set to "City"
    - The Salary TextBox must have its name property set to "Salary"
    - The Add Instructor button must its name property set to "Add"
    - The View All Instructors button must have its name property set to "View"
    - The form itself must have its name property set to "InstructorEntry"

  5. Double click on the form background, go to the top of the text editor and edit the uses clause to add OleServer, ComObj and Unit2:
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, OleServer, ComObj, Unit2;

    Edit the var clause to add a Bridge variable:
    var
      InstructorEntry: TInstructorEntry;
      Bridge: Variant;

    Edit the FormCreate method to instantiate the bridge:
    procedure TInstructorEntry.FormCreate(Sender: TObject);
    begin
      Bridge := CreateOleObject('Instructor.Bridge');
    end;

  6. Double click on the Add button on the form and edit the AddClick method to add a new instructor:
    // Inserting an Instructor record
    procedure TInstructorEntry.AddClick(Sender: TObject);
    var
      NewAddress: Variant;
      InstructorHome: Variant;
    begin
      NewAddress  := CreateOleObject('Instructor.Address');
      InstructorHome := Bridge.narrow(CreateOleObject('Instructor.Instructors'),
                                      'InstructorHome');
    
      // We are only storing the city in the Address
      NewAddress.setCity(InstructorEntry.City.Text);
    
      // The Social security number and department are left as 0 and blank
      InstructorHome.create(InstructorEntry.Id.Text,InstructorEntry.InstructorName.Text,
                            0, IDispatch(NewAddress), '', InstructorEntry.Salary.Text);
    
      ShowMessage('Instructor ' + InstructorEntry.InstructorName.Text + ' added successfully');
    end;
    

  7. Double click on the View button on the form and edit the ViewClick method to display the instructors:
    // Viewing all instructor records
    procedure TInstructorEntry.ViewClick(Sender: TObject);
    var
      AllInstructorsDialog : TAllInstructors;
      AllInstructorsIterator : Variant;
      SearchAddr : Variant;
      InstructorHome: Variant;
      Instructor : Variant;
    begin
      SearchAddr := CreateOleObject('Instructor.Address');
      InstructorHome := Bridge.narrow(CreateOleObject('Instructor.Instructors'),
                                      'InstructorHome');
      AllInstructorsIterator := InstructorHome.findByAddress(IDispatch(SearchAddr)).iterator;
      AllInstructorsDialog := TAllInstructors.Create(self);
      while AllInstructorsIterator.hasNext do
      begin
        Instructor := Bridge.narrow(AllInstructorsIterator.next, 'Instructor');
        AllInstructorsDialog.Instructors.Items.Append(Instructor.getName);
      end;
      AllInstructorsDialog.ShowModal;
    end;
    

  8. Add a new form and modify it to look like this, the large white window is a ListBox:

    Please Note:
    - The ListBox must have its name property set to "Instructors"
    - The form must have its name property set to "AllInstructors"

  9. You are now ready to run your project. Hit the F9 key and you should see the following:

    Enter the data as shown below and click on the "Add" button:

    You should get confirmation of your input, click OK:

    Change the data in the form to the following and click on the "Add" button:

    You should get confirmation of your input, click OK:

    Click on the "View" button and you should see the list of all instructors:

    Close the All Instructors form and click on the "Add Instructor" button to add the same Instructor again, you should receive this message from the database:

Extra step - Using early binding with Delphi

Those of your familiar with using COM in Delphi will know that it supports both early and late binding to COM objects. The example so far has used late binding.

The next steps show you how to use early binding with J-Integra®. Early binding has the advantages that the type information for the Java object is available within the Delphi environment, and the runtime performance is increased. 

Again the steps are divided into those performed on the server machine and those performed on the client machine.

The server machine

  1. Make sure that the files jintegra.jar and instructor_beans.jar are in your CLASSPATH as for the late binding example.

  2. From the directory containing your COMtoCORBA files, run the java2com tool as follows:

    vbj com.linar.java2com.Main

  3. Click on the Settings menu, select the Names menu item, and remove the "class java.lang.Class" entry. Normally methods which use the java.lang.Class class are ignored by 'java2com' (mapped to ""), but since some CORBA interfaces use this type, we must remove this mapping:

    Enter the following in the input fields of java2com, and click on Generate :

    Once the generation is finished, close the dialog. You have just generated code used by the J-Integra® runtime to map DCOM requests from COM clients into corresponding method invocations on Java objects. You have also generated a COM Interface Definition Language (IDL) file called instructor.idl, which is used below.

  4. Compile the generated code :

    vbjc IID*.java

  5. Start COMtoCORBA running, as before:

    vbj -VBJprop JINTEGRA_DCOM_PORT=1333 COMtoCORBA

The client machine

  1. Copy the file instructor.idl, which was just generated on the server using java2com, to the client machine.

  2. You require Microsoft's IDL compiler (midl.exe) to compile your COM IDL. Midl.exe is part of the Visual C++ installation. Visual C++ also includes the command vcvars32.bat which will set up the environment variables required to run midl.exe. From the directory containing instructor.idl, run the following command to compile the COM IDL into a COM Type Library (instructor.tlb).

    midl instructor.idl

  3. To use early binding you require one more file on the client. If you do not have J-Integra® installed on the client, copy the file regtlb.exe from the bin directory of your J-Integra® installation on the server to the same directory on the client in which you placed jintmk.dll, regjvmcmd.exe and regprogid.exe. Register instructor.tlb:

    regtlb <full path to instructor.tlb> InstructorJvm

  4. Open up the Delphi project you created earlier. From the menu select Project->Import Type Library... Select the library called "Instructor generated from COMtoCORBRA Address ... (J-Integra®) on ....", make sure that the Palette page says "ActiveX", and the "Generate Component Wrapper" checkbox is checked, and click on "Install...":

  5. Select the "Into new package" pane, set the File Name to "InstructorEjb" and the description to "Instructor Enterprise JavaBean Package", click on OK, and confirm the installation:


  6. Now you have access to the type library information for your Java objects in Delphi. Replace the complete code for your InstructorEntry.frm with the following code which takes advantage of the type library information. Underlined you will see the type library imported, and the objects being declared of a specific type (rather than just Object):
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, OleServer, ComObj, Unit2, ActiveX, Instructor_TLB;
    
    type
      TInstructorEntry = class(TForm)
        Id: TEdit;
        InstructorName: TEdit;
        City: TEdit;
        Salary: TEdit;
        Add: TButton;
        View: TButton;
        Label1: TLabel;
        Label2: TLabel;
        Label3: TLabel;
        Label4: TLabel;
        procedure FormCreate(Sender: TObject);
        procedure AddClick(Sender: TObject);
        procedure ViewClick(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      InstructorEntry: TInstructorEntry;
      Bridge: TCOMtoCORBA;
    
    implementation
    
    {$R *.DFM}
    
    procedure TInstructorEntry.FormCreate(Sender: TObject);
    begin
      Bridge := TCOMtoCORBA.Create(Nil);
    end;
    
    // Inserting an Instructor record
    procedure TInstructorEntry.AddClick(Sender: TObject);
    var
      TheInstructorHome : InstructorHome;
      NewAddress : TAddress;
    begin
      NewAddress := TAddress.Create(Nil);
      TheInstructorHome := IUnknown(Bridge.narrow(CreateOleObject('Instructor.Instructors'), 'InstructorHome'))
                             as InstructorHome;
    
      // We are only storing the city in the Address
      NewAddress.setCity(InstructorEntry.City.Text);
    
      // The Social security number and department are left as 0 and blank
      TheInstructorHome.create(Integer(InstructorEntry.Id.Text),
                             InstructorEntry.InstructorName.Text,
                             '',
                             NewAddress.DefaultInterface,
                             '',
                             Single(InstructorEntry.Salary.Text));
    
      ShowMessage('Instructor ' + InstructorEntry.InstructorName.Text + ' added successfully');
    end;
    
    procedure TInstructorEntry.ViewClick(Sender: TObject);
    var
      AllInstructorsDialog : TAllInstructors;
      AllInstructorsIterator : JavaUtilIterator;
      SearchAddr : TAddress;
      TheInstructorHome : InstructorHome;
      Instructor : Instructor_;
    begin
      SearchAddr := TAddress.Create(Nil);
      TheInstructorHome := IUnknown(Bridge.narrow(CreateOleObject('Instructor.Instructors'), 'InstructorHome'))
                             as InstructorHome;
    
      AllInstructorsIterator := TheInstructorHome.findByAddress(SearchAddr.DefaultInterface).iterator();
      AllInstructorsDialog := TAllInstructors.Create(self);
      while AllInstructorsIterator.hasNext do
      begin
        Instructor := IUnknown(Bridge.narrow(AllInstructorsIterator.next, 'Instructor')) as Instructor_;
        AllInstructorsDialog.Instructors.Items.Append(Instructor.getName);
      end;
      AllInstructorsDialog.ShowModal;
    end;
    
    end.

  7. For the second time you are ready to run your project, hit F9 and try it out.