2011-12-01 11 views
8

特定のアノテーションを持つすべてのクラスを照会するためにReflectionsライブラリを使用してアプリケーションを開発しました。私のアプリケーションからEclipseプラグインを作成するまで、すべてのことが魅力的に機能していました。その後、反射が機能しなくなります。Eclipseプラグインでリフレクションライブラリを使用できない場合

私のアプリケーションがEclipseプラグインの一部ではないときにうまく動作しているとすれば、クラスローダーの問題であると思います。 Reflectionsクラスに、プラグインアクティベータクラス、コンテキストクラスローダ、および他のすべてのクラスローダーのクラスローダを追加しました。これは成功しませんでした。

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 

私も、私はConfigurationBuilderクラスにロードしたいクラスのURLを追加しようとしたが、それは助けにはならなかった:これは私のコードの簡易版です。

ReflectionsをEclipseプラグインの一部として動作させる方法があるのか​​誰かに教えてもらえますか、別の選択肢を探すべきでしょうか?ありがとう、私はそれについて本当に困惑しています。

答えて

10

バンドルを作成する方法を既に理解していることを前提としています(そうでない場合は、thisをチェックしてください)。私は問題は反射は単にOSGiのURLを読み取りに失敗したということであることを認識している反射APIのいくつかのデバッグのと探検した後

(bundleresource:// ...)例外が生じ:

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

と、この提案:

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. 

だから私は、OSGiのためのUrlType(例えばclass BundleUrlType implements UrlType {...})を実装し、このようにそれを登録すると信じて:

Vfs.addDefaultURLTypes(new BundleUrlType()); 

は、Reflections APIをバンドルの内部から使用できるようにする必要があります。リフレクションの依存関係は、hereのようにEclipse Pluginプロジェクトに追加する必要があります。

これは私のサンプルMANIFEST.MFが必要なjarファイルを追加した後のように見えた方法です:

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 

注:使用される反射V 0.9.5

ここではサンプル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(); 
     } 
    } 
} 

これはActivatorクラスでリフレクションを作成する方法です:

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

最後のビットは非常に混乱しますが、重要です。プラグインをEclipse(Run As/OSGi Framework)内で実行する場合、クラス出力ディレクトリもReflectionsパスパターンに追加する必要があります。 "bin"または "target/classes")。ただし、リリースされたプラグインでは必要ありません(プラグイン/バンドルを構築するには、 "Export" - > "Deployable plug-ins and fragments"を実行してください)。

+0

ありがとう@Vladに答えてください。そのようなバンドルを作る方法は考えられませんが、私は試してみます(OSGiの専門家ではありません...)。ですから、2つのバンドルが必要です:1)プラグインマニフェストに "DynamicImport-Package:*"を持つReflectionsライブラリ。 2)プラグインとのバンドル、プラグイン設定エディタの「エクスポートされたパッケージ」リスト、Reflexionsバンドルにアクセス可能なすべてのパッケージに記入する必要があります。それから、私はこれら2つのバンドルを別のものに組み立てるべきだと思います。あなたの答えをもう少し詳しく教えてください。このすべてはちょっと複雑なようです!ありがとう – Sergio

+0

@Sergio、私は私の所見(前の答えは解決策を提供しなかった)で答えを更新しました。残念なことに、それはずっと簡単ではなかった。希望、それは助けになるだろう。 – Vlad

+0

お返事ありがとうございました@Vlad。私は今まで成功せずに実装しようとしました。あなたの新しい答えから正しく理解していれば、バンドルクラスパスにプラグインバンドルとReflexions jarだけが必要です(私が元々持っていたように)。だから私は別のバンドルにReflexionsを置くことに関する以前の答えを無視しています、これは正しいのですか?あなたのマニフェストファイルについて:あなたの 'Bundle-ClassPath'は私の 'Export-Package'に似ていますが、 '.data'ファイルはありません。また、 'Import-Package'では依存関係は表示されませんあなたはReflexionsが働くことが重要ですか? – Sergio

1

EclipseはOSGiの上にビルドされており、OSGiクラスの読み込みに反対しています...これは勝つのは簡単な戦いではありません。

ニールバートレットのこの記事をご覧ください:OSGi Readiness — Loading Classesまた、 "OSGi buddy policy"についてgoogleすることもできます。

+0

を私は見:(@Tonnyを、あなたはこの種の情報を照会するための別のライブラリを知っていたアプリケーション作る部分でそうオン特定のアノテーションで-classesと私が知っている限り、Springも同様の質問に答えることができますが、これまでに試したことはありませんし、Eclipse Plug-inの一部を作るときにうまく動作するかどうか、 – Sergio

+0

ReflectionsライブラリをOSGiバンドルにしてから、 'DynamicImport-Package:*'見出しをmanifest.mfに追加することができます。 "新規プロジェクト" - > "既存のJARアーカイブからのプラグイン"ウィザードを使用してjarファイルを作成してください...幸運 –

3

他の誰かが同じ問題を抱えている場合のためだけの記録です。 ここでは、Refladsパスパターンに出力ディレクトリを追加する必要がないように、Vladの答えを少し変更しました。 違いはBundleDirクラスのみです。すべて私のテストで正常に動作するようです:。

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

私は今日この正確な問題を抱えています。以前はバンドルを作成していませんでしたが、これを試しました。私はバンドルオブジェクトをこのコンストラクタに渡す場所を理解できません。どうすればいいですか? – Link19

関連する問題