Как мне динамически заменить загрузчик классов для подключаемого модуля Eclipse?

Я разрабатываю подключаемый модуль Eclipse, который подходит для модели клиент-сервер. Это коммерческий проект, поэтому мы не можем повторно распространять драйверы JDBC для различных баз данных, которые мы поддерживаем с помощью подключаемого модуля.

Поэтому я разработал страницу настроек, позволяющую пользователю находить банки и иметь простой механизм обнаружения, который выполняет итерацию по классам в файлах jar, загружая каждый из них, чтобы убедиться, что он реализует интерфейс java.sql.Driver. Все это отлично работает.

Но загвоздка в том, что я использую Hibernate. А Hibernate использует Class.forName () для создания экземпляра драйвера JDBC.

Если я попытаюсь использовать следующее, я получу ClassNotFoundException.

public Object execute(final IRepositoryCallback callback)
{
    final DatabaseDriverClassLoader loader = new DatabaseDriverClassLoader(
        Activator.getDefault().getDatabaseDriverRegistry());
    final ClassLoader oldLoader = Thread.currentThread()
        .getContextClassLoader();
    try
    {
        Thread.currentThread().setContextClassLoader(loader);
        try
        {
            final SessionFactory sessionFactory = this.configuration
                .buildSessionFactory();
            if (sessionFactory != null)
            {
                final Session session = sessionFactory
                    .openSession();
                if (session != null)
                {
                    // CHECKSTYLE:OFF
                    try
                    // CHECKSTYLE:ON
                    {
                        return callback.doExecute(session);
                    }
                    finally
                    {
                        session.close();
                    }
                }
            }
            connection.close();
        }
        finally
        {
        }
    }
    // CHECKSTYLE:OFF
    catch (Exception e)
    // CHECKSTYLE:ON
    {
        RepositoryTemplate.LOG.error(e.getMessage(), e);
    }
    finally
    {
        Thread.currentThread().setContextClassLoader(oldLoader);
    }
    return null;
}

И если я попытаюсь создать драйвер самостоятельно, как показано ниже, я получу SecurityException.

public Object execute(final IRepositoryCallback callback)
{
    final DatabaseDriverClassLoader loader = new DatabaseDriverClassLoader(
        Activator.getDefault().getDatabaseDriverRegistry());
    final ClassLoader oldLoader = Thread.currentThread()
        .getContextClassLoader();
    try
    {
        Thread.currentThread().setContextClassLoader(loader);
        final Class driverClass = loader.loadClass(this.connectionDriverClassName);
        final Driver driver = (Driver)driverClass.newInstance();
        DriverManager.registerDriver(driver);
        try
        {
            final Connection connection = DriverManager.getConnection(
                this.connectionUrl, this.connectionUsername,
                this.connectionPassword);
            final SessionFactory sessionFactory = this.configuration
                .buildSessionFactory();
            if (sessionFactory != null)
            {
                final Session session = sessionFactory
                    .openSession(connection);
                if (session != null)
                {
                    // CHECKSTYLE:OFF
                    try
                    // CHECKSTYLE:ON
                    {
                        return callback.doExecute(session);
                    }
                    finally
                    {
                        session.close();
                    }
                }
            }
            connection.close();
        }
        finally
        {
            DriverManager.deregisterDriver(driver);
        }
    }
    // CHECKSTYLE:OFF
    catch (Exception e)
    // CHECKSTYLE:ON
    {
        RepositoryTemplate.LOG.error(e.getMessage(), e);
    }
    finally
    {
        Thread.currentThread().setContextClassLoader(oldLoader);
    }
    return null;
}

Обновлено: Я не уверен, что это лучший вариант, но я применил подход к реализации моего собственного ConnectionProvider, который позволил мне создать экземпляр драйвера с помощью Class.forName(), а затем я открыл соединение с помощью Driver.connect() вместо DriverManager.getConnection(). Это довольно просто, но мне не нужен пул соединений в моем конкретном случае использования.

Метод configure() был следующим:

public void configure(final Properties props)
{
    this.url = props.getProperty(Environment.URL);
    this.connectionProperties = ConnectionProviderFactory
        .getConnectionProperties(props);

    final DatabaseDriverClassLoader classLoader = new DatabaseDriverClassLoader(
        Activator.getDefault().getDatabaseDriverRegistry());

    final String driverClassName = props.getProperty(Environment.DRIVER);
    try
    {
        final Class driverClass = Class.forName(driverClassName, true,
            classLoader);
        this.driver = (Driver)driverClass.newInstance();
    }
    catch (ClassNotFoundException e)
    {
        throw new HibernateException(e);
    }
    catch (IllegalAccessException e)
    {
        throw new HibernateException(e);
    }
    catch (InstantiationException e)
    {
        throw new HibernateException(e);
    }
}

А метод getConnection() выглядит следующим образом:

public Connection getConnection()
    throws SQLException
{
    return this.driver.connect(this.url, this.connectionProperties);
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
4 427
1

Ответы 1

Class.forName() в OSGi - основная проблема. На самом деле это не чья-то вина, просто оба используют загрузчики классов, которые не работают так, как ожидает другой клиент (т.е.загрузчик классов OSGi не работает так, как ожидает спящий режим).

Я думаю, вы можете пойти одним из нескольких путей, но сейчас я могу вспомнить следующие:

  • чистый способ, заключающийся в том, чтобы упаковать драйверы JDBC в виде пакетов OSGi. Внесите свой вклад в класс как услугу. Вы можете сделать это с помощью декларативных сервисов (возможно, лучше) или написать активатор, которым вам нужно будет управлять запуском. Когда вы будете готовы получить драйвер, получите службу JDBCDriver и найдите интересующий вас класс.
  • менее чистый способ, но он будет работать с меньшими усилиями, чем первый - используйте DynamicImport-Package для добавления экспортированных пакетов из ваших связанных драйверов. Таким образом, клиентский код все еще может видеть класс, который он будет использовать, но ему не нужно знать об этом до времени выполнения. Однако вам, возможно, придется поэкспериментировать с шаблоном пакета, чтобы охватить все случаи (поэтому он менее чистый).
  • менее OSGi способ; который должен добавить ваши драйверы в путь к классам eclipse и добавить загрузчик классов родительского приложения. Вы можете добавить это: osgi.parentClassloader=app в ваш config.ini. Это может не соответствовать вашему развертыванию, особенно если у вас нет контроля над файлом config.ini.
  • не-OSGi, вместо использования загрузчика классов контекста используйте URLClassLoader. Это будет работать только в том случае, если у вас есть каталог, полный jar-файлов с драйверами, или если пользователь может прямо или косвенно указать местоположение jar-файла с драйверами.

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

Brian Matthews 19.12.2008 01:22

Другие вопросы по теме