Хорошо, каждый компонент, OSGi bundle, загружается в своем ClassLoader. И выгружается, как только пропадают связи других компонентов на текущий компонент. Это теория. А что у нас с практикой?
На практике совсем плохо. При поднятии компонента, BundleListener читает манифест и видит параметр «Meta-Persistence». Создается OSGi Service, который привязан к данному компоненту. Далее, этот сервис регистрируется в ServiceTracker. Для чего понадобился сервис? Для итого, чтобы он мог свободно получать доступ к классам Entity объектов. Сервис создает JTA фабрику.
Как вы думаете, когда освобождается данный сервис? Правильный ответ — когда OSGi Framework останавливает OSGi Bundle, в котором зарегистрирован сервис. Другими словами, по доброй воле сервис не останавливается. Получается, что память от созданных объектов освобождается, а сами классы не выгружаются, так как JTA фабрика не разрушается в работающем сервисе. Как то так.
Альтернатива этому мероприятию — Declarative Services. OSGi Framework поднимает все компоненты (OSGi bundle). Если есть активатор, он будет запущен. Есть параметр «Meta-Persistence», система создаст сервис с поднятой JPA фабрикой. Есть spring или blueprint, создаст сервисы для этих служб.
Декларативный сервис — может тихо проследовать дальше, не поднимая классов. Да, есть аналог BundleActivator, который заставит загрузить классы.
Декларативный сервис тем и полезен, что создается по требованию, когда из ServiceReferenсe требуем сам сервис. Как только сервис освобождается, с помощью ungetService, он сразу разрушается. Ни кто не держит BundleContext и классы из ClassLoader могут свободно выгружаться.
Если созданный декларативный сервис захватить с помощью ServiceTracker, мы не получим выигрыша. ServiceTracker по доброй воле не отпускает захваченные сервисы. Ему надо говорить close().
Честно, я не видел спецификации, как в JPA можно динамически загружать и выгружать сведения об Entity компонентах. В JDO спецификации такой функционал предусмотрен.
Зачем это нужно? Для примера рассмотрим WEB сервис, принимающий разные типы документов в систему. Каждый вызов метода приема нового документа ведет к обработке одного типа документа. Десятки других типов документов не нужны. Процесс поднятия JTA Factory очень дорогая операция. По этой причине, она идет в момент поднятия OSGi Bundle и регистрации сервиса.
В MyBatis можно загружать mapper интерфейсы по необходимости. Тем самым, поднятие фабрики в MyBatis становится дешевой операцией.
В OSGi, для JTA сейчас используется описанный вами подход.
В файле манифеста указывается параметр «Meta-Persistence». Всю работу взваливает на себя система хуков (Bundle Hook Service
Specification).
JTA не всегда подходит под специфику задач. Скажем, есть у нас несколько версий документов. Для понимания, представим документ в виде XML структуры (xsd описание). По мере развития системы структуры документов изменялись. В системе есть WEB сервис для импорта документов. Для примера, скажем, документов из системы 1С.
Напрашивается решение из нескольких OSGi компонентов, каждый для своей версии структуры документов.
Каждый OSGi компонент приводит к сканированию набора классов, Entity компонентов.
Через некоторое время, старые версии документов будут встречаться всё реже и реже. Значит, наша система должна уметь каким-то образом выгружать все классы OSGi компонентов. В том числе, весь Java Reflection, задействованный в MyBatis или в JPA.
На текущий момент, единственным решением является «Declarative Services Specification». Сведения о сервисе опубликованы в реестре, а классы не подняты. При освобождении сервиса, ungetService, класс может быть выгружен OSGi Framework. Далее, GC может очистить раздел JVM permgen от занимаемых классов.
OSGi hook не дадут нам нужного эффекта — освобождения JVM permgen. Нужно полностью останавливать компонент.
JTA реализация в JPA выглядит красиво для конечного потребителя. На самом деле, это очень большой и запутанный раздел. EntityManager не может ответить, работает ли в соединение в распределенной транзакции или нет. Даже запросив TransactionManager на предмет активности транзакции, мы не знам, работает ли текущий EntityManager в рамках этой транзакции или в рамках другой транзакции.
На практике совсем плохо. При поднятии компонента, BundleListener читает манифест и видит параметр «Meta-Persistence». Создается OSGi Service, который привязан к данному компоненту. Далее, этот сервис регистрируется в ServiceTracker. Для чего понадобился сервис? Для итого, чтобы он мог свободно получать доступ к классам Entity объектов. Сервис создает JTA фабрику.
Как вы думаете, когда освобождается данный сервис? Правильный ответ — когда OSGi Framework останавливает OSGi Bundle, в котором зарегистрирован сервис. Другими словами, по доброй воле сервис не останавливается. Получается, что память от созданных объектов освобождается, а сами классы не выгружаются, так как JTA фабрика не разрушается в работающем сервисе. Как то так.
Альтернатива этому мероприятию — Declarative Services. OSGi Framework поднимает все компоненты (OSGi bundle). Если есть активатор, он будет запущен. Есть параметр «Meta-Persistence», система создаст сервис с поднятой JPA фабрикой. Есть spring или blueprint, создаст сервисы для этих служб.
Декларативный сервис — может тихо проследовать дальше, не поднимая классов. Да, есть аналог BundleActivator, который заставит загрузить классы.
Декларативный сервис тем и полезен, что создается по требованию, когда из ServiceReferenсe требуем сам сервис. Как только сервис освобождается, с помощью ungetService, он сразу разрушается. Ни кто не держит BundleContext и классы из ClassLoader могут свободно выгружаться.
Если созданный декларативный сервис захватить с помощью ServiceTracker, мы не получим выигрыша. ServiceTracker по доброй воле не отпускает захваченные сервисы. Ему надо говорить close().
Честно, я не видел спецификации, как в JPA можно динамически загружать и выгружать сведения об Entity компонентах. В JDO спецификации такой функционал предусмотрен.
Зачем это нужно? Для примера рассмотрим WEB сервис, принимающий разные типы документов в систему. Каждый вызов метода приема нового документа ведет к обработке одного типа документа. Десятки других типов документов не нужны. Процесс поднятия JTA Factory очень дорогая операция. По этой причине, она идет в момент поднятия OSGi Bundle и регистрации сервиса.
В MyBatis можно загружать mapper интерфейсы по необходимости. Тем самым, поднятие фабрики в MyBatis становится дешевой операцией.
В файле манифеста указывается параметр «Meta-Persistence». Всю работу взваливает на себя система хуков (Bundle Hook Service
Specification).
JTA не всегда подходит под специфику задач. Скажем, есть у нас несколько версий документов. Для понимания, представим документ в виде XML структуры (xsd описание). По мере развития системы структуры документов изменялись. В системе есть WEB сервис для импорта документов. Для примера, скажем, документов из системы 1С.
Напрашивается решение из нескольких OSGi компонентов, каждый для своей версии структуры документов.
Каждый OSGi компонент приводит к сканированию набора классов, Entity компонентов.
Через некоторое время, старые версии документов будут встречаться всё реже и реже. Значит, наша система должна уметь каким-то образом выгружать все классы OSGi компонентов. В том числе, весь Java Reflection, задействованный в MyBatis или в JPA.
На текущий момент, единственным решением является «Declarative Services Specification». Сведения о сервисе опубликованы в реестре, а классы не подняты. При освобождении сервиса, ungetService, класс может быть выгружен OSGi Framework. Далее, GC может очистить раздел JVM permgen от занимаемых классов.
OSGi hook не дадут нам нужного эффекта — освобождения JVM permgen. Нужно полностью останавливать компонент.
JTA реализация в JPA выглядит красиво для конечного потребителя. На самом деле, это очень большой и запутанный раздел. EntityManager не может ответить, работает ли в соединение в распределенной транзакции или нет. Даже запросив TransactionManager на предмет активности транзакции, мы не знам, работает ли текущий EntityManager в рамках этой транзакции или в рамках другой транзакции.
Вот, где окажется flush или persist?
Я скажу — в t_old. А ведь мы сказали, затормозить старую транзакцию и начать новую.
В будущей версии mybatis-guice этот вопрос решен. Весь код, в методе выполнится в новой транзакции.
А в JTA, нам надо открыть новый EntityManager. Поправьте меня, если это не так.