Skip to content
FINORA Academy
SistemaFINORA ERP

7. Cómo se crea un plugin paso a paso

Advanced25 minVerified on Sep 25, 2026

This content is not available in your language yet.

Este tutorial arma, paso a paso, un plugin mínimo para iDempiere 13 siguiendo exactamente el patrón que usa FINORA LVE: un bundle OSGi compilado con javac (sin Tycho), que no toca ni una línea del core. Usamos como referencia el bundle real más simple del proyecto, com.finora.lve.base, y el precedente que ya está en la versión instalada, com.finora.erp.dashboard (finora-lve/docs/ARCHITECTURE.md, ADR-LVE-006).

com.finora.lve.miplugin/
├── META-INF/
│ └── MANIFEST.MF
├── OSGI-INF/
│ └── com.finora.lve.miplugin.MiComponente.xml
├── build.properties
└── src/
└── com/finora/lve/miplugin/
├── Activator.java
└── MiComponente.java

Este layout es idéntico al de cualquier bundle del core y compatible con Tycho el día que el proyecto lo adopte (finora-lve/docs/ARCHITECTURE.md, §8) — no hay nada “simplificado” en la estructura, solo en la forma de compilarla.

  1. Manifest-Version: 1.0
    Bundle-ManifestVersion: 2
    Bundle-Name: FINORA LVE Mi Plugin
    Bundle-SymbolicName: com.finora.lve.miplugin;singleton:=true
    Bundle-Version: 1.0.0.qualifier
    Bundle-Vendor: DC GEEKS, C.A.
    Bundle-License: GPL-2.0-only
    Bundle-RequiredExecutionEnvironment: JavaSE-17
    Bundle-Activator: com.finora.lve.miplugin.Activator
    Bundle-ClassPath: .
    Require-Bundle: org.adempiere.base;bundle-version="[13.0.0,14.0.0)"
    Service-Component: OSGI-INF/*.xml

    El nombre simbólico (Bundle-SymbolicName) debe ser único en todo el servidor — por convención, com.finora.lve.<módulo> (finora-lve/docs/ARCHITECTURE.md, §2.2). Require-Bundle fija el rango de iDempiere soportado; nunca se deja abierto hacia arriba.

  2. Todo bundle necesita una clase que el contenedor pueda llamar al arrancar. Si el plugin no trae diccionario propio, puede ser tan simple como:

    package com.finora.lve.miplugin;
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    public class Activator implements BundleActivator {
    @Override
    public void start(BundleContext context) {
    // nada que hacer al arrancar: la lógica vive en componentes DS (paso 3)
    }
    @Override
    public void stop(BundleContext context) {
    }
    }

    Si el plugin sí trae 2Pack (diccionario propio), el Activator extiende Incremental2PackActivator del core en vez de implementar BundleActivator directamente — así lo hace com.finora.lve.base.Activator (ver Tutorial 6 y Tutorial 8).

  3. Registra un componente con Declarative Services

    Sección titulada «Registra un componente con Declarative Services»

    Cualquier lógica real (un event handler, un proceso, un servicio) se registra como componente DS, no dentro del Activator. Primero la clase:

    package com.finora.lve.miplugin;
    public class MiComponente {
    public void activate() {
    // se ejecuta cuando OSGi resuelve las dependencias de abajo
    }
    }

    Y el XML que lo declara:

    <?xml version="1.0" encoding="UTF-8"?>
    <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
    name="com.finora.lve.miplugin.MiComponente" activate="activate">
    <implementation class="com.finora.lve.miplugin.MiComponente"/>
    </scr:component>

    Si el componente necesita un servicio de iDempiere (por ejemplo IEventManager para un event handler), se agrega como <reference> — exactamente como en el ejemplo de com.finora.lve.base.event.BPartnerTaxIdHandler.xml del tutorial anterior.

  4. FINORA LVE no invoca javac a mano por bundle: un único script, tools/build.sh, hace todo el pipeline por cada bundle en orden de dependencias:

    Ventana de terminal
    tools/build-sdk.sh --from-dir <idempiere>/plugins # fija los jars del SDK por sha256
    tools/build.sh # lint de manifest + javac --release 17
    # + JUnit 5 + empaquetado + distribución

    (finora-lve/docs/ARCHITECTURE.md, §8). El resultado es un directorio o jar com.finora.lve.miplugin_1.0.0.<qualifier>/ listo para copiar a plugins/ del servidor y agregar a bundles.info (ver Tutorial 2).

  • No hay pom.xml ni Tycho. El build es javac --release 17 contra un SDK de jars fijados por sha256 (tools/build-sdk.lock) — segundos, no minutos, y sin depender de un repositorio p2 que el equipo no controla (finora-lve/docs/ARCHITECTURE.md, ADR-LVE-006).
  • No hay resolución OSGi en tiempo de build. Un Import-Package faltante no se detecta hasta que el bundle intenta arrancar en un servidor real — el build lo compensa con lint de manifiesto (nombre simbólico = carpeta, versión consistente, clases de los componentes DS realmente compiladas) y una prueba de humo en DEV.
  • No hay cambios al core. Ni un archivo de org.adempiere.* se toca; todo lo que este plugin necesita del core lo obtiene por Require-Bundle/Import-Package y por los mecanismos de extensión que el core ya expone (event handlers, fábricas — ver Tutorial 9).

Copiar la carpeta a plugins/, editar bundles.info y reiniciar el servicio no garantiza que el bundle haya arrancado bien — un Import-Package faltante o un XML de componente mal formado falla en silencio si no se revisa. La consola de Equinox (accesible por telnet al puerto configurado, o desde la salida estándar del proceso si corre en primer plano) resuelve la duda en tres comandos:

osgi> ss com.finora.lve.miplugin
id State Bundle
87 ACTIVE com.finora.lve.miplugin_1.0.0.qualifier
osgi> diag 87
com.finora.lve.miplugin_1.0.0.qualifier [87] has no unresolved dependencies
osgi> start 87

ss (short status) muestra el estado del bundle: ACTIVE es lo esperado; INSTALLED o RESOLVED sin llegar a ACTIVE casi siempre significa un componente DS que no pudo activarse. diag explica por qué un bundle no resuelve — normalmente un Require-Bundle/Import-Package que no encuentra su proveedor en el servidor, el error más común al mover un plugin de un ambiente a otro. Revisar esto antes de asumir que “no aparece en el menú” es un bug de la ventana es el primer paso de cualquier diagnóstico de plugin nuevo.

  • finora-lve/plugins/com.finora.lve.base/ (MANIFEST.MF, Activator.java, OSGI-INF/*.xml reales)
  • finora-lve/docs/ARCHITECTURE.md, §2.2, §2.3 y §8 (convenciones de nombres, capas de un bundle, ADR-LVE-006)
  • finora-erp-experience/idempiere/plugins/com.finora.erp.dashboard/ (precedente ya en la versión instalada, compilado con el mismo enfoque)