 | Уровень сложности: простой Тед Ньюворд, директор, Neward & Associates
19.01.2009 Сохранение объектов непосредственно в объектно-ориентированной базе данных, такой как db4o, имеет множество преимуществ для Java™-разработчиков. Однако они будут фактически сведены на нет при отсутствии поддержки транзакционности, распределенной работы с данными и обеспечения безопасности. В данной статье, являющейся последней частью серии "Руководство по db4о для Java-разработчика", Тед Ньворд (Ted Neward) расскажет о возможностях db4o для поддержки ключевых аспектов разработки на Java EE: транзакциях, распределенном управлении данными и обеспечении безопасности Web-приложений.
 |
Об этой серии В последние десять лет хранение и поиск информации прочно ассоциировались исключительно с реляционными СУБД. Однако недавно появилась тенденция к изменению текущего положения дел. В частности, Java-разработчики все больше жалуются на плохую совместимость объектной и реляционной моделей данных (так называемый "object-oriented mismatсh") и с нетерпением ждут решения этой проблемы. Все это, а также появление применимых на практике альтернатив привело к возрождению интереса к объектно-ориентированному хранению и поиску данных. Серия "Путеводитель по db4o для Java-разработчика" представляет db4o – объектную базу данных с открытым кодом. db4o дополняет существующие объектно-ориентированные языки, системы и принципы проектирования. Скачайте db4o со страницы проекта - она вам понадобится для запуска примеров. |
|
В предыдущих статьях серии я освещал основы объектно-ориентированной работы с данными в db4o. Одним из вопросов, которые я обошел вниманием, является использование ООСУБД в Web-приложениях и чем оно отличается от использования в приложениях Swing или SWT. Кто-то может сказать, что я проигнорировал целый круг вопросов, которые обязан знать любой .NET- или Java-разработчик.
Дело в том, что я хотел сосредоточиться на наиболее привлекательных возможностях ООСУБД: объектно-ориентированном хранении, манипулировании и выборке данных. К тому же поставщики ООСУБД как правило реализуют базовые функции, такие как управление транзакциями и обеспечение безопасности, аналогично РСУБД, предоставляя столь же широкий набор опций.
В этой статье - последней части серии "Руководство db4о для Java-разработчика" - я расскажу о возможностях, которые должны присутствовать в любой системе для хранения данных, будь то объектно-ориентированная, реляционная или какая-либо еще. Вздохните поглубже и приготовьтесь слушать про обеспечение безопасности, распределенный доступ и транзакционность в db4o.
Поддержка множественных соединений
В примерах всех предыдущих частей подразумевалось, что только одно клиентское приложение будет работать с базой данных. Поэтому будет достаточно всего одного логического соединения, через которое будет проходить весь обмен данными. Это вполне логичное допущение для приложения Swing или SWT, работающего с базой данных настроек или локальным хранилищем. Но это гораздо менее реалистично для Web-приложения, даже если допустить, что все данные будут храниться в слое Web-представления.
В db4o открытие дополнительного логического соединения с базой данных является тривиальной задачей, даже если база данных расположена на локальном диске. Все, что требуется – это создать объект ObjectServer и получить от него экземпляр класса ObjectContainer. Если указать объекту ObjectServer, чтобы он ожидал входящие соединения через порт 0, то он будет работать во "встроенном" (embedded) режиме. Таким образом, в нашем пробном тесте он не будет открывать реальные порты TCP/IP, что могло бы помешать другим приложениям (листинг 1).
Листинг 1. Множественные соединения во встраиваемом режиме
@Test public void letsTryMultipleEmbeddedClientConnections()
{
ObjectServer server = Db4o.openServer("persons.data", 0);
try
{
ObjectContainer client1 = server.openClient();
Employee ted1 = (Employee)
client1.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next();
System.out.println("client1 found ted: " + ted1);
ObjectContainer client2 = server.openClient();
Employee ted2 = (Employee)
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next();
System.out.println("client2 found ted: " + ted2);
ted1.setTitle("Lord and Most High Guru");
client1.set(ted1);
System.out.println("set(ted1)");
System.out.println("client1 found ted1: " +
client1.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
System.out.println("client2 found ted2: " +
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
client1.commit();
System.out.println("client1.commit()");
System.out.println("client1 found ted1: " +
client1.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
System.out.println("client2 found ted2: " +
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
client2.ext().refresh(ted2, 1);
System.out.println("After client2.refresh()");
System.out.println("client2 found ted2: " +
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
client1.close();
client2.close();
}
finally
{
server.close();
}
}
|
Обновление представления объектов
Обратите внимание на строки для отладочного вывода в середине тестового примера. Очень важно постоянно контролировать состояние объектов, потому что db4o хранит ссылки на объекты, доступ к которым был получен ранее. Необходимо точно знать, как и когда изменения в данных объектах становятся видимыми для второго клиента.
Например, после вызова метода set(ted1) db4o помечает объект ted1 как "грязный" и требующий обновления. Но само обновление не происходит до момента подтверждения неявной транзакции с помощью вызова commit() класса ObjectContainer. В этот момент данные сохраняются на диске, но client2 по-прежнему видит предыдущее состояние объектов. Убедитесь в этом, просмотрев отладочный вывод демонстрационного тестового примера. Ведь вместе с браузером у вас открыто и окно консоли, в котором вы запускаете тесты, читая статьи этой серии, не так ли?
С этой проблемой легко справиться: client2 должен обновить свое представление графа объектов в памяти, вызвав метод refresh() объекта extension, который можно получить с помощью метода ext(). При этом необходимо помнить о глубине активации: она определяет, насколько далеко db4o должен углубиться в граф, обновляя состояние объектов. В нашем случае достаточно глубины, равной единице, но вообще решение должно приниматься с учетом конкретной ситуации.
client2 увидит изменения сразу после обновления представления объектов. В этом легко убедиться, выполнив запрос, возвращающий новое имя главы компании (возможно, оно выглядит несколько эгоистично).
Разница останется невидимой
Как правило наличие нескольких клиентов означает, что разные процессы обращаются к базе данных. Например, это может быть группа клиентов внутри сервлет-контейнера, обращающихся к одному серверу по классической схеме типа "клиент-сервер". В db4o это организуется практически так же, как показано в листинге 1. Единственное очевидное различие в том, что понадобится ненулевой номер порта для обращения к серверу. Он соответствует номеру порта TCP/IP, через который сервер ожидает соединения. Как и всегда при обращении по протоколу TCP/IP, клиенты должны указывать имя хоста и номер порта при запросе соединения.
Обеспечение безопасности
Как только появилось понятие "порт", естественно, сразу встал вопрос безопасности, потому что нельзя позволить, чтобы "кто угодно" подключался к серверу и начинал выполнять запросы. Поставщики традиционных РСУБД сами предоставляют мощную модель безопасности, позволяющую получать доступ ко всей базе данных или ее фрагментам после авторизации с помощью имени пользователя и пароля. Эти параметры доступа передаются на сервер при запросе на установление соединения.
Реализация db4o ничем не отличается, по крайней мере внешне. Серьезным отличием от РСУБД является то, как при создании экземпляра базы данных устанавливается политика безопасности. Пример показан в листинге 2.
Листинг 2. Пример взаимодействия
@Test public void letsTryMultipleNetworkClientConnections()
{
ObjectServer server = Db4o.openServer("persons.data", 2771);
server.grantAccess("client1", "password");
server.grantAccess("client2", "password");
// Нет ничего хуже пароля "password". Никогда так не делайте в
// реальном приложении.
// Я могу это себе позволить, потому что у меня есть специальная
// педагогическая лицензия от Sun. А у вас нет.
// Так что никогда так не поступайте и не заставляйте меня
// возвращаться к этому. Я серьезно. Просто забудьте об этом. Ясно?
try
{
ObjectContainer client1 =
server.openClient("localhost", 2771, "client1", "password");
ObjectContainer client2 =
server.openClient("localhost", 2771, "client1", "password");
Employee ted1 = (Employee)
client1.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next();
System.out.println("client1 found ted: " + ted1);
Employee ted2 = (Employee)
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next();
System.out.println("client2 found ted: " + ted2);
ted1.setTitle("Lord and Most High Guru");
client1.set(ted1);
System.out.println("set(ted1)");
System.out.println("client1 found ted1: " +
client1.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
System.out.println("client2 found ted2: " +
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
client1.commit();
System.out.println("client1.commit()");
System.out.println("client1 found ted1: " +
client1.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
System.out.println("client2 found ted2: " +
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
client2.ext().refresh(ted2, 1);
System.out.println("After client2.refresh()");
System.out.println("client2 found ted2: " +
client2.get(
new Employee("Ted", "Neward", null, null, 0, null))
.next());
client1.close();
client2.close();
}
finally
{
server.close();
}
}
|
Описание прав доступа в db4o состоит из единственного вызова метода grantAccess(). При этом доступ предоставляется ко всему экземпляру базы данных. В этом есть как плюсы, так и минусы. Плюсом является упрощение процесса определения прав доступа, а минусом - невозможность применить принцип наименьших привилегий.
 |
Принцип наименьших привилегий
Как и множество правил по обеспечению безопасности, принцип наименьших привилегий выглядит простым в теории, но иногда трудно реализуемым на практике. Принцип гласит, что каждому пользователю или программе необходимо предоставить минимальный набор привилегий, обязательных для выполнения его функций. Нельзя предоставлять больше (как, разумеется, и меньше) прав. В частности, при работе с РСУБД программа должна иметь разрешения на выполнение операторов SELECT/INSERT/UPDATE/DELETE только над теми таблицами, к которым ей необходимо обращаться. Таким образом, даже если код подвергается атаке через SQL-инъекцию, он не сможет выполнить опасные операции из-за ограниченных прав доступа.
|
|
db4o не предоставляет возможностей более тонкой работы по обеспечению безопасности, хотя другие ООСУБД могут быть более гибкими. Поэтому необходимо позаботиться о том, чтобы использовать как можно меньше учетных записей. Если невозможно ограничить набор ресурсов, к которым получит доступ успешно авторизовавшийся клиент, то, по крайней мере, можно ограничить список легальных пользователей.
Форматы шифрования
Принципы обеспечения безопасности в распределенных системах, особенно ошибка № 4 при создании систем уровня предприятия (см. ссылку на книгу "Effective Enterprise Java" в разделе Ресурсы), гласят, что нельзя полагаться на то, что доступ к сетевому трафику будут иметь только доверенные люди и программы. А значит недопустимо, чтобы данные, передаваемые по сети, были в незащищенном текстовом или двоичном виде (бинарный код, при условии открытости формата, ничем не отличается от текста в плане безопасности).
Разработчики, озабоченные проблемой безопасности, должны также осознавать, что данные, находящиеся в файлах db4o, тоже нуждаются в защите, так как ничто не мешает нарушителю получить доступ к файлу и прочитать его содержимое. Обратите внимание, что эта проблема существует и для РСУБД.
В качестве решения можно зашифровать файл, что в db4о делается достаточно просто. В большинстве случаев стандартный алгоритм шифрования, применяющийся в db4o – XTEA (eXtended Tiny Encryption Algorithm) – работает достаточно надежно, маскируя данные от случайного нарушителя. В остальных ситуациях можно использовать специальный прием, позволяющий подключить сторонний алгоритм шифрования. (Не стоит использовать собственный формат шифрования, если только вы не сумели написать статью, показывающую, как взломать существующие стандарты, которую бы испытали на прочность лучшие мировые криптографы и математики, а также защитить ее, выступив на одной из крупнейших конференций по безопасности. После этого вы можете подумать об этом. Но не забывайте, что все перечисленное выше не гарантирует отсутствие дыр в безопасности).
Вы меня не видите! Как и мои данные
Защита данных, передаваемых через проводные соединения, не так очевидна, потому что вплоть до версии 6.3 db4o не поддерживал обмен по безопасным протоколам, например, SSL с помощью SecureSocket. Это означает, что любые важные данные необходимо передавать в зашифрованном виде, т.е. шифрование должно происходить внутри объектов. (Конечно, было бы удобнее, если бы db4o делала это сама. К моменту написания этой статьи стало известно, что в db4o 6.4 планируется предоставить дополнительный параметр SocketFactory в методе openServer, так что станет возможным передавать SecureSocket вместо незащищенного Socket.)
Учтите, что защищать данные можно таким комбинированным способом как использование нестандартных маршаллеров. Как следует из названия, это дает возможность контролировать процесс упаковки (и распаковки) данных перед пересылкой. Принцип работы очень похож на нестандартную сериализацию через интерфейс Externalizable: необходимо создать класс, реализующий интерфейс ObjectMarshaller, переопределить методы readFields() и writeFields(), а затем указать db4o, что для конкретного типа объектов необходимо использовать данный маршаллер. Для этого следует вызвать метод marshallWith() экземпляра ObjectClass нужного класса. Это делается следующим образом:
Db4o.configure().objectClass(Item.class).marshallWith(customMarshaller);
|
Такой подход не обеспечивает полную защиту: нарушители все же смогут увидеть тип зашифрованных объектов. Но это не позволит им получить доступ к данным объектов при их передаче между узлами сети.
В ситуациях, когда выбора между "встраиваемым" и "клиент-серверным" режимами работы не хватает для ваших нужд, например, если требуется сохранять данные в заданном формате или в нестандартном хранилище, можно воспользоваться еще одной возможностью db4o: создать класс-наследник IoAdaptor, который вызывается db4o при сохранении объектов. Это обеспечивает уровень гибкости при сохранении данных, не поддерживаемый большинством РСУБД.
Заключение
Существует множество тем для обсуждения ООСУБД и db4o, но я осветил тот круг вопросов, который я для себя наметил, и решил пока на этом остановиться. Мне кажется, что я неплохо описал преимущества db4o и объектно-ориентированного управления данными и рассказал обо всех возможностях, отличающих ООСУБД от РСУБД с точки зрения разработчика Java. Я продемонстрировал, как db4o может работать со связанными данными, как наследование используется в качестве первостепенного понятия внутри базы данных, а также как происходит выборка объектов с помощью родного (native) языка программирования, использующегося для их описания.
Я также старался обрисовать недостатки ООСУБД и ситуации, в которых db4o сталкивается с теми же проблемами, что и РСУБД, например, проблемой производительности, связанной с циклическими обращениями (round-trips) между клиентом и сервером.
Если вы прорабатывали примеры и экспериментировали с фрагментами кода, прилагающимися к статьям этой серии, то вы получили базовые навыки, необходимые для работы с ООСУБД в целом, а не только с db4o. Неплохим упражнением для закрепления материала может стать применение этих навыков при использовании других ООСУБД, например, Cache или Versant. Большинство ООСУБД используют одинаковые стандарты кодирования и идиоматические выражения. Более того, поддержка родных (native) запросов в db4o привела к попыткам стандартизировать эту возможность в качестве обязательной функции всех ООСУБД.
Надеюсь, что статьи удовлетворили ваш интерес, и вы узнали все необходимое, чтобы начать использовать ООСУБД в своих проектах. Вполне возможно, что вам понравится работать, не заботясь о реляционных схемах и всех сопутствующих деталях. Другими словами, экспериментируйте, программируйте, получайте удовольствие и не забудьте отправить мне открытку о ваших впечатлениях (впрочем, e-mail меня вполне устроит).
Ресурсы Научиться
- Оригинал статьи: "The busy Java developer's guide to db4o: Transactions, distribution, and security. (EN)
- Прочитайте другие статьи серии "Путеводитель по db4o для Java-разработчика", представляющей db4o – объектную БД с открытым кодом, дополняющую современные объектно-ориентированные языки и платформы (Тед Ньюворд, developerWorks).
- Прочитайте статью "В погоне за качеством кода" (Эндрю Гловер, developerWorks), где рассказывается о различных методах тестирования, таких, например, как исследовательское тестирование. (EN)
- Ознакомьтесь со статьей "The Eight Fallacies of
Distributed Computing", авторство которой общепризнанно принадлежит Питеру Дойчу, 1994 г.). Арнон Ротем-Гал-Оз (Arnon Rotem-Gal-Oz) позже писал на тему ошибок при распределенных вычислениях, но с более современной точки зрения. (EN)
- Прочитайте книгу Effective Enterprise Java (Тед Ньюворд, Addison-Wesley Professional, август 2004 г.), в которой рассматриваются те же "8 ошибок", но применительно к созданию корпоративных приложений на Java. (EN)
- В статье "When to Use an ODBMS" (Рик Грехэн, ODBMS.org) перечисляются основные ситуации, в которых оправдано использование ООСУБД, подобных db4o. (EN)
- Обратите внимание на The Database Column – поддерживаемый несколькими авторами блог, посвященный вопросам исследований и инноваций в области технологий баз данных. (EN)
- Прочитайте больше о db4o на странице проекта. (EN)
- Посетите ресурс ODBMS.org, где вы найдете великолепное собрание бесплатных материалов по объектным базам данных. (EN)
- Статьи по всем аспектам программирования на Java можно найти на сайте developerWorks, в разделе Технология Java.
- Если вы еще не готовы к переходу на ООСУБД, то зайдите на страницу: "Обзор решений IBM для обработки информации" за дополнительными материалами о семействе мощных реляционных СУБД (RDBMS) от компании IBM.
Получить продукты и технологии
-
Скачайте db4o: объектную базу данных с открытым кодом для Java и .NET. (EN)
Обсудить
Об авторе  | |  | Тед Ньюворд (Ted Neward) является директором компании “Neward & Associates”. Он занимается консультированием, преподаванием и презентациями продуктов на основе Java, .NET, XML-сервисов и других платформ. В настоящее время он живет недалеко от Сиэттла, штат Вашингтон. |
Выскажите мнение об этой странице
|  |