Продолжается подписка на наши издания! Вы не забыли подписаться?

Обратные вызовы в CORBA

Код к статье*

В последнее время CORBA быстро превратилась в стандарт разработки распределенных приложений. Приложения CORBA могут содержать один (или больше) серверный объект и множество клиентов, подключающихся к этому серверу. Объект сервера CORBA становится доступным клиенту после регистрации сервисами CORBA Naming Service или CORBA Trader. С помощью Naming Service или Trade клиент определяет местоположение нужного серверного объекта и получает ссылку на него. Используя эту ссылку, клиент может вызвать методы объекта сервера и проделать необходимую работу. Таким образом, этот клиент будет использовать средства сервера. Сервер просто возвращает результаты вызовов метода клиенту. Однако, в некоторых случаях, может понадобиться, чтобы сервер вызвал метод объекта клиента. Например, серверу нужно известить клиента о наступлении определенного события на сервере или о завершении работы, запрошенной клиентом. Такой вызов клиента называется обратным вызовом - callback. Спецификации CORBA позволяют осуществлять обратный вызов клиента. В настоящей статье обсуждается реализация обратных вызовов CORBA на Java.

Спецификации CORBA не зависят от языка и, таким образом, как клиентская, так и серверная программа могут быть реализованы на различных языках по выбору программиста. Для реализации callback-вызовов клиент должен передать серверу ссылку на свои объекты. Сервер отвечает за сохранение таких ссылок и за вызовы методов соответствующих клиентов в случае необходимости. Сервер должен хранить эти ссылки в каком-то независимом от определенного языка формате. Другими словами, для хранения таких ссылок должен использоваться подходящий тип данных из определенных спецификацией CORBA. В настоящей статье для демонстрации callback-вызовов используется пример с управлением светофорами, реализованный на Java. Java используется как язык реализации и клиентского, и серверного приложения, однако для этого вполне пригоден и любой другой язык.

Схематично приложение управления светофорами состоит из объекта, управляющего светофорами и нескольких объектов "светофор" (см. рис.1).

При создании каждый объект "светофор " регистриру ется у контроллера . Контроллер сохраняет ссылки на каждый зарегистрировавшийся объект. Объект "контроллер " работает как сервер CORBA, регистрирующийся с помощью CORBA Naming Service. Объект "регулировщик ", которому нужно управлять светом определенного перекрестка находит нужный ему объект-контроллер по имени через CORBA Naming Service. После обнаружения контроллера регулировщиком будет запрошено число светофоров , находящихся под управлением этого контроллера . Пользовательский интерфейс приложения -регулировщика , приведенный на рисунке 2, отображает панель каждого зарегистрированного светофора .

Используя эти панели, регулировщик включает соответствующие лампы различных светофоров и передает изменения контроллеру . Контроллер , в свою очередь, просматривает список зарегистрированных светофоров и, вызывая метод для каждого из них, задает нужный свет.

CORBA IDL

Разработка CORBA-приложений начинается с написания кода на Interface Definition Language (IDL). CORBA IDL для приложения -светофора представлен в листинге 1.

Листинг 1: traffic.idl
module traffic
{
   interface TrafficLight
   {
      attribute short LightNumber;
      void SetColor (in string color);
   };
   
   interface TrafficCoordinator
   {
      typedef sequence <TrafficLight> LightsList;
      attribute LightsList lights;
      void Register(in TrafficLight light);
      void SetColor (in short LightNumber, in string color);
   };
}

Модуль traffic определяет два интерфейса - TrafficLight и TrafficCoordinator. Интерфейс TrafficLight определяет объекты-светофоры , а интерфейс TrafficCoordinator определяет интерфейс координатора .

Интерфейс TrafficLight содержит переменную LightNumber типа short, в которой хранится номер лампы. Кроме того, в нем предусмотрен метод с именем SetColor(), который вызывается координатором для установки статуса светофора . Это и есть callback-метод.

Интерфейс координатора объявляет переменную типа sequence (последовательность, список) для сохранения ссылок на зарегистрированные объекты TrafficLight. Последовательность реализована на Java с использованием массива Java или класса Java Vector. Координатор объявляет метод Register(), который объект TrafficLight вызывает для регистрации себя у координатора . Координатор объявляет другой метод - SetColor(), который вызывается приложением -регулировщиком . Метод SetColor() имеет два параметра _ номер светофора и цвет лампы. Координатор использует этот номер для определения нужного объекта "светофор " и устанавливает нужный цвет, вызывая метод SetColor().

Рисунок 1. Схема приложения управления светофорами

Для разработки этого приложения был использован Visigenic VBroker for Java. Первый шаг в разработке приложения _ это трансляция кода IDL в код Java. Это преобразование выполняется утилитой командной строки idl2java из состава VBroker:

idl2java _no_tie _no_comments traffic.idl 

Опция _no_tie говорит транслятору , что классы tie генерировать не нужно, а опция _no_comments отключает генерацию комментариев . Транслятор создает пакет (package) Java с тем же именем, что и у модуля CORBA _ traffic. Два интерфейса CORBA преобразуются в два интерфейса Java _ TrafficLight и TrafficCoordinator. Кроме того, idl2java создает классы реализации и классы примеров для этих интерфейсов . Для реализации CORBA-интерфейсов могут быть использованы файлы _example_TrafficLight.java и _example_TrafficCoordinator.java. Чтобы обезопаситься от перезаписи оригинальных файлов при следующем запуске idl2java, лучше сделать их копии _ _example_TrafficLight.java скопировать в файл TrafficLightImpl.java, а _example_TrafficCoordinator.java _ в файл TrafficCoordinator.java.

Разработка приложения

Исходный код для TrafficLightImpl.java приведен в Листинге 2. Класс объявляет переменную Number число коротких типа short для хранения номера лампы. Он также объявляет еще одну переменную LightServer типа TrafficLightServer, являющегося Java-классом. Исходный код класса TrafficLightServer дан в листинге 3 и будет обсуждаться чуть позже. Класс TrafficLightServer создает объект типа TrafficLightImpl. Метод SetColor() класса реализации (TrafficLightImpl) использует эту ссылку для вызова метода SetColor() объекта TrafficLightServer. Конструктор класса TrafficLightImpl получает два параметра _ ссылку на объект TrafficLightServer, который вызывал этот конструктор , и номер лампы. Эти два параметра сохранены в двух переменных класса, обсуждавшихся выше. В случае успешного создания объекта, конструктор сообщает об этом пользователю . Методы получения и изменения атрибута LightNumber, соответственно извлекают и сохраняют значение переменной Number. Метод SetColor() класса TrafficLightImpl просто вызывает метод SetColor() класса TrafficLightServer.

Листинг 2: TrafficLightImpl.java
//****************************************************
// Файл: TrafficLightImpl.java
// Этот файл содержит реализацию следующих классов 
// 1. TrafficLightImpl
// copyright 1998: ABCOM Information Systems Pvt.
// Ltd., All rights reserved.
//*************************************************************

//*************************************************************
// Реализация класса : TrafficLightImpl
// Унаследован от : _TrafficLightImplBase 
// Реализует   : None
// Описание:
// Класс TrafficLightImpl предоставляет реализацию 
// интерфейса CORBA 
// TrafficLight 
//*************************************************************

package traffic;
import TrafficLightServer;

public class TrafficLightImpl extends traffic._TrafficLightImplBase 
{
   //переменная, хранящая номер лампы 
   private short Number; 
/* Переменная LightServer, объявляемая ниже, сохраняет ссылку на объект сервера, создающий этот объект */
   private TrafficLightServer LightServer;
/* конструктор класса _ принимает ссылку на объект TrafficLightServer и номер лампы, который должен быть присвоен создаваемому объекту. */
   public TrafficLightImpl(TrafficLightServer LightServer,
      short LightNumber) 
   {
      super("Traffic Light" 
         + String.valueOf (LightNumber));
      this.LightServer = LightServer;
      
/* печать сообщения на системную консоль для информирования пользователя о создании Светофора. */
      System.out.println ("Traffic Light #" 
         + String.valueOf (LightNumber)
         + " created");
      Number = LightNumber;
   }
   // конструктор без аргументов 
   public TrafficLightImpl() {
      super();
   }
   
/* метод SetColor вызывает метод SetColor сервера для задания состояния ламп */ 
   public void SetColor(java.lang.String color) 
   {
      LightServer.SetColor (color);
   }
   
   // метод изменения атрибута LightNumber
   public void LightNumber(short LightNumber) 
   {
      this.Number = LightNumber;
   }
   // метод доступа к атрибуту LightNumber 
   public short LightNumber() {
      return Number;
   }
}

Теперь обратимся к реализации класса TrafficLightServer (Листинг 3). Этот класс унаследован от класса Frame и является программой командной строки Java. Эта программа получает аргумент командной строки, определяющий номер создаваемого светофора . Метод init(), в зависимости от номера светофора, устанавливает местоположение фрейма. Для простоты представим , что у нас только 4 светофора. После этого, путем создания трех объектов класса TlightPanel, метод init() создает пользовательский интерфейс объекта Светофор.

Рисунок 2. Пользовательский интерфейс приложения контроллера светофоров.
Листинг 3: TrafficLightServer.java
//**************************************************************
// Файл: TrafficLightServer.java
// Этот файл содержит реализацию следующих классов
// 1. TrafficLightServer
// 2. TLightPanel
//**************************************************************

// Импортируем используемые классы
import traffic.TrafficLightImpl;
import traffic.TrafficCoordinator;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import java.awt.*;
import java.awt.event.*;

//**************************************************************
// Реализация класса: TrafficLightServer
// Унаследован от: Frame
// Реализаций : нет // Описание:
/* Класс TrafficLightServer определяет программу Java Command line. Сервер создает объект класса TrafficLightImpl, получает ссылку на объект TrafficCoordinator и с помощью координатора, вызовом метода TrafficCoordinator объекта координатора, регистрирует создаваемый объект Светофор */
//**************************************************************
public class TrafficLightServer extends Frame 
{
   // объявляются переменные TLightPanel для трех цветов. 
   TLightPanel RedLight, AmberLight, GreenLight;

   // конструктор класса 
   public TrafficLightServer (String[] args)
   {
      super ("# " + args[0]);
      init (args);
   }
   
   // здесь начинается выполнение программы    
   // первый аргумент командной строки определяет номер лампы. 
   static public void main (String[] args)
   {
      if (args[0] == null)
      {
         System.out.println (
            "Usage: Java TrafficLightServer LightNumber");
         System.exit(0);
      }
     
      // создается экземпляр класса сервера
      TrafficLightServer obj = new TrafficLightServer (args);
      try
      {
         // получаем ссылка на ORB 
         ORB orb = ORB.init (args, null);
         // создаем реализацию объекта Светофора
         TrafficLightImpl TL = new TrafficLightImpl(
            obj, (short)Integer.parseInt(args[0]));
         // Экспорт вновь созданного объекта orb.connect(TL);
         // Получение ссылки на сервис Naming service 
         org.omg.CORBA.Object nameServiceObj = 
         orb.resolve_initial_references("NameService");
         if (nameServiceObj == null)
         {
            System.out.println("nameServiceObj = null");
            return;
         }

         // проводится приведение типа
         NamingContext nameService = 
            NamingContextHelper.narrow (nameServiceObj);
         if (nameService == null)
         {
            System.out.println("nameService = null");
            return;
         }
         // Нахождение и регистрация с помощью координатора
         NameComponent[] Coordinator = 
            {new NameComponent("Coordinator", "")};
         traffic.TrafficCoordinator MyCoordinator =
             traffic.TrafficCoordinatorHelper.narrow(
                    nameService.resolve(Coordinator));
         if (MyCoordinator == null)
            System.out.println ("could not locate Coordinator");       
         MyCoordinator.Register (TL);
         
         // ожидание завершения текущего потока
         Thread.currentThread().join();
      }
      catch (org.omg.CORBA.SystemException e)
      {
         System.err.println (e);
      }
      catch(Exception e)
      { 
         System.err.println(e);
      }
   }
   public void init(String[] args)
   {
      // определение местоположения фрейма светофора
      int x=0, y=0;
      
      // здесь рассмотрен случай, когда светофоров 4
      switch (Integer.parseInt (args[0]))
      {
      case 1:
         x=0; y=0;
         break;
      case 2:
         x=125; y=0;
         break;
      case 3:
         x=250; y=0;
         break;
      case 4:
         x=375; y=0;
         break;
      }
      // задание размера и местоположения фрейма приложения
      setBounds (x, y, 5, 300);
      setLayout (new GridLayout (3, 1, 10, 10));
      
      // создаются три объекта панелей, 
      // которые добавляются к контейнеру
      RedLight = new TLightPanel (Color.red);
      AmberLight = new TLightPanel (Color.orange);
      GreenLight = new TLightPanel (Color.green);
      add (RedLight);
      add (AmberLight);
      add (GreenLight);
      
      // устанавливается listener окна
      addWindowListener (new WindowAdapter ()
         {
            public void windowClosing (WindowEvent evt)
            { System.exit(0); }
         }
      );

      // показывается фрейм
      show();
      // включение красного цвета 
      SetRedLight();
   }
   
   // Метод SetColor получает строку, описывающую
   // цвет лампы. В зависимости от значения полученной строки, 
   // Он вызывает соответствующий метод Set... 

   public void SetColor (String color)
   {
      if (color.equals ("Red"))
         SetRedLight();
else if (color.equals ("Amber"))
         SetAmberLight();
      else if (color.equals ("Green"))
         SetGreenLight();
      repaint();
   }
   
   // Метод SetRedLight() вызывает метод SetColor() для 
   // каждого объекта Лампа и включает красную лампу, отключая
   // две остальных лампы

   private void SetRedLight()
   {
      RedLight.SetColor (Color.red);
      AmberLight.SetColor (Color.darkGray);
      GreenLight.SetColor (Color.darkGray);
   }
   
   // Метод SetAmberLight() вызывает метод SetColor()
   // для каждого объекта Лампа и включает желтый цвет,
   // отключая две остальных лампы

   private void SetAmberLight()
   {
      RedLight.SetColor (Color.darkGray);
      AmberLight.SetColor (Color.orange);
      GreenLight.SetColor (Color.darkGray);
   }
   // Метод SetGreenLight() вызывает метод SetColor()
   // для каждого объекта Лампа и включает зеленый цвет,
   // отключая две остальных лампы    
   private void SetGreenLight()
   {
      RedLight.SetColor (Color.darkGray);
      AmberLight.SetColor (Color.darkGray);
      GreenLight.SetColor (Color.green);
   }
}

// класс TLightPanel является расширением класса Panel и
// используется для отображения лампы. 
class TLightPanel extends Panel
{
   final int MARGIN=10;
   Color color;

   //конструктор класса 
   public TLightPanel(Color color)
   {
      super();
      this.color = color;
   }
   
   // создается и заполняется нужным цветом лампа. 
   public void paint (Graphics g)
   {
      Rectangle r = getBounds();
      setBackground (Color.lightGray);
      g.draw3DRect (2, 2, r.width-4, r.height-4, false);
      g.setColor (color);
      g.fillOval 
          (MARGIN,MARGIN,r.width-2*MARGIN,r.height-2*MARGIN);
   }
   
   // Задание цвета лампы
   public void SetColor (Color color)
   {
      this.color = color;
      repaint();
   }
}

Иерархия объектов пользовательского интерфейса представлена на рис. 3.

Класс TLightPanel унаследован от класса Panel и создает на панели лампу нужного цвета.

Рис.3. Иерархия объектов пользовательского интерфейса TrafficLightServer

После создания трех объектов TLightPanel и добавления их к контейнеру , метод init() класса TrafficLightServer устанавливает listener событий окна и по умолчанию устанавливает красный цвет, вызывая метод SetRedLight(). После того, как пользовательский интерфейс создан, метод main() класса TrafficLightServer разрешает ссылку на CORBA Naming Service и находит объект-координатор . С помощью координатора вызовом метода Register() он регистрирует созданный Светофор. После этого программа ожидает, когда контроллер произведет вызов callback-метода.

Класс TrafficCoordinatorImpl предоставляет реализацию интерфейса CORBA TrafficCoordinator. Исходный код класса TrafficCoordinatorImpl продемонстрирован в Листинге 4.

Листинг 4. TrafficCoordinatorImpl.java
//*************************************************************
// Файл: TrafficCoordinatorImpl.java
// Этот файл содержит реализации следующих классов
// 1. TrafficCoordinatorImpl
//*************************************************************

package traffic;

// Импорт используемых классов
import java.util.*;

//**************************************************************
// Реализация класса : TrafficCoordinatorImpl
// Унаследован от : _TrafficCoordinatorImplBase
// Реализации        : None
// Описание          :
/* TrafficCoordinatorImpl предоставляет реализацию интерфейса TrafficCoordinator, определенного в нашей CORBA IDL. Класс унаследован от класса _TrafficCoordinatorImplBase, который сгенерирован idl2java. Экземпляр этого класса работает как координатор объектов Светофор. Каждый объект Светофор регистрируется координатором. Для управления различными светофорами регулировщик использует координатор. */
//**************************************************************

public class TrafficCoordinatorImpl extends traffic._TrafficCoordinatorImplBase 
{
   // объявление вектора для хранения ссылок на объекты Светофоры
   private Vector LightsList = new Vector();
   
   // конструктор класса
   public TrafficCoordinatorImpl(java.lang.String name) 
   {
      super(name);
      System.out.println ("Traffic Coordinator object created");
   }

   // конструктор без аргумента 
   public TrafficCoordinatorImpl() 
   {
      super();
   }
/* Этот метод будет вызываться объектом Светофор (TrafficLight). Метод принимает ссылку на объект Светофор, который должен быть зарегистрирован координатором. Метод сохраняет ссылку  на объект в локальном векторе. */
   public void Register(traffic.TrafficLight light) 
   {
      // добавление к локальному вектору
      LightsList.addElement (light);
      // печать сообщения для пользователя
      System.out.println ("Registered Traffic Light # " + 
                          String.valueOf (light.LightNumber()));
   }
   
/* Метод SetColor вызывается Регулировщиком. Метод получает номер светофора и нужный цвет. Метод выполняет поиск данного номера светофора в списке зарегистрированных светофоров. При нахождении номера, для задания нужного цвета этот метод вызывает метод SetColor для объекта Светофор. */

   public void SetColor(short LightNumber, 
      java.lang.String color) 
   {
      // Итерация списка зарегистрированных светофоров
      for (int i=0; i<LightsList.size(); i++)
      {
         TrafficLight light =
             (TrafficLight)LightsList.elementAt(i);
         // если светофор найден, вызывается ее метод SetColor.
         if (light.LightNumber() == LightNumber)
         {
            light.SetColor (color);        
            break;
         }
      }
   }
 
/* метод изменения атрибута LightsList. Поскольку данному приложению этот атрибут изменять не нужно, этот метод не реализован */ 
   public void lights(traffic.TrafficLight[] lights) {
      // запись реализации атрибута...
   }

   // метод доступа к атрибуту LightsList. Этот метод возвращает
   // массив объектов типа TrafficLight.

   public traffic.TrafficLight[] lights() {
      // создание массива для сохранения объектов Светофор 
      traffic.TrafficLight[]  lightsList = 
            new traffic.TrafficLight[LightsList.size()];
      
      // инициализация каждого элемента массива

      for (int i=0;  i<LightsList.size(); i++)
         lightsList[i] = 
             (traffic.TrafficLight)LightsList.elementAt(i);
      
      // возврщаем массива.
      return (lightsList);
   }
}

Класс объявляет переменную LightsList типа Vector. Эта переменная используется для хранения списка светофоров , объявленного в IDL как sequence. При успешном создании объекта конструктор класса выдает соответствую щее сообщение . Метод доступа к атрибутам светофоров копирует каждый элемент списка в массив объектов TrafficLight и возвращает этот массив. Метод Register() получает ссылку на объект Светофор и запоминает его в массив. Затем, при успешной регистрации светофора , этот метод выдает соответствующее сообщение . Метод SetColor() вызывается приложением Регулировщик . Этот метод получает два параметра _ номер светофора и устанавливаемый цвет. Заметим, что параметр цвета передается как Java String, а не как объект класса Java Color.

CORBA не предоставляет тип данных Color. Поэтому, при использовании в Java класса Color, потребуется реализовать как приложение Регулировщик , так и приложение Координатор . Метод SetColor() просматривает весь список зарегистрированных объектов Светофор, и номер каждого из этих объектов сверяется с номером, полученным в качестве параметра . Если номера совпадают , для данного объекта вызывается метод SetColor(). Классом TrafficCoordinatorServer создается экземпляр класса TrafficCoordinatorImpl.

Исходный код класса TrafficCoordinatorServer дается в Листинге 5. В этом классе метод main() инициализирует ORB, создает объект координатора и экспортирует его в ORB. Затем этот метод получает ссылку на Naming Service и связывает созданный объект с Naming Service с именем Coordinator. Объекты TrafficLight и TrafficCop, используя это имя, находят координатора , после чего координатор просто ожидает вызова.

Листинг 5: TrafficCoordinatorServer.java
//**************************************************************
// Файл: TrafficCoordinatorServer.java
// Этот файл содержит реализацию следующих классов
// 1. TrafficCoordinatorServer
//**************************************************************

// Импорт используемых классов
import traffic.TrafficCoordinatorImpl;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;

//**************************************************************
// Реализация класса: TrafficCoordinatorServer
// Унаследован от: нет
// Реализует: Нет
// Описание:
/* Класс TrafficCoordinatorServer реализует Java-программу командной строки. Сервер создает объект типа  TrafficCoordinatorImpl class и регистрирует объект с помощью CORBA Naming service, используя для объекта имя "Coordinator". После регистрации объекта, программа просто ожидает вызова. */
//**************************************************************

public class TrafficCoordinatorServer
{
   // здесь начинается выполнение программы 
   static public void main (String[] args)
   {
      try
      {
         // получена ссылка на ORB.
         ORB orb = ORB.init (args, null);
         // создание объекта реализации класса координатора
         TrafficCoordinatorImpl TC = 
             new TrafficCoordinatorImpl("TrafficCoordinator");
         // Экспорт нового объекта 
         orb.connect(TC);

         // Получение ссылки на Naming service
         org.omg.CORBA.Object nameServiceObj =
               orb.resolve_initial_references("NameService");
         if (nameServiceObj == null)
         {
            System.out.println("nameServiceObj = null");
            return;
         }
          
         // Приведение типа
         NamingContext nameService =
               NamingContextHelper.narrow(nameServiceObj);
         if (nameService == null)
         {
            System.out.println("nameService = null");
            return;
         }
         // связывание объекта Coordinator с Naming service 
         NameComponent[] Name = 
              {new NameComponent("Coordinator", "")};
         nameService.rebind(Name, TC);

         // Ожидание завершения текущего потока 
         Thread.currentThread().join();
      }
      catch (org.omg.CORBA.SystemException e)
      {
         System.err.println (e);
      }
      catch(Exception e)
      { 
         System.err.println(e);
      }
   }
}

Наконец, класс TrafficCop создает приложение-регули ровщика. Это приложение находит нужный объект-коор динатор и получает от координатора список зарегистри рованных объектов. После этого пользовательский интерфейс показывает все светофоры , причем первоначально все они светят красным светом. Иерархия объектов для пользовательского интерфейса представлена на рис. 4.

Пользовательский интерфейс позволяет щелкнуть мышью на любом из объектов Светофор для выбора нужного цвета. После выбора статуса каждого объекта Светофор, пользователь для установки нужного статуса различным светофорам нажимает на кнопку Set.

Метод main() класса TrafficCop инициализирует ORB и получает ссылку на Naming Service. После этого он находит объект Координатор и получает на него ссылку. Вызовом метода доступа lights() для объекта Координатор , программа получает от координатора массив объектов Светофор. После этого метод main() создает экземпляр класса TrafficCop. Для создания пользовательского интерфейса приложения , конструктор TrafficCop вызывает его метод init(). Метод init() создает объект класса TrafficBasePanel и объект Кнопку, и, с помощью BorderLayout-менеджера , добавляет эти два компонента в контейнер . Затем программа устанавливает listener событий окна и отрисовывает окно.

Класс TrafficBasePanel унаследован от класса Panel. Конструктор классов определяет , какое число объектов Светофор получено объектом-регулировщиком , и создает равное ему число объектов TrafficLightsPanel.

MAXPANELS = Cop.Lights.length; 
LightsPanel = new TrafficLightsPanel[MAXPANELS]; 

Каждый объект TrafficLightsPanel отображает светофор, состоящий из трех объектов Ламп. Конструктор классов вызывает метод init(), который заставляет менеджер компоновки отображать четыре световых панели и добавляет их к базовой панели. Для простоты представим себе, что у нас снова четыре светофора .

Как только пользователь нажимает кнопку Set, объектом TrafficCop вызывается метод Update() класса TrafficBasePanel. Это приводит к обновлению всех панелей. Метод Update() повторяется для всех отображаемых световых панелей, получая установки цвета для каждой из них, и вызывает для каждого объекта Лампа метод SetColor().

   for (int i=0; i<MAXPANELS; i++) 
   { 
      Cop.Lights[i].SetColor (LightsPanel[i].clr); 
   }  

Класс TrafficBasePanel может хранить до четырех объектов TrafficLightsPanel. Конструктор класса TrafficLightsPanel для создания пользовательского интерфейса вызывает метод init(). Метод init() создает три объекта LightPanel _ для красного, желтого и зеленого цветов, а затем добавляет их в контейнер . После этого для установки красного цвета по умолчанию , он вызывает метод SetRedLight(). Метод SetRedLight() устанавливает для красных ламп красный цвет, а для двух остальных ламп _ серый. Аналогично , два других метода (SetAmberLight() и SetGreenLight()) устанавливают желтый и зеленый цвета соответственно , а остальные лампы заполняют серым. Класс TrafficBasePanel реализует интерфейс MouseListener. В событии mousePressed() переменной clr (типа string) присваивается соответствующее значение цвета, в зависимости от того, на каком объекте LightPanel был сделан щелчок мыши. Кроме того, для задания нужного цвета светофора обработчик событий вызывает соответствующий метод set(). К примеру, при щелчке на красной лампе загорается красный свет, а остальные две лампы окрашиваются серым.

Рис. 4. Иерархия объектов для пользовательского интерфейса TrafficCop.

Наконец, класс LightPanel используется для отображения отдельных ламп - красной, желтой или зеленой. Конструктор класса получает ссылку на объект TrafficLightsPanel, и теперь ему смогут передаваться события мыши. Второй параметр определяет цвет лампы. В зависимости от этого параметра , на лампе печатается название цвета - красный, желтый или зеленый. Метод paint() осуществля ет визуализацию светофора . При любом обновлении приложения регулировщика нажатием на кнопку Set вызывается метод SetColor(). Этот метод копирует полученный цвет в локальную переменную класса, и для отображения изменений выполняет перекрашивание .

Компиляция приложения

Как говорилось ранее, для разработки и тестирования приложения использовался Visigenic VBroker for Java. Первым шагом при создании приложения является трансляция IDL-кода на Java с помощью утилиты idl2java, которая поставляется Visigenic. Для этого используется следующая команда:

idl2java _no_tie _no_comments traffic.idl 

Для компиляции нескольких классов Java, обсуждавших ся ранее и приведенных в листингах 2 - 6, советуем воспользоваться файлом make.bat, приведенным в Листинге 7. Он создает все необходимые файлы приложения . Следующим действием будет запуск приложения .

Запуск приложения

Для запуска приложения запустите сервис osagent, а также CORBA Naming Service. Эти сервисы запускаются следующими двумя командами :

start osagent -C 
vbj -DORBservices=CosNaming -DSVCnameroot=TRAFFIC -DJDKrenameBug com.visigenic.vbroker.services.CosNaming.ExtFactory TRAFFIC namingLog 

Затем с помощью следующей командной строки запустите координатора :

start vbj -DORBservices=CosNaming -DSVCnameroot=TRAFFIC -DOAid=TSession TrafficCoordinatorServer 

Следующая команда создаст светофоры :

vbj -DORBservices=CosNaming -DSVCnameroot=TRAFFIC -DOAid=TSession TrafficLightServer 1 

Создается Светофор 1, который регистрируется координатором . В окне координатора будет напечатано соответствующее сообщение . Точно так же создайте еще три светофора , заменяя параметр командной строки соответствующим номером для каждого светофора .

На этой стадии на экране должно быть отображено четыре светофора (см. рис. 2). Затем запустите приложение -регулировщика :

vbj -DORBservices=CosNaming -DSVCnameroot=TRAFFIC -DOAid=TSession TrafficCop 

Пользовательский интерфейс приложения-регулиров щика показан на рис. 2. Для установки нужного цвета щелкните по соответствующей лампе и нажмите на кнопку Set. На четырех объектах будет включен соответствующий свет.

Для работы светофоров нами не предусматривалось наличие метода удаления регистрационной записи. Поэтому при закрытии светофора и повторном его создании, координатор зарегистрирует две копии. Если повторно запустить приложение-регулировщик, на его панели будут показаны обе копии. Чтобы избежать подобной ситуации, закройте Координатор и Регулировщик и перезапустите приложение, если любой из светофоров должен быть закрыт.

Листинг 6: TrafficCop.java
//**************************************************************
// Файл : TrafficCop.java
// Этот файл содержит реализации следующих классов 
// 1. TrafficCop
// 2. TrafficBasePanel
//**************************************************************

// Импорт используемых классов
import traffic.TrafficLightImpl;
import traffic.TrafficCoordinator;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import java.awt.*;
import java.awt.event.*;

//**************************************************************
// Реализация класса : TrafficCop
// Унаследован  : Frame
// Реализует   : ActionListener
// Описание   :
// Этот класс управляет различными светофорами перекрестка 
// с помощью координатора. Пользовательский интерфейс
// отображает доступные светофоры. Пользователь
// выбирает нужные лампы и нажимает на кнопку Set.
//**************************************************************

class TrafficCop extends Frame 
implements ActionListener 
{
   // сохранение ссылки на регулировщика 
   private static traffic.TrafficCoordinator MyCoordinator;
   // кнопка Set
   private Button SetButton;
   // массив для хранения ссылок на объекты Светофор
   public static traffic.TrafficLight[] Lights;
   // базовая панель
   private TrafficBasePanel basePanel;
   
   // конструктор класса
   public TrafficCop()
   {
      super("Traffic Cop");
      init();
      // установка размера и местоположения
      setBounds(0,0, 200, 300);
   }
   
   // здесь начинается выполнение программы
   static public void main(String[] args)
   {
      try
      {
         // инициализация ORB
         ORB orb = ORB.init(args, null);
         
         // Получение ссылки на Naming service
         org.omg.CORBA.Object nameServiceObj =
            orb.resolve_initial_references("NameService");

         if(nameServiceObj == null)
         {
            System.out.println("nameServiceObj = null");
            return;
         }
         
         // приведение типа ссылки
         NamingContext nameService =
            NamingContextHelper.narrow(nameServiceObj);
         if(nameService == null)
         {
            System.out.println("nameService = null");
            return;
         }
         
         // Поиск объекта Coordinator 
         NameComponent[] Coordinator = 
         {new NameComponent("Coordinator", "")};
         
         traffic.TrafficCoordinator t = 
            traffic.TrafficCoordinatorHelper.narrow(
            nameService.resolve(Coordinator));
         if(t == null)
            System.out.println("could not locate Coordinator");
         
         // получение списка зарегистрированных объектов
         Lights = t.lights();
      }
      catch(org.omg.CORBA.SystemException e)
      {
         System.err.println(e);
      }
      catch(Exception e)
      { 
         System.err.println(e);
      }
      
      // Создание экземпляра класса нашего приложения
      new TrafficCop();
   }
   
   // init() - обеспечивает инициализацию нашего приложения
   public void init()
   {
      // создаются кнопка set button и set action listener
      SetButton = new Button("Set");
      SetButton.addActionListener(this);
      // создается базовая панель объекта
      basePanel = new TrafficBasePanel(this);
      // добавление компонентов в контейнер
      add("Center", basePanel);
      add("South", SetButton);
      
      // установка listener'а окна
      addWindowListener(new WindowAdapter()
      {
         public void windowClosing(WindowEvent evt)
         {
            System.exit(0);
         }
      });
      
      // создание пакета и отображение фрейма
      pack(); 
      show();
   }
   
   // выполнение действий, сгенерированных кнопкой Set
   public void actionPerformed(ActionEvent evt)
   {
      if(evt.getSource() == SetButton)
      {
         // обновление базовой панели для отображения
         // изменений цветов
         basePanel.update();
      }
   }
}
//**************************************************************
// Реализация класса: TrafficBasePanel
// Унаследован от   : Panel
// Реализации   : None
// Описание   :
/* Класс TrafficBasePanel реализует панель для отображения панелей отдельных ламп всех зарегистрированных светофоров */
//**************************************************************

class TrafficBasePanel extends Panel
{
   private int MAXPANELS;
   private TrafficCop Cop;
   private TrafficLightsPanel[] LightsPanel;
   
   // Метод update перебирает все отображаемые световые панели и 
   // для каждой вызывает метод SetColor.
   public void update()
   {
      for(int i=0; i<MAXPANELS; i++)
      {
         Cop.Lights[i].SetColor(LightsPanel[i].clr);
      }    
   }
   
   // конструктор класса
   public TrafficBasePanel(TrafficCop Cop)
   {
      super();
      this.Cop = Cop;
      // присвоение переменной числа ламп,
      // полученного регулировщиком.
      MAXPANELS = Cop.Lights.length;
      // создание световых панелей, эквивалентных 
      // числу ламп
      LightsPanel = new TrafficLightsPanel[MAXPANELS];
      // инициализация 
      init();
   }
   
   // метод init() инициализирует пользовательский интерфейс 
   private void init()
   {
      // Максимальное число светофоров - 4.
      setLayout(new GridLayout(1, 4, 10, 10));
      // создание световых панелей и добавление их в контейнер. 
      for(int i=0; i<MAXPANELS; i++)
      {
         LightsPanel[i] = new TrafficLightsPanel();
         add(LightsPanel[i]); 
      }
   }
}
//**************************************************************
// Реализация класса: TrafficLightsPanel
// Унаследован от  : Panel
// Реализации   : MouseListener
// Описание   :
/* Класс TrafficLightsPanel создает три объекта LightPanel и отображает их. Когда пользователь щелкает на любой из ламп, класс вызывает событие и задает строковую переменную цвета. После этого, для задания цветов ламп, вызывается соответствующий метод Set. */
//**************************************************************

class TrafficLightsPanel extends Panel implements MouseListener 
{
   // переменные для трех объектов LightPanel 
   LightPanel RedLight, AmberLight, GreenLight;
   // переменная, хранящая текущее состояние цветофора, по 
   // умолчанию - красный
   String clr = "Red";
   
   // конструктор класса
   public TrafficLightsPanel()
   {
      init();
   }
   
   private void init()
   {
      setBackground(Color.lightGray);
      setLayout(new GridLayout(3, 1, 10, 10));
      // создание и добавление трех объектов LightPanel 
      RedLight = new LightPanel(this, Color.red);
      AmberLight = new LightPanel(this, Color.orange);
      GreenLight = new LightPanel(this, Color.green);
      add(RedLight);
      add(AmberLight);
      add(GreenLight);
      // установка красного цвета по умолчанию 
      SetRedLight();
   }
   
   // закраска объектов.
   private void SetRedLight()
   {
      RedLight.SetColor(Color.red);
      AmberLight.SetColor(Color.darkGray);
      GreenLight.SetColor(Color.darkGray);
   }
   
   private void SetAmberLight()
   {
      RedLight.SetColor(Color.darkGray);
      AmberLight.SetColor(Color.orange);
      GreenLight.SetColor(Color.darkGray);
   }
   
   private void SetGreenLight()
   {
      RedLight.SetColor(Color.darkGray);
      AmberLight.SetColor(Color.darkGray);
      GreenLight.SetColor(Color.green);
   }
   
   // обработка события "щелчок мыши"
   public void mousePressed(MouseEvent evt) 
   {
      // в зависимости от источника события 
      // "щелчок мыши",установить подходящий цвет
      
      if(evt.getSource() == RedLight)
      {
         clr = "Red";
         SetRedLight();
      }
      if(evt.getSource() == AmberLight)
      {
         clr = "Amber";
         SetAmberLight();
      }
      if(evt.getSource() == GreenLight)
      {
         clr = "Green";
         SetGreenLight();
      }
   }
   
   public void mouseExited(MouseEvent evt) {}
   public void mouseClicked(MouseEvent evt) {}
   public void mouseReleased(MouseEvent evt) {}
   public void mouseEntered(MouseEvent evt) {}
}
// класс LightPanel отображает один светофор 
class LightPanel extends Panel
{
   private final int MARGIN=10;
   private Color color;
   private String str = "";
   
   // конструктор класса
   public LightPanel(TrafficLightsPanel LightsPanel, Color color)
   {
      super();
      // задание цвета
      this.color = color;
      if(color == Color.red)

 

         str = "Red";
      else if(color == Color.orange)
         str = "Amber";
      else if(color == Color.green)
         str = "Green";
      // установка listener'а мыши
      addMouseListener(LightsPanel);
   }
   // отрисовка лампы
   public void paint(Graphics g)
   {
      Rectangle r = getBounds();
      setBackground(Color.lightGray);
      g.draw3DRect(2, 2, r.width-4, r.height-4, false);
      g.setColor(color);
      g.fillOval(MARGIN,MARGIN, r.width-2*MARGIN, 
         r.height-2*MARGIN);
      FontMetrics tm = this.getFontMetrics(this.getFont());
      if(color == Color.darkGray)  
         g.setColor(Color.white);
      else
         g.setColor(Color.black);
      g.drawString(str, 
         (int)(r.width/2.0 - tm.stringWidth("Red")/2.0),
         (int)(r.height/2.0));
   }

   // задание конкретного цвета 
   public void SetColor(Color color)
   {
      this.color = color;
      repaint();
   }
}
Листинг 7. make.bat
javac -d . TrafficLightImpl.java 
javac TrafficLightServer. 
javajavac -d . TrafficCoordinatorImpl.java 
javac TrafficCoordinatorServer.java 
javac TrafficCop.java 

Заключение

Callback-вызовы в CORBA позволяют серверу CORBA вызывать метод клиента. Обратные вызовы реализуются благодаря сохранению ссылок на клиентские объекты в классе сервера. Объект сервера отвечает за отслежива ние подобных ссылок и за запуск с их помощью любого public-метода нужного клиента. Обратные вызовы весьма полезны в ситуациях, моделирующих реальную жизнь, когда необходимо проинформировать клиента о наступлении определенного события или о завершении процесса на сервере.


Copyright © 1994-2016 ООО "К-Пресс"