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 infrastruct...@apache.org or file a JIRA ticket
with INFRA.
---

Reply via email to