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

Постреляционная еСУБД Cachе и WWW

Олег Сиротюк,
sirotyuk@intersystems.ru
инженер по контролю качества InterSystems corp.

Всемирная паутина за десять лет своего существования превратилась в самое популярное средство распространения информации. На сегодняшний день мы имеем развитые средства Web, обеспечивающие простую публикацию информации, удобство и сравнительную унифицированность доступа к документам, а также наличие достаточно развитых средств поиска. Кроме этого, существует большое количество ценных баз данных, управляемых разнородными СУБД, а также желание сделать эти базы доступными всем людям (в случае публичных баз данных) или членам территориально-распределенной корпорации (в случае корпоративных баз данных). Возникает естественное желание объединить эти две технологии и обеспечить доступ к базам данных посредством интерфейсов Web. Данная статья посвящена рассмотрению некоторых способов публикации данных постреляционной СУБД Cachе в WWW.

Типичная схема размещения баз данных в WWW включает в себя Web-броузер в качестве пользовательского интерфейса, сервер приложений в качестве средства реализации бизнес-логики приложений и сервер БД. Существуют различные способы размещения вышеперечисленных компонентов. Различные схемы размещения приведены на рисунке 1.

Рисунок 1. Сравнение web-технологий.

Как видно из рисунка 1, вся бизнес-логика серверных страниц Cachе (CSP) выполняется в непосредственной близости к хранилищу данных Cachе, таким образом сокращается объем данных, которыми обмениваются web-сервер и сервер БД Cachе. Это приводит к выигрышу в производительности по сравнению с другими технологиями создания web-приложений. Для еще большего увеличения производительности CSP-приложений при обмене данными между сервером Cachе и web-сервером используются высокоскоростные интерфейсы API (NSAPI, ISAPI). Схематично прохождение запроса на .csp страницу приведено на рис.2.

Рассмотрим каждый из этапов подробнее. Запрос на .csp страницу, поступающий с броузера на Web-сервер обрабатывается серверной компонентой – NSAPI/ISAPI CSPGateway, основной функцией которой является перенаправление вызова на соответствующий сервер Cachе. Сервер Cachе обрабатывает запрос, находит исходный код CSP-документа, преобразует его в класс и компилирует класс в объектный код. Далее вызывается метод Page() созданного класса, который и формирует ответ web-серверу в виде HTML-документа. Следует отметить то, что объектный код создаваемого класса кэшируется. Это приводит к значительному повышению производительности сервера приложений за счет исключения создания и компиляции класса при повторном обращении. Возможен прямой вызов метода Page() csp-класса через расширение csp_страница.cls. При этом также увеличивается производительность работы приложений, т.к. исключается процедура поиска исходного документа CSP и сравнения даты его изменения с датой компиляции соответствующего класса.

Рисунок 2. Схема прохождения CSP-запроса.

Классы csp-документов могут использовать все преимущества объектной модели Cachе, такие как полиморфизм, наследование и инкапсуляция. Например, определяя дизайн создаваемого web-портала, можно создать класс Cachе и наследовать от него все csp-классы приложений портала. После этого для изменения дизайна всех приложений портала необходимо изменить только класс-предок.

Пример создаваемого csp-класса на внутреннем языке описания классов (Unified Definition Language) для следующей csp-страницы приведен в Листинге 1.

<html>
  <head>
    <title>Simple Cache Server Page</title>
  </head>
  <body>
    Hello World
  </body>
</html>

Листинг 1. Пример csp-класса.

Import User

Class csp.helloworld Extends %CSP.Page
{
  Parameter CSPFILE = “c:\cache42\csp\test\HelloWorld.csp”;
  Parameter CSPFILE = “/csp42/test/HelloWorld.csp”;
  Parameter FileTimestamp = “58910,28326”;

  ClassMethod OnPage() As %Status
  {
    Do ..OnPageCSPROOT()
    Quit $$$OK
  }

  ClassMethod OnPageBODY() As %Boolean
  {
    Write “<Body>” 
    Write !,$c(9)_”Hello World”
    Write !,“<Body>”
  }

  ClassMethod OnPageCSPROOT() As %Boolean
  {
    Do ..OnPageHTML()
  }
  ClassMethod OnPageHEAD() As %Boolean
  {
    Write “<HEAD>”
    Write !,$c(9)_”<TITLE>Simple Server Page</TITLE>”
    Write !,“</HEAD>”
..}

  ClassMethod OnPageHTML() As %Boolean
  {
    Write “<HTML>”
    Write !
    Do ..OnPageHEAD()
    Write !
    Do ..OnPageBODY()
    Write !,“</HTML>”
  }
}

Созданный класс наследует свое поведение и состояние от класса %CSP.Page. Данный системный класс предоставляет ряд методов, которые используются для обработки запроса и генерации ответа web-серверу. Рассмотрим основные методы класса:

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

Перечень основных предопределенных тегов и директив Cachе приведен в таблице 1.

Таблица 1

Динамические элементы CSP

Описание

#(COS-выражение)#

Выполнение выражения в момент обращения к CSP-странице

##(COS-выражение)##

Выполнение выражения в режиме компиляции класса

#include

Включение других СSP-страниц

#server()#,#call()#

Вызов методов класса на сервере

<SCRIPT></SCRIPT>

Language=’cache’

Language=’basic’

Language=’sql’

Расширение стандартного тэга HTML языками программирования Cachе Object Script, Cachе Basic и SQL

<CSP:IF> <CSP:ELSE> <CSP:ELSEIF> <CSP:LOOP> <CSP:WHILE>

Тэги управления последовательностью выполнения csp-документов.

<CSP:QUERY> <CSP:SQLQUERY>

Тэги, указывающие место выполнения SQL-запроса.

<CSP:OBJECT>

Тэг, связывающий объекты Cachе и CSP-страницы. С помощью атрибута CSPBIND возможно связывание методов и свойств объекта с элементами HTML-форм

<CSP:SEARCH>

Привязка поисковой страницы к CSP-странице

Рассмотрим использование некоторых из этих тэгов. Так, с помощью тэгов <CSP:IF><CSP:ELSE> и <CSP:ELSEIF> производится выбор и выполнение одного из фрагментов CSP-документа в случае истинного значения условия соответствующей ветви условного перехода. При этом условие задается в виде значения аргумента CONDITION, например:

<csp:IF CONDITION='user="Oleg"'>
  Welcome Oleg!
<csp:ELSEIF CONDITION='user="Lina"'>
Welcome Lina!
<csp:ELSE>
Welcome!

Для многократного выполнения фрагмента csp-документа можно воспользоваться тэгами <CSP:LOOP> - аналог цикла FOR или <CSP:WHILE>.

Рассмотрим пример работы цикла <CSP:LOOP>. Для определения переменной счетчика, значения начала и конца цикла, а также шага, используются атрибуты COUNTER, FROM, STEP, TO тэга <CSP:LOOP>:

<UL>
  <csp:LOOP COUNTER="x" FROM="1" TO="5" STEP="1">
  <LI>Item #(x)#
  </csp:LOOP>
</UL>

Для исполнения кода Cachе Object Script (COS), Cachе Basic или SQL-запроса внутри CSP-страницы используется тэг <SCRIPT> с расширенным набором значений атрибута LANGUAGE:

<SCRIPT LANGUAGE="CACHE”>
New id,SQLCODE
Set name=”Ivan”
  &sql(SELECT ID INTO :id FROM MyApp.Customer WHERE Name = :name)
  If (SQLCODE = 0) 
  {
    &js<alert('Customer with name: #(name)# already exists.');>
  }
</SCRIPT>
<SCRIPT LANGUAGE="SQL” P1=#(%request.Get(“P1”,”A”))#>
SELECT ID FROM MyApp.Customer WHERE Name=?
</SCRIPT>

Обратите внимание на то, что код COS, определенный в теле тэга <SCRIPT LANGUAGE=”CACHE”>, использует макровызовы &sql<> и &js<> для включения выражений SQL и команд JavaScript. При этом сервер Cachе при подготовке ответа web-серверу исполняет sql-запросы и подставляет в код JavaScript результат выражений COS, заключенных в #()#. Затем код JavaScript публикуется на передаваемой браузеру HTML-странице. Возможно также использование директивы &html<> для включения тэгов HTML.

Рассмотрим подробнее конструкцию, которая используется для определения параметра SQL-запроса в вышеприведенном примере:

#(%request.Get(“P1”,”A”))#

Как было сказано ранее, директива #()# используется для выполнения выражения на сервере в момент обращения к csp-странице. В нашем случае вызывается метод Get системного объекта %request. Экземпляр %request системного класса %Request создается для каждой вызываемой csp-страницы и содержит переменные окружения, а также параметры, передаваемые HTTP-методом GET или POST. Указывая в качестве аргумента метода Get имя параметра можно получить его значение. В вышеприведенном примере указывается также значение, которое вернет метод Get в отсутствие параметра P1 в HTTP-запросе. Список методов класса %Request, а также их описание приведены в таблице 2.

Сессии в Cachе.

Как известно, протокол HTTP не ориентирован на подключение. Каждый HTTP-документ не сохраняет своего состояния в промежутках между вызовами и не связан с другими. Одной из задач различных web-технологий является сохранение требуемого контекста при переходе от одной страницы к другой в рамках сессии.

Cache предоставляет развитый механизм управления сессиями, который реализуется системным классом %Session. При первом обращении к csp-странице создается объект класса %Session, ссылка на который хранится в переменной %session. Переменная %session доступна из CSP-приложений и позволяет создавать или закрывать сессии, переносить переменные, открытые объекты от одной CSP-странице к другой в рамках сессии.

Таблица 2

Имя

Описание

Count

Возвращает число значений для передаваемого параметра в HTTP-запросе.

Аргументы: имя параметра (%Library.String)

Возвращаемое значение: %Library.Integer

Get

Извлекает данные, посланные в HTTP-запросе

Аргументы: имя поля ввода (%Library.String), значение по умолчанию (%Library.String, необязательный, default=””), номер экземпляра (%Library.Integer, необязательный, default=1)

Возвращаемое значение: %Library.String

Kill

Удаляет поле ввода из объекта %request

Аргументы: имя поля (%Library.String, необязательный, по умолчанию все), номер экземпляра (%Library.Integer, необязательный, по умолчанию все)

Возвращаемое значение: нет

Next

Позволяет перебрать все поля ввода формы; возвращает имя следующего поля

Аргументы: имя предыдущего поля или «» (%Library.String)

Возвращаемое значение: %Library.String

Set

Присваивает измененное значение элементу данных, определенному в HTTP-запросе, или создает новый элемент

Аргументы: имя поля (%Library.String), значение (%Library.String), номер экземпляра (%Library.Integer, необязательный, default=1)

Рассмотрим некоторые из методов класса %Session.

Метод Preserve() класса позволяет определить уровень контекста, который должен сохраняться между запросами. Предлагается 3 уровня контекста: 0-только объект %session, 1-все локальные переменные и открытые объекты, 2- полный контекст.

Сессия может быть закрыта либо по тайм-ауту, либо вызовом метода EndSession(). Время простоя указывается в Cache Configuration Manager для текущей конфигурации, а метод, вызываемый при достижении времени простоя указывается в качестве аргумента метода OnAppTimeOutMethod().

Класс %Session предоставляет полный набор методов для работы с сессионными переменными. С помощью методов Set() и Get() создаются и извлекаются переменные сессии. Метод Kill() предназначен для удаления переменных сессии, а методы Next(), Count() для навигации по списку переменных сессии и подсчету количества значений переменной.

Каждая сессия приложений CSP уникально характеризуется идентификационным номером. Этот номер, помимо прочего, используется также и в качестве ключа шифрования. Шифрование может быть применено для:

Технология гипер-событий

Множество технологий разработки web-приложений, использующих данные из БД для обновления содержимого страницы, обновляют страницу целиком. Данная стратегия оправдывает себя, когда обновляется содержимое всей страницы или большая ее часть. Однако, когда необходимо обновить только часть страницы и оставить большую часть неизменной, стратегия обновления страницы целиком влечет неоправданно большие затраты, связанные со временем отклика и повышением трафика. Например, представим, что на странице регистрации при вводе адреса необходимо обновить ниспадающий список городов, для выбранной страны. Для этого при обновлении страницы целиком необходимо повторно вызвать страницу с передачей ей уже введенных в форме регистрации значений. Сервер приложений обрабатывает запрос и отправляет браузеру ответ в виде целой страницы HTML. При этом мощность сервера приложений используется для выполнения избыточной работы, а трафик значительно увеличивается.

СУБД Cachе эффективно решает эту проблему, путем реализации технологии гипер-событий.

Основной идеей технологии гипер-событий является вызов функций сервера без обновления CSP-страницы целиком. Функции сервера вызываются в ответ на возникающие события в браузере и изменяют только определенную часть страницы.

В Cachе реализовано 2 способа вызова методов на сервере – синхронный, с использованием небольшого апплета Java на стороне клиента, и асинхронный, с использованием специальных тегов HTML IFRAME или ILAYER, в зависимости от браузера. При этом при синхронном вызове метода, в отличии от асинхронного, браузер ожидает завершение работы метода и возвращает его значение. Синхронный вызов метода реализуется директивой #server(имя_класса.имя_метода)#, где в качестве аргумента указывается имя вызываемого метода и соответствующего класса. Директива #call(имя_класса.имя_метода)# реализует асинхронный вызов методов.

Рассмотрим пример синхронного вызова метода сервера:

<SCRIPT LANGUAGE="JavaScript">
function test(value)
{
  // вызвать метод на сервере
  #server(MyClass.Test(value))#;
}
</SCRIPT>

Сервер Cachе при обнаружении директивы #server()# публикует на генерируемой HTML-странице небольшой JAVA-апплет (CSP Event Broker) и заменяет все вызовы #server()# на программы JavaScript. Затем, при возникновении события в браузере, вызывается соответствующая функция JavaScript, которая через апплет CSP Event Broker посылает зашифрованный HTTP-запрос на сервер и обрабатывает полученный ответ.

Экспорт и импорт XML

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

Можно провести четкую параллель между уникальной архитектурой Cachе и структурой элементов XML-документов. При этом структурные элементы XML можно рассматривать в качестве объектов объектной модели Cachе без определенного поведения, характеризуемого методами объектов. При этом значения элементов и атрибутов XML-структур можно рассматривать как значения свойств объектов Cachе. Реляционная модель данных неэффективна для хранения XML-элементов, т.к. не поддерживает сложных типов данных, а разбиение на плоские нормализованные реляционные таблицы и воссоздание первоначальных структур данных связано с большими временными, аппаратными затратами и затратами, связанными с изменением структуры данных. Поэтому обеспечение эффективной поддержки XML-спецификации в новой версии Cachе 5, которая включает в себя как импорт/экспорт данных, проектов, программ и классов Cachе, так и поддержку протокола удаленного вызова функций сервера SOAP, не составило большой трудности.

Рассмотрим некоторые возможности СУБД Cachе по экспорту и импорту XML-данных. Ниже, в отдельном разделе, рассмотрена поддержка web-служб по протоколу SOAP.

Экспорт данных

Объектная модель Cachе позволяет создавать XML документы напрямую либо из программ Cachе:

write "<book isdn="" ",book.isdn, " "" >"
write "<title>",book.title, "</title>"

либо из CSP страниц:

<book isdn=" #’book.isdn’# ">
<title> #(book.title)# </title>

Рассмотрим экспорт данных на примере создания динамического XML-документа, выводящего данные из БД Family. Листинг CSP приложения приведен ниже:

<script language="Cache" runat="Server">
  do %response.SetHeader("content-type","txt/xml")
  write "<?xml version=""1.0""?>"
  write "<xml>"
  set o=##Class(Family.Family).%OpenId(1)      
  write "<Family>"
  write "<Father>"
  write "<FIO>",o.Father.FIO,"</FIO>"
  write "</Father>"
  write "<Children>"
  write "<FIO>",o.Children.GetAt(1).Personal.FIO,"</FIO>"
  write "</Children>"
  write "</Family>"
  write "</xml>"
  do o.%Close()
</script>

Методом SetHeader системного класса %Response выставлен HTTP-заголовок, который указывает браузеру, что данный документ является XML-документом. Далее, с использованием команд языка программирования COS, выводятся значения свойств открытого объекта класса Family.Family.

Для экспорта всех свойств класса необходимо либо вывести их с помощью команды write, как показано выше, либо использовать метод XMLExport класса %XML.Adaptor.

Для использования методов класса %XML.Adaptor, необходимо определить его как суперкласс или класс-предок для всех классов, подлежащих экспорту:

Class Family.Family Extends (%Library.Persistent,%XML.Adaptor) 

Далее создается следующая CSP-страница:

<script language="Cache" runat="Server">
do %response.SetHeader("content-type","txt/xml")
write "<?xml version=""1.0""?>"
set o=##Class(Family.Family).%OpenId(1)
do o.XMLExport()
do o.%Close()
</script>

Основным достоинством метода XMLExport является рекурсивный принцип работы, который заключается в экспорте всех объектов, связанных с экспортируемым. При этом поддерживаются все определенные в объектной модели Cachе типы связей, такие, как отношения, коллекции значений и объектов, встраиваемые объекты и другие.

Из всего многообразия методов класса %XML.Adaptor следует выделить также следующие методы:

Данные методы позволяют экспортировать описание структуры хранения данных в БД Cachе в виде стандартизованных структур XML Schema или XML DTD.

В Cachе поддерживается стандарт XML MS SQLServer, реализованный функциональностью класса %CSP.XMLSQL. С помощью данного класса можно создавать структурированные соответствующим образом XML-документы с данными, удовлетворяющими поступающему SQL-запросу.

Импорт данных

Существует два способа импорта данных из XML-документов в объекты БД:

Рассмотрим оба способа импорта на примерах:

Для импорта данных непосредственно в класс Cachе необходимо воспользоваться методами системного класса %XML.Reader. Импорт осуществляется в несколько этапов. На первом этапе производится проверка XML-документа на соответствие структурным правилам XML и структуре класса Cachе. Далее создается экземпляр класса в памяти и соответствующим свойствам класса присваиваются значения XML-элементов. Пользователю возвращается OREF (объектная ссылка) на созданный экземпляр. Для непосредственной записи экземпляра в БД необходимо вызвать соответствующие методы класса (%Save(), если класс наследует свое поведение от %Persistent). Далее процесс повторяется, пока не будет достигнут закрывающий корневой тэг. Пример:

Set reader = ##class(%XML.Reader).%New()
do reader.Correlate(xmlElem,OclassName)
set ex = reader.Start(fileName)
While(reader.Next(.object))
{
  set ex = object.%Save()
},

где Correlate() устанавливает отношение между XML тегом и классом Cachе, Start() начинает обработку XML документа, Next() приказывает перейти к следующему тэгу.

SAX (Simple API for XML) парсер – это событийный парсер. Принцип работы событийного парсера заключается в последовательном сканировании XML-документа. При обнаружении структурного элемента XML(открывающий тег, закрывающий тег, инструкция обработки, корневой тег и т.д.) возбуждается соответствующее событие, которое обрабатывается пользовательским методом. Системный класс %XML.SAX.ContentHandler предоставляет методы всех возможных событий SAX-парсера. Наследуя свой класс от XML.SAX.ContentHandler и переопределяя методы событий, можно определить собственный способ обработки событий и сохранения данных из XML-документов в произвольные структуры данных Cachе. Пример такого класса:

Class User.MyParser Extends (%Persistent, %XML.SAX.ContentHandler)
{
  Method characters(chars As %Library.String, length As %Library.Integer)
  {
    w !,"Символы - ", chars
    Quit
  }
  
  Method endElement(uri As %Library.String, localname As %Library.String, 
                    qname As %Library.String)
  {
    w !,"Element with name "_localname_" finished"
    Quit
  }
  
  Method startElement(uri As %Library.String, localname As %Library.String,
                    qname As %Library.String, attrs As %Library.List)
  {
    w !,"Element with name "_localname_" started"
    Quit
  }
}

Web-службы и SOAP

Специалисты разделяют несколько этапов эволюции приложений всемирной паутины. Первый этап – этап статических страниц – начался в момент зарождения Internet. Пользователь был ограничен навигацией по статическим страницам, связанным между собой гиперссылками. Следующий этап – этап динамических web-приложений. Набор функций, предоставляемых пользователю, значительно расширяется, появляется возможность интеграции web-приложений и БД, динамического обновления содержимого страниц в зависимости от запросов, использования интерактивной графики и др. Основным инструментом взаимодействия пользователя и Internet является тонкий клиент – браузер. Третий этап – этап взаимодействия приложений без участия человека посредством сети Интернет к настоящему моменту находится в своей начальной стадии. К основному транспорту третьего этапа относят протокол SOAP (Simple Object Access Protocol).

Протокол SOAP – это слабосвязанный механизм, ориентированный на сообщения и предназначенный для удаленного вызова объектов по глобальным сетям.

Как он работает? Зная URL, клиент на языке описания сервиса SDL (Service Description Language) запрашивает у сервера информацию о нем (предоставляемые методы, параметры и т.д.). В ответ клиент получает SDL-файл с нужными сведениями о том, какие услуги (методы) ему будут предоставлятся и как ими пользоваться.

Методы вызываются с помощью HTTP-запросов и ответов. В запрос вкладывается XML-текст, состоящий из трех частей:

Ответ приходит также в виде XML, содержащего результаты обработки запроса или код ошибки.

Новая версия Cachе, Cachе 5, реализует протокол SOAP для всех поддерживаемых платформ. При этом не требуется использования промежуточных приложений сторонних компаний, все необходимые функции реализованы в виде системных классов БД Cachе.

Cachе SOAP предоставляет широкий спектр функций, который включает:

Для создания web-службы и публикации каталога на сервере не требуется никаких дополнительных знаний о Cachе. Реализация служб осуществляется на основе объектной модели Cachе. При этом, для создания службы необходимо определить класс, наследуемый от системного класса %SOAP.WebService. Методы, которые подлежат публикации на сервере, характеризуются параметром WebMethod. Код, реализующий логику метода, как и код обыкновенного метода класса Cachе, может быть реализован с использованием Cachе Object Script, Cachе Basic или Cachе SQL. Возможно использование сложных типов данных (встраиваемые объекты, коллекции, отношения и др.) в качестве аргументов и возвращаемого значения публикуемого метода. При этом Cachе автоматически создает необходимые объекты сложных типов данных при поступлении соответствующего запроса на сервер. Пример web-службы с одним методом приведен ниже:

Class MyApp.StockService Extends %SOAP.WebService
{
  Parameter SERVICENAME = "MyStockService";
  Parameter LOCATION = "http://localhost/csp/user";
  Parameter NAMESPACE = "http://tempuri.org";

  /// Метод возвращает цену на завтра
  ClassMethod Forecast(StockName As %String) As %Integer [WebMethod]
  {
    // применяем генератор случайных чисел
    Set price = $Random(1000)
    Quit price
  }
}

Кроме того, Cachе автоматически генерирует тестовые CSP-страницы методов и CSP-страницу каталога, которые можно использовать для проверки правильной работы создаваемой службы. При этом CSP-страницы представляются в виде обыкновенных web-приложений, доступных через стандартный браузер.

Кроме рассмотренных выше способов публикации данных СУБД Cachе в WWW можно использовать другие технологии создания web-приложений, такие как JSP или ASP. Для этого необходимо либо воспользоваться поставляемыми драйверами ODBC/JDBC, либо использовать JAVA-проекцию объектов Cachе.


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