Borland Inprise Application Server (Delphi variant) Accessing EJBs |
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
Java
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 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
The example is divided into two parts:
In the example commands are often entered on the command line, if this is the case then they are displayed using an italic font.
Start IAS.
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
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)); } } } |
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
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.
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.
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]
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"
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"
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; |
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; |
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; |
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"
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:
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.
Make sure that the files jintegra.jar and instructor_beans.jar are in your CLASSPATH as for the late binding example.
From the directory containing your COMtoCORBA files, run the java2com tool as follows:
vbj com.linar.java2com.Main
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.
Compile the generated code :
vbjc IID*.java
Start COMtoCORBA running, as before:
vbj -VBJprop JINTEGRA_DCOM_PORT=1333 COMtoCORBA
Copy the file instructor.idl, which was just generated on the server using java2com, to the client machine.
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
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
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...":
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:
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. |
For the second time you are ready to run your project, hit F9 and try it out.