Март 2005


Профессионалу разработчику


Робин Зиммерманн

Oracle Forms в мире SOA
(Oracle Forms in the SOA World
By Robin Zimmermann)

Источник: журнал Oracle Magazine, no.2, 2005,
http://www.oracle.com/technology/oramag/oracle/05-mar/o25forms.html

Oracle Forms могут быть частью обслуживающей архитектуры.

Сегодня производство подчинено необходимости во взаимосвязи множества разнородных систем и приложений. Эта необходимость интеграции является основной движущей силой после выбора Web-сервисов и обслуживающей архитектуры (service-oriented architecture - SOA) при производственном планировании для уменьшения стоимости и сложности интеграции. Business Process Execution Language (BPEL) быстро стал индустриальным стандартом для такой интеграции, а Oracle BPEL Process Manager предоставляет обширную и легко используемую инфраструктуру для создания, применения и управления бизнес-процессами. Поскольку в Oracle Forms имеется поддержка Java, они могут участвовать в любом потоке процессов BPEL.

Oracle Forms взаимодействует с Oracle BPEL Process Manager с помощью Java Importer, средства, которое позволяет серверным Java-классам увидеть приложение Oracle Forms. Java Importer импортирует Java-классы в модули Oracle Forms, генерируя PL/SQL-обработчики для выбранных классов. Затем PL/SQL можно использовать в любом месте приложения Oracle Forms, и исполняемый модуль Oracle Forms будет передавать эти PL/SQL-вызовы в Java-классы, которые были импортированы.

Эта статья иллюстрирует, как одно простое приложение Oracle Forms может взаимодействовать с Oracle BPEL Process Manager.

Описание достоинств BPEL и почему его рекомендуется использовать, читайте в статье Майка Леманна "Weaving Web Services Together" в выпуске Oracle Magazine за июль/август 2004.

Приложение этой статьи основано на демонстрационном приложении под названием Loan Flow Plus, которое является частью установки Oracle BPEL. Цель – создание простого бизнес-процесса (как показано на Рис.1) для определения наилучшей годовой процентной ставки для предполагаемого получателя займа, После того, как получатель предоставит свои данные, Oracle BPEL Process Manager находит персональный номер Social Security (SSN) и получает по нему кредитный рейтинг претендента. Затем Oracle BPEL запрашивает услугу по предоставлению займа в двух банках от имени получателя, чтобы увидеть, какую ежегодную процентную ставку (annual percentage rate – APR) предложить за услугу предоставления займа. Затем он выбирает наилучший займ и докладывает получателю о наиболее подходящей ставке.


Рис 1: Поток процессов для приложения Loan Flow Plus

Демонстрационное приложение Loan Flow Plus иллюстрирует следующие достоинства BPEL:

  • Интегрирование различных производителей, использующих различные реализации
  • Обработка асинхронных вызовов Web-сервиса
  • Вызов Web-сервиса, который выполняется продолжительное время и который затем должен вернуться в исходный Web-сервис
  • Запуск параллельных процессов вместо последовательных процессов

Исходное демонстрационное приложение Loan Flow Plus (установленное в домашней директории Oracle BPEL Process Manager) использует JavaServer Pages (JSP) для взаимодействия с потоком процессов BPEL. Однако нет причин для того, чтобы Oracle Forms не мог взаимодействовать с потоком процессов BPEL, поэтому мы возьмем один из экранов исходного демонстрационного приложения и для него будем использовать Oracle Forms. Далее описано, как реализовано демонстрационное приложение:

Как видно из Рис.1, Oracle BPEL Process Manager применяется для займов как из Star Loan Bank, так и из United Loan Bank. Услуга United Loan Bank автоматизирована и быстро возвращает результат в BPEL Process Manager. Но Star Loan Bank требует ручного вмешательства для подтверждения займа. Рис. 2 показывает приложение на Oracle Forms для подтверждения, которое используется служащими Star Loan Bank, где имеется займ Дэйва, требующий подтверждения годовой ставки 7.4%.


Рис 2: Форма подтверждения займа в Star Loan Bank

Заметьте, что форма на Рис.2 не запрашивает никакой информации из базы данных; и действительно, это демонстрационное приложение даже не соединено ни с какой базой данных. Когда пользователь нажимает кнопку Refresh, Oracle Forms обращается к Java Virtual Machine (JVM) для вызова Java-классов, которые, в свою очередь, обращаются к Oracle BPEL Process Manager для получения информации, отображаемой на экране. Таким же образом, когда пользователь щелкает по кнопке Approve или Reject, Oracle Forms обращается к JVM, которая уведомляет Oracle BPEL Process Manager отказано ли в займе или он подтвержден (и с каким годовым процентом).

Рис. 3 показывает обзор верхнего уровня взаимодействия между Oracle Forms и Oracle BPEL Process Manager. Oracle Forms автоматически запускает JVM при первом обращении к Java. Oracle BPEL Process Manager – это ядро, которое управляет потоком процессов и взаимодействием с различными Web-сервисами и другими процессами.


Рис. 3: Обзор высокоуровневой архитектуры

Интерфейс Web-сервисов или Java API

Любой BPEL-процесс, применяемый к Oracle BPEL Process Manager доступен через интерфейс Web-сервиса или через Java API.

Интерфейс Web-сервиса использует стандартное SOAP-взаимодействие и может использоваться клиентскими приложениями, которые реализованы на любом языке. С другой стороны, если клиентские приложения реализованы на Java, то взаимодействие с Oracle BPEL Process Manager может осуществляться через Java API. У Java API есть два преимущества: производительность выше, чем у Web-сервисов, а бизнес-процессы могут обрабатываться с применением транзакционного механизма.

Oracle Forms может легко взаимодействовать с любыми Web-сервисами через возможности Java Importer. Статьи о вызове Web-сервисов из Oracle Forms можно найти в oracle.com/technology/products/forms/techlisting9i.html и oracle.com/technology/products/forms/techlisting10g.html. Кроме того, в oracle.com/technology/sample_code/products/forms можно скачать демонстрационное приложение (включая исходный код), в котором Oracle Forms вызывает Web-сервис.

Далее эта статья показывает взаимодействие Oracle Forms с Oracle BPEL Process Manager с помощью использования самых последних продуктов Java API. Скачать полное демонстрационное приложение, которое описывается в этой статье можно из oracle.com/technology/sample_code/products/forms.

Шаг 1: Создание Java для вызова BPEL. Так как Oracle BPEL имеет Java API и из Oracle Forms можно вызывать Java, то можно сделать так, чтобы приложение на Oracle Forms взаимодействовало непосредственно с Oracle BPEL Process Manager. Однако, для взаимодействия недостаточно только встраивания некоторых методов на Java API: необходимо, например, еще манипулировать XML-сообщениями, которые надо будет передавать или получать от API. Поэтому имеет смысл написать немного на Java, чтобы она располагалась между Oracle Forms и Oracle BPEL Process Manager и передавала в Oracle Forms только необходимую информацию. Поэтому необходимо создать Java-класс для экрана Star Loan Bank, который позволит Oracle Forms выполнить следующее:

  1. Запросить список займов, ожидающих подтверждения служащим
  2. Подтвердить конкретный займ и установить годовую процентную ставку
  3. Отклонить конкретный займ

Ниже показана часть Java-класса, LoanFlowPlusForms, который создается и затем импортируется в Oracle Forms, демонстрирующий методы для пунктов A, B и C, перечисленным выше:

public class LoanFlowPlusForms 
{
  public static String[] 
getLoanApplicationList(String pEmail)
  {
    ...
  }
  public static void 
approveLoan(String pTaskId, double pApr)
  {
   ...
  }
  public static void 
rejectLoan(String pTaskId)
  {
   ...
  }
}

Полный код класса LoanFlowPlusForms можно увидеть на Листинге 3.

Метод getLoanApplicationList() имеет параметр pEmail, адрес электронной почты служащего по займам в Star Loan Bank — каждый пользователь (служащий по займам) должен видеть только те займы, которые необходимо подтвердить. Методы approveLoan() и rejectLoan() имеют параметр pTaskId, который представляет собой ID займа, который подлежит подтверждению или отклонению. Метод approveLoan() имеет еще один параметр для годовой процентной ставки займа.

Метод getLoanApplicationList() возвращает массив строк в следующем формате:

<taskId>|<customerName>|<ssn>|
<carModel>|<carYear>|<vloanAmount>

Oracle Forms может разобрать эти строки и отобразить данные на экране. Следующий пример показывает данные, возвращенные методом getLoanApplicationList().

1466|Kris|bullit@cs.com|987-65-432|
583|HondaHybrid|2004|28000.0
1554|Dave|demo1@otn.com|123-12-1234|
560|BMW Z8|2001|100000.0
1893|Jonas|chef@bork.com|135-246-78|
517|Ford Freestar|1999|12000.0

Рис. 2 показывает, как эти данные отображаются в исполняемой форме демонстрационного приложения.

Шаг 2: Объединение Java-кода с приложением на Oracle Forms. Перед импортированием в Oracle Forms, Java-класс необходимо поместить в директорию доступа к классу, так, чтобы Oracle Forms Builder смог его найти. В Windows это можно сделать в реестре. Модифицируйте параметр FORMS90_BUILDER_CLASSPATH, добавив к нему путь, по которому находится файл с классом.

Запустите Oracle Forms Builder и создайте или откройте форму. Выберите Program->Import Java Classes... из меню, чтобы открыть Java Importer. Выберите путь к LoanFlowPlusForms, и нажмите кнопку Import, как показано на Рис. 4.


Рис. 4: Импорт Java-класса LoanFlowPlusForms

Java Importer проверяет Java-класс и создает PL/SQL-обработчик для него. В Oracle Forms Object Navigator вы увидите новый пакет, содержащий PL/SQL-версию созданных Java-методов, как показано на Рис. 5. Теперь эти процедуры и функции можно вызывать из любого места формы и передавать необходимые параметры.


Рис. 5: PL/SQL-обработчик для Java-класса

Далее создаем приложение Star Loan, которое просматривает детали займов, подлежащих подтверждению или отклонению.

Листинг 1 содержит код триггера WHEN-BUTTON-PRESSED для кнопки Refresh, который вызывает JVM. Java-класс возвращает массив объектов типа String, содержащий займы, которые следует подтвердить, как показано на Рис. 2. Вспомните, что метод getLoanApplicationList() имеет параметр, адрес электронной почты служащего Star Loan. Для простоты этот email жестко прописан в Листинге 1 как 'jsmith@starloan.com'.

Листинг 1: Код триггера WHEN-BUTTON-PRESSED


DECLARE
  loanList     		ORA_JAVA.JARRAY;
  listLength   		NUMBER;
  loanString   		VARCHAR2(255);
  delimiterPos 	NUMBER;

    - - Каждый займ имеет следующие поля.
  taskId       		VARCHAR2(50);
  customerName 	VARCHAR2(50);
  email        		VARCHAR2(50);
  ssn          		VARCHAR2(50);
  creditrating 		VARCHAR2(50);
  carmodel     	VARCHAR2(50);
  caryear      		VARCHAR2(50);
  loanamount   	VARCHAR2(50);
BEGIN
  - - Вызов Java для получения списка займов, 
  - - которым требуется подтверждение.
loanList := LoanFlowPlusForms.getLoanApplicationList('jsmith@starloan.com');

  - -  Сколько строк возвращено?
  listLength := ORA_JAVA.GET_ARRAY_LENGTH(myGlobal.loanList);

  - - Для каждой строки извлекаются атрибуты займа.
  for i in 1..listLength loop

     - - Получаем элемент i из массива строк.
    loanString := ORA_JAVA.GET_STRING_ARRAY_ELEMENT(loanList, i);

     - - Разбираем строку, разыскивая символ '|' 
     - - для получения очередного поля.
    delimiterPos := instr(loanString, '|');
    taskId       := substr(loanString, 1, delimiterPos-1);

    loanString   := substr(loanString, delimiterPos+1);
    delimiterPos := instr(loanString, '|');
    customerName := substr(loanString, 1, delimiterPos-1);

    - - Делаем то же самое для оставшихся полей
    ...

    - - Теперь у нас есть вся информация о займе, отображаем его на экране.
    ...

  end loop;
END;

Код для кнопки Approve показан на Листинге 2.

Код для кнопки Reject на Рис. 2 такой же, как и код на Листинге 2, за исключением того, что строка

LoanFlowPlusForms.approveLoan(jo, :applications.taskId, :applications.apr);

заменяется на строку:

LoanFlowPlusForms.rejectLoan(jo, :applications.taskId);

Листинг 2: Код для кнопки Approve:

DECLARE
  jo ora_java.jobject;
  ex ora_java.jobject;
  myAlert NUMBER;
BEGIN
  jo := LoanFlowPlusForms.new;

  clear_record;
END;

Теперь форма готова к запуску. Когда в запущенной форме пользователь нажимает на кнопку Refresh, Oracle Forms вызывает класс LoanFlowPlusForms, который взаимодействует с Oracle BPEL Process Manager для получения списка займов. Атрибуты займов извлекаются и отображаются на экране. Вы можете уведомить Oracle BPEL Process Manager о вашем решении, нажав кнопку Reject или назначив годовую процентную ставку и нажав кнопку Approve.

Эта статья затрагивает важные аспекты создания одного экрана демонстрационного приложения, хотя само приложение гораздо больше. Полное демонстрационное приложение можно скачать из oracle.com/technology/sample_code/products/forms.

Следующие шаги

Читайте
еще об Oracle BPEL
oracle.com/technology/products/ias/bpel
oracle.com/technology/oramag/oracle/web

еще об Oracle Forms
oracle.com/technology/products/forms/techlisting9i.html
oracle.com/technology/products/forms/techlisting10g.html

Скачайте
приложение, представленное в этой статье
Oracle BPEL Process Manager

Заключение

В этой статье вы увидели, как Oracle Forms взаимодействует с другой системой, которая имеет совершенно независимую реализацию. Oracle Forms Java Importer позволяет интегрировать Oracle Forms с Oracle BPEL Process Manager, и, таким образом, приложение Oracle Forms может взаимодействовать с другими приложениями в вашей промышленной среде.

Робин Зиммерманн (robin.zimmermann@oracle.com)) - старший менеджер по производству утилит в Oracle и проработал в Oracle восемь лет.

Листинг 3: Полный код класса LoanFlowPlusForms

public class LoanFlowPlusForms
{

  public static String[] getLoanApplicationList(String pEmail)
  {
    
    String[] taskList = null;
    
    // Получаем информацию, необходимую для соединения с BPEL-сервером.
    Properties props = new java.util.Properties();
    URL url = ClassLoader.getSystemResource("oc4j_context.properties");
     try
    {
      // Присоединяемся к BPEL-серверу.
      props.load(url.openStream());
      Locator locator = new Locator("default","bpel", props);
      
      // Получаем все задачи от нашего пользователя.
      IWorklistService worklist = (IWorklistService)locator.lookupService( IWorklistService.SERVICE_NAME );
      ITask[] tasks = worklist.listTasksByAssignee(pEmail);
  
      // Просматриваем в цикле задачи в поисках StarLoan, т.к. в списке могут быть
      // другие задачи, которые нас не интересуют.
      ITask thisTask;
      String taskId;
      taskList = new String[tasks.length];
      for (int i=0; i<tasks.length; i++) 
      {
        thisTask = tasks[i];
        if (!thisTask.getCreator().equals("StarLoan"))
        {
          // Эта задача была сгенерирована другим BPEL-процессом, поэтому
          // она будет проигнорирована.
          continue;
        }
  
        // Получаем уникальный id/key, связанный с этой задачей. Он потребуется
        // позднее, когда служащий по займам подтвердит или отклонит займ,
        // связанный с этой задачей.
        taskId = thisTask.getTaskId();
  
        // Создаем строку для возврата в Forms.     
        taskList[i] = taskId+"|"+getLoanApplicationDetails(thisTask);
      }
      
    }
    catch (Exception e)
    {
      // Обрабатываем ошибку
    }
  
    return taskList;
  
  }
  
  //////////////////////////////////////////////////////////////////////////////
  
  // Для данной задачи получаем информацию, связанную с ней.
  private static String getLoanApplicationDetails(ITask pTask)
  {
    String customerName=null, ssn=null, email=null, creditRating=null;
    String carModel=null, carYear=null, loanAmount=null;
  
    try
    {
      // Атрибуты займа сохраняются в XML-формате в прикреплении.
      Element e = (Element) pTask.getAttachment();
  
      // Теперь просмотрим в цикле XML и получим информацию, которая нам требуется.
      NodeList nodelist = e.getChildNodes();
      Node node;
      for (int i=0; i<nodelist.getLength(); i++)
      {
        node = nodelist.item(i);
  
        if (node.getNodeName() != null)
        {
          if (node.getNodeName().equals("customerName"))
            customerName = node.getNodeValue();
          if (node.getNodeName().equals("email"))
            email = node.getNodeValue();
          if (node.getNodeName().equals("SSN"))
            ssn = node.getNodeValue();
          if (node.getNodeName().equals("creditRating"))
            creditRating = node.getNodeValue();
          if (node.getNodeName().equals("carModel"))
            carModel = node.getNodeValue();
          if (node.getNodeName().equals("carYear"))
            carYear = node.getNodeValue();
          if (node.getNodeName().equals("loanAmount"))
            loanAmount = node.getNodeValue();
        }
      }
    }
    catch (Exception e)
    {
      // Обрабатываем ошибку.
    }
    
    return customerName+"|"+email+"|"+ssn+"|"+creditRating+"|"+carModel+"|"
           + carYear+"|"+loanAmount;
  
  }
  
  //////////////////////////////////////////////////////////////////////////////
  
  public static void approveLoan(String pTaskId, double pApr)
  {
    processLoanApplication(pTaskId, pApr);
  }
  
  //////////////////////////////////////////////////////////////////////////////
  
  public static void rejectLoan(String pTaskId)
  {
    processLoanApplication(pTaskId, -1);
  }
  
  //////////////////////////////////////////////////////////////////////////////
  
  private static void processLoanApplication(String pTaskId, double pApr)
  {
    // Получаем информацию, необходимую для соединения с BPEL-сервером.
    Properties props = new java.util.Properties();
    java.net.URL url = ClassLoader.getSystemResource("oc4j_context.properties");
    
    try
    {
      // Соединяемся с BPEL-сервером.
      props.load(url.openStream());
      Locator locator = new Locator("default","bpel", props);
      
      // Получаем задачу, id которой было передано как pTaskId.
      IWorklistService worklist = (IWorklistService)locator.lookupService( IWorklistService.SERVICE_NAME );
      ITask task = worklist.lookupTask(pTaskId);
  
      String approved   = "true";
      String conclusion = "Approved";
  
      // Если pApr равно –1, то займ не был подтвержден.
      if (pApr == -1)
      {
        approved   = "false";
        conclusion = "Rejected";
      }
  
      // Создаем прикрепление, указываем заключение и завершаем задачу.
      createLoanTaskAttachment(task, approved, pApr);
      task.setConclusion(conclusion);
      worklist.completeTask(task);
  
    }
    catch (Exception e)
    {
      // Обрабатываем ошибку
    }
  
  }
  
  //////////////////////////////////////////////////////////////////////////////
  
  private static void createLoanTaskAttachment(ITask pTask, String pApproved, double pApr)
  {
  
    try
    {
      // Создаем новое прикрепление для нашего XML.
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = null;
      builder = factory.newDocumentBuilder();
      Document doc = builder.newDocument();
  
      // Создаем XML.
      Element root = (Element)doc.createElement("loanOffer");
      root.setAttribute("xmlns", "http://www.autoloan.com/ns/autoloan");
      doc.appendChild(root);
      
      appendElement(doc, root, "providerName", ""                  );
      appendElement(doc, root, "selected"    , "false"             );
      appendElement(doc, root, "approved"    , pApproved           );
      appendElement(doc, root, "APR"         , String.valueOf(pApr));
  
      // Делаем XML прикреплением.
      pTask.setAttachment(XMLUtils.convertToCollaxaElement(root));
         
    }
    catch (Exception e)
    {
      // Обрабатываем ошибку
    }
    
  >}
  
  ////////////////////////////////////////////////////////////////
  
  public static void appendElement
  (
    Document pDoc
  , Element pRoot
  , String pNodeName
  , String pNodeValue
  )
  {
    Element element;
    Text text;
  
    element = (Element)pDoc.createElement(pNodeName);
    pRoot.appendChild(element);
    text = pDoc.createTextNode("#text");
    text.setNodeValue(pNodeValue);
    element.appendChild(text);
  }

}
E-mail this page