]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2106: Improve sonar-batch-bootstrapper
authorEvgeny Mandrikov <mandrikov@gmail.com>
Thu, 3 Feb 2011 02:23:14 +0000 (05:23 +0300)
committerEvgeny Mandrikov <mandrikov@gmail.com>
Thu, 3 Feb 2011 04:04:37 +0000 (07:04 +0300)
* Add BootstrapClassLoader

* Instead of RuntimeException use BootstrapException

* Improve quality according to Sonar report

sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java [deleted file]
sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapClassLoader.java [new file with mode: 0644]
sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapException.java [new file with mode: 0644]
sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/Bootstrapper.java [new file with mode: 0644]
sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapperIOUtils.java
sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java [deleted file]
sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BootstrapClassLoaderTest.java [new file with mode: 0644]
sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BootstrapperTest.java [new file with mode: 0644]

diff --git a/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java
deleted file mode 100644 (file)
index eead8dc..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2009 SonarSource SA
- * 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.bootstrapper;
-
-import java.io.*;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-public class BatchDownloader {
-
-  private static final String VERSION_PATH = "/api/server/version";
-  private static final String BATCH_PATH = "/batch/";
-
-  public static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
-  public static final int READ_TIMEOUT_MILLISECONDS = 60000;
-
-  private String serverUrl;
-  private String serverVersion;
-
-  public BatchDownloader(String serverUrl) {
-    if (serverUrl.endsWith("/")) {
-      this.serverUrl = serverUrl.substring(0, serverUrl.length() - 1);
-    } else {
-      this.serverUrl = serverUrl;
-    }
-  }
-
-  /**
-   * @return server url
-   */
-  public String getServerUrl() {
-    return serverUrl;
-  }
-
-  /**
-   * @return server version
-   */
-  public String getServerVersion() {
-    if (serverVersion == null) {
-      try {
-        serverVersion = remoteContent(VERSION_PATH);
-      } catch (IOException e) {
-        throw new RuntimeException(e.getMessage(), e);
-      }
-    }
-    return serverVersion;
-  }
-
-  /**
-   * To use this method version of Sonar should be at least 2.6.
-   * 
-   * @return list of downloaded files
-   */
-  public List<File> downloadBatchFiles(File toDir) {
-    try {
-      List<File> files = new ArrayList<File>();
-
-      String libs = remoteContent(BATCH_PATH);
-
-      for (String lib : libs.split(",")) {
-        File file = new File(toDir, lib);
-        remoteContentToFile(BATCH_PATH + lib, file);
-        files.add(file);
-      }
-
-      return files;
-    } catch (Exception e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  private void remoteContentToFile(String path, File toFile) {
-    InputStream input = null;
-    FileOutputStream output = null;
-    String fullUrl = serverUrl + path;
-    try {
-      HttpURLConnection connection = newHttpConnection(new URL(fullUrl));
-      output = new FileOutputStream(toFile, false);
-      input = connection.getInputStream();
-      BootstrapperIOUtils.copyLarge(input, output);
-    } catch (Exception e) {
-      BootstrapperIOUtils.closeQuietly(output);
-      BootstrapperIOUtils.deleteFileQuietly(toFile);
-      throw new RuntimeException("Fail to download the file: " + fullUrl, e);
-    } finally {
-      BootstrapperIOUtils.closeQuietly(input);
-      BootstrapperIOUtils.closeQuietly(output);
-    }
-  }
-
-  String remoteContent(String path) throws IOException {
-    String fullUrl = serverUrl + path;
-    HttpURLConnection conn = newHttpConnection(new URL(fullUrl));
-    Reader reader = new InputStreamReader((InputStream) conn.getContent());
-    try {
-      int statusCode = conn.getResponseCode();
-      if (statusCode != HttpURLConnection.HTTP_OK) {
-        throw new IOException("Status returned by url : '" + fullUrl + "' is invalid : " + statusCode);
-      }
-      return BootstrapperIOUtils.toString(reader);
-    } finally {
-      BootstrapperIOUtils.closeQuietly(reader);
-      conn.disconnect();
-    }
-  }
-
-  static HttpURLConnection newHttpConnection(URL url) throws IOException {
-    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-    connection.setConnectTimeout(CONNECT_TIMEOUT_MILLISECONDS);
-    connection.setReadTimeout(READ_TIMEOUT_MILLISECONDS);
-    connection.setInstanceFollowRedirects(true);
-    connection.setRequestMethod("GET");
-    // TODO connection.setRequestProperty("User-Agent", userAgent);
-    return connection;
-  }
-
-}
diff --git a/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapClassLoader.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapClassLoader.java
new file mode 100644 (file)
index 0000000..c61a238
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.bootstrapper;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Special {@link URLClassLoader} to execute Sonar, which restricts loading from parent.
+ */
+public class BootstrapClassLoader extends URLClassLoader {
+
+  private String[] unmaskedPackages;
+
+  public BootstrapClassLoader(ClassLoader parent, String... unmaskedPackages) {
+    super(new URL[0], parent);
+    this.unmaskedPackages = unmaskedPackages;
+  }
+
+  /**
+   * {@inheritDoc} Visibility of a method has been relaxed to public.
+   */
+  @Override
+  public void addURL(URL url) {
+    super.addURL(url);
+  }
+
+  /**
+   * {@inheritDoc} Visibility of a method has been relaxed to public.
+   */
+  @Override
+  public Class<?> findClass(String name) throws ClassNotFoundException {
+    return super.findClass(name);
+  }
+
+  /**
+   * @return true, if class can be loaded from parent ClassLoader
+   */
+  boolean canLoadFromParent(String name) {
+    for (String pkg : unmaskedPackages) {
+      if (name.startsWith(pkg + ".")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Same behavior as in {@link URLClassLoader#loadClass(String, boolean)}, except loading from parent.
+   */
+  @Override
+  protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    // First, check if the class has already been loaded
+    Class<?> c = findLoadedClass(name);
+    if (c == null) {
+      try {
+        // Load from parent
+        if ((getParent() != null) && canLoadFromParent(name)) {
+          c = getParent().loadClass(name);
+        } else {
+          // Load from system
+          c = getSystemClassLoader().loadClass(name);
+        }
+      } catch (ClassNotFoundException e) {
+        // If still not found, then invoke findClass in order
+        // to find the class.
+        c = findClass(name);
+      }
+    }
+    if (resolve) {
+      resolveClass(c);
+    }
+    return c;
+  }
+
+}
diff --git a/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapException.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapException.java
new file mode 100644 (file)
index 0000000..8b75b61
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.bootstrapper;
+
+public class BootstrapException extends RuntimeException {
+
+  public BootstrapException(String message) {
+    super(message);
+  }
+
+  public BootstrapException(Throwable cause) {
+    super(cause);
+  }
+
+  public BootstrapException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+}
diff --git a/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/Bootstrapper.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/Bootstrapper.java
new file mode 100644 (file)
index 0000000..065c193
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.bootstrapper;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Bootstrapper {
+
+  private static final String VERSION_PATH = "/api/server/version";
+  private static final String BATCH_PATH = "/batch/";
+
+  public static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
+  public static final int READ_TIMEOUT_MILLISECONDS = 60000;
+
+  private File bootDir;
+  private String serverUrl;
+  private String serverVersion;
+
+  public Bootstrapper(String serverUrl, File workDir) {
+    bootDir = new File(workDir, "batch");
+    bootDir.mkdirs();
+    if (serverUrl.endsWith("/")) {
+      this.serverUrl = serverUrl.substring(0, serverUrl.length() - 1);
+    } else {
+      this.serverUrl = serverUrl;
+    }
+  }
+
+  /**
+   * @return server url
+   */
+  public String getServerUrl() {
+    return serverUrl;
+  }
+
+  /**
+   * @return server version
+   */
+  public String getServerVersion() {
+    if (serverVersion == null) {
+      try {
+        serverVersion = remoteContent(VERSION_PATH);
+      } catch (IOException e) {
+        throw new BootstrapException(e.getMessage(), e);
+      }
+    }
+    return serverVersion;
+  }
+
+  /**
+   * Download batch files from server and creates {@link BootstrapClassLoader}.
+   * To use this method version of Sonar should be at least 2.6.
+   * 
+   * @param urls additional URLs for loading classes and resources
+   * @param parent parent ClassLoader
+   * @param unmaskedPackages only classes and resources from those packages would be available for loading from parent
+   */
+  public BootstrapClassLoader createClassLoader(URL[] urls, ClassLoader parent, String... unmaskedPackages) {
+    BootstrapClassLoader classLoader = new BootstrapClassLoader(parent, unmaskedPackages);
+    List<File> files = downloadBatchFiles();
+    for (URL url : urls) {
+      classLoader.addURL(url);
+    }
+    for (File file : files) {
+      try {
+        classLoader.addURL(file.toURI().toURL());
+      } catch (MalformedURLException e) {
+        throw new BootstrapException(e);
+      }
+    }
+    return classLoader;
+  }
+
+  private void remoteContentToFile(String path, File toFile) {
+    InputStream input = null;
+    FileOutputStream output = null;
+    String fullUrl = serverUrl + path;
+    try {
+      HttpURLConnection connection = newHttpConnection(new URL(fullUrl));
+      output = new FileOutputStream(toFile, false);
+      input = connection.getInputStream();
+      BootstrapperIOUtils.copyLarge(input, output);
+    } catch (IOException e) {
+      BootstrapperIOUtils.closeQuietly(output);
+      BootstrapperIOUtils.deleteFileQuietly(toFile);
+      throw new BootstrapException("Fail to download the file: " + fullUrl, e);
+    } finally {
+      BootstrapperIOUtils.closeQuietly(input);
+      BootstrapperIOUtils.closeQuietly(output);
+    }
+  }
+
+  String remoteContent(String path) throws IOException {
+    String fullUrl = serverUrl + path;
+    HttpURLConnection conn = newHttpConnection(new URL(fullUrl));
+    Reader reader = new InputStreamReader((InputStream) conn.getContent());
+    try {
+      int statusCode = conn.getResponseCode();
+      if (statusCode != HttpURLConnection.HTTP_OK) {
+        throw new IOException("Status returned by url : '" + fullUrl + "' is invalid : " + statusCode);
+      }
+      return BootstrapperIOUtils.toString(reader);
+    } finally {
+      BootstrapperIOUtils.closeQuietly(reader);
+      conn.disconnect();
+    }
+  }
+
+  static HttpURLConnection newHttpConnection(URL url) throws IOException {
+    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+    connection.setConnectTimeout(CONNECT_TIMEOUT_MILLISECONDS);
+    connection.setReadTimeout(READ_TIMEOUT_MILLISECONDS);
+    connection.setInstanceFollowRedirects(true);
+    connection.setRequestMethod("GET");
+    // TODO connection.setRequestProperty("User-Agent", userAgent);
+    return connection;
+  }
+
+  private List<File> downloadBatchFiles() {
+    try {
+      List<File> files = new ArrayList<File>();
+      String libs = remoteContent(BATCH_PATH);
+      for (String lib : libs.split(",")) {
+        File file = new File(bootDir, lib);
+        remoteContentToFile(BATCH_PATH + lib, file);
+        files.add(file);
+      }
+      return files;
+    } catch (Exception e) {
+      throw new BootstrapException(e);
+    }
+  }
+}
index e7d4ec42e1a6f6c81983f5c96dac2c30ccd64303..d400bc8c40d2d9178cb6c2e87975acbd0750d8a8 100644 (file)
@@ -21,9 +21,10 @@ package org.sonar.batch.bootstrapper;
 
 import java.io.*;
 
-final class BootstrapperIOUtils {
+public final class BootstrapperIOUtils {
 
   private BootstrapperIOUtils() {
+    // only static methods
   }
 
   /**
@@ -39,8 +40,7 @@ final class BootstrapperIOUtils {
       if (closeable != null) {
         closeable.close();
       }
-    } catch (IOException ioe) {
-      // ignore
+    } catch (IOException ioe) { // NOSONAR
     }
   }
 
diff --git a/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java b/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java
deleted file mode 100644 (file)
index 1822a53..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2009 SonarSource SA
- * 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.bootstrapper;
-
-import org.junit.Test;
-
-import java.io.IOException;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-public class BatchDownloaderTest {
-
-  @Test
-  public void shouldRemoveLastUrlSlash() {
-    BatchDownloader bootstrapper = new BatchDownloader("http://test/");
-    assertThat(bootstrapper.getServerUrl(), is("http://test"));
-  }
-
-  @Test(expected = Exception.class)
-  public void shouldFailIfCanNotConnectServer() {
-    BatchDownloader bootstrapper = new BatchDownloader("http://unknown.foo");
-    bootstrapper.getServerVersion();
-  }
-
-  @Test
-  public void shouldReturnValidVersion() {
-    BatchDownloader bootstrapper = new BatchDownloader("http://test") {
-      @Override
-      String remoteContent(String path) throws IOException {
-        return "2.6";
-      }
-    };
-    assertThat(bootstrapper.getServerVersion(), is("2.6"));
-  }
-
-}
diff --git a/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BootstrapClassLoaderTest.java b/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BootstrapClassLoaderTest.java
new file mode 100644 (file)
index 0000000..b8a13d5
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.bootstrapper;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class BootstrapClassLoaderTest {
+
+  @Test
+  public void shouldRestrictLoadingFromParent() throws Exception {
+    BootstrapClassLoader classLoader = new BootstrapClassLoader(getClass().getClassLoader(), "org.sonar.ant");
+    assertThat(classLoader.canLoadFromParent("org.sonar.ant.Launcher"), is(true));
+    assertThat(classLoader.canLoadFromParent("org.objectweb.asm.ClassVisitor"), is(false));
+  }
+
+}
diff --git a/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BootstrapperTest.java b/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BootstrapperTest.java
new file mode 100644 (file)
index 0000000..945940a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.bootstrapper;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class BootstrapperTest {
+
+  @Test
+  public void shouldRemoveLastUrlSlash() {
+    Bootstrapper bootstrapper = new Bootstrapper("http://test/", new File("target"));
+    assertThat(bootstrapper.getServerUrl(), is("http://test"));
+  }
+
+  @Test(expected = Exception.class)
+  public void shouldFailIfCanNotConnectServer() {
+    Bootstrapper bootstrapper = new Bootstrapper("http://unknown.foo", new File("target"));
+    bootstrapper.getServerVersion();
+  }
+
+  @Test
+  public void shouldReturnValidVersion() {
+    Bootstrapper bootstrapper = new Bootstrapper("http://test", new File("target")) {
+      @Override
+      String remoteContent(String path) throws IOException {
+        return "2.6";
+      }
+    };
+    assertThat(bootstrapper.getServerVersion(), is("2.6"));
+  }
+
+}