001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
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.io.FileUtils;
025    import org.apache.commons.io.IOUtils;
026    import org.slf4j.LoggerFactory;
027    import org.sonar.api.BatchComponent;
028    import org.sonar.api.ServerComponent;
029    import org.sonar.api.config.Settings;
030    import org.sonar.api.platform.Server;
031    
032    import java.io.File;
033    import java.io.FileOutputStream;
034    import java.io.IOException;
035    import java.io.InputStream;
036    import java.net.*;
037    import java.util.List;
038    
039    /**
040     * This component downloads HTTP files
041     *
042     * @since 2.2
043     */
044    public class HttpDownloader implements BatchComponent, ServerComponent {
045    
046      public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
047    
048      private String userAgent;
049    
050      public HttpDownloader(Server server, Settings settings) {
051        this(settings, server.getVersion());
052      }
053    
054      public HttpDownloader(Settings settings) {
055        this(settings, null);
056      }
057    
058      private HttpDownloader(Settings settings, String userAgent) {
059        initProxy(settings);
060        initUserAgent(userAgent);
061      }
062    
063      private void initProxy(Settings settings) {
064        propagateProxySystemProperties(settings);
065        if (requiresProxyAuthentication(settings)) {
066          registerProxyCredentials(settings);
067        }
068      }
069    
070      private void initUserAgent(String sonarVersion) {
071        userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion));
072        System.setProperty("http.agent", userAgent);
073      }
074    
075      public String getProxySynthesis(URI uri) {
076        return getProxySynthesis(uri, ProxySelector.getDefault());
077      }
078    
079      static String getProxySynthesis(URI uri, ProxySelector proxySelector) {
080        List<String> descriptions = Lists.newArrayList();
081        List<Proxy> proxies = proxySelector.select(uri);
082        if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) {
083          descriptions.add("no proxy");
084        } else {
085          for (Proxy proxy : proxies) {
086            if (!proxy.type().equals(Proxy.Type.DIRECT)) {
087              descriptions.add("proxy: " + proxy.address().toString());
088            }
089          }
090        }
091        return Joiner.on(", ").join(descriptions);
092      }
093    
094      private void registerProxyCredentials(Settings settings) {
095        Authenticator.setDefault(new ProxyAuthenticator(settings.getString("http.proxyUser"), settings
096            .getString("http.proxyPassword")));
097      }
098    
099      private boolean requiresProxyAuthentication(Settings settings) {
100        return settings.getString("http.proxyUser") != null;
101      }
102    
103      private void propagateProxySystemProperties(Settings settings) {
104        propagateSystemProperty(settings, "http.proxyHost");
105        propagateSystemProperty(settings, "http.proxyPort");
106        propagateSystemProperty(settings, "http.nonProxyHosts");
107        propagateSystemProperty(settings, "http.auth.ntlm.domain");
108        propagateSystemProperty(settings, "socksProxyHost");
109        propagateSystemProperty(settings, "socksProxyPort");
110      }
111    
112      private void propagateSystemProperty(Settings settings, String key) {
113        if (settings.getString(key) != null) {
114          System.setProperty(key, settings.getString(key));
115        }
116      }
117    
118      public void download(URI uri, File toFile) {
119        InputStream input = null;
120        FileOutputStream output = null;
121        try {
122          HttpURLConnection connection = newHttpConnection(uri);
123          output = new FileOutputStream(toFile, false);
124          input = connection.getInputStream();
125          IOUtils.copy(input, output);
126    
127        } catch (Exception e) {
128          IOUtils.closeQuietly(output);
129          FileUtils.deleteQuietly(toFile);
130          throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
131    
132        } finally {
133          IOUtils.closeQuietly(input);
134          IOUtils.closeQuietly(output);
135        }
136      }
137    
138      public byte[] download(URI uri) {
139        InputStream input = null;
140        try {
141          HttpURLConnection connection = newHttpConnection(uri);
142          input = connection.getInputStream();
143          return IOUtils.toByteArray(input);
144    
145        } catch (Exception e) {
146          throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
147    
148        } finally {
149          IOUtils.closeQuietly(input);
150        }
151      }
152    
153      public String downloadPlainText(URI uri, String encoding) {
154        InputStream input = null;
155        try {
156          HttpURLConnection connection = newHttpConnection(uri);
157          input = connection.getInputStream();
158          return IOUtils.toString(input, encoding);
159    
160        } catch (Exception e) {
161          throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
162    
163        } finally {
164          IOUtils.closeQuietly(input);
165        }
166      }
167    
168      public InputStream openStream(URI uri) {
169        try {
170          HttpURLConnection connection = newHttpConnection(uri);
171          return connection.getInputStream();
172    
173        } catch (Exception e) {
174          throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
175        }
176      }
177    
178      private HttpURLConnection newHttpConnection(URI uri) throws IOException {
179        LoggerFactory.getLogger(getClass()).debug("Download: " + uri + " (" + getProxySynthesis(uri) + ")");
180        HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
181        connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
182        connection.setReadTimeout(TIMEOUT_MILLISECONDS);
183        connection.setUseCaches(true);
184        connection.setInstanceFollowRedirects(true);
185        connection.setRequestProperty("User-Agent", userAgent);
186        return connection;
187      }
188    }
189    
190    class ProxyAuthenticator extends Authenticator {
191      private PasswordAuthentication auth;
192    
193      ProxyAuthenticator(String user, String password) {
194        auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray());
195      }
196    
197      protected PasswordAuthentication getPasswordAuthentication() {
198        return auth;
199      }
200    }