Я разрабатываю подключаемый модуль 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);
}




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