Dr.Bob Examines... #57: Using Janeva to Connect CORBA and .NET
Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #57
See Also: other Dr.Bob Examines columns or Delphi articles

This article is based on my Janeva sessions at the Borland Conference (Sept 2004) in San Jose.

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:

Delphi 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.

CORBA Server Wizard

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 Server Wizard

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.
The other four (generated) files that are part of your project have already been saved in the current directory, and are (in alphabetical order) BirthdayReminder_c.pas (for the stub classes), BirthdayReminder_i.pas (for the interface definition), BirthdayReminder_impl.pas (for the implementation), and BirthdayReminder_s.pas (for the skeleton classes). Of all the generated files, only the BirthdayReminder_impl.pas should be edited and modified by us. This is the place to write the implementation of the actual CORBA server object, which I've kept simple but you can make it as sophisticated as you wish.
  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.

C#Builder New C# Application Wizard

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.

Add CORBA Reference

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.

Add CORBA Reference

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).

Manual Janeva Compile

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.idl
And 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 .


This webpage © 2004-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.