2011-12-01 10 views
8

He desarrollado una aplicación que utiliza la biblioteca Reflections para consultar todas las clases que tienen una anotación particular. Todo funcionaba como un amuleto hasta que decidí crear un complemento de Eclipse desde mi aplicación. Entonces las reflexiones dejan de funcionar.La biblioteca de reflexiones no funciona cuando se usa en un complemento de Eclipse

Dado que mi aplicación funciona correctamente cuando no forma parte de un complemento de Eclipse, creo que debería ser un problema de cargador de clases. Así que agregué a mi clase Reflections los cargadores de clase de la clase de activador de plug-in, el cargador de clase de contexto y todos los cargadores de otras clases que pudiera imaginar, sin ningún éxito. Esta es una versión simplificada de mi código:

ConfigurationBuilder config = new ConfigurationBuilder(); 
config.addClassLoaders(thePluginActivatorClassLoader); 
config.addClassLoaders(ClasspathHelper.getContextClassLoader()); 
config.addClassLoaders("all the classloaders I could imagine"); 
config.filterInputsBy(new FilterBuilder().include("package I want to analyze")); 

Reflections reflections = new Reflections(config); 
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(MyAnnotation.class); //this Set is empty 

También probé la adición de direcciones URL de las clases que desea cargar a la clase ConfigurationBuilder, pero no sirvió de nada.

¿Podría alguien decirme si hay una manera de hacer que Reflections funcione como parte de un complemento de Eclipse ?, o ¿debería buscar otra alternativa? Muchas gracias, estoy realmente desconcertado al respecto.

Respuesta

10

Supongo que ya sabe cómo crear paquetes (de lo contrario, marque this).

Después de algún debuging y exploración de API Reflexiones me he dado cuenta de que el problema es que las reflexiones simplemente no puede leer OSGi URL (bundleresource: // ...) lo que resulta en una excepción:

org.reflections.ReflectionsException: could not create Vfs.Dir from url, 
no matching UrlType was found [bundleresource://1009.fwk651584550/] 

y esto sugerencia:

either use fromURL(final URL url, final List<UrlType> urlTypes) 
or use the static setDefaultURLTypes(final List<UrlType> urlTypes) 
or addDefaultURLTypes(UrlType urlType) with your specialized UrlType. 

así que creo que la implementación de un UrlType de OSGi (por ejemplo class BundleUrlType implements UrlType {...}) y registrarlo como esto:

Vfs.addDefaultURLTypes(new BundleUrlType()); 

debe hacer que Reflections API se pueda utilizar desde dentro de un paquete. Las dependencias de reflexiones se deben agregar al proyecto del complemento de Eclipse como se describe en here.

Esta es la forma en mi muestra MANIFIEST.MF parecía después de añadir frascos necesarios:

Manifest-Version: 1.0 
Bundle-ManifestVersion: 2 
Bundle-Name: ReflectivePlugin 
Bundle-SymbolicName: ReflectivePlugin 
Bundle-Version: 1.0.0.qualifier 
Bundle-Activator: reflectiveplugin.Activator 
Bundle-ActivationPolicy: lazy 
Bundle-RequiredExecutionEnvironment: JavaSE-1.6 
Import-Package: javax.annotation;version="1.0.0", 
org.osgi.framework;version="1.3.0", 
org.osgi.service.log;version="1.3", 
org.osgi.util.tracker;version="1.3.1" 
Bundle-ClassPath: ., 
lib/dom4j-1.6.1.jar, 
lib/guava-r08.jar, 
lib/javassist-3.12.1.GA.jar, 
lib/reflections-0.9.5.jar, 
lib/slf4j-api-1.6.1.jar, 
lib/xml-apis-1.0.b2.jar 
Export-Package: reflectiveplugin, 
reflectiveplugin.data 

Nota: Se utiliza Reflexiones v 0.9.5

Aquí está un ejemplo de implementación UrlType:.

package reflectiveplugin; 

import java.io.IOException; 
import java.io.InputStream; 
import java.net.URL; 
import java.util.Enumeration; 
import java.util.Iterator; 

import org.osgi.framework.Bundle; 
import org.reflections.vfs.Vfs; 
import org.reflections.vfs.Vfs.Dir; 
import org.reflections.vfs.Vfs.File; 
import org.reflections.vfs.Vfs.UrlType; 

import com.google.common.collect.AbstractIterator; 

public class BundleUrlType implements UrlType { 

    public static final String BUNDLE_PROTOCOL = "bundleresource"; 

    private final Bundle bundle; 

    public BundleUrlType(Bundle bundle) { 
     this.bundle = bundle; 
    } 

    @Override 
    public boolean matches(URL url) { 
     return BUNDLE_PROTOCOL.equals(url.getProtocol()); 
    } 

    @Override 
    public Dir createDir(URL url) { 
     return new BundleDir(bundle, url); 
    } 

    public class BundleDir implements Dir { 

     private String path; 
     private final Bundle bundle; 

     public BundleDir(Bundle bundle, URL url) { 
      this(bundle, url.getPath()); 
     } 

     public BundleDir(Bundle bundle, String p) { 
      this.bundle = bundle; 
      this.path = p; 
      if (path.startsWith(BUNDLE_PROTOCOL + ":")) { 
       path = path.substring((BUNDLE_PROTOCOL + ":").length()); 
      } 
     } 

     @Override 
     public String getPath() { 
      return path; 
     } 

     @Override 
     public Iterable<File> getFiles() { 
      return new Iterable<Vfs.File>() { 
       public Iterator<Vfs.File> iterator() { 
        return new AbstractIterator<Vfs.File>() { 
         final Enumeration<URL> entries = bundle.findEntries(path, "*.class", true); 

         protected Vfs.File computeNext() { 
          return entries.hasMoreElements() ? new BundleFile(BundleDir.this, entries.nextElement()) : endOfData(); 
         } 
        }; 
       } 
      }; 
     } 

     @Override 
     public void close() { } 
    } 

    public class BundleFile implements File { 

     private final BundleDir dir; 
     private final String name; 
     private final URL url; 

     public BundleFile(BundleDir dir, URL url) { 
      this.dir = dir; 
      this.url = url; 
      String path = url.getFile(); 
      this.name = path.substring(path.lastIndexOf("/") + 1); 
     } 

     @Override 
     public String getName() { 
      return name; 
     } 

     @Override 
     public String getRelativePath() { 
      return getFullPath().substring(dir.getPath().length()); 
     } 

     @Override 
     public String getFullPath() { 
      return url.getFile(); 
     } 

     @Override 
     public InputStream openInputStream() throws IOException { 
      return url.openStream(); 
     } 
    } 
} 

Y así es como creo los reflejos en la clase Activador:

private Reflections createReflections(Bundle bundle) { 
    Vfs.addDefaultURLTypes(new BundleUrlType(bundle)); 
    Reflections reflections = new Reflections(new Object[] { "reflectiveplugin.data" }); 
    return reflections; 
} 

El último bit es muy confuso, pero sigue siendo importante: si ejecuta su complemento dentro de Eclipse (Ejecutar como/OSGi Framework), debe agregar también el directorio de salida de las clases a los patrones de ruta de Reflexiones (es decir "bin" o "target/classes").Sin embargo, no es necesario para un complemento liberado (para compilar un complemento/paquete haga "Exportar" -> "Complementos desplegables y fragmentos").

+0

Gracias por su responde @Vlad. No tengo idea de cómo crear esos paquetes, pero lo intentaré (aún estoy lejos de ser un experto en OSGi ...). Así que necesito dos paquetes: 1) la biblioteca Reflections que tiene "DynamicImport-Package: *" en su Plug-in MANIFEST; 2) un paquete con mi complemento, donde tengo que completar en la lista "Paquetes exportados" del editor de configuración del complemento, todos los paquetes que deberían ser accesibles para el Paquete de Reflexiones. Entonces supongo que debería componer estos dos paquetes en otro, ¿verdad? ¿Podría por favor elaborar un poco más su respuesta? ¡todo esto parece ser un poco complejo! Gracias – Sergio

+0

@Sergio, he actualizado la respuesta con mis conclusiones (la respuesta anterior no proporcionó una solución). Desafortunadamente, resultó ser mucho menos directo. Espero, será de ayuda. – Vlad

+0

Muchas gracias por ampliar tu respuesta @Vlad. Intenté implementarlo sin éxito hasta ahora. Si comprendí correctamente su nueva respuesta, solo necesito el paquete de complementos y el jar de Reflexiones en su paquete classpath (como originalmente lo tuve). Así que estoy ignorando la respuesta anterior acerca de poner Reflexiones en un paquete separado, ¿es correcto? Acerca de su archivo de manifiesto: su 'Bundle-ClassPath' es similar a la mía, en mi 'Export-Package' no tengo un archivo '.data' como usted, y en el 'Import-Package' no veo las dependencias ¿Es importante que Reflexiones funcione? – Sergio

1

Eclipse se basa en OSGi y se enfrenta a la carga de la clase OSGi ... y esa no es una batalla fácil de ganar.

Eche un vistazo a este artículo de Neil Bartlett: OSGi Readiness — Loading Classes. También puedes buscar en Google la "política de amigos de OSGi".

+0

veo :(@Tonny, ¿Sabe usted otra biblioteca para la consulta de este tipo de información -Clases con una anotación particular, etc.- en una parte de la aplicación haciendo de un complemento de Eclipse. Por lo que sé Spring también puede responder preguntas similares, pero no lo he intentado antes, y no tengo idea de si funcionará bien al hacer parte de un complemento de Eclipse o si se presentará los mismos problemas que tengo actualmente. Gracias por sus comentarios. – Sergio

+0

Puede hacer que la biblioteca Reflections se convierta en un paquete OSGi y luego agregar el encabezado 'DynamicImport-Package: *' a manifest.mf. Puede crear el paquete inicial a partir de los existentes. archivo jar usando el asistente "Nuevo proyecto" -> "Plug-in del archivo JAR existente" ... Buena suerte. –

3

Solo para los registros en caso de que alguien más tenga el mismo problema. Aquí hay una pequeña modificación a la respuesta de Vlad para evitar tener que agregar el directorio de salida a los patrones de ruta de Reflexiones. La diferencia está solo en la clase BundleDir. Parece que funciona bien en todas mis pruebas:.

public class BundleUrlType implements UrlType { 

public static final String BUNDLE_PROTOCOL = "bundleresource"; 

private final Bundle bundle; 

public BundleUrlType(Bundle bundle) { 
    this.bundle = bundle; 
} 

@Override 
public Dir createDir(URL url) { 
    return new BundleDir(bundle, url); 
} 

@Override 
public boolean matches(URL url) { 
    return BUNDLE_PROTOCOL.equals(url.getProtocol()); 
} 


public static class BundleDir implements Dir { 

    private String path; 
    private final Bundle bundle; 

    private static String urlPath(Bundle bundle, URL url) { 
     try { 
      URL resolvedURL = FileLocator.resolve(url); 
      String resolvedURLAsfile = resolvedURL.getFile(); 

      URL bundleRootURL = bundle.getEntry("/"); 
      URL resolvedBundleRootURL = FileLocator.resolve(bundleRootURL); 
      String resolvedBundleRootURLAsfile = resolvedBundleRootURL.getFile(); 
      return("/"+resolvedURLAsfile.substring(resolvedBundleRootURLAsfile.length())); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public BundleDir(Bundle bundle, URL url) { 
     //this(bundle, url.getPath()); 
     this(bundle, urlPath(bundle,url)); 
    } 

    public BundleDir(Bundle bundle, String p) { 
     this.bundle = bundle; 
     this.path = p; 
     if (path.startsWith(BUNDLE_PROTOCOL + ":")) { 
      path = path.substring((BUNDLE_PROTOCOL + ":").length()); 
     } 
    } 

    @Override 
    public String getPath() { 
     return path; 
    } 

    @Override 
    public Iterable<File> getFiles() { 
     return new Iterable<Vfs.File>() { 
      public Iterator<Vfs.File> iterator() { 
       return new AbstractIterator<Vfs.File>() { 
        final Enumeration<URL> entries = bundle.findEntries(path, "*.class", true); 

        protected Vfs.File computeNext() { 
         return entries.hasMoreElements() ? new BundleFile(BundleDir.this, entries.nextElement()) : endOfData(); 
        } 
       }; 
      } 
     }; 
    } 

    @Override 
    public void close() { } 
} 


public static class BundleFile implements File { 

    private final BundleDir dir; 
    private final String name; 
    private final URL url; 

    public BundleFile(BundleDir dir, URL url) { 
     this.dir = dir; 
     this.url = url; 
     String path = url.getFile(); 
     this.name = path.substring(path.lastIndexOf("/") + 1); 
    } 

    @Override 
    public String getName() { 
     return name; 
    } 

    @Override 
    public String getRelativePath() { 
     return getFullPath().substring(dir.getPath().length()); 
    } 

    @Override 
    public String getFullPath() { 
     return url.getFile(); 
    } 

    @Override 
    public InputStream openInputStream() throws IOException { 
     return url.openStream(); 
    } 
} 
} 
+0

Hoy tengo este problema exacto, nunca antes he creado paquetes, pero he intentado hacerlo. No entiendo de dónde obtener el objeto Bundle para pasar a este constructor, ¿me pueden ayudar? – Link19

Cuestiones relacionadas