Могу ли я динамически обнаруживать XML-файлы в classpath внутри контейнера EJB 3? - PullRequest
1 голос
/ 09 ноября 2010

Справочная информация:

Один из компонентов нашего проекта работает с использованием пружины. Некоторый код SQL генерируется динамически на основе заданной конфигурации XML-пружины.

Сначала было хорошо хранить все конфигурации XML в одном пакете на пути к классам (а затем загружать его как ресурс при вызове службы), но со временем у нас появилось большое количество конфигураций. Пришло время разделить конфигурации на разные пространства имен.

Цель

Что мне нужно, учитывая начальный пакет на пути к классам, рекурсивно обходить структуру каталогов и динамически обнаруживать любые пружинные XML-файлы. (Так что при добавлении новых конфигураций / пакетов файлы все равно будут найдены службой).

Проблема

Я смог достичь своей цели в порядке, работая вне контейнера EJB, используя Thread.getContextClassloader().getResource(myBasePackage), затем получив объект File и используя его для обхода дерева в файловой системе. Неуклюжий, я знаю, но он все еще был родственником по классу и работал.

Однако вы не можете сделать это внутри контейнера EJB (вы вообще не можете взаимодействовать с файловой системой), поэтому мне пришлось использовать довольно раздражающий обходной путь, при котором я веду поиск списка жестко закодированных пакетов для поиска.

Вопрос

Есть ли способ (работающий внутри контейнера EJB) динамически обходить путь к классам (из заданного начального местоположения) в поисках произвольных ресурсов?

Ответы [ 2 ]

1 голос
/ 10 ноября 2010

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Как уже указывалось, создание ресурсов в classpath не рекомендуется и в зависимости от контейнера EJB явно запрещено.Это может вызвать у вас много проблем, потому что контейнеры могут взорвать ваши ресурсы в другую папку или даже скопировать ресурсы по всему кластеру (если это так).Для динамического создания ресурсов вы должны создать собственный загрузчик классов.Так что я бы никогда этого не сделал.Лучше получить доступ к файловой системе напрямую, чем к пути к классам.Это менее уродливо и в конечном итоге безопасно для кластеров, если вы используете удаленную файловую систему + блокировки файлов.

Если даже после всего, что я объяснил, вы все еще хотите поиграть с classpath, вы можете попробовать сделать что-то вроде: получитьЗагрузчик классов через

ClassLoader cld = Thread.currentThread().getContextClassLoader();

Начиная с базового пакета, перечислять все вхождения

Enumeration<URL> basePackageUrls = cld.getResources(basePackagePath);

Каждый URL-адрес обычно является либо ссылкой на файл (file: ///home/scott/.../MyResource.properties) или ссылка на jar (файл: ///lib.jar! /Com/domain/MyResource.properties).Вы должны проверить шаблон в URL.Используя это, перечислите содержимое папки с помощью обычного API Java и найдите подпакеты.Продолжайте, пока не отсканируете все пакеты.

См. Класс ниже (скоро будет выпущен с моим проектом с открытым исходным кодом).Он реализует сканер пути к классам, который вы можете передать в селекторе.Это работает как посетитель.Это моя работа для вас, если нет, получить идеи из него.См. Пример селектора аннотаций в конце.

public class ClasspathScanner
{

    private static final Log log = LogFactory.getLog(ClasspathScanner.class);
    private static final String JAR_FILE_PATTERN = ".jar!";

    private ClassSelector selector;
    private Set<Class<?>> classes;

    // PUBLIC METHODS ------------------------------------------------------------------------------

    public synchronized Set<Class<?>> scanPackage(String basePackage, ClassSelector selector)
        throws Exception
    {
        if (selector == null)
        {
            throw new NullPointerException("Selector cannot be NULL");
        }
        this.selector = selector;
        this.classes = new HashSet<Class<?>>();
        Set<Class<?>> aux;
        try
        {
            scanClasses0(basePackage);
            aux = this.classes;
        }
        finally
        {
            this.selector = null;
            this.classes = null;
        }

        return aux;
    }

    // HELPER CLASSES ------------------------------------------------------------------------------

    private void scanClasses0(String basePackage)
        throws IOException, ClassNotFoundException, FileNotFoundException
    {
        File packageDirectory = null;
        ClassLoader cld = getLoader();
        String basePackagePath = basePackage.replace('.', '/');
        Enumeration<URL> basePackageUrls = cld.getResources(basePackagePath);
        if (basePackageUrls == null || !basePackageUrls.hasMoreElements())
        {
            throw new ClassNotFoundException("Base package path not found: [" + basePackagePath
                + "]");
        }
        while (basePackageUrls.hasMoreElements())
        {
            String packagePath = basePackageUrls.nextElement().getFile();
            if (packagePath.contains(JAR_FILE_PATTERN))
            {
                scanJarFile(basePackagePath, packagePath);
            }
            else
            {
                packageDirectory = new File(packagePath);
                scanDirectory(basePackage, packageDirectory);
            }
        }
    }

    private void scanDirectory(String packageName, File packagePath)
        throws ClassNotFoundException, FileNotFoundException
    {
        if (packagePath.exists())
        {
            File[] packageFiles = packagePath.listFiles();
            for (File file : packageFiles)
            {
                if (file.isFile() && file.getName().endsWith(".class"))
                {
                    String fullFileName = packageName + '.' + file.getName();
                    checkClass(fullFileName);
                }
                else if (file.isDirectory())
                {
                    scanDirectory(packageName + "." + file.getName(), file);
                }
            }
        }
        else
        {
            throw new FileNotFoundException(packagePath.getPath());
        }
    }

    private void scanJarFile(String basePackagePath, String jarFileUrl)
        throws IOException, ClassNotFoundException
    {
        String jarFilePath = jarFileUrl.substring("file:".length(), jarFileUrl
            .indexOf(JAR_FILE_PATTERN)
            + JAR_FILE_PATTERN.length() - 1);
        log.debug("URL JAR file path: [" + jarFilePath + "]");
        jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
        log.debug("Decoded JAR file path: [" + jarFilePath + "]");
        JarFile jar = new JarFile(new File(jarFilePath));
        for (Enumeration<JarEntry> jarFiles = jar.entries(); jarFiles.hasMoreElements();)
        {
            JarEntry file = jarFiles.nextElement();
            String fileName = file.getName();
            if (!file.isDirectory() && fileName.endsWith(".class")
                && fileName.startsWith(basePackagePath))
            {
                String className = fileName.replace('/', '.');
                checkClass(className);
            }
        }
    }

    private void checkClass(String fullFilePath) throws ClassNotFoundException
    {
        String className = fullFilePath.substring(0, fullFilePath.length() - 6);
        Class<?> c = getLoader().loadClass(className);
        if (selector.select(c))
        {
            classes.add(c);
        }
    }

    private ClassLoader getLoader()
    {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null)
        {
            loader = getClass().getClassLoader();
        }
        return loader;
    }

    // INNER CLASSES -------------------------------------------------------------------------------

    public interface ClassSelector
    {
        boolean select(Class<?> clazz);
    }

    public static class AnnotatedClassSelector implements ClassSelector
    {
        private final Class<? extends Annotation>[] annotations;

        public AnnotatedClassSelector(Class<? extends Annotation>... annotations)
        {
            this.annotations = annotations;
        }

        public boolean select(Class<?> clazz)
        {
            for (Class<? extends Annotation> ac : annotations)
            {
                if (clazz.isAnnotationPresent(ac))
                {
                    return true;
                }
            }
            return false;
        }
    }
}
1 голос
/ 09 ноября 2010

Краткий ответ: не соответствует спецификации EJB.Поскольку в спецификации предусмотрены контейнеры, работающие во всех нестандартных ситуациях, это не делает это возможным.

Более длинный ответ: поскольку вы не создаете эти ресурсы динамически, я написал бы подпрограмму, которая дает вам списоквсех ресурсов во время сборки и помещает их в динамически генерируемый файл, на который ваш EJB-файл знает, как ссылаться.Таким образом, вы в основном создаете список каталогов пакетов и файлов, которые вы можете загрузить в EJB, на которые есть ссылки в одном мастер-файле.

Ответ Spring: Spring поддерживает поиск ресурсов на пути к классам, хотя я не знаю, насколько хорошоэто работает в контексте EJB (и я сомневаюсь, что оно соответствует EJB, но я не проверял).Некоторые детали здесь .

...