суббота, 26 ноября 2011 г.

Berkeley DB Java Edition, ч.3

Импортировал в БД 7 записей, исходный файл занимал 2Кб, при том что большая часть это вспомогательный мусор, который в базу никоим образом попасть не может. Реальный объем данных - байт 100, не больше.

 

База после импорта заняла 100Кб. Проверил программу - ничего лишнего я туда не толкаю. Буду надеяться, что это просто резервируется место и следующие 7777 записей займут столько же сколько заняли 7. Иначе придется плотно разбираться что это за ерунда такая .

Berkeley DB Java Edition, ч.2

Для любого объекта, который хранится в BDB опосредованно (как часть другого объекта) его класс должен быть объявлен как @Persistent и иметь конструктор по умолчанию. Это не требуется только базовым типам вроде String, Long, Integer а также коллекциям (по крайней мере для ArrayList, до прочих у меня руки не дорасли).

 

И как я уже писал, если мы хотим хранить в БД потомков некоторогоо класса, то они все должны быть помечены как @Persistent (@Entity для них мы использовать уже не можем). И если мы хотим иметь к ним доступ не только через базовый класс, но и индивидуально, то они должны иметь поле помеченное как @SecondaryKey, причем имя поля должно быть уникальным среди всех потомков базового класса. Пример:

 

@Entity
class Base {
@PrimaryKey
String id;
Data data;
public Base() {}
}

@Persistent
class Data {
String somedata;
public Data() {}
}

@Persistent
class Child1 extends Base{
@SecondaryKey(relate=Relationship.ONE_TO_ONE)
String id1;
public Child1() {}
}

@Persistent
class Child2 extends Base{
@SecondaryKey(relate=Relationship.ONE_TO_ONE)
String id2;

public Child2() {}
}

Если вы фактически не используете SecondaryKey. то использовать

Relationship.ONE_TO_ONE

не обязательно, можно и

Relationship.MANY_TO_ONE

это избавит вас от заботы об уникальности ключа. Для SecondaryKey нет встроенного механизма генерации уникального значения, так сто это может быть полезно.

среда, 23 ноября 2011 г.

Berkeley DB Java Edition, ч.1

Осознал что пора задуматься о хранении данных и нахожусь в творческом поиске БД. Хочется:

  • нечто заточенное под хранение произвольных объектов с заранее неизвестной структурой. Т.е. в идеале - объектно-ориентированная БД, или просто key-value хранилище
  • нечто компактное, но потенциально расширяемое. Поднимать на недорогом (на дорогой нет денег) хостинге серьезную БД задача не для слабонервных. Это в любом случае не просто "с нуля", в том числен и с нуля знаний. MySQL в минимальном варианте я конечно подниму, но как раз MySQL мне не очень подойдет по п.1. Да и он достаточно прожорлив в плане ресурсов

По всей видимости мне нужна компактная "embedded" БД и после некоторых поисков я остановился на Berkeley DB. Она не совсем проходит по первому пункту, БД не объектно-ориентированная. Но собственно мне не так много этого объектно-ориентированного функционала и нужно. Т.е. не помешает, но ставить ради этого отдельный сервер БД я не готов.

Посмотрел еще всякие разные объектные БД, в том числе и встраиваемые в приложения, но либо откровенное не то, либо нехорошие отзывы, либо лицензия не подходящая. Попробую познакомиться поближе с BDB JE и обойти ее ограничения, которые начали всплывать практически сразу.

java.lang.IllegalArgumentException: An entity class may not be derived from another entity class

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

class Ship {
 ArrayList<Equipment> equipment;
}

Equipment это может быть и Gun и PowerGenerator и бог знает что еще. Если у меня есть необходимость хранить их как отдельные Entity (а такая необходимость в общем случае у меня есть), а не просто как часть Ship, то возникают некоторые сложности. А именно - Gun, PowerUnit и GoodOnlyKnowsWhatElse - это отдельные Entity и записать их вместе и просто получить потом по значению ключа одним запросом нельзя. Т.е:

PrimaryIndex<Key,Equipment> gunindex = store.getPrimaryIndex(Key.class,Equipment.class)

Gun gun=new Gun();
PowerUnit pu=new PowerUnit();
index.put(gun); // вылетит Exception

если же я сделаю

PrimaryIndex<Key,Gun> gunindex = store.getPrimaryIndex(Key.class,Gun.class)

PrimaryIndex<Key,PowerUnit> puindex = store.getPrimaryIndex(Key.class,PowerUnit.class)

то и записывать и получать я их потом буду отдельно:
gunindex.put(gun);
puindex.put(pu);
gun=gunindex.get(key);
pu=puindex.get(key);
а как я уже сказал - я не знаю заранее к чему относится key, т.е. придется делать обращение ко всем индексам чтобы выяснить к какому относится данный key. Либо закладывать в key информацию о типе объекта и выбирать нужный index.
Есть другой путь
Даже 2. Первый - использовать Base API вместо Direct Persistent Layer. Не вдаваясь в подробности - это возможность закопаться глубоко в недра БД и делать с ней все что захочется. Никаких надстроек, чистая работа с key-value и не важно что именно в них находится. По сути это ядро БД, на которое можно накрутить свою надстройку которая будет делать то что нужно и так как нужно именно вам. Но возни будет много.
Второй путь проще. Делаем обертку:
@Entity
class Item {
  @PrimaryKey
  Key key;
  Equipment data;
  public Item() {}
  public Item(Equipment dt) {
     data=dt;
     key=dt.getKey();
     }
}
а классы Gun, PowerUnit и GoodOnlyKnowsWhatElse помечаем как @Persistent и реализуем в них метод getKey. В результате мы имеем избыточность в данных, но зато все они вставляются и извлекаются единообразно как Item, чтобы у него там внутри не было. Можно добавить еще вторичный ключ который задает тип хранимого объекта. - избыточность возрастет еще, но можно будет выбирать объекты по их типу
Update: Все это бред
Ларчик открылся просто.
Есди у меня есть иерархия объектов, но я хочу хранить их вместе, то я должен пометить базовый класс как @Entity, а его потомки  - как @Persistent.
Тогда не нужны никакие левые обертки.

Некоторые замечания

Долго колебался, делать ли значения типа (корабля, оборудования, сообщения) цифровыми или строкой. В конце концов во многих случаях склонился к строке. Пусть это медленнее, и отнимает больше памяти но зато проще делать экспорт/импорт данных в/из человекочитаемый формат. Да и при отладке проще догадаться что означает "user.login" в поле msgtype, чем 42.

 

Жалко switch (строка) появился только в Java 1.7

вторник, 22 ноября 2011 г.

IOException: Read end dead

Сегодня затеял небольшой рефакторинг и очень удивился получив вот такой вот эксепшн и еще ряд интересных глюков. Выяснил интересную вещь:

допустим у нас есть поток 1, который создает поток 2, а сам тихо завершается - за не надобностью. Если у вас возник вопрос зачем это надо, сразу могу сказать - это только тестовый пример. В действительности у меня их запускается несколько штук, а родительский можно как-то использовать, но пока это не сделано, да и стройность программы от этого пострадает. 

Так вот, родительский поток 1 среди прочего инициализирует парочку PipedInput/OutputStream, а использоваться эта связка будет исключительно в потоке 2. Тут и ожидает сюрприз. Эти Piped как-то привязаны к потоку в котором были созданы, и если он завершается, то при попытке их использовать получаем тот самый эксепшн. Открытие неприятное ввиду того что

а) совершенно не очевидно

б) непонятно, касается ли это чего-то еще? Какие еще ресурсы не являются в этом отношении thread-safe?

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

д) непонятно, на что еще может повлиять такая зависимость? А не окажется ли, что используя некоторые объекты (те же PipedInput/OutputStream) я неявным образом могу вызывать блокировку потока 2, из-за необходимости  синхронизации его с потоком 1 (который в это время занят чем-нибудь)?

 

суббота, 19 ноября 2011 г.

Blender, цирк с конями часть вторая

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

Попробовал пересобрать блендер самостоятельно из исходников, результатов не воспоследовало. Я почти решился откатиться на 2.4х, но тут выяснилось что эта версия страшно тормозит.

Остался еще один радикальный вариант. Сбросив все данные на резервный диск и морально приготовившись ставить систему заново, я выключил комп и изъял из него видеокарту ATI Rageon X1600PRO. Осталась интегрированная в процессор (да, именно в процессор, даже не в мать, вот до чего дошла буржуйская техника) и отключенная до сих пор. Название проца и что именно там в него встроено я сейчас уже не вспомню, однако замечу - Ubuntu в свое время категорически отказалась с этой встроенной видеокартой работать, из-за чего и пришлось вскрыть покоившийся с миром старый комп и извлечь из трупа атишку.

Надо отметить, что в отличие от винды, которая обычно переносит подобные манипуляции достаточно безболезненно, линуксы крайне не любят когда им меняют видеокарту. Так что я в лучшем случае готовился запустить консоль и применить волшебное заклинание "Xorg -configure", в более вероятном - поставить систему заново, ну и в наихудшем вернуть все как было, еcли федора разделит нелюбовь убунты к встроенным видюхам.

 

Поэтому сильно удивился, когда Федора молча проигнорировала замену. Причем падения блендера, тьфу-тьфу-тьфу, прекратились. И версия 2.49 не тормозит, так что запасной аэродром у меня имеется.

З.Ы. Надо будет проверить, раньше у меня были некоторые странности в JOGL, может тоже видюха виновата? 

пятница, 18 ноября 2011 г.

Java и NPAPI

Низзя.

 

Вчера уже спать ложился, когда задумался - а нельзя ли сделать игру в виде плагина к браузеру? Сегодня с утра выяснил - в принципе можно, но не на java. Увы. Причина - интерфейс плагинов у популярных браузеров ориентирован на нативные плагины, написанные на языке Си. Либо кроссплатформенные плагины, написанные на JavaScript. Поддержки Java нет и не планируется, более того, отчетливо просматривается тенденция по выжиманию java из браузеров. Java - продукт проприетарный, и потому нелюбимый разработчиками открытого софта. Как по религиозным соображениям, так и в связи с лицензионными ограничениями. Ну а гугл со своим chrome видимо не желает плевать проив ветра и поддерживать не то чтобы конкурента... но с какой стати им вообще поддерживать постороннюю фирму?

 

Так что - апплет, вебстарт или обычное приложение. Ну и ладно, и этого хватит.