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 org.apache.commons.configuration.Configuration;
023    import org.apache.commons.io.IOUtils;
024    import org.apache.commons.lang.StringUtils;
025    import org.sonar.api.BatchExtension;
026    
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.io.InputStreamReader;
030    import java.io.Reader;
031    import java.net.HttpURLConnection;
032    import java.net.URL;
033    
034    /**
035     * @since 1.10
036     */
037    public class ServerHttpClient implements BatchExtension {
038    
039      protected static final String SERVER_API_PATH = "/api/server";
040      private static final String KEY_PATH = SERVER_API_PATH + "/key";
041      private static final String VERSION_PATH = SERVER_API_PATH + "/version";
042      protected static final String MAVEN_PATH = "/deploy/maven";
043      private static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
044      private static final int READ_TIMEOUT_MILLISECONDS = 60000;
045    
046      private String url;
047      private Integer connectTimeoutMiliseconds = CONNECT_TIMEOUT_MILLISECONDS;
048      private Integer readTimeoutMiliseconds = READ_TIMEOUT_MILLISECONDS;
049    
050      public ServerHttpClient(String remoteServerUrl) {
051        this(remoteServerUrl, null, null);
052      }
053    
054      public ServerHttpClient(String remoteServerUrl, Integer connectTimeoutMiliseconds, Integer readTimeoutMiliseconds) {
055        this.url = StringUtils.chomp(remoteServerUrl, "/");
056        if (connectTimeoutMiliseconds != null) {
057          this.connectTimeoutMiliseconds = connectTimeoutMiliseconds;
058        }
059        if (readTimeoutMiliseconds != null) {
060          this.readTimeoutMiliseconds = readTimeoutMiliseconds;
061        }
062    
063      }
064    
065      public ServerHttpClient(Configuration configuration) {
066        this(configuration.getString("sonar.host.url", "http://localhost:9000"),
067            configuration.getInteger("sonar.host.connectTimeoutMs", CONNECT_TIMEOUT_MILLISECONDS),
068            configuration.getInteger("sonar.host.readTimeoutMs", READ_TIMEOUT_MILLISECONDS));
069    
070      }
071    
072      /**
073       * Throws a runtime ServerConnectionException if it fails to connect Sonar server
074       */
075      public void checkUp() {
076        String exceptionLabel = "Sonar server at " + url +
077            " is unreacheable. Either start it or setup the sonar.host.url maven setting if the URL is incorrect";
078        if (getId() == null) {
079          throw new ServerConnectionException(exceptionLabel);
080        }
081      }
082    
083      public String getId() {
084        return executeAction(KEY_PATH);
085      }
086    
087      public String getVersion() {
088        return executeAction(VERSION_PATH);
089      }
090    
091      public String getMavenRepositoryUrl() {
092        return this.url + MAVEN_PATH;
093      }
094    
095      protected String executeAction(String action) {
096        String result = getRemoteContent(url + action);
097        if (result.trim().length() == 0) {
098          throw new ServerApiEmptyContentException("Empty " + action + " returned from server");
099        }
100        return result;
101      }
102    
103      protected String getRemoteContent(String url) {
104        HttpURLConnection conn = null;
105        Reader reader = null;
106        try {
107          conn = getConnection(url, "GET");
108          reader = new InputStreamReader((InputStream) conn.getContent());
109    
110          int statusCode = conn.getResponseCode();
111          if (statusCode != HttpURLConnection.HTTP_OK) {
112            throw new ServerConnectionException("Status returned by url : '" + url + "' is invalid : " + statusCode);
113          }
114    
115          return IOUtils.toString(reader);
116        } catch (IOException e) {
117          throw new ServerConnectionException("url=" + url, e);
118    
119        } finally {
120          IOUtils.closeQuietly(reader);
121          if (conn != null) {
122            conn.disconnect();
123          }
124        }
125      }
126    
127      public String getUrl() {
128        return url;
129      }
130    
131      private HttpURLConnection getConnection(String url, String method) throws IOException {
132        URL page = new URL(url);
133        HttpURLConnection conn = (HttpURLConnection) page.openConnection();
134        conn.setConnectTimeout(connectTimeoutMiliseconds);
135        conn.setReadTimeout(readTimeoutMiliseconds);
136    
137        conn.setRequestMethod(method);
138        conn.connect();
139        return conn;
140      }
141    
142      public static class ServerApiEmptyContentException extends SonarException {
143    
144        public ServerApiEmptyContentException(String s) {
145          super(s);
146        }
147      }
148    
149      public static class ServerConnectionException extends SonarException {
150    
151        public ServerConnectionException(String msg) {
152          super(msg);
153        }
154    
155        public ServerConnectionException(String msg, Throwable throwable) {
156          super(msg, throwable);
157        }
158    
159      }
160    }