+++ /dev/null
-/*
- * 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;
- }
-
-}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
import java.io.*;
-final class BootstrapperIOUtils {
+public final class BootstrapperIOUtils {
private BootstrapperIOUtils() {
+ // only static methods
}
/**
if (closeable != null) {
closeable.close();
}
- } catch (IOException ioe) {
- // ignore
+ } catch (IOException ioe) { // NOSONAR
}
}
+++ /dev/null
-/*
- * 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"));
- }
-
-}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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"));
+ }
+
+}