| Delphi Clinic | C++Builder Gate | Kylix Kicks | C#Builder Visions | Delphi for .NET |
| ||||||
Using Janeva to Connect CORBA, Java, and .NET
In this paper, you learn how Microsoft .NET Framework developers can use Borland Janeva to connect to CORBA servers that run on different platforms (like Win32, Linux and others).
Janeva
Janeva is Borland's "middle-ware" (or glue) technology to connect "legacy" J2EE (Enterprise JavaBeans) or CORA objects to .NET clients.
There are several examples using J2EE available already, but with my Delphi and C++Builder experience, I like to work a CORBA example instead.
So, I'll start by building a Delphi CORBA server (using Borland Delphi 7 Enterprise and the VisiBroker ORB) and will connect that CORBA server to C# clients written in C#Builder using Borland's Janeva as "glue".
CORBA to .NET
CORBA is the acronym for Common Object Request Broker Architecture and is a widely used architecture to build distributed (multi-tier) applications with both cross-platform and cross-language in mind.
Like J2EE, CORBA is not easily connected to the .NET world, where ASP.NET Web Services and .NET Remoting are the generic ways to build distributed applications.
However, using Borland's Janeva, we can connect CORBA or J2EE objects to the .NET world.
Delphi CORBA Server
Let's start with the CORBA server, built in Borland Delphi 7 Enterprise.
The CORBA server that I want to use is based on an existing application to maintain a personal agenda with appointments that can be used to schedule meetings electronically.
I will not spend a lot of time to discuss the actual scheduling implementation details, but rather focus on the way to build and distribute the CORBA server interface specification (that will be used at the client side).
To build the CORBA server, start Delphi 7 Enterprise (or C++Builder 6 Enterprise), do File | New - Other and go to the Multitier tab of the Object Repository:
There are a number of icons here related to CORBA. To start a new CORBA server project from scratch, double-click on the CORBA Server icon, which will bring up the CORBA Server Wizard to specify the options for our new project.
You can create a Console Application (with or without using Borland's Visual Component Library) or a Windows Application.
I've selected the latter, since I want the server to display some visual (debug) information during development.
You can set some options in the Options tab of the dialog.
CORBA servers can contain one or more CORBA objects, and the interface specification of these CORBA objects are usually stored in IDL (Interface Definition Language) files. When you start a new CORBA server project, you can either add a number of existing IDL files, or start with an empty IDL file and add your own CORBA object definitions. For this example, I've decided to use a simplified version of the actual IDL file, which is as follows::
module BirthdayReminder
{
struct TDate {
short Year;
short Month;
short Day;
};
exception IndexOutOfRange{};
interface IBirthday
{
exception InvalidBirthday
{
string Reason;
};
void RememberBirthday(in string Name, in TDate Date)
raises (InvalidBirthday);
short NumberOfBirthdaysOnDate(in TDate Date);
string BirthdayOnDate(in TDate Date, in short index)
raises (IndexOutOfRange);
};
};
When you click on OK in the CORBA Server Wizard, the new project is generated, including a number of generated files.
Do File | Save All to save the project files, placing the main form in MainForm.pas and the project main file itself in D7CorbaServer.dpr.
unit BirthdayReminder_impl; {This file was generated on 18 May 2004 12:52:13 GMT by version 03.03.03.C1.A2} {of the Inprise VisiBroker idl2pas CORBA IDL compiler. } {Please do not edit the contents of this file. You should instead edit and } {recompile the original IDL which was located in the file } {C:\usr\Birthday.idl. } {Delphi Pascal unit : BirthdayReminder_impl } {derived from IDL module : BirthdayReminder } interface uses SysUtils, CORBA, BirthdayReminder_i, BirthdayReminder_c; type TIBirthday = class; TIBirthday = class(TInterfacedObject, BirthdayReminder_i.IBirthday) protected {******************************} {*** User variables go here ***} {******************************} public constructor Create; procedure RememberBirthday ( const Name : AnsiString; const Date : BirthdayReminder_i.TDate); function NumberOfBirthdaysOnDate ( const Date : BirthdayReminder_i.TDate): SmallInt; function BirthdayOnDate ( const Date : BirthdayReminder_i.TDate; const index : SmallInt): AnsiString; end; implementation uses IniFiles; const IniFileName = 'c:\usr\birthday.ini'; constructor TIBirthday.Create; begin inherited; { *************************** } { *** User code goes here *** } { *************************** } end; procedure TIBirthday.RememberBirthday ( const Name : AnsiString; const Date : BirthdayReminder_i.TDate); var DateStr: String; Count: integer; begin try DateStr := Format('%.4d-%.2d-%.2d',[Date.Year,Date.Month,Date.Day]); with TIniFile.Create(IniFileName) do try Count := ReadInteger(DateStr, 'count', 0) + 1; WriteInteger(DateStr, 'count', Count); WriteString(DateStr,IntToStr(Count),Name) finally Free end except on E: Exception do raise EIBirthday_InvalidBirthday.Create(E.ClassName + ': ' + E.Message) end end; function TIBirthday.NumberOfBirthdaysOnDate ( const Date : BirthdayReminder_i.TDate): SmallInt; var DateStr: String; begin Result := 0; DateStr := Format('%.4d-%.2d-%.2d',[Date.Year,Date.Month,Date.Day]); with TIniFile.Create(IniFileName) do try Result := ReadInteger(DateStr, 'count', 0) finally Free end end; function TIBirthday.BirthdayOnDate ( const Date : BirthdayReminder_i.TDate; const index : SmallInt): AnsiString; var DateStr: String; Count: integer; begin DateStr := Format('%.4d-%.2d-%.2d',[Date.Year,Date.Month,Date.Day]); with TIniFile.Create(IniFileName) do try Count := ReadInteger(DateStr, 'count', 0); if index > Count then raise EIndexOutOfRange.Create else Result := ReadString(DateStr, IntToStr(index), ''); finally Free end end; initialization end.Apart from the implementation of the TIBirthday class, we must also go to the MainForm unit and make sure the CORBA Server Object is actually created. In the TForm1 declaration, add a field called Birthday of type TIBirthday.
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Corba, BirthdayReminder_c, BirthdayReminder_i, BirthdayReminder_impl, BirthdayReminder_s,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ private declarations }
protected
{ protected declarations }
Birthday: IBirthday; // skeleton object
procedure InitCorba;
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.InitCorba;
begin
CorbaInitialize;
Birthday := TIBirthdaySkeleton.Create('Happy Birthday', TIBirthday.Create);
BOA.ObjIsReady(Birthday as _Object);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
InitCorba;
Sender.Free // remove button from TForm1
end;
end.
If you wanted to create a Delphi 7 CORBA Console Server instead (also using the Basic Object Adapter - BOA - by default), then the implementation of the main project file must be as follows:
program D7CorbaConsoleServer;
{$APPTYPE CONSOLE}
uses
SysUtils, CORBA, BirthdayReminder_c, BirthdayReminder_i, BirthdayReminder_impl, BirthdayReminder_s;
var
Birthday: IBirthday;
begin
CorbaInitialize;
Birthday := TIBirthdaySkeleton.Create('Happy Birthday', TIBirthday.Create);
Writeln('Birthday Server Object Created...');
Writeln;
BOA.ObjIsReady(Birthday as _Object);
Writeln('Server is ready...');
BOA.ImplIsReady;
end.
Before you can run the CORBA Server, you must make sure the VisiBroker Smart Agent is also running (either normal or as NT service), so you can then run the server itself:
Now you can use the Borland VisiBroker Console to run OS Find and see the resulting Corba Server as one of the manually started objects, with name "Happy Birthday".
C# CORBA Client
Start C#Builder Enterprise or Architect, and start a new C# Application.
In the New Application dialog you can specify the name as well as the location of the new project.
If you've installed Borland Janeva, then you can right-click on the main project file inside the Project Manager and add a J2EE or CORBA Reference. Without Janeva, you can add only regular references (normal files) or Web references (Web Services) to the project.
Since our example project is a CORBA server, we should select the Add CORBA Reference... choice. This will open a dialog were we can specify the IDL file that holds the CORBA server definition: Birthday.idl.
Note that we only import the definition to the CORBA server now, but we do not yet specify how to connect to that CORBA server; that will be done after we've imported the definition.
Once you've added the IDL file to the project, Janeva will import and compile the IDL file and generate a corresponding .cs file - in our case a Birthday.cs file.
If you've made changes to the IDL file, you can always right-click on the IDL file to recompile it (and regenerate the Birthday.cs file).
In order to use the Birthday.cs file, we must add the CORBA namespace as well as the generated BirthdayReminder namespaces to our using clause of the WinForm.cs file.
Inside the WinForm constructor, we must create an instance of the ORB - the Object Request Broker that connects our client to the CORBA server.
This is done by calling CORBA.ORB.Init with the -vbroker.agent.port, 14000 arguments.
string[] args = new string[] {"-vbroker.agent.port", "14000"};
orb = CORBA.ORB.Init(args);
After this, we can use a button to create an instance of the CORBA Birthday object and call the Meeting method.
This can be seen in the last method of the complete listing below:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using CORBA;
using BirthdayReminder;
namespace BDSCorbaClient
{
///
/// Summary description for WinForm.
///
public class WinForm : System.Windows.Forms.Form
{
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
private CORBA.ORB orb = null;
private BirthdayReminder.IBirthday MyBirthdayServer = null;
private System.Windows.Forms.Button button1;
public WinForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
string[] args = new string[] {"-vbroker.agent.port", "14000"};
orb = CORBA.ORB.Init(args);
}
///
/// Clean up any resources being used.
///
protected override void Dispose (bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(56, 56);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// WinForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.button1);
this.Name = "WinForm";
this.Text = "WinForm";
this.ResumeLayout(false);
}
#endregion
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.Run(new WinForm());
}
private void button1_Click(object sender, System.EventArgs e)
{
MyCorbaBirthdayServer =
ICorbaBirthdayServerHelper.Bind("Happy Birthday");
TDate Date = new TDate();
Date.Day = 10;
Date.Month = 9;
Date.Year = 1967;
MyBirthdayServer.RememberBirthday("Mira Swart", Date);
}
}
}
The Bind statement uses the same name - "Happy Birthday" - that we specified in the Delphi 7 CORBA Server to create an instance of the server object.
Janeva 6.0
The above C# code works just fine when using Janeva 1.0 (which is included with C#Builder Enterprise or higher) or Janeva 2.5 (which can be downloaded from the Borland website).
However, the latest version Janeva 6.0 does no longer support the BOA (Basic Object Adapter) by default.
In fact, it doesn't even generate the Bind methods that we're calling in the code above.
Fortunately, you can still force it to generate the Bind methods and tell it to use the BOA.
In order to generate the Bind methods, you need to call idl2cs outside of the C#Builder IDE with the -bind argument, as follows:
idl2cs -o Birthday.cs -bind -root_dir . Birthday.idlAnd in order to tell the resulting client application to let Janeva use the VisiBroker OSAgent, you need to add some property values in the janeva configuration section of your application's config file. In our case, the .config file should be as follows:
<configuration>
<configSections>
<section name="janeva" type="Janeva.Settings, Borland.Janeva.Runtime"/>
</configSections>
<janeva>
<agent enabled="true" port="14000"/>
</janeva>
</configuration>
Where the (OS)agent is enabled and the port is set to the default value of 14000.
If you migrate from Janeva version 1.0 or 2.5 to Janeva 6.0, then you may want to check out the Janeva 6.0 Release Notes for some important compatibility details (including more information about BOA support).
Summary
In this paper, I've shown how Microsoft .NET Framework developers can use Borland Janeva to connect to CORBA servers that run on different platforms (like Win32, Linux and others).
Janeva offers a significant speed benefit over the use of web services when connecting .NET clients to server applications.
However, there are deployment issues and license fees that you have to pay - contact your local Borland office for details.
See also my Connecting CORBA to .NET article for DevX, and the BorCon 2004 conference paper using Janeva to Connect CORBA, Java, and .NET .