Библиотека отражений не работает при использовании в плагине Eclipse - PullRequest
8 голосов
/ 01 декабря 2011

Я разработал приложение, использующее библиотеку Reflections для запроса всех классов, имеющих конкретную аннотацию.Все работало как шарм, пока я не решил создать плагин 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

Я также пытался добавить URL-адреса классов, которые я хочу загрузить, в класс ConfigurationBuilder, но это не помогло.

Можеткто-нибудь подскажет, есть ли способ заставить Reflections работать как часть плагина Eclipse?, или мне лучше поискать другую альтернативу?Большое спасибо, я действительно озадачен этим.

Ответы [ 3 ]

10 голосов
/ 01 декабря 2011

Полагаю, вы уже знаете, как создавать пакеты (в противном случае отметьте this ).

После некоторой отладки и исследования API Reflections я понял, что проблема в том, что Reflections просто не может прочитать URL OSGi (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.

Поэтому я считаю, что необходимо реализовать UrlType для OSGi (например, class BundleUrlType implements UrlType {...}) и зарегистрировать его следующим образом:

Vfs.addDefaultURLTypes(new BundleUrlType());

должен сделать API Reflections пригодным для использования внутри пакета. В проект Eclipse Plugin необходимо добавить зависимости отражений, как описано здесь .

Вот так выглядел мой пример MANIFEST.MF после добавления необходимых банок:

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" или " мишень / классы "). Хотя для выпущенного плагина это не нужно (для создания плагина / пакета выполните «Экспорт» -> «Развертываемые плагины и фрагменты»).

3 голосов
/ 10 декабря 2011

Только для записей на случай, если у кого-то есть такая же проблема.Вот небольшая модификация ответа Влада, чтобы избежать необходимости добавлять каталог вывода в шаблоны пути Reflections.Разница только в классе 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();
    }
}
}
1 голос
/ 01 декабря 2011

Eclipse построен на основе OSGi, и вы противостоите загрузке классов OSGi ... и победить нелегко.

Взгляните на эту статью Нила Бартлетта: Готовность к OSGi - загрузка классов . Также вы можете поискать в Google "Политика друзей OSGi".

...