Разработка приложений для Lotus Notes/Domino в среде Borland Delphi

Источник: Вычислительный центр ОАО "Кондопога", КНМЦ СЗО РАМН
Гусев А.В., Дмитриев А.Г., Тихонов С.И.

Lotus Notes / Domino - прекрасная платформа для создания мощных корпоративных информационных систем, ориентированных на групповую работу с электронными документами. В своей работе над комплексной медицинской информационной системой "Кондопога" мы на основе тщательного анализа средств разработки и имеющихся на рынке СУБД выбрали Lotus Notes / Domino в качестве основы всей системы. Разработка осуществляется с 1999 года, за это время мы постепенно перешли с версии 4.6 на версию R 5, а затем - на R 6. В данный момент идет тестирование R 6.5 на совместимость с существующим ПО.

Lotus Notes / Domino полностью отвечает ключевым требования к созданию медицинской информационной системы по надежности, безопасности, отказоустойчивости и масштабированию. Работа пользователя в этой среде в максимальной степени приближена к привычной работе с документами - фактически, бумага и авторучка у медицинских сотрудников заменена на компьютер. Формы электронных документов могут быть разработаны по точной аналогии с их бумажными аналогами (при необходимости), а стандартные средства для работы с документами (создание, редактирование, печать, отправка по e-mail, электронная цифровая подпись и т.д.) требуют от пользователя минимального объема обучения.

Однако, как и в любой информационной технологии, имеется ряд недостатков, с которыми приходится мириться и искать пути их преодоления. Основной их недостатков Lotus Notes / Domino для применения в медицинской сфере - это слабая поддержка таблиц в электронных документах. На практике даже с точки зрения пользователя встроенные в клиентское программное обеспечение Lotus Notes средства для работы с таблицами значительно уступают аналогичным инструментам в Microsoft Office. А с точки зрения инструментария разработчика средства для управления таблицами тем более являются малоэффективными. Некоторые изменения в этом направлении были сделаны в версии R 6 Domino, однако и они являются недостаточными. Фактически, в Lotus Notes таблица, как средство отображения, управления и хранения информации, отсутствует как класс. Но это и понятно - ведь Lotus Notes - это, прежде всего, объектно-ориентированная СУБД, предназначенная для групповой работы над документами.

Вместе с тем в нашей работе поддержка табличного формата хранения информации является неотъемлемой функцией системы. Некоторые документы (лист назначений, например) и некоторые приложения (бухгалтерия, аптека, склад, автоматизация службы питания и т.д.) несравненно более эффективно работаю под управлением реляционной СУБД, чем в среде Lotus Notes / Domino. Все это породило необходимость совместного использования Lotus Notes / Domino и реляционной СУБД, в качестве которой был выбран Microsoft SQL Server. В качестве средства разработки в Lotus Notes / Domino используется специальное программное обеспечение Lotus Designer, позволяющее создавать мультиплатформенные приложения на Visual Basic-подобном языке Lotus Script, @-формулах или Java Script. Это мощное приложение позволяет за очень небольшое время разрабатывать необходимые программы как для выполнения в среде Lotus Notes, так и для работы в обычном браузере Internet. Однако для создания приложения для реляционной СУБД его возможностей явно недостаточно. Поэтому в качестве дополнительного инструментария мы используем Borland Delphi (в настоящее время - версию 6.0).

Одним из серьезных препятствий на использовании Delphi является задача совместного доступа как к информации в реляционной базе данных, так и для доступа к базам данных Lotus Notes / Domino. Для решения этой задачи имеется несколько подходов:

  1. Использование компонентов сторонних производителей (http://www.torry.net/index.htm, http://www.geocities.com/SiliconValley/Peaks/8307/)
  2. Использование приложения Lotus Notes SQL (http://www-10.lotus.com/ldd)
  3. Разработка собственных компонентов, используя Notes API (http://www.notesnet.ru/)
  4. Доступ к ресурсам Lotus Notes посредством OLE.

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

Второй подход также, к сожалению, не отвечал требованиям. При этом Notes SQL фактически эмулирует обращение к базе данных Lotus Notes, как к обычной реляционной таблице. Тестирование различных версий Notes SQL показала нестабильность этого программного обеспечения. Особенно ярко недостатки Notes SQL проявлялись при обработке больших объемов информации - в случайные моменты работы программы возникали неустранимые ошибки, которые приводили к полном прекращению работы программ.

Третий подход является более предпочтительным, однако и от него мы со временем отошли в силу его трудоемкости, большой сложно написания программы, массы низкоуровневого кода и высоких требований к знанию внутренний архитектуры Lotus Notes.

Первое время доступ к Lotus Notes посредством OLE казался нам неприемлемым вариантом с точки зрения скорости работы. Однако наш 5-летний опыт работы доказал высокую устойчивость программ на основе этого подхода и вполне приемлемую скорость обработки информации.

Далее мы на примерах покажем, как написать приложение в среде Borland Delphi для баз данных Lotus Notes.

1. Инициализация сессии

Основной принцип в написании программ состоит в использовании встроенных классов Lotus Notes в коде программ. Для этого в первую очередь необходимо инициализировать сессию связи с Lotus Notes. Для этого требуется, чтобы клиентское программное обеспечение Lotus Notes было инсталлировано на каждом компьютере, использующим программу и подключено к одному или нескольким серверам Domino.

Создадим новое приложение. В разделе uses главного окна приложения укажем ComOBJ - это библиотека, позволяющая вызывать и обращаться к OLE-объектам.

В разделе public объявим переменные, общие для всего приложения:

public
 { Public declarations }
 MySession : OLEVariant; // текущая сессия Lotus Notes
 MyLNDataBase : OLEVariant; // база данных Lotus Notes...
	

Теперь необходимо написать обработчик события OnCreate главной формы приложения, в котором мы должны создать объект NotesSession, чтобы, используя его в дальнейшем, иметь возможность в рамках одного приложения обращаться сразу к нескольким базам данных, серверам, документам, представлениям и т.д. Обработчик должен иметь следующий вид:

procedure TfmMain.FormCreate(Sender: TObject);
begin
MySession:= createOLEObject('Notes.Notessession');
if varisempty(MySession) then
 begin
 ShowMessage('Не могу создать сессию с сервером Lotus Notes');
 Exit;
 end;
end;

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

2. Доступ к базе данных

После того, как мы инициализировали сессию связи с Lotus Notes, мы можем обращаться к любым серверам Lotus Domino и базам данных на них. Принципиально получить доступ к БД Lotus Notes можно 2 способами - либо обратиться к текущей базе данных, открытой в Lotus Notes, либо вызвать соответствующий метод NotesSession и открыть любую другую БД. Последний случая является наиболее востребованным, поэтому рассмотрим его:

procedure TMyButtomClick(Sender: TObject);
var MyServer: string;
begin
 // Необходимо вычислить имя сервера,
 // на котором находится необходимая нам БД
 MyServer:=... 
 // Теперь открываем БД - например, откроем адресную книгу сервера
 MyLNDataBase:=MySession.GetDataBase(MyServer, ‘names.nsf’);

end;

Мы в своей работе используем реестр Windows, в котором храним имя сервера Domino по умолчанию, к которому подключаются приложения. Для упрощения разработки и администрирования программ мы использую функцию GetDefaultServerName, которая работает по следующему алгоритму:

  1. При вызове функции она проверяет наличие определенного ключа в реестре Windows.
  2. Если этот ключ отсутствует или он содержит пустое значение, функция обращается к файлу notes.ini и считывает из него значение параметра почтового сервера Notes. Имя сервера записывается как значение ключа Windows
  3. Если ключ в реестре Windows имеется и не содержит пустое значение, функция считывает его и возвращает в качестве ответа.

Таким образом, при первом старте приложения оно самостоятельно выполняет настройку реестра Windows на подключение к текущему серверу Domino. Если в дальнейшем требуется перенаправить работу приложения на другой сервер, то либо пользователь при помощи встроенных в приложение визуальных средств, либо администратор сети вручную меняют значение ключа. Т.о. со следующего запуска начинает обращаться к другому серверу - обеспечивает поддержка мультисерверной конфигурации информационной системы.

3. Работа с базой данных

Из программы, написанной в Borland Delphi, доступны практически все свойства и методы, предусмотренные разработчиками Lotus Notes / Domino. В том числе Вы можете осуществлять навигацию по представлениям, осуществлять поиск документов в базе данных, в том числе и гипертекстовый поиск и т.д. Особенностей по работе с базой данных вследствие использования Delphi мы не обнаружили. Поэтому в качестве примера приведем фрагмент кода, осуществляющий последовательный перебор и считывание документов из коллекции документов NotesDocumentCollection базы данных адресной книги сервера.

procedure TfmMainWindow.BitBtn1Click(Sender: TObject);
var DocumCount: longint; // количество документов в коллекции
 i : longint; // шаг цикла
 B1: OLEVariant; // переменная для объекта NotesDatabase
 BodyQuery: ansistring;
 C1: OLEVariant; // переменная для объекта NotesDocumentCollection
 D1: OLEVariant; // переменная для объекта NotesDocument
begin
DocumCount:=0;
// Получаем доступ к БД.
B1:= MySession.GetDatabase(GetDefaultServerName,'names.nsf');
BodyQuery:='Form = "Person"';
// Для поиска используем специальную функцию LNSearch 
C1:=LNSearch(MySession,B1,’Пример запроса’,BodyQuery);
DocumCount:=C1.Count;
if DocumCount=0 then Exit; // искомые документы не найдены
D1:=C1.GetFirstDocument;
for i:=1 to DocumCount do
 begin.... здесь осуществляется обработка документа
 D1:=C1.GetNextDocument(D1);
 end; 

end;

В этом примере программа обращается к текущему серверу и открывает на нем базу данных адресной книги. Затем, используя специально разработанную функцию LNSearch, производит поиск документов в базе данных. Если не найдено ни одного документа, то работа процедуры завершается. Если какие-то документы найдены, то они последовательно обрабатываются в цикле. Применение специальной функции LNSearch обусловлено тем, что стандартный метод Search в классе NotesDatabase, кроме формулы для поискового запроса, требует передать дату самого старого документа, который этот запрос сможет вернуть в качестве результата. При этом дата должна быть передана не в качестве переменной типа TDate или TDateTime, а в качестве OLEVariant -переменной, созданной как объект класса NotesDataTime.

function LNSearch(LNSession, LNDataBase: OLEVAriant;
 Logo: string;query: string):OLEVariant;
var r1:WideString;
 r2: OLEVariant;
 r3: Smallint;
 C1: OleVariant;
begin
 r1:=query;
 r2:=LNSession.CreateDateTime('01.01.1990'); // здесь может быть любая дата
 r3:=0;
 C1:=LNDataBase.SEARCH(r1,r2,r3);
 Result:=C1;
end;

Отметим, что по нашим наблюдениям, при написании программ в Borland Delphi следует стремиться использовать навигацию по представлениям вместо использования метода search. При этом скорость обработки одной и той же коллекции документов, полученной из представления, примерно на 40% выше, чем при обработке документов, полученных поиском в базе данных.

4. Работа с документами

В отличие от работы с базами данных, обработка документов Lotus Notes имеет массу подводных камней. При этом в программах приходится выполнять много одинаковых операций, которые целесообразно выделять в отдельные функции. Вначале рассмотрим стандартную функцию считывания текстового значения поля из документа.

function LNGetFieldStrValue(Document: OLEVariant;
 FieldName: string;
 DefaultValue: string): AnsiString;
var SendValue, RetValue: OLEVariant;
 TmpS: Ansistring;
 MyPos: integer;
begin
TmpS:=' ';
if FieldName<>'' then
 begin 
 SendValue:=FieldName;
 if not varisempty(Document) then
 begin
 Try
 RetValue:=Document.HasItem(FieldName);
 except
 begin
 RetValue:=false;
 end; // do
 end; // Try
 if RetValue then
 begin
 RetValue :=Document.GetFirstItem(SendValue);
 try
 TmpS:=RetValue.Text;
 except
 TmpS:=DefaultValue;
 end;
 end else TmpS:=DefaultValue;
 end else TmpS:=DefaultValue; // varisempty chek
 end else TmpS:=DefaultValue;

if TmpS='' then TmpS:=DefaultValue;
Result :=tmpS;
end;

Эта простая функция позволяет значительно упростить написание программ, особенно в случае, когда документ содержит большое количество полей, значения которых необходимо считать и обработать. Необходимо отметить, что очень часто в полях документов Lotus Notes хранится несколько значений или значения записаны с символами, препятствующими корректной работе со строками в Borland Delphi. Мы в таком случае используем перегруженную версию представленной функции, которая может возвращать "очищенную" строку или определенную подстроку.

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

Несколько усложненный пример - это считывание значения поля в профайле. Как известно, Lotus Notes, кроме стандартных документов, позволяет поддерживать хранение информации в т.н. профайлах - документах, к которым можно обратиться по имени формы и, как дополнение, по имени текущего пользователя. Для чтения текстового значения из профайла рассмотрим следующую функцию:

function LNGetProfileField(MySession, MyDBName: OLEVariant;
 MyServerName, MyProfileName, MyUserName, MyFieldName: string):string;
var D1: OLEVariant;
 tmpS: AnsiString;
begin
if MyServerName='' then
 tmpS:= GetDefaultServerName else tmpS:=MyServerName;
if varisempty(MyDBName) then
 begin
 ShowMessage('Фатальная ошибка! Переданный объект 
 <База данных> пуст. Продолжение невозможно!');
 Exit;
 end;
D1:=MyDBName.GetProfileDocument( MyProfileName, MyUserName);
if varisempty(D1) then
 begin
 ShowMessage ('Ошибка при получении профайла '+MyProfileName+'
 из базе данных '+MyServerName+
 ' / '+MyDBName.Name+'. Продолжение невозможно!');
 Exit;
 end;
tmpS:=LNGetFieldStrValue(D1,MyFieldName,'',False);
Result :=tmpS;
end;

Как видно из примера, эта функция использует стандартную функцию LNGetFieldStrValue, представленную ранее, но перед этим выполняет ряд дополнительных проверок и операций.

5. Выводы

Мы используем представленную технологию в практической разработке медицинской информационной системы "Кондопога" вот уже в течение 5 лет. За это время многократно убедились в прекрасной устойчивости и приемлемой скорости работы программ, написанных на Borland Delphi для баз данных Lotus Notes / Domino. Фактически мы убедились, что способны создавать программы на Borland Delphi, которые используют весь арсенал встроенных в Lotus Notes классов.

Отметим, что со временем в арсенале программиста накапливается самая разнообразная масса готовых функций и процедур, которые целесообразно аккумулировать либо в виде подключаемых библиотек, либо в виде отдельных модулей (pas -файлов). При этом, по нашим наблюдениям, время на разработку новой программы можно сократить в несколько раз именно за счет использования готовых и отлаженных приложений. А это позволяет снизить стоимость разработки и повысить устойчивость приложений, что является уже не столько инструментарием разработчика, сколько экономическим стимулом.

Постепенно нами была накоплена целая библиотека класса middleware, которая реализует практически весь необходимый функционал для написания программ в Borland Delphi для среды Lotus Notes. Это позволило разработать нашу информационную систему таким образом, что взаимные недостатки реляционных и объектно-ориентированных баз данных фактически полностью компенсируются взаимными достоинствами. Поэтому пользователи ИС "Кондопога" одинаково комфортно используют и возможности совместной работы над электронными документами Lotus Notes и встроенные в базы данных Domino приложения, предоставляющие расширенные возможности работы с таблицами реляционных баз данных, мгновенное построение диаграмм на основе данных из документов Lotus Notes и т.д.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=1106