Enviar respuesta 
 
Calificación:
  • 0 votos - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
ServiceLoader
jonathan Monsalve Sin conexión
lider de proyecto
****

Mensajes: 114
Registro en: Mar 2011
Reputación: 0
Mensaje: #1
ServiceLoader
2.Como detectar que un .jar es un plugin, detectando automaticamente el xml
(Este mensaje fue modificado por última vez en: 09-07-2012 07:12 PM por demian.)
07-07-2012 05:12 PM
Encuentra todos sus mensajes Cita este mensaje en tu respuesta
demian Sin conexión
Administrator
*******

Mensajes: 1.808
Registro en: Jun 2010
Reputación: 0
Mensaje: #2
RE: OSGi y .jar
(07-07-2012 05:12 PM)jonathan Monsalve escribió:  2.Como detectar que un .jar es un plugin, detectando automaticamente el xml

Esto se hace con un ServiceLoader. Pueden encontrar información aquí:

http://docs.oracle.com/javase/6/docs/api...oader.html

Yo pude poner a funcionar un ejemplo sólo con la información de esa página (sugiero leerla cuidadosamente). El ejemplo está en el SVN (bueno, va a estar en unos minutos). Son cuatro proyectos distintos que se llaman SomeServiceInterface, SomeServiceImplNumber1, SomeServiceImplNumber2 y SomeServiceTest.

SomeServiceInterface define un servicio y su interfaz, pero no implementa nada. Allí hay una interfaz que se llama ISomeService:

Código:
public interface ISomeService {

  public static final ServiceLoader<ISomeService> someServiceLoader = //
  ServiceLoader.load(ISomeService.class);

  // --------------------------------------------------------------------------------
  
  public String sayHelloTo(String someone);

  // --------------------------------------------------------------------------------
  // other methods...
  // --------------------------------------------------------------------------------
}

Los métodos públicos de esta interfaz definen el contrato del servicio. Allí en teoría ustedes pueden poner lo que quieran. Por ejemplo, podrían poner un metodo "getPluginXML()" o cualquier otro método relevante. En el caso de ustedes esa clase representa el concepto "abstracto" de un plugin.

Luego tienen implementaciones de dichos servicios. Hice dos (que son idénticos) sólo por probar que funcionaba bien. Están en los proyectos SomeServiceImplNumber1 y SomeServiceImplNumber2 respectivamente. A continuación una de las clases que implementan el servicio:

Código:
public class SomeServiceNumber1Impl implements ISomeService {

  @Override
  public String sayHelloTo(String someone) {
    return "This is number 1 saying hello to " + someone;
  }
}

La otra clase del otro proyecto es exactamente igual sólo que los "1" cambian por "2".

Luego viene el cliente, que es el que "usa" los servicios. Pueden encontrar un ejemplo de cliente que usa la definición del servicio ISomeService y las dos implementaciones en el proyecto SomeServiceTest.

El cliente no sabe que servicios (Plugins) exactamente están instalados, es decir, no sabe que jars contienen servicios de tipo ISomeService. Eso es exactamente lo que quiere encontrar. Eso se hace así:

Código:
public class Main {

  public static void main(String[] args) {

    Iterator<ISomeService> itt = ISomeService.someServiceLoader.iterator();

    while (itt.hasNext()) {
      ISomeService someService = itt.next();

      System.err.println(someService.getClass());
      System.err.println(someService.sayHelloTo("me!"));
    }
  }
}

El resultado de correr ese programa es:

Código:
class com.somename.number1.SomeServiceNumber1Impl
This is number 1 saying hello to me!
class com.somename.number2.SomeServiceNumber2Impl
This is number 2 saying hello to me!

Para obtener este resultado es necesario que el proyecto SomeServiceTest tenga en el classpath a los proyectos (o jars si ya se han generado jars) SomeServiceInterface, SomeServiceImplNumber1 y SomeServiceImplNumber2. Esto se logra si lo corren desde eclipse poniendo a SomeServiceTest a depender de los otros tres proyectos (Botón Derecho->Properties->etc...).

Por ejemplo, si SomeServiceTest sólo depende de SomeServiceInterface y SomeServiceImplNumber1 (Es decir, eliminamos SomeServiceImplNumber2 del classpath) entonces van a obtener:

Código:
class com.somename.number1.SomeServiceNumber1Impl
This is number 1 saying hello to me!

Que es lo esperado.

Pueden notar que nunca se hace mención explícita en el código a las clases "SomeServiceNumber1Impl" o a "SomeServiceNumber1Impl". Sólo se hace mención al atributo (estático y final) ServiceLoader<ISomeService> someServiceLoader.

La pregunta que se pueden estar haciendo es ¿Cómo hace el ServiceLoader para encontrar las implementaciones correspondientes de los servicios? La respuesta es simple, hay una pieza que olvidé mencionar.

En cada uno de los proyectos SomeServiceImplNumber1 y SomeServiceImplNumber2 hay un directorio META-INF/services que está ubicado en el src. El directorio META-INF generalmente se usa en Java para poner información relevante sobre el jar/proyecto (no voy a entrar en detalles). El asunto es que dentro de cada proyecto que implementa un servicio van a encontrar en META-INF/services un archivo de texto que tiene como nombre el nombre (completamente calificado) de la interfaz que define el servicio. Es decir van a encontrar un archivo en este caso llamado:

org.cyrano.someservice.ISomeService

Y dentro de ese archivo una sola linea, que es el nombre completamente calificado de la clase dentro de este jar que implementa el servicio, en el caso del proyecto SomeServiceImplNumber1:

Código:
com.somename.number1.SomeServiceNumber1Impl

Java en algún momento (desconozco los detalles internos) busca en esos directorios archivos con el nombre de los servicios que se están definiendo y crea una lista de todas las implementaciones conocidas.

Creo que con esto pueden ya comenzar a sumar 2+2=4.

Plugin (Interfaz) -> Un servicio

Cada Plugin Particular -> Una clase que implementa "Plugin", ubicada en un proyecto/jar distinto que registra el servicio usando el mecanismo de META-INF/services que acabo de mencionar.

El cliente es un código en CledaPlugin que lista los servicios registrados y en base a eso detecta si hay un nuevo plugin.

Por lo pronto no se preocupen por lo del jar, si lo hacen con proyectos y les funciona así va a funcionar directamente al crear un jar.

Por cierto que para conseguir el xml, lo pueden poner en cualquier lugar del proyecto que representa el plugin con cualquer nombre. Luego, en la implementación del servicio (y evidentemente en la interfaz) pueden tener un método getPluginXML() que carga el XML donde sea que esté y retorna el InputStream correspondiente.

Una alternativa también sería poner el XML en el META-INF con un nombre estándar que siga una convención (por ejemplo "plugin.xml"). Luego tiene que haber alguna forma de iterar por todos los distintos "plugins.xml", ese mecanismo si nunca lo he usado, pero si el ServiceLoader lo puede hacer estoy seguro que se puede hacer (podrían buscar un poco y cuando lo descubran me dicen).

En fin, suerte con esto.
Saludos,

PS: Apenas termine este post me pongo a subir los ejemplos al SVN.

[Imagen: dmi-1.jpg]
(Este mensaje fue modificado por última vez en: 09-07-2012 07:11 PM por demian.)
09-07-2012 07:08 PM
Visita su sitio web Encuentra todos sus mensajes Cita este mensaje en tu respuesta
jonathan Monsalve Sin conexión
lider de proyecto
****

Mensajes: 114
Registro en: Mar 2011
Reputación: 0
Mensaje: #3
RE: OSGi y .jar
(09-07-2012 07:08 PM)demian escribió:  Para obtener este resultado es necesario que el proyecto SomeServiceTest tenga en el classpath a los proyectos (o jars si ya se han generado jars) SomeServiceInterface, SomeServiceImplNumber1 y SomeServiceImplNumber2. Esto se logra si lo corren desde eclipse poniendo a SomeServiceTest a depender de los otros tres proyectos (Botón Derecho->Properties->etc...).

PS: Apenas termine este post me pongo a subir los ejemplos al SVN.

Que tal profesor, como sabe hay tres plug-ins implementados (cledaSandbox,test1,test2) probados y se ha hecho las operaciones basicas sobre ellos(des/instalar,des/activar), en este momento desinstalar, activar y desactivar estan hechos, pero el mecanismo que nos planteo para instalar me trae algunas dudas y es con lo que resalto de su mensaje; resulta y pasa que hice un test (proyecto aparte igual al test de su ejemplo) y machete todo corre como se espera despues de varios intentos fallidos(errores tontos) reconoce los plugin aplicando serviceloader pero esto si y solo si este nuevo proyecto incluye en su classpath a los demas plugin (proyectos cledaSandbox,test1,test2), ahora bien sucede que el nucleo de cleda debe hacer algo similar al main de este test, entonces el nucleo de cleda debe incluir en su classpath a todos los proyectos que sean plugin hechos por otros desarroladores???. Por otro lado esta la deteccion del xml que para hacerlo generico deberia llamarse igual al service que implementa a ICledaPlugin solo que le añadiriamos extension .xml, ó lo otro seria que ICledaPlugin tenga un metodo que retorne el nombre del xml, son dos opciones cual le gusta mas?.
23-09-2012 07:30 PM
Encuentra todos sus mensajes Cita este mensaje en tu respuesta
demian Sin conexión
Administrator
*******

Mensajes: 1.808
Registro en: Jun 2010
Reputación: 0
Mensaje: #4
RE: OSGi y .jar
Hola Jonathan,

El asunto es este. Vas a tener tres tipos de proyectos:

1) El núcleo de cleda: CledaPlugin, Workflow, I18N, etc.
2) Plugins: Todos los plugins que quieras tener.

Y aquí viene el truco, te guste o no, tienes que tener:

3) Un sólo "proyecto WEB" (que en este caso vendría a ser CledaSandbox).

El problema que tienes es que están viendo CledaSandbox tanto como un proyecto WEB como un plugin.

Yo voy a llamar (3) de ahora en adelante como "PrincipalWEB", para ayudar a resolver la confusión.

La idea es que PrincipalWEB es el proyecto que de hecho corre en el tomcat, el que define el contexto de la aplicación WEB. PrincipalWEB va a tener como dependencia a todos los proyectos de tipo (1) y a todos los proyectos de tipo (2).

El código equivalente a SomeServiceTest va a estar en el núcleo de Cleda, por ejemplo en CledaPlugin (ojo, no recuerdo bien los nombres, me puedo equivocar). La idea es que este proyecto, por si sólo, evidentemente no sabe quienes son los plugins asociados. Pero cuando todo corre bajo el paraguas de PrincipalWEB, que en sus dependencias tiene tanto a (1) como a todos los (2), cuando corras ese código en ese contexto entonces SI va a encontrar los servicios relacionados a los plugins (2), esto porque todo está en el classpath.

Me avisas cualquier cosa.
Saludos

[Imagen: dmi-1.jpg]
23-09-2012 09:17 PM
Visita su sitio web Encuentra todos sus mensajes Cita este mensaje en tu respuesta
Enviar respuesta 


Salto de foro:


Usuario(s) navegando en este tema: 1 invitado(s)