luni, mai 03, 2010

Pluginabilitate in aplicatiile web | JavaRomania

Introducere

Unul din lucrurile interesante pe care le-am observat in timp este pluginabilitatea aplicatiilor. Aceasta inseamna adaugarea de functionalitati noi in mod dinamic in runtime prin activarea/incarcarea unor pluginuri. Este cazul multor soft-uri deja consacrate dintre care cel mai cunoscut pentru dezvoltatorii de Java este Eclipse. Eclipse este in sine un conglomerat de plugin-uri care de la o versine la alta se bazeaza pe OSGi. Un alt exeplu pentru dezvoltatrii de Java este Hudson: http://wiki.hudson-ci.org/display/HUDSON/Plugin+tutorial.
In cazul aplicatiilor Web facute in Java exista deja cateva exemple de pluginabilitate. Unul din ele ar fi Jira si in general produdsele de la Atlassian care se bucura de asa ceva (a se vedea aici informatii despre sistemul lor de plugin-uri: http://confluence.atlassian.com/display/DEVNET/How+to+Build+an+Atlassian+Plugin). Aplicatiile din portofoliul companiei Atlassian permit crearea de plugin-uri de catre terte persoane prin care se imbogatesc functionalitatile aplicatiilor de baza.
In Java solutia clasica pentru pluginabilitate este OSGi (www.osgi.org) care reprezinta un standardul incontestabil pe acest domeniu. OSGi permite crearea de bundle-uri (care sunt de fapt jar-uri cu un manifest bine definit) care reprezinta plugin-urile care se pot instala in mod dinamic. In mod dinamic in runtime se rezolva dependentele bundle-ului de alte bundle-uri si de asemenea poate inregistra dinamic servicii intr-un registru de servicii.

Cazul meu

Eu sunt interesat in pluginabilitate la nivel de interfata Web pentru o aplicatie deja scrisa. In sensul ca vreau ca prin activare/deactivare de pluginuri sa apara/dispara functionalitati din interfata Web (mai specific fragmente din meniul lateral al aplicatiei care cuprind diverse statistici despre articolele publicate).
In acest sens am nevoie de o consola de administrare a pluginurilor:
Spre exemplu prin activarea pluginului Balaur va aparea urmatorul (ultimului) fragment in menu:

Resurse

Exista mai multe implementari de OSGi: Equinox, Felix,
Pentru a integra integra pluginabilitate in aplicatia mea am folosit Equinox drept implementare de OSGi embedat in aplicatia mea deja facuta (aplicatia de baza poarta numele de HOST application). Am ales Equinox deoarece pune la dispozitie un bridge - un servlet care este ridicat in runtime din host application si toate call-urile (HTTP requests) peste path-ul servit de acest servlet sunt redirectate spre servletii/JSP-urile din bundleurile OSGi. O aplicatie web exemplu pentru cum se embedeaza Equinox intr-un container de servlets se gaseste aici: http://www.eclipse.org/equinox/server/http_in_container.php.
Deci trebuie remarcat faptul ca sub bundle-urile de Equinox se pot inregistra servlet-uri si JSP-uri ca sub o aplicatie web obisnuita - declararea lor se face programatic dar se poate si declarativ folosind extensii intr-un fisier plugin.xml. De asemenea exista suport de JSTL + el. Prezint o lista a bundle-urilor necesare de pe equinox.org:
.
  • org.eclipse.equinox.servletbridge.http
    Hooks back into the servlet bridge and proxies requests through to the servlet container to provide an OSGi Http Service.
  • org.eclipse.equinox.http.servlet
    Provides the HttpServiceServlet used by http.servletbridge that when initialized registers an OSGi Http Service.
  • [optional] org.eclipse.equinox.http.registry
    Provides servlet, resource, and httpcontext extension points based on an OSGi HttpService
  • org.eclipse.equinox.servletbridge
    Launches the framework and provides a place for the framework to hook back into the servlet container.
JSP suport:
  • org.eclipse.equinox.jsp.jasper (Bundle)
    Provides an OSGi friendly JSPServlet based on the use of the Jasper 2 compiler and runtime.
  • org.eclipse.equinox.jsp.jasper.registry (Bundle)
    Provides a JSPFactory that allows JSP usage with the org.eclipse.equinox.http.registry.servlets extension-point

Lista completa a plugin-urilor incluzand si cele 2 plugin-uri custom este aceasta:

Ca o curiozitate am instalat si un plugin de Felix care este o consola web pentru gestionarea pluginurilor din container - in mod surprinzator chiar daca este un bundle de Felix si nu de Equinox a mers din prima incercare. Consola de administrare o puteti vedea aici:

Dezvoltare plugin-uri

Desigur pentru a testa sistemul am creat 2 plugin-uri custom folosind Eclipse IDE. Un articol interesant despre cum se dezvolta plugin-uri de OSGi este aici: http://www.vogella.de/articles/OSGi/article.html. Eclipse are o perspectiva aparte pentru dezvoltarea de plugin-uri (in principiu plugin-uri de Eclipse care au definite niste extensii peste OSGi). Se pot folosi drept model exemplele de pe equinox.org. Cele 2 plugin-uri construide mine sunt:

Primul plugin (com.integrationpath.mengine.console) este folosit pentru a afisa in host application lista de plugin-uri existente in aplicatie (/com.integrationpath.mengine.console/web/consoleAsync.jsp), status-ul lor si posibilitatea de activare/deactivare. De asemenea expune informatii (/com.integrationpath.mengine.console/web/plugins.jsp) despre plugin-urile active si locatia unde expun ele fragmentul ce trebuie integrat in meniu (via JSON). In host application in meniul lateral (sidebar) se vor agrega toate aceste fragmente folossind JQuery.

Integrare

Cea mai mare provocare este incampatibilitatea intre OSGi si oricare server de aplicatii existent. Cea mai buna idee e sa rescrii aplicatia conform OSGi dar in cazul meu aplicatia host este deja scrisa si deployata ca war intr-un container de servlets. Containerul OSGi care este embedat in aplicatia host este startat de aceasta si ruleaza in acelasi JVM, dar definitiile claselor sunt gestionate de classloade-re diferite (lucrurile sunt si mai complexe pentru ca la nivel de fiecare bundle exista un class loader separat prin modul de constructie OSGi). Desigur se pune problema comunicarii intre HOST application si bundle-urile din containerul OSGi (de fapt in ambele sensuri) deoarece bundle-urile trebuie sa prezinte functionaitati care au treaba cu buisinessul aplicatiei de baza.
Prima incercare a fost de a exporta pachetele de Spring dispre host app spre OSGi si a accesa web application contex-ul de Spring din host application sub OSGi. Fara success. O ultima solutie a ramas expunerea functionalitatilor din host application ca Web Service (am preferat JSON) si consumarea lor in bundle-urile de OSGi.