6. Qué es OSGi
This content is not available in your language yet.
OSGi (Open Services Gateway initiative) es la especificación de modularidad para Java que iDempiere adoptó al pasar de ADempiere a iDempiere en 2012 (ver Tutorial 1). En vez de un único classpath gigante con todo el código del ERP, el servidor es un conjunto de bundles — jars (o directorios) con su propio manifiesto de dependencias — que un contenedor OSGi (iDempiere usa Equinox, la implementación de Eclipse) resuelve, arranca y conecta entre sí en tiempo de ejecución. Todo plugin de FINORA LVE es, técnicamente, un bundle OSGi más.
El manifiesto: la identidad y las dependencias de un bundle
Sección titulada «El manifiesto: la identidad y las dependencias de un bundle»Cada bundle declara en META-INF/MANIFEST.MF quién es y qué necesita. Este es el manifiesto real de
com.finora.lve.base:
Manifest-Version: 1.0Bundle-ManifestVersion: 2Bundle-Name: FINORA LVE BaseBundle-SymbolicName: com.finora.lve.base;singleton:=trueBundle-Version: 1.0.0.qualifierBundle-Vendor: DC GEEKS, C.A.Bundle-License: GPL-2.0-onlyBundle-RequiredExecutionEnvironment: JavaSE-17Bundle-Activator: com.finora.lve.base.ActivatorBundle-ClassPath: .Require-Bundle: org.adempiere.base;bundle-version="[13.0.0,14.0.0)", org.adempiere.plugin.utils;bundle-version="[13.0.0,14.0.0)"Import-Package: org.osgi.framework;version="1.9.0", org.osgi.service.event;version="1.4.0"Export-Package: com.finora.lve.base;version="1.0.0", com.finora.lve.base.model;version="1.0.0", com.finora.lve.base.process.setup;version="1.0.0"Service-Component: OSGI-INF/*.xml(finora-lve/plugins/com.finora.lve.base/META-INF/MANIFEST.MF)
Punto por punto:
Bundle-SymbolicNamees el identificador único del bundle en todo el servidor —com.finora.lve.base, no un nombre de clase.singleton:=truesignifica que solo puede haber una versión de este bundle activa a la vez (el patrón normal para bundles de aplicación, a diferencia de librerías que pueden convivir en varias versiones).Require-Bundledeclara una dependencia de otro bundle completo, con un rango de versión:[13.0.0,14.0.0)significa “cualquier 13.x, nunca un 14”. Esto es lo que impide que FINORA LVE 1.x arranque por accidente sobre un iDempiere 14 sin haber sido probado (finora-lve/docs/ARCHITECTURE.md, §2.4).Import-Packagedeclara una dependencia de paquetes específicos, más fino que un bundle entero — útil para depender solo de las interfaces del framework OSGi mismo (org.osgi.framework), sin acoplarse a todo un bundle.Export-Packagees lo contrario: qué paquetes de este bundle pueden importar otros bundles. Un paquete que no se exporta es privado, aunque esté en un jar público.Bundle-Activatores la clase que el contenedor llama al arrancar y detener el bundle (más abajo).Service-Component: OSGI-INF/*.xmlle dice a OSGi dónde buscar los componentes de Declarative Services (siguiente sección) — el comodín permite que cada paquete de trabajo agregue su propio XML sin tener que editar el manifiesto compartido cada vez (finora-lve/docs/ARCHITECTURE.md, §4.1).
El ciclo de vida de un bundle
Sección titulada «El ciclo de vida de un bundle»stateDiagram-v2
[*] --> INSTALLED: se copia a plugins/ y se agrega a bundles.info
INSTALLED --> RESOLVED: Require-Bundle e Import-Package satisfechos
RESOLVED --> STARTING: el contenedor lo arranca
STARTING --> ACTIVE: Activator.start() termina sin error
ACTIVE --> STOPPING: se detiene o se actualiza
STOPPING --> RESOLVED: Activator.stop() termina
RESOLVED --> UNINSTALLED: se retira
Un bundle que declara una dependencia que no existe en el servidor se queda en INSTALLED
(nunca resuelve) — es el error más común al desplegar un plugin nuevo, y casi siempre significa una
versión de Require-Bundle que no coincide con lo instalado.
Activator: código que corre al arrancar el bundle
Sección titulada «Activator: código que corre al arrancar el bundle»public class Activator extends LvePackActivator { @Override protected String[] requiredBundles() { return new String[0]; // base no depende de otro bundle LVE }}LvePackActivator extiende a su vez Incremental2PackActivator del core, que en su método
start() busca los 2Pack empaquetados dentro del propio bundle y los aplica contra la base de datos
(ver Tutorial 8). Este es el patrón que usa FINORA para “instalar
diccionario” sin ningún paso manual: el simple hecho de que el bundle llegue a ACTIVE dispara la
actualización.
Declarative Services: registrar un componente sin tocar el Activator
Sección titulada «Declarative Services: registrar un componente sin tocar el Activator»Escribir toda la lógica en el Activator no escala — cada nuevo event handler, proceso o modelo
tendría que registrarse ahí a mano. Declarative Services (DS) resuelve esto: un archivo XML
describe un componente y sus dependencias de servicios OSGi, y el contenedor lo instancia y conecta
solo, sin que el Activator sepa nada de él.
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0" immediate="true" name="com.finora.lve.base.MappedFactories" activate="activate"> <implementation class="com.finora.lve.base.MappedFactories"/> <reference cardinality="1..1" field="mappedModelFactory" interface="org.idempiere.model.IMappedModelFactory" name="mappedModelFactory"/> <reference cardinality="1..1" field="mappedProcessFactory" interface="org.idempiere.process.IMappedProcessFactory" name="mappedProcessFactory"/></scr:component>public class MappedFactories { static final String[] MODEL_PACKAGES = { "com.finora.lve.base.model" }; static final String[] PROCESS_PACKAGES = { "com.finora.lve.base.process.fx", "com.finora.lve.base.process.setup" };
private IMappedModelFactory mappedModelFactory; private IMappedProcessFactory mappedProcessFactory;
public void activate(BundleContext context) { mappedModelFactory.scan(context, MODEL_PACKAGES); mappedProcessFactory.scan(context, PROCESS_PACKAGES); }}El XML dice “cuando existan los servicios IMappedModelFactory e IMappedProcessFactory en el
contenedor, inyéctalos en estos campos y llama a activate”. Ese componente, a su vez, le pide a esas
fábricas del core que escaneen los paquetes de este bundle en busca de clases anotadas
(@Model, @Process, @Callout) y las registren — el mecanismo exacto que usa FINORA LVE para dar
de alta su lógica de negocio (ver Tutorial 9).
Fuentes
Sección titulada «Fuentes»finora-lve/plugins/com.finora.lve.base/META-INF/MANIFEST.MFfinora-lve/plugins/com.finora.lve.base/src/com/finora/lve/base/Activator.javafinora-lve/plugins/com.finora.lve.base/OSGI-INF/com.finora.lve.base.MappedFactories.xmlfinora-lve/docs/ARCHITECTURE.md, §2.4, §4.1 y §8 (versionado lockstep, event handlers OSGi, ADR-LVE-006)- OSGi Alliance — especificaciones
- Documentación de Equinox — Eclipse Foundation