Java tutorial
/* * Sonar, open source software quality management tool. * Copyright (C) 2008-2012 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.api.utils; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; import com.google.common.io.Files; import com.google.common.io.InputSupplier; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.config.Settings; import org.sonar.api.platform.Server; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.Authenticator; import java.net.HttpURLConnection; import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.ProxySelector; import java.net.URI; import java.nio.charset.Charset; import java.util.List; import java.util.Map; /** * This component downloads HTTP files * * @since 2.2 */ public class HttpDownloader extends UriReader.SchemeProcessor implements BatchComponent, ServerComponent { public static final int TIMEOUT_MILLISECONDS = 20 * 1000; private final BaseHttpDownloader downloader; public HttpDownloader(Server server, Settings settings) { downloader = new BaseHttpDownloader(settings.getProperties(), server.getVersion()); } public HttpDownloader(Settings settings) { downloader = new BaseHttpDownloader(settings.getProperties(), null); } @Override String description(URI uri) { return String.format("%s (%s)", uri.toString(), getProxySynthesis(uri)); } @Override String[] getSupportedSchemes() { return new String[] { "http", "https" }; } @Override byte[] readBytes(URI uri) { return download(uri); } @Override String readString(URI uri, Charset charset) { try { return CharStreams.toString(CharStreams.newReaderSupplier(downloader.newInputSupplier(uri), charset)); } catch (IOException e) { throw failToDownload(uri, e); } } public String downloadPlainText(URI uri, String encoding) { return readString(uri, Charset.forName(encoding)); } public byte[] download(URI uri) { try { return ByteStreams.toByteArray(downloader.newInputSupplier(uri)); } catch (IOException e) { throw failToDownload(uri, e); } } public String getProxySynthesis(URI uri) { return downloader.getProxySynthesis(uri); } public InputStream openStream(URI uri) { try { return downloader.newInputSupplier(uri).getInput(); } catch (IOException e) { throw failToDownload(uri, e); } } public void download(URI uri, File toFile) { try { Files.copy(downloader.newInputSupplier(uri), toFile); } catch (IOException e) { FileUtils.deleteQuietly(toFile); throw failToDownload(uri, e); } } private SonarException failToDownload(URI uri, IOException e) { throw new SonarException(String.format("Fail to download: %s (%s)", uri, getProxySynthesis(uri)), e); } public static class BaseHttpDownloader { private static final List<String> PROXY_SETTINGS = ImmutableList.of("http.proxyHost", "http.proxyPort", "http.nonProxyHosts", "http.auth.ntlm.domain", "socksProxyHost", "socksProxyPort"); private String userAgent; public BaseHttpDownloader(Map<String, String> settings, String userAgent) { initProxy(settings); initUserAgent(userAgent); } private void initProxy(Map<String, String> settings) { propagateProxySystemProperties(settings); if (requiresProxyAuthentication(settings)) { registerProxyCredentials(settings); } } private void initUserAgent(String sonarVersion) { userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion)); System.setProperty("http.agent", userAgent); } private String getProxySynthesis(URI uri) { return getProxySynthesis(uri, ProxySelector.getDefault()); } @VisibleForTesting static String getProxySynthesis(URI uri, ProxySelector proxySelector) { List<Proxy> proxies = proxySelector.select(uri); if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) { return "no proxy"; } List<String> descriptions = Lists.newArrayList(); for (Proxy proxy : proxies) { if (proxy.type() != Proxy.Type.DIRECT) { descriptions.add("proxy: " + proxy.address()); } } return Joiner.on(", ").join(descriptions); } private void registerProxyCredentials(Map<String, String> settings) { Authenticator.setDefault( new ProxyAuthenticator(settings.get("http.proxyUser"), settings.get("http.proxyPassword"))); } private boolean requiresProxyAuthentication(Map<String, String> settings) { return settings.containsKey("http.proxyUser"); } private void propagateProxySystemProperties(Map<String, String> settings) { for (String key : PROXY_SETTINGS) { if (settings.containsKey(key)) { System.setProperty(key, settings.get(key)); } } } public InputSupplier<InputStream> newInputSupplier(URI uri) { return new HttpInputSupplier(uri, userAgent, null, null); } public InputSupplier<InputStream> newInputSupplier(URI uri, String login, String password) { return new HttpInputSupplier(uri, userAgent, login, password); } private static class HttpInputSupplier implements InputSupplier<InputStream> { private final String login; private final String password; private final URI uri; private final String userAgent; HttpInputSupplier(URI uri, String userAgent, String login, String password) { this.uri = uri; this.userAgent = userAgent; this.login = login; this.password = password; } public InputStream getInput() throws IOException { LoggerFactory.getLogger(getClass()).debug( "Download: " + uri + " (" + getProxySynthesis(uri, ProxySelector.getDefault()) + ")"); HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); if (!Strings.isNullOrEmpty(login)) { String encoded = new String(Base64.encodeBase64((login + ":" + password).getBytes())); connection.setRequestProperty("Authorization", "Basic " + encoded); } connection.setConnectTimeout(TIMEOUT_MILLISECONDS); connection.setReadTimeout(TIMEOUT_MILLISECONDS); connection.setUseCaches(true); connection.setInstanceFollowRedirects(true); connection.setRequestProperty("User-Agent", userAgent); int responseCode = connection.getResponseCode(); if (responseCode >= 400) { throw new HttpException(uri, responseCode); } return connection.getInputStream(); } } private static class ProxyAuthenticator extends Authenticator { private final PasswordAuthentication auth; ProxyAuthenticator(String user, String password) { auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray()); } @Override protected PasswordAuthentication getPasswordAuthentication() { return auth; } } } public static class HttpException extends RuntimeException { private final URI uri; private final int responseCode; public HttpException(URI uri, int responseCode) { super("Fail to download [" + uri + "]. Response code: " + responseCode); this.uri = uri; this.responseCode = responseCode; } public int getResponseCode() { return responseCode; } public URI getUri() { return uri; } } }