001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.api.utils;
021
022 import com.google.common.base.Joiner;
023 import com.google.common.collect.Lists;
024 import org.apache.commons.configuration.Configuration;
025 import org.apache.commons.configuration.PropertiesConfiguration;
026 import org.apache.commons.io.FileUtils;
027 import org.apache.commons.io.IOUtils;
028 import org.slf4j.LoggerFactory;
029 import org.sonar.api.BatchComponent;
030 import org.sonar.api.ServerComponent;
031 import org.sonar.api.platform.Server;
032
033 import java.io.File;
034 import java.io.FileOutputStream;
035 import java.io.IOException;
036 import java.io.InputStream;
037 import java.net.*;
038 import java.util.List;
039
040 /**
041 * This component downloads HTTP files
042 *
043 * @since 2.2
044 */
045 public class HttpDownloader implements BatchComponent, ServerComponent {
046
047 public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
048
049 private String userAgent;
050
051 public HttpDownloader(Server server, Configuration configuration) {
052 this(configuration, server.getVersion());
053 }
054
055 public HttpDownloader(Configuration configuration) {
056 this(configuration, null);
057 }
058
059 /**
060 * Should be package protected for unit tests only, but public is still required for jacoco 0.2.
061 */
062 public HttpDownloader() {
063 this(new PropertiesConfiguration(), null);
064 }
065
066 private HttpDownloader(Configuration configuration, String userAgent) {
067 initProxy(configuration);
068 initUserAgent(userAgent);
069 }
070
071 private void initProxy(Configuration configuration) {
072 propagateProxySystemProperties(configuration);
073 if (requiresProxyAuthentication(configuration)) {
074 registerProxyCredentials(configuration);
075 }
076 }
077
078 private void initUserAgent(String sonarVersion) {
079 String userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion));
080 System.setProperty("http.agent", userAgent);
081 this.userAgent = userAgent;
082 }
083
084 public String getProxySynthesis(URI uri) {
085 return getProxySynthesis(uri, ProxySelector.getDefault());
086 }
087
088 static String getProxySynthesis(URI uri, ProxySelector proxySelector) {
089 List<String> descriptions = Lists.newArrayList();
090 List<Proxy> proxies = proxySelector.select(uri);
091 if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) {
092 descriptions.add("no proxy");
093 } else {
094 for (Proxy proxy : proxies) {
095 if (!proxy.type().equals(Proxy.Type.DIRECT)) {
096 descriptions.add("proxy: " + proxy.address().toString());
097 }
098 }
099 }
100 return Joiner.on(", ").join(descriptions);
101 }
102
103 private void registerProxyCredentials(Configuration configuration) {
104 Authenticator.setDefault(new ProxyAuthenticator(configuration.getString("http.proxyUser"), configuration.getString("http.proxyPassword")));
105 }
106
107 private boolean requiresProxyAuthentication(Configuration configuration) {
108 return configuration.getString("http.proxyUser") != null;
109 }
110
111 private void propagateProxySystemProperties(Configuration configuration) {
112 propagateSystemProperty(configuration, "http.proxyHost");
113 propagateSystemProperty(configuration, "http.proxyPort");
114 propagateSystemProperty(configuration, "http.nonProxyHosts");
115 propagateSystemProperty(configuration, "http.auth.ntlm.domain");
116 propagateSystemProperty(configuration, "socksProxyHost");
117 propagateSystemProperty(configuration, "socksProxyPort");
118 }
119
120 private void propagateSystemProperty(Configuration configuration, String key) {
121 if (configuration.getString(key) != null) {
122 System.setProperty(key, configuration.getString(key));
123 }
124 }
125
126 public void download(URI uri, File toFile) {
127 InputStream input = null;
128 FileOutputStream output = null;
129 try {
130 HttpURLConnection connection = newHttpConnection(uri);
131 output = new FileOutputStream(toFile, false);
132 input = connection.getInputStream();
133 IOUtils.copy(input, output);
134
135 } catch (Exception e) {
136 IOUtils.closeQuietly(output);
137 FileUtils.deleteQuietly(toFile);
138 throw new SonarException("Fail to download the file: " + uri + getProxySynthesis(uri), e);
139
140 } finally {
141 IOUtils.closeQuietly(input);
142 IOUtils.closeQuietly(output);
143 }
144 }
145
146 public byte[] download(URI uri) {
147 InputStream input = null;
148 try {
149 HttpURLConnection connection = newHttpConnection(uri);
150 input = connection.getInputStream();
151 return IOUtils.toByteArray(input);
152
153 } catch (Exception e) {
154 throw new SonarException("Fail to download the file: " + uri + getProxySynthesis(uri), e);
155
156 } finally {
157 IOUtils.closeQuietly(input);
158 }
159 }
160
161 public InputStream openStream(URI uri) {
162 try {
163 HttpURLConnection connection = newHttpConnection(uri);
164 return connection.getInputStream();
165
166 } catch (Exception e) {
167 throw new SonarException("Fail to download the file: " + uri + getProxySynthesis(uri), e);
168 }
169 }
170
171 private HttpURLConnection newHttpConnection(URI uri) throws IOException {
172 LoggerFactory.getLogger(getClass()).info("Download: " + uri + getProxySynthesis(uri));
173 HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
174 connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
175 connection.setReadTimeout(TIMEOUT_MILLISECONDS);
176 connection.setUseCaches(true);
177 connection.setInstanceFollowRedirects(true);
178 connection.setRequestProperty("User-Agent", userAgent);
179 return connection;
180 }
181 }
182
183 class ProxyAuthenticator extends Authenticator {
184 private PasswordAuthentication auth;
185
186 ProxyAuthenticator(String user, String password) {
187 auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray());
188 }
189
190 protected PasswordAuthentication getPasswordAuthentication() {
191 return auth;
192 }
193 }