Je développe un plug-in Eclipse qui correspond à un modèle client-serveur. C'est un projet commercial donc nous ne pouvons pas redistribuer les pilotes JDBC pour les différentes bases de données que nous supportons avec le plug-in.Comment remplacer dynamiquement le chargeur de classe pour un plug-in Eclipse?
J'ai donc développé une page de préférences pour permettre à l'utilisateur de localiser les jars et d'avoir un mécanisme de découverte simple qui parcourt les classes dans les fichiers jar, en chargeant chacun pour vérifier qu'il implémente l'interface java.sql.Driver. Tout cela fonctionne très bien.
Mais le hic, c'est que j'utilise Hibernate. Et Hibernate utilise Class.forName() pour instancier le pilote JDBC.
Si j'essaie d'utiliser ce qui suit, j'obtiens ClassNotFoundException
. Et si j'essaie de créer le pilote moi-même comme suit, j'obtiens une exception 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;
}
EDIT: Je ne suis pas sûr qu'il est la meilleure option, mais je pris l'approche de la mise en œuvre ma propre ConnectionProvider
qui m'a permis instancier le conducteur en utilisant Class.forName()
et j'ouvre alors la connexion à l'aide Driver.connect()
au lieu de DriverManager.getConnection()
. C'est assez basique, mais je n'ai pas besoin d'un pool de connexion dans mon cas d'utilisation spécifique.
La méthode configure()
était la suivante:
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);
}
}
Et la méthode getConnection()
est la suivante:
public Connection getConnection()
throws SQLException
{
return this.driver.connect(this.url, this.connectionProperties);
}
Je garderai ce conseil en tête pour d'autres projets. Malheureusement, ces approches consistent à reconditionner et distribuer les pilotes des vendeurs avec mon outil. Je dois éviter cela en raison de problèmes de licence. –