Разработка приложений для 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. Для решения этой задачи имеется несколько подходов:
Первый подход подразумевает использование компонентов для доступа к 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, которая работает по следующему алгоритму:
Таким образом, при первом старте приложения оно самостоятельно выполняет настройку реестра 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 и т.д. |