cloudstack-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From miguelaferreira <...@git.apache.org>
Subject [GitHub] cloudstack pull request: Load mysql driver before connecting to db
Date Tue, 03 Nov 2015 19:51:42 GMT
Github user miguelaferreira commented on the pull request:

    https://github.com/apache/cloudstack/pull/950#issuecomment-153468603
  
    @borisroman That's what I thought, but no, the `common.loader` property (in [0] is not
what does the trick). I've tried that too and that did not work, not because the jar isn't
"loaded" by the JVM but because the driver class itself is not loaded into the memory area
that holds classes.
    
    Tomcat has 4 layers of class loaders: Bootstrap, System, Common and each webapp.
    
    In the `catalina.properties` file there is a property called `common.loader` that defines
which jars are available to tomcat's Common class loader. The fact that a jar is available
to the class loader does not mean all classes it defines will be loaded. It only means that
when a class is requested, the class loader will be able to search that jar. And, by the way,
if a class is defined in multiple jars, only the first one that is found will be loaded.
    
    **So, why is it that having the jar with the right driver class available, the DriverManager
still doesn't find a suitable driver?**
    
    It turns out that the DriverManager is not actively searching for all available drivers
(as specified in the Java Docs [1]). It so happens that for the driver class to be available
for the DriverManager, that class has to be explicitly loaded, either by an `import com.mysql.jdbc.Driver;`
statement, or a `Class.forName("com.mysql.jdbc.Driver")` statement.
    I've made a very simple webapp that asks the DriverManager for a JDBC MySQL connector,
and even though the correct driver is sitting in a jar that is in the class path, the DriverManager
still throws a "no suitable driver found" exception. To make that work, all we need is to
add a line of code saying `Class.forName("com.mysql.jdbc.Driver");` right before asking for
the connection. Adding that line proves the jar with the driver is correctly defined in the
class path, and when requested the correct driver class is loaded by the JVM. 
    
    What happens in CloudStack is that the mysql connector jar is not only searchable by the
Common class loader (via `common.loader` in [0]), but it is also searchable by tomca's System
class loader set in [2]. And that's the class loader that is actually loading the driver class.
I'm not sure why, but my best guess is that when tomcat is booting, one of it's components
(maybe tomcat-jdbc [3]) sweeps the available jars for any class implementing `java.sql.Driver`
and actually finds the one we need.
    
    So, if you want to drop a CloudStack WAR in a tomcat instance, make sure that the MySQL
connector jar is the class path of the process that starts JVM.
    
    Finally, just for documentation sake, @ke4qqq, if you even read this, please grep the
codebase for `Class.forName("com.mysql.jdbc.Driver")`. You will find two files that contain
that line, and those make the "runtime dependency" on a class licensed under GPL very explicit.
I wonder how does that play out with Apache's policy?
    
    [0] https://github.com/apache/cloudstack/blob/master/packaging/centos7/tomcat7/catalina.properties#L47
    [1] http://docs.oracle.com/javase/7/docs/api/java/sql/DriverManager.html
    [2] https://github.com/apache/cloudstack/blob/master/packaging/centos7/cloud-management.sysconfig#L48
    [3] https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

Mime
View raw message