aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2011-06-10 15:24:57 +0200
committersimonbrandhof <simon.brandhof@gmail.com>2011-06-10 15:24:57 +0200
commit78e9b96a51e2ef677a4eb8b801e18248d7e32283 (patch)
treee0bae2cb0f36df7ac10edddcb929683437abc2c1 /sonar-batch
parent3203b81516eb93052a05b74c6e0a5ddc7f1977ca (diff)
parent231d8aeceb89848a8099f1d516f85d76106f3fef (diff)
downloadsonarqube-78e9b96a51e2ef677a4eb8b801e18248d7e32283.tar.gz
sonarqube-78e9b96a51e2ef677a4eb8b801e18248d7e32283.zip
Merge remote branch 'upstream/master'
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapClassLoader.java53
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcDriverHolder.java132
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcLeakPrevention.java64
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapClassLoaderTest.java2
6 files changed, 201 insertions, 58 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapClassLoader.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapClassLoader.java
deleted file mode 100644
index ca00f9a0691..00000000000
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapClassLoader.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.batch.bootstrap;
-
-import org.sonar.api.utils.SonarException;
-
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-
-/**
- * ClassLoader extended with the JDBC Driver hosted on the server-side.
- */
-public class BootstrapClassLoader {
-
- private URLClassLoader classLoader;
-
- public BootstrapClassLoader(ArtifactDownloader extensionDownloader) {
- this(extensionDownloader.downloadJdbcDriver());
- }
-
- BootstrapClassLoader(File jdbcDriver) {
- try {
- ClassLoader parentClassLoader = BootstrapClassLoader.class.getClassLoader();
- classLoader = URLClassLoader.newInstance(new URL[]{jdbcDriver.toURI().toURL()}, parentClassLoader);
-
- } catch (MalformedURLException e) {
- throw new SonarException("Fail to get URL of : " + jdbcDriver.getAbsolutePath(), e);
- }
- }
-
- public URLClassLoader getClassLoader() {
- return classLoader;
- }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java
index 0eaf338f8fa..fc09ef25dd0 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java
@@ -55,9 +55,9 @@ public class BootstrapModule extends Module {
addComponent(TempDirectories.class);// registered here because used by BootstrapClassLoader
addComponent(HttpDownloader.class);// registered here because used by BootstrapClassLoader
addComponent(ArtifactDownloader.class);// registered here because used by BootstrapClassLoader
- addComponent(BootstrapClassLoader.class);
+ addComponent(JdbcDriverHolder.class);
- URLClassLoader bootstrapClassLoader = getComponent(BootstrapClassLoader.class).getClassLoader();
+ URLClassLoader bootstrapClassLoader = getComponent(JdbcDriverHolder.class).getClassLoader();
// set as the current context classloader for hibernate, else it does not find the JDBC driver.
Thread.currentThread().setContextClassLoader(bootstrapClassLoader);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcDriverHolder.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcDriverHolder.java
new file mode 100644
index 00000000000..427ac20e3ab
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcDriverHolder.java
@@ -0,0 +1,132 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.bootstrap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.SonarException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.List;
+
+/**
+ * Contains and provides class loader extended with the JDBC Driver hosted on the server-side.
+ */
+public class JdbcDriverHolder {
+
+ private static Logger LOG = LoggerFactory.getLogger(JdbcDriverHolder.class);
+ private JdbcDriverClassLoader classLoader;
+
+ public JdbcDriverHolder(ArtifactDownloader extensionDownloader) {
+ this(extensionDownloader.downloadJdbcDriver());
+ }
+
+ JdbcDriverHolder(File jdbcDriver) {
+ try {
+ ClassLoader parentClassLoader = JdbcDriverHolder.class.getClassLoader();
+ classLoader = new JdbcDriverClassLoader(jdbcDriver.toURI().toURL(), parentClassLoader);
+
+ } catch (MalformedURLException e) {
+ throw new SonarException("Fail to get URL of : " + jdbcDriver.getAbsolutePath(), e);
+ }
+ }
+
+ public URLClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ /**
+ * This method automatically invoked by PicoContainer and deregisters JDBC drivers, which were forgotten.
+ * <p>
+ * Dynamically loaded JDBC drivers can not be simply used and this is a well known problem of {@link java.sql.DriverManager},
+ * so <a href="http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location">workaround is to use proxy</a>.
+ * However DriverManager also contains memory leak, thus not only proxy, but also original driver must be deregistered,
+ * otherwise our class loader would be kept in memory.
+ * </p>
+ * <p>
+ * This operation contains unnecessary complexity because:
+ * <ul>
+ * <li>DriverManager checks the class loader of the calling class. Thus we can't simply ask it about deregistration.</li>
+ * <li>We can't use reflection against DriverManager, since it would create a dependency on DriverManager implementation,
+ * which can be changed (like it was done - compare Java 1.5 and 1.6).</li>
+ * <li>So we use companion - {@link JdbcLeakPrevention}. But we can't just create an instance,
+ * since it will be loaded by parent class loader and again will not pass DriverManager's check.
+ * So, we load the bytes via our parent class loader, but define the class with this class loader
+ * thus JdbcLeakPrevention looks like our class to the DriverManager.</li>
+ * </li>
+ * </p>
+ */
+ public void stop() {
+ classLoader.clearReferencesJdbc();
+ classLoader = null;
+ }
+
+ private static class JdbcDriverClassLoader extends URLClassLoader {
+
+ public JdbcDriverClassLoader(URL jdbcDriver, ClassLoader parent) {
+ super(new URL[] { jdbcDriver }, parent);
+ }
+
+ public void clearReferencesJdbc() {
+ InputStream is = getResourceAsStream("org/sonar/batch/bootstrap/JdbcLeakPrevention.class");
+ byte[] classBytes = new byte[2048];
+ int offset = 0;
+ try {
+ int read = is.read(classBytes, offset, classBytes.length - offset);
+ while (read > -1) {
+ offset += read;
+ if (offset == classBytes.length) {
+ // Buffer full - double size
+ byte[] tmp = new byte[classBytes.length * 2];
+ System.arraycopy(classBytes, 0, tmp, 0, classBytes.length);
+ classBytes = tmp;
+ }
+ read = is.read(classBytes, offset, classBytes.length - offset);
+ }
+
+ Class<?> lpClass = defineClass("org.sonar.batch.bootstrap.JdbcLeakPrevention", classBytes, 0, offset, this.getClass().getProtectionDomain());
+ Object obj = lpClass.newInstance();
+
+ @SuppressWarnings("unchecked")
+ List<String> driverNames = (List<String>) obj.getClass().getMethod("clearJdbcDriverRegistrations").invoke(obj);
+
+ for (String name : driverNames) {
+ LOG.debug("To prevent a memory leak, the JDBC Driver [{}] has been forcibly deregistered", name);
+ }
+ } catch (Exception e) {
+ LOG.warn("JDBC driver deregistration failed", e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ LOG.warn(ioe.getMessage(), ioe);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcLeakPrevention.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcLeakPrevention.java
new file mode 100644
index 00000000000..6da6514d45e
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/JdbcLeakPrevention.java
@@ -0,0 +1,64 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.bootstrap;
+
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Companion of {@link JdbcDriverHolder} and allows it to deregister JDBC drivers.
+ * <p>
+ * Some hacks are involved in the loading of the class - see {@link JdbcDriverHolder#stop()},
+ * so this class can refer to classes only from java.* package and must not be referred from other classes.
+ * Placement and naming of this class and methods are very important, since it loaded and invoked via reflection.
+ * </p>
+ */
+public class JdbcLeakPrevention {
+
+ /**
+ * @return list of names of deregistered drivers
+ */
+ public List<String> clearJdbcDriverRegistrations() throws SQLException {
+ List<String> driverNames = new ArrayList<String>();
+ HashSet<Driver> originalDrivers = new HashSet<Driver>();
+ Enumeration<Driver> drivers = DriverManager.getDrivers();
+ while (drivers.hasMoreElements()) {
+ originalDrivers.add(drivers.nextElement());
+ }
+ drivers = DriverManager.getDrivers();
+ while (drivers.hasMoreElements()) {
+ Driver driver = drivers.nextElement();
+ if (driver.getClass().getClassLoader() != this.getClass().getClassLoader()) {
+ continue;
+ }
+ if (originalDrivers.contains(driver)) {
+ driverNames.add(driver.getClass().getCanonicalName());
+ }
+ DriverManager.deregisterDriver(driver);
+ }
+ return driverNames;
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java
index 4962be2b8e6..dd59bb2009d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java
@@ -63,8 +63,8 @@ public class PastSnapshotFinder implements BatchExtension {
case 1: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1; break;
case 2: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2; break;
case 3: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3; break;
- case 4: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4; break;
- case 5: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5; break;// NOSONAR false-positive: constant 5 is the same than 4 (empty string)
+ case 4: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4; break; // NOSONAR false-positive: constant 4 is the same than 5 (empty string)
+ case 5: defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5; break; // NOSONAR false-positive: constant 5 is the same than 4 (empty string)
}
return conf.getString(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index, defaultValue);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapClassLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapClassLoaderTest.java
index b28a4c33431..dfee29db761 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapClassLoaderTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapClassLoaderTest.java
@@ -37,7 +37,7 @@ public class BootstrapClassLoaderTest {
assertNull(getClass().getClassLoader().getResource("foo/foo.txt"));
URL url = getClass().getResource("/org/sonar/batch/bootstrap/BootstrapClassLoaderTest/foo.jar");
- BootstrapClassLoader classloader = new BootstrapClassLoader(new File(url.toURI()));
+ JdbcDriverHolder classloader = new JdbcDriverHolder(new File(url.toURI()));
assertNotNull(classloader.getClassLoader());
assertNotNull(classloader.getClassLoader().getResource("foo/foo.txt"));