]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7054 use ws-client in batch
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 25 Nov 2015 09:54:56 +0000 (10:54 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 30 Nov 2015 18:01:45 +0000 (19:01 +0100)
84 files changed:
it/it-tests/src/test/java/it/actionPlan/ActionPlanTest.java
it/it-tests/src/test/java/it/authorisation/AuthenticationTest.java
it/it-tests/src/test/java/it/authorisation/IssuePermissionTest.java
it/it-tests/src/test/java/it/authorisation/PermissionTest.java
it/it-tests/src/test/java/it/issue/CommonRulesTest.java
it/it-tests/src/test/java/it/measureFilter/MeasureFiltersTest.java
it/it-tests/src/test/java/util/ItUtils.java
pom.xml
server/sonar-server/src/main/java/org/sonar/server/batch/Messages.java
server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWs.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/UserTokensWs.java
server/sonar-server/src/test/java/org/sonar/server/batch/ProjectDataLoaderMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserActionTest.java
sonar-batch-protocol/pom.xml
sonar-batch/pom.xml
sonar-batch/src/main/java/org/sonar/batch/analysis/AnalysisWSLoaderProvider.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientProvider.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/cache/StrategyWSLoaderProvider.java
sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java
sonar-batch/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientProviderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderWithServerTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
sonar-ws/pom.xml
sonar-ws/src/main/java/org/sonarqube/ws/MediaTypes.java
sonar-ws/src/main/java/org/sonarqube/ws/client/BaseRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/BaseService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/GetRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpConnector.java
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpException.java
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpRequestFactory.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpResponse.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/MockWsResponse.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/PostRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java
sonar-ws/src/main/java/org/sonarqube/ws/client/WsConnector.java
sonar-ws/src/main/java/org/sonarqube/ws/client/WsRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/WsResponse.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/ce/ComputeEngineService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/ce/SubmitWsRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/ce/package-info.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsClient.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssueFilterParameters.java
sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsClient.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsWsClient.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsWsParameters.java
sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesWsClient.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensWsClient.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensWsParameters.java
sonar-ws/src/test/java/org/sonarqube/ws/client/BaseRequestTest.java [new file with mode: 0644]
sonar-ws/src/test/java/org/sonarqube/ws/client/BaseServiceTest.java [new file with mode: 0644]
sonar-ws/src/test/java/org/sonarqube/ws/client/HttpConnectorTest.java [new file with mode: 0644]
sonar-ws/src/test/java/org/sonarqube/ws/client/HttpExceptionTest.java
sonar-ws/src/test/java/org/sonarqube/ws/client/HttpRequestFactoryTest.java [deleted file]
sonar-ws/src/test/java/org/sonarqube/ws/client/MockHttpServer.java [deleted file]
sonar-ws/src/test/java/org/sonarqube/ws/client/MockHttpServerInterceptor.java [deleted file]
sonar-ws/src/test/java/org/sonarqube/ws/client/PostRequestTest.java [new file with mode: 0644]
sonar-ws/src/test/java/org/sonarqube/ws/client/WsClientTest.java [deleted file]
sonar-ws/src/test/java/org/sonarqube/ws/client/WsRequestTest.java [deleted file]
sonar-ws/src/test/protobuf/ws-testing.proto [new file with mode: 0644]

index 914ed9eb526b5d35e165bd7e248d8dfca8d7aa13..fe73d0dc13cc9b7c775f0678c54702c24305da7f 100644 (file)
@@ -137,7 +137,7 @@ public class ActionPlanTest {
     try {
       // Create a user having admin permission on the project
       adminClient.userClient().create(UserParameters.create().login(projectAdminUser).name(projectAdminUser).password("password").passwordConfirmation("password"));
-      adminWsClient.permissionsClient().addUser(
+      adminWsClient.permissions().addUser(
         new AddUserWsRequest()
           .setLogin(projectAdminUser)
           .setProjectKey(PROJECT_KEY)
@@ -145,7 +145,7 @@ public class ActionPlanTest {
 
       // Create a user having browse permission on the project
       adminClient.userClient().create(UserParameters.create().login(projectUser).name(projectUser).password("password").passwordConfirmation("password"));
-      adminWsClient.permissionsClient().addUser(
+      adminWsClient.permissions().addUser(
         new AddUserWsRequest()
           .setLogin(projectUser)
           .setProjectKey(PROJECT_KEY)
index 30d0bdd50432519a40fe2acff31e46bc54910436..7288287edbe8f1708cb3520367d7b6c8a1a07064 100644 (file)
@@ -32,18 +32,20 @@ import org.junit.ClassRule;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.sonarqube.ws.WsUserTokens;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.HttpConnector;
+import org.sonarqube.ws.client.HttpWsClient;
+import org.sonarqube.ws.client.PostRequest;
 import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsResponse;
 import org.sonarqube.ws.client.permission.AddGroupWsRequest;
 import org.sonarqube.ws.client.permission.AddUserWsRequest;
 import org.sonarqube.ws.client.permission.RemoveGroupWsRequest;
 import org.sonarqube.ws.client.usertoken.GenerateWsRequest;
-import org.sonarqube.ws.client.usertoken.UserTokensWsClient;
+import org.sonarqube.ws.client.usertoken.UserTokensService;
 
 import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonarqube.ws.client.HttpConnector.newHttpConnector;
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-import static org.sonarqube.ws.client.WsRequest.newPostRequest;
 import static util.ItUtils.newAdminWsClient;
 import static util.ItUtils.projectDir;
 
@@ -51,7 +53,7 @@ public class AuthenticationTest {
   @ClassRule
   public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
   private static WsClient adminWsClient;
-  private static UserTokensWsClient userTokensWsClient;
+  private static UserTokensService userTokensWsClient;
 
   private static final String PROJECT_KEY = "sample";
   private static final String LOGIN = "george.orwell";
@@ -64,7 +66,7 @@ public class AuthenticationTest {
     ORCHESTRATOR.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
 
     adminWsClient = newAdminWsClient(ORCHESTRATOR);
-    userTokensWsClient = adminWsClient.userTokensWsClient();
+    userTokensWsClient = adminWsClient.userTokens();
     removeGroupPermission("anyone", "dryRunScan");
     removeGroupPermission("anyone", "scan");
 
@@ -89,9 +91,9 @@ public class AuthenticationTest {
     createUser(login, name, password);
 
     // authenticate
-    WsClient wsClient = new WsClient(newHttpConnector().url(ORCHESTRATOR.getServer().getUrl()).login(login).password(password).build());
-    String response = wsClient.execute(newGetRequest("api/authentication/validate"));
-    assertThat(response).isEqualTo("{\"valid\":true}");
+    WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build());
+    WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate"));
+    assertThat(response.getContent()).isEqualTo("{\"valid\":true}");
   }
 
   @Test
@@ -99,14 +101,13 @@ public class AuthenticationTest {
     WsUserTokens.GenerateWsResponse generateWsResponse = userTokensWsClient.generate(new GenerateWsRequest()
       .setLogin(LOGIN)
       .setName("Validate token based authentication"));
-    WsClient wsClient = new WsClient(newHttpConnector()
+    WsClient wsClient = new HttpWsClient(new HttpConnector.Builder()
       .url(ORCHESTRATOR.getServer().getUrl())
-      .login(generateWsResponse.getToken())
-      .password("").build());
+      .token(generateWsResponse.getToken()).build());
 
-    String response = wsClient.execute(newGetRequest("api/authentication/validate"));
+    WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate"));
 
-    assertThat(response).isEqualTo("{\"valid\":true}");
+    assertThat(response.getContent()).isEqualTo("{\"valid\":true}");
   }
 
   /**
@@ -123,9 +124,9 @@ public class AuthenticationTest {
     createUser(login, format("name-%s", userId), password);
 
     // authenticate
-    WsClient wsClient = new WsClient(newHttpConnector().url(ORCHESTRATOR.getServer().getUrl()).login(login).password(password).build());
-    String response = wsClient.execute(newGetRequest("api/authentication/validate"));
-    assertThat(response).isEqualTo("{\"valid\":false}");
+    WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build());
+    WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate"));
+    assertThat(response.getContent()).isEqualTo("{\"valid\":false}");
   }
 
   @Test
@@ -162,41 +163,41 @@ public class AuthenticationTest {
   }
 
   private static void createUser(String login, String password) {
-    adminWsClient.execute(
-      newPostRequest("api/users/create")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/users/create")
         .setParam("login", login)
         .setParam("name", login)
         .setParam("password", password));
   }
 
   private static void createUser(String login, String name, String password) {
-    adminWsClient.execute(
-      newPostRequest("api/users/create")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/users/create")
         .setParam("login", login)
         .setParam("name", name)
         .setParam("password", password));
   }
 
   private static void addUserPermission(String login, String permission) {
-    adminWsClient.permissionsClient().addUser(new AddUserWsRequest()
+    adminWsClient.permissions().addUser(new AddUserWsRequest()
       .setLogin(login)
       .setPermission(permission));
   }
 
   private static void deactivateUser(String login) {
-    adminWsClient.execute(
-      newPostRequest("api/users/deactivate")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/users/deactivate")
         .setParam("login", login));
   }
 
   private static void removeGroupPermission(String groupName, String permission) {
-    adminWsClient.permissionsClient().removeGroup(new RemoveGroupWsRequest()
+    adminWsClient.permissions().removeGroup(new RemoveGroupWsRequest()
       .setGroupName(groupName)
       .setPermission(permission));
   }
 
   private static void addGroupPermission(String groupName, String permission) {
-    adminWsClient.permissionsClient().addGroup(new AddGroupWsRequest()
+    adminWsClient.permissions().addGroup(new AddGroupWsRequest()
       .setGroupName(groupName)
       .setPermission(permission));
   }
index 5a45198f44497c651f1e0966d619663182681554..3eb384be9d1218d3a115917c24bd151ef2295e61 100644 (file)
@@ -229,7 +229,7 @@ public class IssuePermissionTest {
   }
 
   private void addUserPermission(String login, String projectKey, String permission) {
-    adminWsClient.permissionsClient().addUser(
+    adminWsClient.permissions().addUser(
       new AddUserWsRequest()
         .setLogin(login)
         .setProjectKey(projectKey)
@@ -237,7 +237,7 @@ public class IssuePermissionTest {
   }
 
   private void removeGroupPermission(String groupName, String projectKey, String permission) {
-    adminWsClient.permissionsClient().removeGroup(new RemoveGroupWsRequest()
+    adminWsClient.permissions().removeGroup(new RemoveGroupWsRequest()
       .setGroupName(groupName)
       .setProjectKey(projectKey)
       .setPermission(permission));
index e3dfe05fdb060739fb88cba2f89d47ce37649412..8c27306163796b5033fcf8c35f4ae0856dbe7f47 100644 (file)
@@ -30,6 +30,7 @@ import org.junit.ClassRule;
 import org.junit.Test;
 import org.sonarqube.ws.WsPermissions;
 import org.sonarqube.ws.WsPermissions.SearchTemplatesWsResponse;
+import org.sonarqube.ws.client.PostRequest;
 import org.sonarqube.ws.client.WsClient;
 import org.sonarqube.ws.client.permission.AddGroupToTemplateWsRequest;
 import org.sonarqube.ws.client.permission.AddGroupWsRequest;
@@ -37,14 +38,13 @@ import org.sonarqube.ws.client.permission.AddUserToTemplateWsRequest;
 import org.sonarqube.ws.client.permission.AddUserWsRequest;
 import org.sonarqube.ws.client.permission.CreateTemplateWsRequest;
 import org.sonarqube.ws.client.permission.GroupsWsRequest;
-import org.sonarqube.ws.client.permission.PermissionsWsClient;
+import org.sonarqube.ws.client.permission.PermissionsService;
 import org.sonarqube.ws.client.permission.RemoveGroupFromTemplateWsRequest;
 import org.sonarqube.ws.client.permission.RemoveUserFromTemplateWsRequest;
 import org.sonarqube.ws.client.permission.SearchTemplatesWsRequest;
 import org.sonarqube.ws.client.permission.UsersWsRequest;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonarqube.ws.client.WsRequest.newPostRequest;
 import static util.ItUtils.newAdminWsClient;
 import static util.ItUtils.projectDir;
 
@@ -52,7 +52,7 @@ public class PermissionTest {
   @ClassRule
   public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
   private static WsClient adminWsClient;
-  private static PermissionsWsClient permissionsWsClient;
+  private static PermissionsService permissionsWsClient;
 
   private static final String PROJECT_KEY = "sample";
   private static final String LOGIN = "george.orwell";
@@ -70,7 +70,7 @@ public class PermissionTest {
     orchestrator.executeBuild(sampleProject);
 
     adminWsClient = newAdminWsClient(orchestrator);
-    permissionsWsClient = adminWsClient.permissionsClient();
+    permissionsWsClient = adminWsClient.permissions();
 
     createUser(LOGIN, "George Orwell");
     createGroup(GROUP_NAME);
@@ -157,28 +157,28 @@ public class PermissionTest {
   }
 
   private static void createUser(String login, String name) {
-    adminWsClient.execute(
-      newPostRequest("api/users/create")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/users/create")
         .setParam("login", login)
         .setParam("name", name)
         .setParam("password", "123456"));
   }
 
   private static void deactivateUser(String login) {
-    adminWsClient.execute(
-      newPostRequest("api/users/deactivate")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/users/deactivate")
         .setParam("login", login));
   }
 
   private static void createGroup(String groupName) {
-    adminWsClient.execute(
-      newPostRequest("api/user_groups/create")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/user_groups/create")
         .setParam("name", groupName));
   }
 
   private static void deleteGroup(String groupName) {
-    adminWsClient.execute(
-      newPostRequest("api/user_groups/delete")
+    adminWsClient.wsConnector().call(
+      new PostRequest("api/user_groups/delete")
         .setParam("name", groupName));
   }
 }
index fcd14f5061fe9dbfba3b8a412996b2f4b5c60c0b..6324365637ff8464bf8e3868912ad2eb717dc3d5 100644 (file)
@@ -86,7 +86,7 @@ public class CommonRulesTest extends AbstractIssueTest {
   }
 
   private List<Issue> findIssues(String componentKey, String ruleKey) {
-    return adminWsClient.issuesWsClient().search(
+    return adminWsClient.issues().search(
       new SearchWsRequest()
         .setComponents(singletonList(componentKey))
         .setRules(singletonList(ruleKey)))
index 17f4fb9de521fa6320dda51218096d2221aa8c6f..4d0a3fdadff34b8ff8fb4409df48e73c8002d580 100644 (file)
@@ -147,7 +147,7 @@ public class MeasureFiltersTest {
     client.userClient().create(userCreationParameters);
 
     if (permission != null) {
-      adminWsClient.permissionsClient().addUser(new AddUserWsRequest()
+      adminWsClient.permissions().addUser(new AddUserWsRequest()
         .setLogin(login)
         .setPermission(permission));
     }
index 0f293e3b4ce171f74ff22afd2c8d72cc001f020d..6270b999363c3d6ef1b3551ac5f13848a8917e9a 100644 (file)
@@ -1,8 +1,8 @@
 package util;/*
            * Copyright (C) 2009-2014 SonarSource SA
            * All rights reserved
            * mailto:contact AT sonarsource DOT com
            */
+ * Copyright (C) 2009-2014 SonarSource SA
+ * All rights reserved
+ * mailto:contact AT sonarsource DOT com
+ */
 
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
@@ -32,13 +32,14 @@ import org.sonar.wsclient.issue.IssueClient;
 import org.sonar.wsclient.issue.IssueQuery;
 import org.sonar.wsclient.services.PropertyDeleteQuery;
 import org.sonar.wsclient.services.PropertyUpdateQuery;
+import org.sonarqube.ws.client.HttpConnector;
+import org.sonarqube.ws.client.HttpWsClient;
 import org.sonarqube.ws.client.WsClient;
 
 import static com.google.common.collect.FluentIterable.from;
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.fail;
-import static org.sonarqube.ws.client.HttpConnector.newHttpConnector;
 
 public class ItUtils {
 
@@ -77,10 +78,9 @@ public class ItUtils {
 
   public static WsClient newAdminWsClient(Orchestrator orchestrator) {
     Server server = orchestrator.getServer();
-    return new WsClient(newHttpConnector()
+    return new HttpWsClient(new HttpConnector.Builder()
       .url(server.getUrl())
-      .login(server.ADMIN_LOGIN)
-      .password(server.ADMIN_PASSWORD)
+      .credentials(server.ADMIN_LOGIN, server.ADMIN_PASSWORD)
       .build());
   }
 
diff --git a/pom.xml b/pom.xml
index b1d4a957d201a2adc9486179e0009543d60fa981..598fbdbcc1da32ed6bed9aa84ec29fc1f7fc190e 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,7 @@
     <slf4j.version>1.7.12</slf4j.version>
     <tomcat.version>8.0.18</tomcat.version>
     <elasticsearch.version>1.7.2</elasticsearch.version>
+    <okhttp.version>2.6.0</okhttp.version>
 
     <protobuf.version>3.0.0-beta-1</protobuf.version>
     <protobuf.compiler>${settings.localRepository}/com/google/protobuf/protoc/${protobuf.version}/protoc-${protobuf.version}-${os.detected.classifier}.exe</protobuf.compiler>
           <exists>src/main/protobuf</exists>
         </file>
       </activation>
-      <properties>
-        <sonar.exclusions>target/generated-sources/protobuf/**/*</sonar.exclusions>
-      </properties>
       <build>
         <plugins>
           <plugin>
             <artifactId>maven-antrun-plugin</artifactId>
             <executions>
               <execution>
-                <id>protobuf-compile</id>
+                <id>compile-protobuf-sources</id>
                 <phase>generate-sources</phase>
                 <goals>
                   <goal>run</goal>
             <artifactId>build-helper-maven-plugin</artifactId>
             <executions>
               <execution>
-                <id>protobuf-compile</id>
+                <id>add-protobuf-generated-sources</id>
                 <phase>generate-sources</phase>
                 <goals>
                   <goal>add-source</goal>
           <exists>src/test/protobuf</exists>
         </file>
       </activation>
-      <properties>
-        <sonar.test.exclusions>target/generated-test-sources/protobuf/**/*</sonar.test.exclusions>
-      </properties>
       <build>
         <plugins>
           <plugin>
             <artifactId>maven-antrun-plugin</artifactId>
             <executions>
               <execution>
-                <id>protobuf-test-compile</id>
+                <id>compile-protobuf-tests</id>
                 <phase>generate-sources</phase>
                 <goals>
                   <goal>run</goal>
             <artifactId>build-helper-maven-plugin</artifactId>
             <executions>
               <execution>
-                <id>protobuf-test-compile</id>
+                <id>add-protobuf-generated-tests</id>
                 <phase>generate-test-sources</phase>
                 <goals>
                   <goal>add-test-source</goal>
index 40e0518357706b2b25c30b419bb34b08356ce480..f72f7544373f920ab1069d38cd747e3f74a89962 100644 (file)
@@ -24,5 +24,6 @@ public class Messages {
     // constants
   }
 
-  static final String NO_PERMISSION = "You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator.";
+  public static final String NO_PERMISSION = "You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator.";
+
 }
index aa18be8aba4eb765090d95ad15e98d5a6ad8ce02..2f3a6ba7ba841451a0c41fd3869b0d94c21958c0 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.server.permission.ws;
 
 import org.sonar.api.server.ws.WebService;
 
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
 
 public class PermissionsWs implements WebService {
   private final PermissionsWsAction[] actions;
@@ -33,7 +33,7 @@ public class PermissionsWs implements WebService {
 
   @Override
   public void define(Context context) {
-    NewController controller = context.createController(ENDPOINT);
+    NewController controller = context.createController(CONTROLLER);
     controller.setDescription("Permissions management");
     controller.setSince("3.7");
 
index cc6aabc36792bc65bc2f1ef793572188e7481b34..0d6652a5498e75feae569981bc14885ccf4eebb4 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.server.usertoken.ws;
 
 import org.sonar.api.server.ws.WebService;
 
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.USER_TOKENS_ENDPOINT;
+import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.CONTROLLER;
 
 public class UserTokensWs implements WebService {
   private final UserTokensWsAction[] actions;
@@ -33,7 +33,7 @@ public class UserTokensWs implements WebService {
 
   @Override
   public void define(Context context) {
-    NewController controller = context.createController(USER_TOKENS_ENDPOINT)
+    NewController controller = context.createController(CONTROLLER)
       .setDescription("User token management. To enhance security, tokens can be used to take the place " +
         "of user credentials in analysis configuration. A token can be revoked at any time.")
       .setSince("5.3");
index 8498f3fc3dbba58544e5e6638631ec0a6732f81f..babb1c093624fada8d80b4388a9c05a34aae13d0 100644 (file)
@@ -452,7 +452,7 @@ public class ProjectDataLoaderMediumTest {
       loader.load(ProjectDataQuery.create().setModuleKey(project.key()));
       fail();
     } catch (Exception e) {
-      assertThat(e).isInstanceOf(ForbiddenException.class).hasMessage("You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator.");
+      assertThat(e).isInstanceOf(ForbiddenException.class).hasMessage(Messages.NO_PERMISSION);
     }
   }
 
index ffce0b2aaaf8f826c598ada4297564db37625df6..7e1b135f9735adff1781b688514b60527ac32065 100644 (file)
@@ -53,7 +53,7 @@ import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
 import static org.sonar.db.component.ComponentTesting.newView;
 import static org.sonar.server.permission.ws.AddGroupAction.ACTION;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
@@ -223,7 +223,7 @@ public class AddGroupActionTest {
   public void fail_when_get_request() throws Exception {
     expectedException.expect(ServerException.class);
 
-    ws.newGetRequest(ENDPOINT, ACTION)
+    ws.newGetRequest(CONTROLLER, ACTION)
       .setParam(PARAM_GROUP_NAME, "sonar-administrators")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
@@ -264,7 +264,7 @@ public class AddGroupActionTest {
   }
 
   private WsTester.TestRequest newRequest() {
-    return ws.newPostRequest(ENDPOINT, ACTION);
+    return ws.newPostRequest(CONTROLLER, ACTION);
   }
 
   private void commit() {
index fee6e5a2ae3a33eaa803138af254c7cce439f950..713be7d42a914996f79f397c5bf8062b1b036133 100644 (file)
@@ -53,7 +53,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
 import static org.sonar.db.component.ComponentTesting.newView;
 import static org.sonar.server.permission.ws.AddUserAction.ACTION;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
@@ -89,7 +89,7 @@ public class AddUserActionTest {
 
   @Test
   public void call_permission_service_with_right_data() throws Exception {
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
@@ -105,7 +105,7 @@ public class AddUserActionTest {
     dbClient.componentDao().insert(dbSession, newProjectDto("project-uuid").setKey("project-key"));
     commit();
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "project-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -121,7 +121,7 @@ public class AddUserActionTest {
     dbClient.componentDao().insert(dbSession, newProjectDto("project-uuid").setKey("project-key"));
     commit();
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_KEY, "project-key")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -137,7 +137,7 @@ public class AddUserActionTest {
     dbClient.componentDao().insert(dbSession, newView("view-uuid").setKey("view-key"));
     commit();
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "view-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -152,7 +152,7 @@ public class AddUserActionTest {
   public void fail_when_project_uuid_is_unknown() throws Exception {
     expectedException.expect(NotFoundException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -163,7 +163,7 @@ public class AddUserActionTest {
   public void fail_when_project_permission_without_project() throws Exception {
     expectedException.expect(BadRequestException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
       .execute();
@@ -175,7 +175,7 @@ public class AddUserActionTest {
     insertComponent(newFileDto(newProjectDto("project-uuid"), "file-uuid"));
     commit();
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "file-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -186,7 +186,7 @@ public class AddUserActionTest {
   public void fail_when_get_request() throws Exception {
     expectedException.expect(ServerException.class);
 
-    ws.newGetRequest(ENDPOINT, ACTION)
+    ws.newGetRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "george.orwell")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
@@ -196,7 +196,7 @@ public class AddUserActionTest {
   public void fail_when_user_login_is_missing() throws Exception {
     expectedException.expect(IllegalArgumentException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
   }
@@ -205,7 +205,7 @@ public class AddUserActionTest {
   public void fail_when_permission_is_missing() throws Exception {
     expectedException.expect(IllegalArgumentException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "jrr.tolkien")
       .execute();
   }
@@ -217,7 +217,7 @@ public class AddUserActionTest {
     insertComponent(newProjectDto("project-uuid").setKey("project-key"));
     commit();
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "project-uuid")
index 2bcf60caea9feaa149ed6f9300495c3156e068fc..5c7151701e5cc14a5df06f898cc1e547d69c3fcc 100644 (file)
@@ -54,7 +54,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
 import static org.sonar.db.component.ComponentTesting.newView;
 import static org.sonar.server.permission.ws.RemoveGroupAction.ACTION;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
@@ -205,7 +205,7 @@ public class RemoveGroupActionTest {
   public void fail_when_get_request() throws Exception {
     expectedException.expect(ServerException.class);
 
-    ws.newGetRequest(ENDPOINT, ACTION)
+    ws.newGetRequest(CONTROLLER, ACTION)
       .setParam(PARAM_GROUP_NAME, "sonar-administrators")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
@@ -256,7 +256,7 @@ public class RemoveGroupActionTest {
   }
 
   private WsTester.TestRequest newRequest() {
-    return ws.newPostRequest(ENDPOINT, ACTION);
+    return ws.newPostRequest(CONTROLLER, ACTION);
   }
 
   private GroupDto insertGroup(String groupName) {
index a62a5a285bab37ad9a45fb9891f9c3cb08eb5fe5..4128af2a6af03345f854b955348f1257b70f619f 100644 (file)
@@ -53,7 +53,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
 import static org.sonar.db.component.ComponentTesting.newView;
 import static org.sonar.server.permission.ws.RemoveUserAction.ACTION;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
@@ -86,7 +86,7 @@ public class RemoveUserActionTest {
 
   @Test
   public void call_permission_service_with_right_data() throws Exception {
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
@@ -101,7 +101,7 @@ public class RemoveUserActionTest {
   public void remove_with_project_uuid() throws Exception {
     insertComponent(newProjectDto("project-uuid").setKey("project-key"));
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "project-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -116,7 +116,7 @@ public class RemoveUserActionTest {
   public void remove_with_project_key() throws Exception {
     insertComponent(newProjectDto("project-uuid").setKey("project-key"));
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_KEY, "project-key")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -131,7 +131,7 @@ public class RemoveUserActionTest {
   public void remove_with_view_uuid() throws Exception {
     insertComponent(newView("view-uuid").setKey("view-key"));
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "view-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -146,7 +146,7 @@ public class RemoveUserActionTest {
   public void fail_when_project_does_not_exist() throws Exception {
     expectedException.expect(NotFoundException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
       .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
@@ -157,7 +157,7 @@ public class RemoveUserActionTest {
   public void fail_when_project_permission_without_permission() throws Exception {
     expectedException.expect(BadRequestException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
       .execute();
@@ -168,7 +168,7 @@ public class RemoveUserActionTest {
     expectedException.expect(BadRequestException.class);
     insertComponent(newFileDto(newProjectDto(), "file-uuid"));
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "file-uuid")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
@@ -179,7 +179,7 @@ public class RemoveUserActionTest {
   public void fail_when_get_request() throws Exception {
     expectedException.expect(ServerException.class);
 
-    ws.newGetRequest(ENDPOINT, ACTION)
+    ws.newGetRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "george.orwell")
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
@@ -189,7 +189,7 @@ public class RemoveUserActionTest {
   public void fail_when_user_login_is_missing() throws Exception {
     expectedException.expect(IllegalArgumentException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .execute();
   }
@@ -198,7 +198,7 @@ public class RemoveUserActionTest {
   public void fail_when_permission_is_missing() throws Exception {
     expectedException.expect(IllegalArgumentException.class);
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_USER_LOGIN, "jrr.tolkien")
       .execute();
   }
@@ -209,7 +209,7 @@ public class RemoveUserActionTest {
     expectedException.expectMessage("Project id or project key can be provided, not both.");
     insertComponent(newProjectDto("project-uuid").setKey("project-key"));
 
-    ws.newPostRequest(ENDPOINT, ACTION)
+    ws.newPostRequest(CONTROLLER, ACTION)
       .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
       .setParam(PARAM_USER_LOGIN, "ray.bradbury")
       .setParam(PARAM_PROJECT_ID, "project-uuid")
index e494d430f7b947cf7d2e544b048436f4d8955fa4..5eb2081c161b8efc550ea3336be7b70fc5a9a7af 100644 (file)
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.sonarsource.sonarqube</groupId>
 
   <artifactId>sonar-batch-protocol</artifactId>
   <name>SonarQube :: Batch :: Protocol</name>
-  
+
   <description>Classes used for communication between batch and server</description>
-  
+
   <properties>
     <!-- Viewer is for our internal use. This is not production code and mostly generated with Eclipse GUI builder -->
-    <sonar.exclusions>src/main/java/org/sonar/batch/protocol/viewer/**</sonar.exclusions>
+    <sonar.exclusions>target/generated-sources/**/*,src/main/java/org/sonar/batch/protocol/viewer/**</sonar.exclusions>
+    <sonar.test.exclusions>target/generated-test-sources/**/*</sonar.test.exclusions>
   </properties>
 
   <dependencies>
index e77766e5205a88ba1eba88d74292b1c319f5ff5d..db6efc2bc6830902f1ee45798502bf898fab72a1 100644 (file)
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
     </dependency>
-    <dependency>
-      <groupId>com.github.kevinsawicki</groupId>
-      <artifactId>http-request</artifactId>
-    </dependency>
     <!-- For HTML Report -->
     <dependency>
       <groupId>org.freemarker</groupId>
       <scope>test</scope>
       <version>${project.version}</version>
     </dependency>
-
     <dependency>
       <groupId>com.google.code.bean-matchers</groupId>
       <artifactId>bean-matchers</artifactId>
index ca0f521d42e6df1ffc827ce7214df58e801253b7..328478d3f87515cdd3658db3ba1fa8c362c40ec1 100644 (file)
@@ -21,19 +21,19 @@ package org.sonar.batch.analysis;
 
 import org.picocontainer.injectors.ProviderAdapter;
 import org.sonar.api.batch.AnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.cache.WSLoader;
 import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.WsClient;
 
 public class AnalysisWSLoaderProvider extends ProviderAdapter {
   private WSLoader wsLoader;
 
-  public WSLoader provide(AnalysisProperties props, AnalysisMode mode, PersistentCache cache, ServerClient client) {
+  public WSLoader provide(AnalysisMode mode, PersistentCache cache, WsClient client) {
     if (wsLoader == null) {
       // recreate cache directory if needed for this analysis
       cache.reconfigure();
-      wsLoader = new WSLoader(getStrategy(mode), cache, client, props);
+      wsLoader = new WSLoader(getStrategy(mode), cache, client);
     }
     return wsLoader;
   }
index 88003026d8e4cfeb6b01418b308250c18ebc2b4c..56b4199e8e6bcf0e3b2fdae5f5ba37a2ca3b5ebb 100644 (file)
@@ -23,10 +23,12 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.CharUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.SonarPlugin;
@@ -39,6 +41,11 @@ import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.RemotePlugin;
 import org.sonar.core.platform.RemotePluginFile;
 import org.sonar.home.cache.FileCache;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsResponse;
+
+import static java.lang.String.format;
 
 /**
  * Downloads the plugins installed on server and stores them in a local user cache
@@ -52,13 +59,13 @@ public class BatchPluginInstaller implements PluginInstaller {
   private final WSLoader wsLoader;
   private final FileCache fileCache;
   private final BatchPluginPredicate pluginPredicate;
-  private final ServerClient serverClient;
+  private final WsClient wsClient;
 
-  public BatchPluginInstaller(WSLoader wsLoader, ServerClient serverClient, FileCache fileCache, BatchPluginPredicate pluginPredicate) {
+  public BatchPluginInstaller(WSLoader wsLoader, WsClient wsClient, FileCache fileCache, BatchPluginPredicate pluginPredicate) {
     this.wsLoader = wsLoader;
     this.fileCache = fileCache;
     this.pluginPredicate = pluginPredicate;
-    this.serverClient = serverClient;
+    this.wsClient = wsClient;
   }
 
   @Override
@@ -137,14 +144,17 @@ public class BatchPluginInstaller implements PluginInstaller {
 
     @Override
     public void download(String filename, File toFile) throws IOException {
-      String url = "/deploy/plugins/" + key + "/" + filename;
+      String url = format("/deploy/plugins/%s/%s", key, filename);
       if (LOG.isDebugEnabled()) {
-        LOG.debug("Download {} to {}", url, toFile.getAbsolutePath());
+        LOG.debug("Download plugin {} to {}", filename, toFile);
       } else {
         LOG.info("Download {}", filename);
       }
 
-      serverClient.download(url, toFile);
+      WsResponse response = wsClient.wsConnector().call(new GetRequest(url));
+      try (InputStream stream = response.getContentStream()) {
+        FileUtils.copyInputStreamToFile(stream, toFile);
+      }
     }
   }
 }
index 035c7f489a9b8bcb82d493e32d68ce484478cda2..40b5cacb9d1f1d5a21102a88b669a6c8c9419210 100644 (file)
@@ -91,7 +91,7 @@ public class GlobalContainer extends ComponentContainer {
 
       CachesManager.class,
       GlobalSettings.class,
-      ServerClient.class,
+      new WsClientProvider(),
       DefaultServer.class,
       new GlobalTempFolderProvider(),
       DefaultHttpDownloader.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
deleted file mode 100644 (file)
index ec34bfb..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.bootstrap;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.util.ArrayList;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.BatchSide;
-import org.sonar.api.utils.HttpDownloader;
-import org.sonar.api.utils.MessageException;
-import org.sonar.batch.bootstrapper.EnvironmentInformation;
-import org.sonar.core.util.DefaultHttpDownloader;
-
-/**
- * Replace the deprecated org.sonar.batch.ServerMetadata
- * TODO extends Server when removing the deprecated org.sonar.batch.ServerMetadata
- *
- * @since 3.4
- */
-@BatchSide
-public class ServerClient {
-  private static final String GET = "GET";
-  private GlobalProperties props;
-  private DefaultHttpDownloader.BaseHttpDownloader downloader;
-
-  public ServerClient(GlobalProperties settings, EnvironmentInformation env) {
-    this.props = settings;
-    this.downloader = new DefaultHttpDownloader.BaseHttpDownloader(settings.properties(), env.toString());
-  }
-
-  public String getURL() {
-    return StringUtils.removeEnd(StringUtils.defaultIfBlank(props.property("sonar.host.url"), "http://localhost:9000"), "/");
-  }
-
-  public URI getURI(String pathStartingWithSlash) {
-    Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /: " + pathStartingWithSlash);
-    String path = StringEscapeUtils.escapeHtml(pathStartingWithSlash);
-    return URI.create(getURL() + path);
-  }
-
-  public void download(String pathStartingWithSlash, File toFile) {
-    download(pathStartingWithSlash, toFile, null, null);
-  }
-
-  public void download(String pathStartingWithSlash, File toFile, @Nullable Integer connectTimeoutMillis, @Nullable Integer readTimeoutMillis) {
-    try {
-      InputStream is = load(pathStartingWithSlash, GET, false, connectTimeoutMillis, readTimeoutMillis);
-      Files.copy(is, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
-    } catch (HttpDownloader.HttpException he) {
-      throw handleHttpException(he.getUri().toString(), he.getResponseCode(), he.getResponseContent(), he);
-    } catch (IOException e) {
-      throw new IllegalStateException(String.format("Unable to download '%s' to: %s", pathStartingWithSlash, toFile), e);
-    }
-  }
-
-  public String downloadString(String pathStartingWithSlash) {
-    return downloadString(pathStartingWithSlash, GET, true, null);
-  }
-
-  public String downloadString(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer timeoutMillis) {
-    InputStream is = load(pathStartingWithSlash, requestMethod, wrapHttpException, null, timeoutMillis);
-    try {
-      return new String(IOUtils.toByteArray(is), "UTF-8");
-    } catch (IOException e) {
-      throw new IllegalStateException(String.format("Unable to request: %s", pathStartingWithSlash), e);
-    }
-  }
-
-  /**
-   * @throws IllegalStateException on I/O error, not limited to the network connection and if HTTP response code > 400 and wrapHttpException is true
-   * @throws HttpDownloader.HttpException if HTTP response code > 400 and wrapHttpException is false
-   */
-  public InputStream load(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer connectTimeoutMs,
-    @Nullable Integer readTimeoutMs) {
-    URI uri = getURI(pathStartingWithSlash);
-
-    try {
-      if (Strings.isNullOrEmpty(getLogin())) {
-        return downloader.newInputSupplier(uri, requestMethod, connectTimeoutMs, readTimeoutMs).getInput();
-      } else {
-        return downloader.newInputSupplier(uri, requestMethod, getLogin(), getPassword(), connectTimeoutMs, readTimeoutMs).getInput();
-      }
-    } catch (HttpDownloader.HttpException he) {
-      if (wrapHttpException) {
-        throw handleHttpException(he.getUri().toString(), he.getResponseCode(), he.getResponseContent(), he);
-      } else {
-        throw he;
-      }
-    } catch (IOException e) {
-      throw new IllegalStateException(String.format("Unable to request: %s", uri), e);
-    }
-  }
-
-  public RuntimeException handleHttpException(String url, int responseCode, String responseContent, Exception he) {
-    if (responseCode == 401) {
-      return MessageException.of(String.format(getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD), he);
-    }
-    if (responseCode == 403) {
-      // SONAR-4397 Details are in response content
-      return MessageException.of(tryParseAsJsonError(responseContent), he);
-    }
-    return MessageException.of(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, responseContent), he);
-  }
-
-  private static String tryParseAsJsonError(String responseContent) {
-    try {
-      JsonParser parser = new JsonParser();
-      JsonObject obj = parser.parse(responseContent).getAsJsonObject();
-      JsonArray errors = obj.getAsJsonArray("errors");
-      List<String> errorMessages = new ArrayList<>();
-      for (JsonElement e : errors) {
-        errorMessages.add(e.getAsJsonObject().get("msg").getAsString());
-      }
-      return Joiner.on(", ").join(errorMessages);
-    } catch (Exception e) {
-      return responseContent;
-    }
-  }
-
-  public String getMessageWhenNotAuthorized() {
-    if (Strings.isNullOrEmpty(getLogin()) && Strings.isNullOrEmpty(getPassword())) {
-      return "Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties %s and %s.";
-    }
-    return "Not authorized. Please check the properties %s and %s.";
-  }
-
-  public String getLogin() {
-    return props.property(CoreProperties.LOGIN);
-  }
-
-  public String getPassword() {
-    return props.property(CoreProperties.PASSWORD);
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptor.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptor.java
new file mode 100644 (file)
index 0000000..f94bf08
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.base.Joiner;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.squareup.okhttp.Interceptor;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+
+import static java.lang.String.format;
+
+public class WsClientLoggingInterceptor implements Interceptor {
+
+  private static final Logger LOG = Loggers.get(WsClientLoggingInterceptor.class);
+
+  @Override
+  public Response intercept(Chain chain) throws IOException {
+    Response response = logAndSendRequest(chain);
+    if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+      if (StringUtils.isBlank(response.request().header("Authorization"))) {
+        // not authenticated - see https://jira.sonarsource.com/browse/SONAR-4048
+        throw MessageException.of(format("Not authorized. Analyzing this project requires to be authenticated. " +
+          "Please provide the values of the properties %s and %s.", CoreProperties.LOGIN, CoreProperties.PASSWORD));
+      }
+      // credentials are not valid
+      throw MessageException.of(format("Not authorized. Please check the properties %s and %s.",
+        CoreProperties.LOGIN, CoreProperties.PASSWORD));
+    }
+    if (response.code() == HttpURLConnection.HTTP_FORBIDDEN) {
+      // SONAR-4397 Details are in response content
+      throw MessageException.of(tryParseAsJsonError(response.body().string()));
+    }
+    return response;
+  }
+
+  private Response logAndSendRequest(Chain chain) throws IOException {
+    Request request = chain.request();
+    Response response;
+    Profiler profiler = Profiler.createIfDebug(LOG).startTrace(format("%s %s", request.method(), request.url()));
+    response = chain.proceed(request);
+    profiler.stopDebug(format("%s %d %s", request.method(), response.code(), request.url()));
+    return response;
+  }
+
+  private static String tryParseAsJsonError(String responseContent) {
+    try {
+      JsonParser parser = new JsonParser();
+      JsonObject obj = parser.parse(responseContent).getAsJsonObject();
+      JsonArray errors = obj.getAsJsonArray("errors");
+      List<String> errorMessages = new ArrayList<>();
+      for (JsonElement e : errors) {
+        errorMessages.add(e.getAsJsonObject().get("msg").getAsString());
+      }
+      return Joiner.on(", ").join(errorMessages);
+    } catch (Exception e) {
+      return responseContent;
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientProvider.java
new file mode 100644 (file)
index 0000000..78e50cc
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.BatchSide;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonarqube.ws.client.HttpConnector;
+import org.sonarqube.ws.client.HttpWsClient;
+import org.sonarqube.ws.client.WsClient;
+
+import static java.lang.Integer.parseInt;
+import static java.lang.String.valueOf;
+import static org.apache.commons.lang.StringUtils.defaultIfBlank;
+
+@BatchSide
+public class WsClientProvider extends ProviderAdapter {
+
+  static final int CONNECT_TIMEOUT_MS = 5_000;
+  static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout";
+  static final int DEFAULT_READ_TIMEOUT_SEC = 60;
+
+  private HttpWsClient wsClient;
+
+  public synchronized WsClient provide(final GlobalProperties settings, final EnvironmentInformation env) {
+    if (wsClient == null) {
+      String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE);
+      HttpConnector.Builder builder = new HttpConnector.Builder();
+
+      // TODO proxy
+
+      String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC));
+      builder
+        .readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000)
+        .connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS)
+        .userAgent(env.toString())
+        .url(url)
+        .credentials(settings.property(CoreProperties.LOGIN), settings.property(CoreProperties.PASSWORD))
+        .interceptor(new WsClientLoggingInterceptor());
+
+      wsClient = new HttpWsClient(builder.build());
+    }
+    return wsClient;
+  }
+}
index e64015db867c4bd4c71e86479245def635a137a3..e7c573753dfa01eb11e6952ed8f053380c6f01d0 100644 (file)
 package org.sonar.batch.cache;
 
 import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.batch.bootstrap.GlobalProperties;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.WsClient;
 
 public class StrategyWSLoaderProvider extends ProviderAdapter {
   private final LoadStrategy strategy;
@@ -33,9 +32,9 @@ public class StrategyWSLoaderProvider extends ProviderAdapter {
     this.strategy = strategy;
   }
 
-  public WSLoader provide(PersistentCache cache, ServerClient client, GlobalProperties globalProps) {
+  public WSLoader provide(PersistentCache cache, WsClient client) {
     if (wsLoader == null) {
-      wsLoader = new WSLoader(strategy, cache, client, globalProps);
+      wsLoader = new WSLoader(strategy, cache, client);
     }
     return wsLoader;
   }
index 13845f0808e4aa8c356b381843af85e97ee06d22..69e7c14538f8a9d5885da4a2a6c0daaf2993e3dc 100644 (file)
@@ -21,56 +21,53 @@ package org.sonar.batch.cache;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.Reader;
 import java.nio.charset.StandardCharsets;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.apache.commons.io.IOUtils;
-import org.sonar.api.utils.HttpDownloader.HttpException;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
-import org.sonar.batch.bootstrap.ServerClient;
-import org.sonar.batch.bootstrap.UserProperties;
 import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.WsClient;
 
 import static org.sonar.batch.cache.WSLoader.ServerStatus.ACCESSIBLE;
 import static org.sonar.batch.cache.WSLoader.ServerStatus.NOT_ACCESSIBLE;
 import static org.sonar.batch.cache.WSLoader.ServerStatus.UNKNOWN;
 
 public class WSLoader {
-  static final String SONAR_WS_TIMEOUT_PROPS = "sonar.ws.timeout";
   private static final Logger LOG = Loggers.get(WSLoader.class);
   private static final String FAIL_MSG = "Server is not accessible and data is not cached";
-  private static final int CONNECT_TIMEOUT = 5_000;
-  private static final int DEFAULT_READ_TIMEOUT = 60_000;
-  private static final String REQUEST_METHOD = "GET";
 
   public enum ServerStatus {
-    UNKNOWN, ACCESSIBLE, NOT_ACCESSIBLE;
+    UNKNOWN, ACCESSIBLE, NOT_ACCESSIBLE
   }
 
   public enum LoadStrategy {
-    SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY;
+    SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY
   }
 
   private final LoadStrategy defautLoadStrategy;
-  private final ServerClient client;
+  private final WsClient client;
   private final PersistentCache cache;
-  private final UserProperties userProperties;
   private ServerStatus serverStatus;
 
   private DataLoader<String> stringServerLoader = new DataLoader<String>() {
     @Override
     public String load(String id) throws IOException {
-      InputStream is = client.load(id, REQUEST_METHOD, true, CONNECT_TIMEOUT, getReadTimeout());
-      String str = IOUtils.toString(is, StandardCharsets.UTF_8);
-      try {
-        cache.put(id, str.getBytes(StandardCharsets.UTF_8));
-      } catch (IOException e) {
-        throw new IllegalStateException("Error saving to WS cache", e);
+      GetRequest getRequest = new GetRequest(id);
+      try (Reader reader = client.wsConnector().call(getRequest).getContentReader()) {
+        String str = IOUtils.toString(reader);
+        try {
+          cache.put(id, str.getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+          throw new IllegalStateException("Error saving to WS cache", e);
+        }
+        return str;
       }
-      return str;
     }
-
   };
 
   private DataLoader<String> stringCacheLoader = new DataLoader<String>() {
@@ -83,13 +80,14 @@ public class WSLoader {
   private DataLoader<InputStream> streamServerLoader = new DataLoader<InputStream>() {
     @Override
     public InputStream load(String id) throws IOException {
-      InputStream is = client.load(id, REQUEST_METHOD, true, CONNECT_TIMEOUT, getReadTimeout());
-      try {
-        cache.put(id, is);
-      } catch (IOException e) {
-        throw new IllegalStateException("Error saving to WS cache", e);
+      GetRequest getRequest = new GetRequest(id);
+      try (InputStream is = client.wsConnector().call(getRequest).getContentStream()) {
+        try {
+          cache.put(id, is);
+        } catch (IOException e) {
+          throw new IllegalStateException("Error saving to WS cache", e);
+        }
       }
-      is.close();
       return cache.getStream(id);
     }
   };
@@ -101,7 +99,7 @@ public class WSLoader {
     }
   };
 
-  private class NotAvailableException extends Exception {
+  private static class NotAvailableException extends Exception {
     private static final long serialVersionUID = 1L;
 
     public NotAvailableException(String message) {
@@ -113,18 +111,13 @@ public class WSLoader {
     }
   }
 
-  public WSLoader(LoadStrategy strategy, PersistentCache cache, ServerClient client, UserProperties settings) {
+  public WSLoader(LoadStrategy strategy, PersistentCache cache, WsClient client) {
     this.defautLoadStrategy = strategy;
-    this.userProperties = settings;
     this.serverStatus = UNKNOWN;
     this.cache = cache;
     this.client = client;
   }
 
-  private int getReadTimeout() {
-    return userProperties.properties().containsKey(SONAR_WS_TIMEOUT_PROPS) ? (Integer.parseInt(userProperties.property(SONAR_WS_TIMEOUT_PROPS)) * 1000) : DEFAULT_READ_TIMEOUT;
-  }
-
   @Nonnull
   public WSLoaderResult<InputStream> loadStream(String id) {
     return load(id, defautLoadStrategy, streamServerLoader, streamCacheLoader);
@@ -200,7 +193,7 @@ public class WSLoader {
           throw new IllegalStateException(FAIL_MSG, serverNotAvailable.getCause());
         }
       }
-      throw new IllegalStateException("Server is not available", serverNotAvailable.getCause());
+      throw new IllegalStateException("Server is not available: " + client.wsConnector().baseUrl(), serverNotAvailable.getCause());
     }
   }
 
index 36c3324374b8e6be99e14662cbef555c4f7380b4..209dd29712f0edc0ea06234502eb821ae79c8e22 100644 (file)
  */
 package org.sonar.batch.report;
 
-import com.github.kevinsawicki.http.HttpRequest;
 import com.google.common.annotations.VisibleForTesting;
-import com.google.gson.Gson;
-import java.io.BufferedWriter;
+import com.google.common.io.Files;
+import com.squareup.okhttp.HttpUrl;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import javax.annotation.CheckForNull;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import javax.annotation.Nullable;
 import org.apache.commons.io.FileUtils;
 import org.picocontainer.Startable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
 import org.sonar.api.utils.TempFolder;
 import org.sonar.api.utils.ZipUtils;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.batch.analysis.DefaultAnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.output.BatchReportWriter;
 import org.sonar.batch.scan.ImmutableProjectReactor;
-import org.sonar.batch.util.BatchUtils;
+import org.sonarqube.ws.WsCe;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.ce.SubmitWsRequest;
 
-import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.defaultIfBlank;
 
 @BatchSide
 public class ReportPublisher implements Startable {
 
-  private static final Logger LOG = LoggerFactory.getLogger(ReportPublisher.class);
+  private static final Logger LOG = Loggers.get(ReportPublisher.class);
+
   public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport";
   public static final String VERBOSE_KEY = "sonar.verbose";
-  public static final String DUMP_REPORT_PROP_KEY = "sonar.batch.dumpReportDir";
-  public static final String JSON_DETAILS_FILE = "analysis-details.json";
+  public static final String METADATA_DUMP_FILENAME = "analysis-details.json";
 
-  private final ServerClient serverClient;
-  private final Server server;
   private final Settings settings;
+  private final WsClient wsClient;
+  private final AnalysisContextReportPublisher contextPublisher;
   private final ImmutableProjectReactor projectReactor;
   private final DefaultAnalysisMode analysisMode;
   private final TempFolder temp;
-  private final AnalysisContextReportPublisher contextPublisher;
-
-  private ReportPublisherStep[] publishers;
+  private final ReportPublisherStep[] publishers;
 
   private File reportDir;
   private BatchReportWriter writer;
 
-  public ReportPublisher(Settings settings, ServerClient serverClient, Server server, AnalysisContextReportPublisher contextPublisher,
+  public ReportPublisher(Settings settings, WsClient wsClient, AnalysisContextReportPublisher contextPublisher,
     ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) {
-    this.serverClient = serverClient;
-    this.server = server;
+    this.settings = settings;
+    this.wsClient = wsClient;
     this.contextPublisher = contextPublisher;
     this.projectReactor = projectReactor;
-    this.settings = settings;
     this.analysisMode = analysisMode;
     this.temp = temp;
     this.publishers = publishers;
@@ -101,7 +93,7 @@ public class ReportPublisher implements Startable {
     if (!settings.getBoolean(KEEP_REPORT_PROP_KEY) && !settings.getBoolean(VERBOSE_KEY)) {
       FileUtils.deleteQuietly(reportDir);
     } else {
-      LOG.info("Batch report generated in " + reportDir);
+      LOG.info("Analysis report generated in " + reportDir);
     }
   }
 
@@ -117,22 +109,22 @@ public class ReportPublisher implements Startable {
     // If this is a issues mode analysis then we should not upload reports
     String taskId = null;
     if (!analysisMode.isIssues()) {
-      File report = prepareReport();
+      File report = generateReportFile();
       if (!analysisMode.isMediumTest()) {
-        taskId = sendOrDumpReport(report);
+        taskId = upload(report);
       }
     }
-    logSuccess(LoggerFactory.getLogger(getClass()), taskId);
+    logSuccess(taskId);
   }
 
-  private File prepareReport() {
+  private File generateReportFile() {
     try {
       long startTime = System.currentTimeMillis();
       for (ReportPublisherStep publisher : publishers) {
         publisher.publish(writer);
       }
       long stopTime = System.currentTimeMillis();
-      LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir)));
+      LOG.info("Analysis report generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir)));
 
       startTime = System.currentTimeMillis();
       File reportZip = temp.newFile("batch-report", ".zip");
@@ -141,129 +133,81 @@ public class ReportPublisher implements Startable {
       LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip)));
       return reportZip;
     } catch (IOException e) {
-      throw new IllegalStateException("Unable to prepare batch report", e);
+      throw new IllegalStateException("Unable to prepare analysis report", e);
     }
   }
 
-  @CheckForNull
+  /**
+   * Uploads the report file to server and returns the generated task id
+   */
   @VisibleForTesting
-  String sendOrDumpReport(File report) {
-    ProjectDefinition projectDefinition = projectReactor.getRoot();
-    String effectiveKey = projectDefinition.getKeyWithBranch();
-    String relativeUrl = String.format("/api/ce/submit?projectKey=%s&projectName=%s&projectBranch=%s",
-      projectDefinition.getKey(), BatchUtils.encodeForUrl(projectDefinition.getName()), BatchUtils.encodeForUrl(projectDefinition.getBranch()));
-
-    String dumpDirLocation = settings.getString(DUMP_REPORT_PROP_KEY);
-    if (dumpDirLocation == null) {
-      return uploadMultiPartReport(report, relativeUrl);
-    } else {
-      dumpReport(dumpDirLocation, effectiveKey, relativeUrl, report);
-      return null;
-    }
-  }
-
-  private String uploadMultiPartReport(File report, String relativeUrl) {
-    LOG.debug("Publish results");
+  String upload(File report) {
+    LOG.debug("Upload report");
     long startTime = System.currentTimeMillis();
-    URL url;
-    try {
-      url = new URL(serverClient.getURL() + relativeUrl);
-    } catch (MalformedURLException e) {
-      throw new IllegalArgumentException("Invalid URL", e);
-    }
-    HttpRequest request = HttpRequest.post(url);
-    request.trustAllCerts();
-    request.trustAllHosts();
-    request.header("User-Agent", format("SonarQube %s", server.getVersion()));
-    request.basic(serverClient.getLogin(), serverClient.getPassword());
-    request.part("report", null, "application/octet-stream", report);
-    if (!request.ok()) {
-      throw serverClient.handleHttpException(url.toString(), request.code(), request.body(), null);
-    }
+    ProjectDefinition projectDefinition = projectReactor.getRoot();
+    SubmitWsRequest submitRequest = new SubmitWsRequest();
+    submitRequest.setProjectKey(projectDefinition.getKey());
+    submitRequest.setProjectName(projectDefinition.getName());
+    submitRequest.setProjectBranch(projectDefinition.getBranch());
+    submitRequest.setReport(report);
+    WsCe.SubmitResponse submitResponse = wsClient.computeEngine().submit(submitRequest);
     long stopTime = System.currentTimeMillis();
-    LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms");
-    String responseStr = request.body();
-    SubmitResponse response = new Gson().fromJson(responseStr, SubmitResponse.class);
-    return response.getTaskId();
-  }
-
-  private static class SubmitResponse {
-    private String taskId;
-
-    public String getTaskId() {
-      return taskId;
-    }
-
-    public void setTaskId(String taskId) {
-      this.taskId = taskId;
-    }
-  }
-
-  private static void dumpReport(String dumpDirLocation, String projectKey, String relativeUrl, File report) {
-    LOG.debug("Dump report to file");
-    try {
-      dumpReportImpl(dumpDirLocation, projectKey, relativeUrl, report);
-    } catch (IOException | URISyntaxException e) {
-      LOG.error("Failed to dump report to directory " + dumpDirLocation, e);
-    }
-  }
-
-  private static void dumpReportImpl(String dumpDirLocation, String projectKey, String relativeUrl, File report) throws IOException, URISyntaxException {
-    File dumpDir = new File(dumpDirLocation);
-    if (!dumpDir.exists() || !dumpDir.isDirectory()) {
-      LOG.warn("Report dump directory '{}' does not exist or is not a directory", dumpDirLocation);
-      return;
-    }
-    long dateTime = new Date().getTime();
-    File dumpedZip = new File(dumpDir, format("batch-report_%s_%s.zip", projectKey, dateTime));
-    FileUtils.copyFile(report, new FileOutputStream(dumpedZip));
-    File dumpedMetadata = new File(dumpDir, format("batch-report_%s_%s.txt", projectKey, dateTime));
-    FileUtils.write(dumpedMetadata, relativeUrl);
-    LOG.info("Batch report dumped to {}", dumpedZip.getAbsolutePath());
+    LOG.info("Analysis report uploaded in " + (stopTime - startTime) + "ms");
+    return submitResponse.getTaskId();
   }
 
   @VisibleForTesting
-  void logSuccess(Logger logger, @Nullable String taskId) {
-    if (analysisMode.isIssues() || analysisMode.isMediumTest()) {
-      logger.info("ANALYSIS SUCCESSFUL");
-
+  void logSuccess(@Nullable String taskId) {
+    if (taskId == null) {
+      LOG.info("ANALYSIS SUCCESSFUL");
     } else {
-      String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL);
-      if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) {
-        // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side
-        baseUrl = serverClient.getURL();
-      }
-      if (!baseUrl.endsWith("/")) {
-        baseUrl += "/";
-      }
+      Map<String, String> metadata = new LinkedHashMap<>();
       String effectiveKey = projectReactor.getRoot().getKeyWithBranch();
-      String url = baseUrl + "dashboard/index/" + effectiveKey;
-      logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url);
-      logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
-      String taskUrl = null;
-      if (taskId != null) {
-        taskUrl = baseUrl + "api/ce/task?id=" + taskId;
-        logger.info("More about the report processing at {}", taskUrl);
-      }
-
-      writeJson(url, taskUrl);
+      metadata.put("projectKey", effectiveKey);
+
+      URL dashboardUrl = HttpUrl.parse(publicUrl()).newBuilder()
+        .addPathSegment("dashboard").addPathSegment("index").addPathSegment(effectiveKey)
+        .build()
+        .url();
+      metadata.put("dashboardUrl", dashboardUrl.toExternalForm());
+
+      URL taskUrl = HttpUrl.parse(publicUrl()).newBuilder()
+        .addPathSegment("api").addPathSegment("ce").addPathSegment("task")
+        .addQueryParameter("id", taskId)
+        .build()
+        .url();
+      metadata.put("ceTaskId", taskId);
+      metadata.put("ceTaskUrl", taskUrl.toExternalForm());
+
+      LOG.info("ANALYSIS SUCCESSFUL, you can browse {}", dashboardUrl);
+      LOG.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report");
+      LOG.info("More about the report processing at {}", taskUrl);
+
+      dumpMetadata(metadata);
     }
   }
 
-  private void writeJson(String dashboardUrl, @Nullable String taskUrl) {
-    File exportFile = new File(projectReactor.getRoot().getWorkDir(), JSON_DETAILS_FILE);
-    try (Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(exportFile), StandardCharsets.UTF_8))) {
+  private void dumpMetadata(Map<String, String> metadata) {
+    File file = new File(projectReactor.getRoot().getWorkDir(), METADATA_DUMP_FILENAME);
+    try (Writer output = Files.newWriter(file, StandardCharsets.UTF_8)) {
       JsonWriter json = JsonWriter.of(output);
       json.beginObject();
-      json.prop("dashboardUrl", dashboardUrl);
-      if (taskUrl != null) {
-        json.prop("ceTaskUrl", taskUrl);
+      for (Map.Entry<String, String> entry : metadata.entrySet()) {
+        json.prop(entry.getKey(), entry.getValue());
       }
       json.endObject();
 
-      LOG.debug("Analysis URLs written to {}", exportFile);
+      LOG.debug("Report metadata written to {}", file);
     } catch (IOException e) {
-      throw new IllegalStateException("Unable to write analysis URLs in file " + exportFile.getAbsolutePath(), e);
+      throw new IllegalStateException("Unable to dump " + file, e);
     }
   }
+
+  /**
+   * The public URL is optionally configured on server. If not, then the regular URL is returned.
+   * See https://jira.sonarsource.com/browse/SONAR-4239
+   */
+  private String publicUrl() {
+    return defaultIfBlank(settings.getString(CoreProperties.SERVER_BASE_URL), wsClient.wsConnector().baseUrl());
+  }
 }
index b31741f11157bdd7a3154e748d0dfad5fe42a96c..3a00c304aaac0fae515a0558f1c0fa0ef159c2a7 100644 (file)
@@ -21,12 +21,12 @@ package org.sonar.batch.repository;
 
 import com.google.common.base.Throwables;
 
-import org.sonar.api.utils.HttpDownloader.HttpException;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.HttpURLConnection;
 import java.util.Date;
 import java.util.Map;
 
@@ -42,6 +42,7 @@ import org.sonar.batch.util.BatchUtils;
 import org.sonarqube.ws.WsBatch.WsProjectResponse;
 import org.sonarqube.ws.WsBatch.WsProjectResponse.FileDataByPath;
 import org.sonarqube.ws.WsBatch.WsProjectResponse.Settings;
+import org.sonarqube.ws.client.HttpException;
 
 public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoader {
   private static final Logger LOG = LoggerFactory.getLogger(DefaultProjectRepositoriesLoader.class);
@@ -85,7 +86,7 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
     for (Throwable t : Throwables.getCausalChain(e)) {
       if (t instanceof HttpException) {
         HttpException http = (HttpException) t;
-        return http.getResponseCode() != 404;
+        return http.code() != HttpURLConnection.HTTP_NOT_FOUND;
       }
     }
 
index bd382543e525daa7f2ed51605ec7aed6c596afac..8f6e0dd5e07ca4e810d3416454a4e31c8b93a95f 100644 (file)
@@ -32,9 +32,10 @@ import javax.annotation.Nullable;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collection;
 import java.util.List;
 
+import static com.google.common.base.Preconditions.checkState;
+
 public class DefaultQualityProfileLoader implements QualityProfileLoader {
   private static final String WS_URL = "/api/qualityprofiles/search.protobuf";
 
@@ -62,12 +63,6 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader {
     return loadResource(url, fromCache);
   }
 
-  private static void validate(Collection<QualityProfile> profiles) {
-    if (profiles == null || profiles.isEmpty()) {
-      throw new IllegalStateException("No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis.");
-    }
-  }
-
   private List<QualityProfile> loadResource(String url, @Nullable MutableBoolean fromCache) {
     WSLoaderResult<InputStream> result = wsLoader.loadStream(url);
     if (fromCache != null) {
@@ -85,7 +80,8 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader {
     }
 
     List<QualityProfile> profilesList = profiles.getProfilesList();
-    validate(profilesList);
+    checkState(profilesList != null && !profilesList.isEmpty(),
+      "No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis.");
     return profilesList;
   }
 
index 7624edf7b28ee519b9e40467deed1963f5e50991..5687ebdc27c88307dfb1fb77eeab1aca7c3ab903 100644 (file)
  */
 package org.sonar.batch.analysis;
 
-import java.util.HashMap;
-import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.sonar.api.batch.AnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.cache.WSLoader;
 import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.WsClient;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -38,27 +36,22 @@ public class AnalysisWSLoaderProviderTest {
   private PersistentCache cache;
 
   @Mock
-  private ServerClient client;
+  private WsClient client;
 
   @Mock
   private AnalysisMode mode;
 
   private AnalysisWSLoaderProvider loaderProvider;
-  private Map<String, String> propMap;
-  private AnalysisProperties props;
 
   @Before
   public void setUp() {
     MockitoAnnotations.initMocks(this);
     loaderProvider = new AnalysisWSLoaderProvider();
-    propMap = new HashMap<>();
   }
 
   @Test
   public void testDefault() {
-    props = new AnalysisProperties(propMap, null);
-
-    WSLoader loader = loaderProvider.provide(props, mode, cache, client);
+    WSLoader loader = loaderProvider.provide(mode, cache, client);
     assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY);
   }
 }
index 654616811d594b4831ce9d6e1a138630384bdfa0..fdc159569a07edf38065ff20004d4b44e92ae1a0 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
-import org.sonar.batch.cache.WSLoaderResult;
-
-import org.sonar.batch.cache.WSLoader;
+import java.io.File;
+import java.util.List;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.cache.WSLoader;
+import org.sonar.batch.cache.WSLoaderResult;
 import org.sonar.core.platform.RemotePlugin;
 import org.sonar.home.cache.FileCache;
-
-import java.io.File;
-import java.util.List;
+import org.sonarqube.ws.client.WsClient;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
@@ -48,15 +47,15 @@ public class BatchPluginInstallerTest {
   public ExpectedException thrown = ExpectedException.none();
 
   FileCache fileCache = mock(FileCache.class);
-  ServerClient serverClient = mock(ServerClient.class);
+  WsClient wsClient = mock(WsClient.class);
   BatchPluginPredicate pluginPredicate = mock(BatchPluginPredicate.class);
 
   @Test
   public void listRemotePlugins() {
 
     WSLoader wsLoader = mock(WSLoader.class);
-    when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<String>("checkstyle\nsqale", true));
-    BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, serverClient, fileCache, pluginPredicate);
+    when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<>("checkstyle\nsqale", true));
+    BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate);
 
     List<RemotePlugin> remotePlugins = installer.listRemotePlugins();
     assertThat(remotePlugins).extracting("key").containsOnly("checkstyle", "sqale");
@@ -68,7 +67,7 @@ public class BatchPluginInstallerTest {
     when(fileCache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar);
 
     WSLoader wsLoader = mock(WSLoader.class);
-    BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, serverClient, fileCache, pluginPredicate);
+    BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate);
 
     RemotePlugin remote = new RemotePlugin("checkstyle").setFile("checkstyle-plugin.jar", "fakemd5_1");
     File file = installer.download(remote);
@@ -83,6 +82,6 @@ public class BatchPluginInstallerTest {
     WSLoader wsLoader = mock(WSLoader.class);
     doThrow(new IllegalStateException()).when(wsLoader).loadString("/deploy/plugins/index.txt");
 
-    new BatchPluginInstaller(wsLoader, serverClient, fileCache, pluginPredicate).installRemotes();
+    new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate).installRemotes();
   }
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
deleted file mode 100644 (file)
index ef7f4c5..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.bootstrap;
-
-import org.sonar.batch.util.BatchUtils;
-
-import java.io.File;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.batch.bootstrapper.EnvironmentInformation;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ServerClientTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  private MockHttpServer server = null;
-  private GlobalProperties bootstrapProps = mock(GlobalProperties.class);
-
-  @After
-  public void stopServer() {
-    if (server != null) {
-      server.stop();
-    }
-  }
-
-  @Test
-  public void should_remove_url_ending_slash() {
-    GlobalProperties settings = mock(GlobalProperties.class);
-    when(settings.property("sonar.host.url")).thenReturn("http://localhost:8080/sonar/");
-    ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4"));
-
-    assertThat(client.getURL()).isEqualTo("http://localhost:8080/sonar");
-  }
-
-  @Test
-  public void should_request_url() throws Exception {
-    startServer(null, "this is the content");
-    assertThat(newServerClient().downloadString("/foo")).isEqualTo("this is the content");
-  }
-
-  @Test
-  public void should_escape_html_from_url() throws Exception {
-    startServer(null, "this is the content");
-    assertThat(newServerClient().downloadString("/<foo>")).isEqualTo("this is the content");
-  }
-
-  @Test
-  public void should_download_file() throws Exception {
-    startServer(null, "this is the content");
-    File file = temp.newFile();
-    newServerClient().download("/foo", file);
-    assertThat(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)).isEqualTo("this is the content");
-  }
-
-  @Test
-  public void should_fail_if_unauthorized_with_no_login_password() throws Exception {
-    startServer(401, null);
-    thrown.expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password.");
-    newServerClient().downloadString("/foo");
-  }
-
-  @Test
-  public void should_fail_if_unauthorized_with_login_password_provided() throws Exception {
-    startServer(401, null);
-
-    when(bootstrapProps.property(eq("sonar.login"))).thenReturn("login");
-    when(bootstrapProps.property(eq("sonar.password"))).thenReturn("password");
-
-    thrown.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password");
-    newServerClient().downloadString("/foo");
-  }
-
-  @Test
-  public void should_display_json_error_when_403() throws Exception {
-    startServer(403, "{\"errors\":[{\"msg\":\"Insufficient privileges\"}]}");
-    thrown.expectMessage("Insufficient privileges");
-    newServerClient().downloadString("/foo");
-  }
-
-  @Test
-  public void should_fail_if_error() throws Exception {
-    startServer(500, null);
-    thrown.expectMessage("Fail to execute request [code=500, url=http://localhost:" + server.getPort() + "/foo]");
-    newServerClient().downloadString("/foo");
-  }
-
-  @Test
-  public void string_encode() {
-    assertThat(BatchUtils.encodeForUrl("my value")).isEqualTo("my+value");
-  }
-
-  private ServerClient newServerClient() {
-    when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort());
-    return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"));
-  }
-
-  private void startServer(Integer responseStatus, String responseData) throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    
-    if (responseStatus != null) {
-      server.setMockResponseStatus(responseStatus);
-    }
-    if (responseData != null) {
-      server.setMockResponseData(responseData);
-    }
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptorTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptorTest.java
new file mode 100644 (file)
index 0000000..4655371
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.squareup.okhttp.Interceptor;
+import com.squareup.okhttp.MediaType;
+import com.squareup.okhttp.Protocol;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
+import com.squareup.okhttp.ResponseBody;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WsClientLoggingInterceptorTest {
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  WsClientLoggingInterceptor underTest = new WsClientLoggingInterceptor();
+  Interceptor.Chain chain = mock(Interceptor.Chain.class);
+
+  @Test
+  public void log_and_profile_request_if_debug_level() throws Exception {
+    Request request = newRequest();
+    Response response = newResponse(request, 200, "");
+    when(chain.request()).thenReturn(request);
+    when(chain.proceed(request)).thenReturn(response);
+
+    logTester.setLevel(LoggerLevel.DEBUG);
+    Response result = underTest.intercept(chain);
+
+    // do not fail the execution -> interceptor returns the response
+    assertThat(result).isSameAs(response);
+
+    // check logs
+    List<String> debugLogs = logTester.logs(LoggerLevel.DEBUG);
+    assertThat(debugLogs).hasSize(1);
+    assertThat(debugLogs.get(0)).contains("GET 200 https://localhost:9000/api/issues/search | time=");
+    List<String> traceLogs = logTester.logs(LoggerLevel.TRACE);
+    assertThat(traceLogs).hasSize(1);
+    assertThat(traceLogs.get(0)).isEqualTo("GET https://localhost:9000/api/issues/search");
+  }
+
+  @Test
+  public void fail_if_requires_authentication() throws Exception {
+    expectedException.expect(MessageException.class);
+    expectedException
+      .expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password.");
+
+    Request request = newRequest();
+    Response response = newResponse(request, 401, "");
+    when(chain.request()).thenReturn(request);
+    when(chain.proceed(request)).thenReturn(response);
+
+    underTest.intercept(chain);
+  }
+
+  @Test
+  public void fail_if_credentials_are_not_valid() throws Exception {
+    expectedException.expect(MessageException.class);
+    expectedException.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password.");
+
+    Request request = new Request.Builder()
+      .url("https://localhost:9000/api/issues/search")
+      .header("Authorization", "Basic BAD_CREDENTIALS")
+      .get()
+      .build();
+    Response response = newResponse(request, 401, "");
+    when(chain.request()).thenReturn(request);
+    when(chain.proceed(request)).thenReturn(response);
+
+    underTest.intercept(chain);
+  }
+
+  @Test
+  public void fail_if_requires_permission() throws Exception {
+    expectedException.expect(MessageException.class);
+    expectedException.expectMessage("missing scan permission, missing another permission");
+
+    Request request = newRequest();
+    Response response = newResponse(request, 403, "{\"errors\":[{\"msg\":\"missing scan permission\"}, {\"msg\":\"missing another permission\"}]}");
+    when(chain.request()).thenReturn(request);
+    when(chain.proceed(request)).thenReturn(response);
+
+    underTest.intercept(chain);
+  }
+
+  private Request newRequest() {
+    return new Request.Builder().url("https://localhost:9000/api/issues/search").get().build();
+  }
+
+  private Response newResponse(Request getRequest, int code, String jsonBody) {
+    return new Response.Builder().request(getRequest)
+      .code(code)
+      .protocol(Protocol.HTTP_1_1)
+      .body(ResponseBody.create(MediaType.parse("application/json"), jsonBody))
+      .build();
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientProviderTest.java
new file mode 100644 (file)
index 0000000..ac5a9c8
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonarqube.ws.client.HttpConnector;
+import org.sonarqube.ws.client.WsClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.batch.bootstrap.WsClientProvider.CONNECT_TIMEOUT_MS;
+import static org.sonar.batch.bootstrap.WsClientProvider.DEFAULT_READ_TIMEOUT_SEC;
+
+public class WsClientProviderTest {
+
+  WsClientProvider underTest = new WsClientProvider();
+  EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3");
+
+  @Test
+  public void provide_client_with_default_settings() {
+    GlobalProperties settings = new GlobalProperties(new HashMap<String, String>());
+
+    WsClient client = underTest.provide(settings, env);
+
+    assertThat(client).isNotNull();
+    HttpConnector httpConnector = (HttpConnector) client.wsConnector();
+    assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/");
+    assertThat(httpConnector.okHttpClient().getProxy()).isNull();
+    assertThat(httpConnector.okHttpClient().getConnectTimeout()).isEqualTo(5_000);
+    assertThat(httpConnector.okHttpClient().getReadTimeout()).isEqualTo(60_000);
+    assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3");
+    assertThat(httpConnector.okHttpClient().interceptors())
+      .hasSize(1)
+      .hasOnlyElementsOfType(WsClientLoggingInterceptor.class);
+  }
+
+  @Test
+  public void provide_client_with_custom_settings() {
+    Map<String, String> props = new HashMap<>();
+    props.put("sonar.host.url", "https://here/sonarqube");
+    props.put("sonar.login", "theLogin");
+    props.put("sonar.password", "thePassword");
+    props.put("sonar.ws.timeout", "42");
+    GlobalProperties settings = new GlobalProperties(props);
+
+    WsClient client = underTest.provide(settings, env);
+
+    assertThat(client).isNotNull();
+    HttpConnector httpConnector = (HttpConnector) client.wsConnector();
+    assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/");
+    assertThat(httpConnector.okHttpClient().getProxy()).isNull();
+    assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3");
+  }
+
+  @Test
+  public void build_singleton() {
+    GlobalProperties settings = new GlobalProperties(new HashMap<String, String>());
+    WsClient first = underTest.provide(settings, env);
+    WsClient second = underTest.provide(settings, env);
+    assertThat(first).isSameAs(second);
+  }
+}
index 8d86733a01d81a16fd9b04defa0065931497fe3a..831a0622c1d04672706fae07ae3adbdb617b38ce 100644 (file)
  */
 package org.sonar.batch.cache;
 
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.assertj.core.api.Assertions.assertThat;
 import com.google.common.io.Files;
-import org.junit.rules.ExpectedException;
-
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Date;
-
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
-import org.junit.Rule;
-import org.junit.Before;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.home.cache.PersistentCache;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 public class DefaultProjectCacheStatusTest {
   @Rule
   public TemporaryFolder tmp = new TemporaryFolder();
@@ -45,13 +43,10 @@ public class DefaultProjectCacheStatusTest {
   public ExpectedException exception = ExpectedException.none();
 
   ProjectCacheStatus cacheStatus;
-  PersistentCache cache;
-  ServerClient client;
+  PersistentCache cache = mock(PersistentCache.class);
 
   @Before
   public void setUp() {
-    client = mock(ServerClient.class);
-    cache = mock(PersistentCache.class);
     when(cache.getDirectory()).thenReturn(tmp.getRoot().toPath());
     cacheStatus = new DefaultProjectCacheStatus(cache);
   }
index 15833a06ad534efd9e48dac82dd731972da14b96..560ce56a173218d43d22d742264b62f00b0f892b 100644 (file)
  */
 package org.sonar.batch.cache;
 
-import org.sonar.batch.protocol.input.ProjectRepositories;
-
+import java.util.HashMap;
+import org.junit.Test;
 import org.sonar.batch.bootstrap.GlobalProperties;
-import org.sonar.batch.bootstrap.ServerClient;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.core.platform.ComponentContainer;
 import org.sonar.home.cache.PersistentCache;
-
-import java.util.HashMap;
+import org.sonarqube.ws.client.WsClient;
 
 import static org.mockito.Mockito.mock;
-import org.sonar.core.platform.ComponentContainer;
-import org.junit.Test;
 
 public class ProjectSyncContainerTest {
   private ComponentContainer createParentContainer() {
     PersistentCache cache = mock(PersistentCache.class);
-    ServerClient server = mock(ServerClient.class);
+    WsClient server = mock(WsClient.class);
 
     GlobalProperties globalProps = new GlobalProperties(new HashMap<String, String>());
     ComponentContainer parent = new ComponentContainer();
index 5d9ed4458225a415b7d33a403d2adf58df3f2f8c..4134b7f6653d930dd4ce0d496eff4596ed759b3e 100644 (file)
@@ -23,33 +23,28 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.sonar.batch.bootstrap.GlobalProperties;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.WsClient;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
 
 public class StrategyWSLoaderProviderTest {
   @Mock
   private PersistentCache cache;
 
   @Mock
-  private ServerClient client;
-
-  private GlobalProperties globalProps;
+  private WsClient client;
 
   @Before
   public void setUp() {
     MockitoAnnotations.initMocks(this);
-    globalProps = mock(GlobalProperties.class);
   }
 
   @Test
   public void testStrategy() {
     StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST);
-    WSLoader wsLoader = provider.provide(cache, client, globalProps);
+    WSLoader wsLoader = provider.provide(cache, client);
 
     assertThat(wsLoader.getDefaultStrategy()).isEqualTo(LoadStrategy.CACHE_FIRST);
   }
@@ -57,8 +52,8 @@ public class StrategyWSLoaderProviderTest {
   @Test
   public void testSingleton() {
     StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST);
-    WSLoader wsLoader = provider.provide(cache, client, globalProps);
+    WSLoader wsLoader = provider.provide(cache, client);
 
-    assertThat(provider.provide(null, null, null)).isEqualTo(wsLoader);
+    assertThat(provider.provide(null, null)).isEqualTo(wsLoader);
   }
 }
index c75259b5b36c05afdc77d779f79ee01bbf1e647f..2db585445f8b1c212ac4781441631637ecb50d64 100644 (file)
  */
 package org.sonar.batch.cache;
 
-import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URI;
 import org.apache.commons.io.IOUtils;
 import org.hamcrest.Matchers;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.mockito.InOrder;
-import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.sonar.api.utils.HttpDownloader;
-import org.sonar.batch.bootstrap.ServerClient;
-import org.sonar.batch.bootstrap.UserProperties;
 import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.home.cache.PersistentCache;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.MockWsResponse;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsConnector;
+import org.sonarqube.ws.client.WsRequest;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -58,35 +52,20 @@ public class WSLoaderTest {
   private final static String cacheValue = "cache";
   private final static String serverValue = "server";
 
-  @Mock
-  private ServerClient client;
-  @Mock
-  private PersistentCache cache;
   @Rule
   public ExpectedException exception = ExpectedException.none();
-  private UserProperties props;
 
-  @Before
-  public void setUp() throws IOException {
-    MockitoAnnotations.initMocks(this);
-    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenReturn(IOUtils.toInputStream(serverValue));
-    when(cache.getString(ID)).thenReturn(cacheValue);
-    when(client.getURI(anyString())).thenAnswer(new Answer<URI>() {
-      @Override
-      public URI answer(InvocationOnMock invocation) throws Throwable {
-        return new URI((String) invocation.getArguments()[0]);
-      }
-    });
-    props = mock(UserProperties.class);
-  }
+  WsClient ws = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS);
+  PersistentCache cache = mock(PersistentCache.class);
 
   @Test
   public void dont_retry_server_offline() throws IOException {
     turnServerOffline();
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    when(cache.getString(ID)).thenReturn(cacheValue);
+    WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
 
-    assertResult(loader.loadString(ID), cacheValue, true);
-    assertResult(loader.loadString(ID), cacheValue, true);
+    assertResult(underTest.loadString(ID), cacheValue, true);
+    assertResult(underTest.loadString(ID), cacheValue, true);
 
     assertUsedServer(1);
     assertUsedCache(2);
@@ -94,84 +73,67 @@ public class WSLoaderTest {
 
   @Test
   public void get_stream_from_cache() throws IOException {
-    InputStream is = mock(InputStream.class);
+    InputStream is = IOUtils.toInputStream("is");
     when(cache.getStream(ID)).thenReturn(is);
-    WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, props);
-    WSLoaderResult<InputStream> result = loader.loadStream(ID);
-    assertThat(result.get()).isEqualTo(is);
-    verify(cache).getStream(ID);
 
-    verifyNoMoreInteractions(cache, client);
-  }
-
-  @Test
-  public void put_stream_in_cache() throws IOException {
-    InputStream is1 = mock(InputStream.class);
-    InputStream is2 = mock(InputStream.class);
-
-    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenReturn(is1);
-    when(cache.getStream(ID)).thenReturn(is2);
-
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws);
     WSLoaderResult<InputStream> result = loader.loadStream(ID);
-    assertThat(result.get()).isEqualTo(is2);
 
-    verify(client).load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt());
-    verify(cache).put(ID, is1);
+    assertThat(result.get()).isEqualTo(is);
     verify(cache).getStream(ID);
-
-    verifyNoMoreInteractions(cache, client);
+    verifyNoMoreInteractions(cache, ws);
   }
 
   @Test
-  public void default_timeout() throws IOException {
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
-    loader.loadStream(ID);
-
-    verify(client).load(anyString(), anyString(), anyBoolean(), anyInt(), eq(60_000));
-
-    verifyNoMoreInteractions(client);
-  }
-
-  @Test
-  public void change_timeout() throws IOException {
-    when(props.properties()).thenReturn(ImmutableMap.of(WSLoader.SONAR_WS_TIMEOUT_PROPS, "20"));
-    when(props.property(WSLoader.SONAR_WS_TIMEOUT_PROPS)).thenReturn("20");
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
-    loader.loadStream(ID);
-
-    verify(client).load(anyString(), anyString(), anyBoolean(), anyInt(), eq(20_000));
-
-    verifyNoMoreInteractions(client);
+  public void put_stream_in_cache() throws IOException {
+    InputStream input = IOUtils.toInputStream("is");
+
+    when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(input));
+    when(cache.getStream(ID)).thenReturn(input);
+
+    // SERVER_FIRST -> load from server then put to cache
+    WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
+    WSLoaderResult<InputStream> result = underTest.loadStream(ID);
+    assertThat(result.get()).isEqualTo(input);
+
+    WsConnector wsConnector = ws.wsConnector();
+    InOrder inOrder = inOrder(wsConnector, cache);
+    inOrder.verify(wsConnector).call(any(WsRequest.class));
+    inOrder.verify(cache).put(eq(ID), any(InputStream.class));
+    inOrder.verify(cache).getStream(ID);
+    verifyNoMoreInteractions(cache, wsConnector);
   }
 
   @Test
   public void test_cache_strategy_fallback() throws IOException {
     turnCacheEmpty();
-    WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, props);
+    when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+    WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws);
 
     assertResult(loader.loadString(ID), serverValue, false);
 
-    InOrder inOrder = Mockito.inOrder(client, cache);
+    InOrder inOrder = inOrder(ws.wsConnector(), cache);
     inOrder.verify(cache).getString(ID);
-    inOrder.verify(client).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt());
+    inOrder.verify(ws.wsConnector()).call(any(WsRequest.class));
   }
 
   @Test
   public void test_server_strategy_fallback() throws IOException {
     turnServerOffline();
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    when(cache.getString(ID)).thenReturn(cacheValue);
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
 
     assertResult(loader.loadString(ID), cacheValue, true);
 
-    InOrder inOrder = Mockito.inOrder(client, cache);
-    inOrder.verify(client).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt());
+    InOrder inOrder = inOrder(ws.wsConnector(), cache);
+    inOrder.verify(ws.wsConnector()).call(any(WsRequest.class));
     inOrder.verify(cache).getString(ID);
   }
 
   @Test
   public void test_put_cache() throws IOException {
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
     loader.loadString(ID);
     verify(cache).put(ID, serverValue.getBytes());
   }
@@ -181,7 +143,7 @@ public class WSLoaderTest {
     turnServerOffline();
 
     when(cache.getString(ID)).thenThrow(new NullPointerException());
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
 
     try {
       loader.loadString(ID);
@@ -196,7 +158,7 @@ public class WSLoaderTest {
   public void test_throw_cache_exception() throws IOException {
     when(cache.getString(ID)).thenThrow(new IllegalStateException());
 
-    WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws);
 
     try {
       loader.loadString(ID);
@@ -209,12 +171,12 @@ public class WSLoaderTest {
 
   @Test
   public void test_throw_http_exceptions() {
-    HttpDownloader.HttpException httpException = mock(HttpDownloader.HttpException.class);
+    HttpException httpException = new HttpException("url", 500, "Internal Error");
     IllegalStateException wrapperException = new IllegalStateException(httpException);
 
-    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(wrapperException);
+    when(ws.wsConnector().call(any(WsRequest.class))).thenThrow(wrapperException);
 
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
 
     try {
       loader.loadString(ID);
@@ -230,9 +192,9 @@ public class WSLoaderTest {
     turnServerOffline();
 
     exception.expect(IllegalStateException.class);
-    exception.expectMessage(Matchers.is("Server is not available"));
+    exception.expectMessage(Matchers.containsString("Server is not available"));
 
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws);
     loader.loadString(ID);
   }
 
@@ -244,7 +206,7 @@ public class WSLoaderTest {
     exception.expect(IllegalStateException.class);
     exception.expectMessage(Matchers.is("Server is not accessible and data is not cached"));
 
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
     loader.loadString(ID);
   }
 
@@ -255,13 +217,14 @@ public class WSLoaderTest {
     exception.expect(IllegalStateException.class);
     exception.expectMessage(Matchers.is("Data is not cached"));
 
-    WSLoader loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, ws);
     loader.loadString(ID);
   }
 
   @Test
   public void test_server_strategy() throws IOException {
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
     assertResult(loader.loadString(ID), serverValue, false);
 
     // should not fetch from cache
@@ -272,13 +235,14 @@ public class WSLoaderTest {
   @Test(expected = IllegalStateException.class)
   public void test_server_only() throws IOException {
     turnServerOffline();
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, props);
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws);
     loader.loadString(ID);
   }
 
   @Test
   public void test_string() {
-    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props);
+    when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue));
+    WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws);
     assertResult(loader.loadString(ID), serverValue, false);
   }
 
@@ -287,14 +251,7 @@ public class WSLoaderTest {
   }
 
   private void assertUsedServer(int times) {
-    verify(client, times(times)).load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt());
-  }
-
-  private void assertResult(WSLoaderResult<InputStream> result, byte[] expected, boolean fromCache) throws IOException {
-    byte[] content = IOUtils.toByteArray(result.get());
-    assertThat(result).isNotNull();
-    assertThat(content).isEqualTo(expected);
-    assertThat(result.isFromCache()).isEqualTo(fromCache);
+    verify(ws.wsConnector(), times(times)).call(any(WsRequest.class));
   }
 
   private void assertResult(WSLoaderResult<String> result, String expected, boolean fromCache) {
@@ -304,7 +261,7 @@ public class WSLoaderTest {
   }
 
   private void turnServerOffline() {
-    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(new IllegalStateException());
+    when(ws.wsConnector().call(any(WsRequest.class))).thenThrow(new IllegalStateException());
   }
 
   private void turnCacheEmpty() throws IOException {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderWithServerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderWithServerTest.java
deleted file mode 100644 (file)
index cd5b2e3..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.cache;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.batch.bootstrap.GlobalProperties;
-import org.sonar.batch.bootstrap.MockHttpServer;
-import org.sonar.batch.bootstrap.ServerClient;
-import org.sonar.batch.bootstrap.Slf4jLogger;
-import org.sonar.batch.bootstrap.UserProperties;
-import org.sonar.batch.bootstrapper.EnvironmentInformation;
-import org.sonar.batch.cache.WSLoader.LoadStrategy;
-import org.sonar.home.cache.DirectoryLock;
-import org.sonar.home.cache.PersistentCache;
-import org.sonar.home.cache.TTLCacheInvalidation;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class WSLoaderWithServerTest {
-  private static final String RESPONSE_STRING = "this is the content";
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  private MockHttpServer server;
-  private PersistentCache cache;
-  private ServerClient client;
-  private WSLoader loader;
-  private UserProperties userProps;
-
-  @Before
-  public void setUp() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-
-    GlobalProperties bootstrapProps = mock(GlobalProperties.class);
-    when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort());
-
-    client = new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"));
-    cache = new PersistentCache(temp.getRoot().toPath(), new TTLCacheInvalidation(100_000L), new Slf4jLogger(), mock(DirectoryLock.class));
-    userProps = mock(UserProperties.class);
-  }
-
-  @After
-  public void tearDown() {
-    if (server != null) {
-      server.stop();
-    }
-  }
-
-  @Test
-  public void testCacheOnly() {
-    loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, userProps);
-    makeRequests();
-
-    loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, client, userProps);
-    makeRequests();
-    assertThat(server.getNumberRequests()).isEqualTo(3);
-  }
-
-  @Test
-  public void testCacheFirst() {
-    loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, userProps);
-    makeRequests();
-    assertThat(server.getNumberRequests()).isEqualTo(1);
-  }
-
-  @Test
-  public void testServerFirst() {
-    loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, userProps);
-    makeRequests();
-    assertThat(server.getNumberRequests()).isEqualTo(3);
-  }
-
-  @Test
-  public void testCacheStrategyDisabled() {
-    loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, userProps);
-    makeRequests();
-    assertThat(server.getNumberRequests()).isEqualTo(3);
-  }
-
-  private void makeRequests() {
-    server.setMockResponseData(RESPONSE_STRING);
-    assertThat(loader.loadString("/foo").get()).isEqualTo(RESPONSE_STRING);
-    assertThat(loader.loadString("/foo").get()).isEqualTo(RESPONSE_STRING);
-    assertThat(loader.loadString("/foo").get()).isEqualTo(RESPONSE_STRING);
-  }
-
-}
index 75b8af6dc5147b860e48ab31def0ac2c44d5eafe..d1c5e1a93c60a38cab406be06f20dbb1a19cdd1f 100644 (file)
  */
 package org.sonar.batch.report;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.slf4j.Logger;
+import org.mockito.Mockito;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
 import org.sonar.api.utils.TempFolder;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.batch.analysis.DefaultAnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.scan.ImmutableProjectReactor;
+import org.sonar.test.JsonAssert;
+import org.sonarqube.ws.client.WsClient;
 
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
+import static org.apache.commons.io.FileUtils.readFileToString;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 public class ReportPublisherTest {
 
-  private DefaultAnalysisMode mode;
-
-  private ImmutableProjectReactor reactor;
-
-  private Settings settings;
-
-  private ProjectDefinition root;
+  @Rule
+  public LogTester logTester = new LogTester();
 
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
+  DefaultAnalysisMode mode = mock(DefaultAnalysisMode.class);
+  Settings settings = new Settings();
+  WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS);
+  ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class);
+  ProjectDefinition root;
+  AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class);
+
   @Before
   public void setUp() {
-    settings = new Settings();
-    mode = mock(DefaultAnalysisMode.class);
-    reactor = mock(ImmutableProjectReactor.class);
     root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot());
     when(reactor.getRoot()).thenReturn(root);
+    when(wsClient.wsConnector().baseUrl()).thenReturn("https://localhost");
   }
 
   @Test
-  public void should_log_successful_analysis() {
-    settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/");
-    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode,
-      mock(TempFolder.class), new ReportPublisherStep[0]);
-
-    Logger logger = mock(Logger.class);
-    job.logSuccess(logger, null);
-
-    verify(logger).info("ANALYSIS SUCCESSFUL, you can browse {}", "http://myserver/dashboard/index/struts");
-    verify(logger).info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
-    verifyNoMoreInteractions(logger);
+  public void log_and_dump_information_about_report_uploading() throws IOException {
+    ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+    underTest.logSuccess("TASK-123");
+
+    assertThat(logTester.logs(LoggerLevel.INFO))
+      .contains("ANALYSIS SUCCESSFUL, you can browse https://localhost/dashboard/index/struts")
+      .contains("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report")
+      .contains("More about the report processing at https://localhost/api/ce/task?id=TASK-123");
+
+    File detailsFile = new File(temp.getRoot(), "analysis-details.json");
+    JsonAssert.assertJson(readFileToString(detailsFile)).isSimilarTo("{" +
+      "\"projectKey\": \"struts\"," +
+      "\"dashboardUrl\": \"https://localhost/dashboard/index/struts\"," +
+      "\"ceTaskId\": \"TASK-123\"," +
+      "\"ceTaskUrl\": \"https://localhost/api/ce/task?id=TASK-123\"" +
+      "}"
+      );
   }
 
   @Test
-  public void should_log_successful_analysis_with_ce_task() {
-    settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/");
-    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode,
-      mock(TempFolder.class), new ReportPublisherStep[0]);
-
-    Logger logger = mock(Logger.class);
-    job.logSuccess(logger, "abc123");
-
-    verify(logger).info("ANALYSIS SUCCESSFUL, you can browse {}", "http://myserver/dashboard/index/struts");
-    verify(logger).info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report.");
-    verify(logger).info("More about the report processing at {}", "http://myserver/api/ce/task?id=abc123");
-    verifyNoMoreInteractions(logger);
+  public void log_public_url_if_defined() throws IOException {
+    settings.setProperty(CoreProperties.SERVER_BASE_URL, "https://publicserver/sonarqube");
+    ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+
+    underTest.logSuccess("TASK-123");
+
+    assertThat(logTester.logs(LoggerLevel.INFO))
+      .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard/index/struts")
+      .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123");
+
+    File detailsFile = new File(temp.getRoot(), "analysis-details.json");
+    JsonAssert.assertJson(readFileToString(detailsFile)).isSimilarTo("{" +
+      "\"projectKey\": \"struts\"," +
+      "\"dashboardUrl\": \"https://publicserver/sonarqube/dashboard/index/struts\"," +
+      "\"ceTaskId\": \"TASK-123\"," +
+      "\"ceTaskUrl\": \"https://publicserver/sonarqube/api/ce/task?id=TASK-123\"" +
+      "}"
+    );
   }
 
   @Test
-  public void should_write_json_file() throws IOException {
-    settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/");
-
-    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode,
-      mock(TempFolder.class), new ReportPublisherStep[0]);
-    job.logSuccess(mock(Logger.class), "abc123");
-
-    File jsonFile = new File(temp.getRoot(), "analysis-details.json");
-    assertThat(jsonFile).exists();
+  public void log_but_not_dump_information_when_report_is_not_uploaded() {
+    ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
 
-    String jsonFileContent = new String(Files.readAllBytes(jsonFile.toPath()), StandardCharsets.UTF_8);
-    String expectedContent = "\"dashboardUrl\":\"http://myserver/dashboard/index/struts\",\"ceTaskUrl\":\"http://myserver/api/ce/task?id=abc123\"";
-    assertThat(jsonFileContent).contains(expectedContent);
+    underTest.logSuccess(/* report not uploaded, no server task */null);
 
-  }
-
-  @Test
-  public void should_log_successful_issues_analysis() {
-    when(mode.isIssues()).thenReturn(true);
-    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode,
-      mock(TempFolder.class), new ReportPublisherStep[0]);
+    assertThat(logTester.logs(LoggerLevel.INFO))
+      .contains("ANALYSIS SUCCESSFUL")
+      .doesNotContain("dashboard/index");
 
-    Logger logger = mock(Logger.class);
-    job.logSuccess(logger, null);
-
-    verify(logger).info("ANALYSIS SUCCESSFUL");
-    verifyNoMoreInteractions(logger);
+    File detailsFile = new File(temp.getRoot(), "analysis-details.json");
+    assertThat(detailsFile).doesNotExist();
   }
 
   @Test
-  public void should_not_delete_report() throws IOException {
-    settings.setProperty("sonar.verbose", true);
+  public void should_not_delete_report_if_property_is_set() throws IOException {
+    settings.setProperty("sonar.batch.keepReport", true);
     Path reportDir = temp.getRoot().toPath().resolve("batch-report");
     Files.createDirectory(reportDir);
-    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode,
-      mock(TempFolder.class), new ReportPublisherStep[0]);
+    ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
 
-    job.start();
-    job.stop();
+    underTest.start();
+    underTest.stop();
     assertThat(reportDir).isDirectory();
   }
 
   @Test
-  public void should_delete_report() throws IOException {
+  public void should_delete_report_by_default() throws IOException {
     Path reportDir = temp.getRoot().toPath().resolve("batch-report");
     Files.createDirectory(reportDir);
-    ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode,
-      mock(TempFolder.class), new ReportPublisherStep[0]);
+    ReportPublisher job = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
 
     job.start();
     job.stop();
index b8c9012399ae2b55e751cab44e90cc01bf082d96..e0eebd486d3e2568e617a8d4af1a590f319726fa 100644 (file)
  */
 package org.sonar.batch.repository;
 
-import org.sonar.api.utils.MessageException;
-
 import com.google.common.io.Resources;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URI;
-
-import org.sonar.api.utils.HttpDownloader.HttpException;
 import org.apache.commons.lang.mutable.MutableBoolean;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.MessageException;
 import org.sonar.batch.cache.WSLoader;
 import org.sonar.batch.cache.WSLoaderResult;
 import org.sonarqube.ws.WsBatch.WsProjectResponse;
+import org.sonarqube.ws.client.HttpException;
+
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -56,7 +53,7 @@ public class DefaultProjectRepositoriesLoaderTest {
   public void prepare() throws IOException {
     wsLoader = mock(WSLoader.class);
     InputStream is = mockData();
-    when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<InputStream>(is, true));
+    when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true));
     loader = new DefaultProjectRepositoriesLoader(wsLoader);
   }
 
@@ -72,13 +69,13 @@ public class DefaultProjectRepositoriesLoaderTest {
     InputStream is = mock(InputStream.class);
     when(is.read()).thenThrow(IOException.class);
 
-    when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<InputStream>(is, false));
+    when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false));
     loader.load(PROJECT_KEY, false, null);
   }
 
   @Test(expected = IllegalStateException.class)
   public void failFastHttpError() {
-    HttpException http = new HttpException(URI.create("uri"), 403);
+    HttpException http = new HttpException("url", 403, "Forbidden");
     IllegalStateException e = new IllegalStateException("http error", http);
     when(wsLoader.loadStream(anyString())).thenThrow(e);
     loader.load(PROJECT_KEY, false, null);
@@ -89,7 +86,7 @@ public class DefaultProjectRepositoriesLoaderTest {
     thrown.expect(MessageException.class);
     thrown.expectMessage("http error");
     
-    HttpException http = new HttpException(URI.create("uri"), 403);
+    HttpException http = new HttpException("uri", 403, "Forbidden");
     MessageException e = MessageException.of("http error", http);
     when(wsLoader.loadStream(anyString())).thenThrow(e);
     loader.load(PROJECT_KEY, false, null);
index 3409a153d0285e2c5eaf0654a17c11b2262444f7..457bfa77dbbe22b95cbe8b55a0ac8c497909dfe7 100644 (file)
   <name>SonarQube :: Web Service</name>
   <description>Protocol Buffers specification of Web Services</description>
 
+  <properties>
+    <sonar.exclusions>target/generated-sources/**/*</sonar.exclusions>
+    <sonar.test.exclusions>target/generated-test-sources/**/*</sonar.test.exclusions>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>com.google.protobuf</groupId>
       <artifactId>protobuf-java</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>okhttp</artifactId>
+      <version>${okhttp.version}</version>
+    </dependency>
     <dependency>
       <groupId>com.google.code.findbugs</groupId>
       <artifactId>jsr305</artifactId>
       <scope>provided</scope>
     </dependency>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-    </dependency>
     <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>sonar-testing-harness</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>mockwebserver</artifactId>
+      <version>${okhttp.version}</version>
       <scope>test</scope>
     </dependency>
-
-    <!-- Jetty dependencies -->
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-server</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>test-jetty-servlet</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.googlecode.json-simple</groupId>
-      <artifactId>json-simple</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.github.kevinsawicki</groupId>
-      <artifactId>http-request</artifactId>
-    </dependency>
   </dependencies>
 </project>
index ab3beddeca945f0515e4499142a32cb6bad6205b..6947b77b59e25870490039d7f14652dad4a7d115 100644 (file)
@@ -27,7 +27,7 @@ import java.util.Locale;
 import java.util.Map;
 
 /**
- * @since 3.1
+ * @since 5.3
  */
 public final class MediaTypes {
 
@@ -35,6 +35,7 @@ public final class MediaTypes {
   public static final String XML = "application/xml";
   public static final String TXT = "text/plain";
   public static final String PROTOBUF = "application/x-protobuf";
+  public static final String ZIP = "application/zip";
   public static final String DEFAULT = "application/octet-stream";
 
   private static final Map<String, String> MAP = new ImmutableMap.Builder<String, String>()
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/BaseRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/BaseRequest.java
new file mode 100644 (file)
index 0000000..41c3339
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.sonarqube.ws.MediaTypes;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.util.Objects.requireNonNull;
+
+abstract class BaseRequest<SELF extends BaseRequest> implements WsRequest {
+
+  private final String path;
+
+  private String mediaType = MediaTypes.JSON;
+
+  // keep the same order -> do not use HashMap
+  private final Map<String, String> params = new LinkedHashMap<>();
+
+  BaseRequest(String path) {
+    this.path = path;
+  }
+
+  @Override
+  public String getPath() {
+    return path;
+  }
+
+  @Override
+  public String getMediaType() {
+    return mediaType;
+  }
+
+  /**
+   * Expected media type of response. Default is {@link MediaTypes#JSON}.
+   */
+  public SELF setMediaType(String s) {
+    requireNonNull(s, "media type of response cannot be null");
+    this.mediaType = s;
+    return (SELF) this;
+  }
+
+  public SELF setParam(String key, @Nullable Object value) {
+    checkArgument(!isNullOrEmpty(key), "a WS parameter key cannot be null");
+    if (value != null) {
+      this.params.put(key, value.toString());
+    }
+    return (SELF) this;
+  }
+
+  @Override
+  public Map<String, String> getParams() {
+    return params;
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/BaseService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/BaseService.java
new file mode 100644 (file)
index 0000000..11d4d00
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import com.google.protobuf.Message;
+import com.google.protobuf.Parser;
+import java.io.InputStream;
+import org.sonarqube.ws.MediaTypes;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+public abstract class BaseService {
+
+  private final WsConnector wsConnector;
+  private final String controller;
+
+  public BaseService(WsConnector wsConnector, String controllerPath) {
+    checkArgument(!isNullOrEmpty(controllerPath));
+    this.wsConnector = wsConnector;
+    this.controller = controllerPath;
+  }
+
+  protected <T extends Message> T call(BaseRequest request, Parser<T> parser) {
+    request.setMediaType(MediaTypes.PROTOBUF);
+    WsResponse response = wsConnector.call(request);
+    return convert(response, parser);
+  }
+
+  protected WsResponse call(WsRequest request) {
+    return wsConnector.call(request);
+  }
+
+  public <T extends Message> T convert(WsResponse response, Parser<T> parser) {
+    try (InputStream byteStream = response.getContentStream()) {
+      // HTTP header "Content-Type" is not verified. It may be different than protobuf.
+      return parser.parseFrom(byteStream);
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to parse protobuf response of " + response.getRequestUrl(), e);
+    }
+  }
+
+  protected String path(String action) {
+    return String.format("%s/%s", controller, action);
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/GetRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/GetRequest.java
new file mode 100644 (file)
index 0000000..b59903f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+/**
+ * @since 5.3
+ */
+public class GetRequest extends BaseRequest<GetRequest> {
+  public GetRequest(String path) {
+    super(path);
+  }
+
+  @Override
+  public Method getMethod() {
+    return Method.GET;
+  }
+}
index 66ac3cc89cdf9fab3df20ed14c7d25909812c20c..7f46d08cacb105a3563c9ae7f846c55716c3ce22 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-
 package org.sonarqube.ws.client;
 
-import com.google.protobuf.Message;
-import com.google.protobuf.Parser;
+import com.squareup.okhttp.Call;
+import com.squareup.okhttp.Credentials;
+import com.squareup.okhttp.Headers;
+import com.squareup.okhttp.HttpUrl;
+import com.squareup.okhttp.Interceptor;
+import com.squareup.okhttp.MediaType;
+import com.squareup.okhttp.MultipartBuilder;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.RequestBody;
+import com.squareup.okhttp.Response;
+import java.io.IOException;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.base.Strings.nullToEmpty;
+import static java.lang.String.format;
+
 public class HttpConnector implements WsConnector {
 
-  public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30000;
-  public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000;
+  public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30_000;
+  public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60_000;
 
   /**
-   * Visibility relaxed for unit tests
+   * Base URL with trailing slash, for instance "https://localhost/sonarqube/".
+   * It is required for further usage of {@link HttpUrl#resolve(String)}.
    */
-  final HttpRequestFactory requestFactory;
+  private final HttpUrl baseUrl;
+  private final String userAgent;
+  private final String credentials;
+  private final String proxyCredentials;
+  private final OkHttpClient okHttpClient = new OkHttpClient();
 
   private HttpConnector(Builder builder) {
-    this.requestFactory = new HttpRequestFactory(builder.url)
-      .setLogin(builder.login)
-      .setPassword(builder.password)
-      .setProxyHost(builder.proxyHost)
-      .setProxyPort(builder.proxyPort)
-      .setProxyLogin(builder.proxyLogin)
-      .setProxyPassword(builder.proxyPassword)
-      .setConnectTimeoutInMilliseconds(builder.connectTimeoutMs)
-      .setReadTimeoutInMilliseconds(builder.readTimeoutMs);
+    this.baseUrl = HttpUrl.parse(builder.url.endsWith("/") ? builder.url : format("%s/", builder.url));
+    this.userAgent = builder.userAgent;
+
+    if (isNullOrEmpty(builder.login)) {
+      // no login nor access token
+      this.credentials = null;
+    } else {
+      // password is null when login represents an access token. In this case
+      // the Basic credentials consider an empty password.
+      this.credentials = Credentials.basic(builder.login, nullToEmpty(builder.password));
+    }
+
+    if (builder.proxy != null) {
+      this.okHttpClient.setProxy(builder.proxy);
+    }
+    // proxy credentials can be used on system-wide proxies, so even if builder.proxy is null
+    if (isNullOrEmpty(builder.proxyLogin)) {
+      this.proxyCredentials = null;
+    } else {
+      this.proxyCredentials = Credentials.basic(builder.proxyLogin, nullToEmpty(builder.proxyPassword));
+    }
+
+    this.okHttpClient.setConnectTimeout(builder.connectTimeoutMs, TimeUnit.MILLISECONDS);
+    this.okHttpClient.setReadTimeout(builder.readTimeoutMs, TimeUnit.MILLISECONDS);
+    this.okHttpClient.interceptors().addAll(builder.interceptors);
   }
 
   @Override
-  public String execute(WsRequest wsRequest) {
-    return requestFactory.execute(wsRequest);
+  public String baseUrl() {
+    return baseUrl.url().toExternalForm();
+  }
+
+  public OkHttpClient okHttpClient() {
+    return okHttpClient;
+  }
+
+  @CheckForNull
+  public String userAgent() {
+    return userAgent;
+  }
+
+  @CheckForNull
+  public String credentials() {
+    return credentials;
   }
 
   @Override
-  public <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser) {
-    return requestFactory.execute(wsRequest, protobufParser);
+  public WsResponse call(WsRequest httpRequest) {
+    if (httpRequest instanceof GetRequest) {
+      return get((GetRequest) httpRequest);
+    }
+    if (httpRequest instanceof PostRequest) {
+      return post((PostRequest) httpRequest);
+    }
+    throw new IllegalArgumentException(format("Unsupported implementation: %s", httpRequest.getClass()));
   }
 
-  /**
-   * Create a builder of {@link WsClient}s.
-   */
-  public static Builder newHttpConnector() {
-    return new Builder();
+  private WsResponse get(GetRequest getRequest) {
+    HttpUrl.Builder urlBuilder = prepareUrlBuilder(getRequest);
+    Request.Builder okRequestBuilder = prepareOkRequestBuilder(getRequest, urlBuilder).get();
+    return doCall(okRequestBuilder.build());
   }
 
-  /**
-   * Create a client with default configuration. Use {@link #newHttpConnector()} to define
-   * a custom configuration (credentials, HTTP proxy, HTTP timeouts).
-   */
-  public static HttpConnector newDefaultHttpConnector(String serverUrl) {
-    return newHttpConnector().url(serverUrl).build();
+  private WsResponse post(PostRequest postRequest) {
+    HttpUrl.Builder urlBuilder = prepareUrlBuilder(postRequest);
+    Request.Builder okRequestBuilder = prepareOkRequestBuilder(postRequest, urlBuilder);
+
+    Map<String, PostRequest.Part> parts = postRequest.getParts();
+    if (parts.isEmpty()) {
+      okRequestBuilder.post(RequestBody.create(null, ""));
+    } else {
+      MultipartBuilder body = new MultipartBuilder().type(MultipartBuilder.FORM);
+      for (Map.Entry<String, PostRequest.Part> param : parts.entrySet()) {
+        PostRequest.Part part = param.getValue();
+        body.addPart(
+          Headers.of("Content-Disposition", format("form-data; name=\"%s\"", param.getKey())),
+          RequestBody.create(MediaType.parse(part.getMediaType()), part.getFile()));
+      }
+      okRequestBuilder.post(body.build());
+    }
+
+    return doCall(okRequestBuilder.build());
+  }
+
+  private HttpUrl.Builder prepareUrlBuilder(WsRequest wsRequest) {
+    String path = wsRequest.getPath();
+    HttpUrl.Builder urlBuilder = baseUrl
+      .resolve(path.startsWith("/") ? path.replaceAll("^(/)+", "") : path)
+      .newBuilder();
+    for (Map.Entry<String, String> param : wsRequest.getParams().entrySet()) {
+      urlBuilder.addQueryParameter(param.getKey(), param.getValue());
+    }
+    return urlBuilder;
+  }
+
+  private Request.Builder prepareOkRequestBuilder(WsRequest getRequest, HttpUrl.Builder urlBuilder) {
+    Request.Builder okHttpRequestBuilder = new Request.Builder()
+      .url(urlBuilder.build())
+      .addHeader("Accept", getRequest.getMediaType())
+      .addHeader("Accept-Charset", "UTF-8");
+    if (credentials != null) {
+      okHttpRequestBuilder.header("Authorization", credentials);
+    }
+    if (proxyCredentials != null) {
+      okHttpRequestBuilder.header("Proxy-Authorization", proxyCredentials);
+    }
+    if (userAgent != null) {
+      okHttpRequestBuilder.addHeader("User-Agent", userAgent);
+    }
+    return okHttpRequestBuilder;
+  }
+
+  private HttpResponse doCall(Request okRequest) {
+    Call call = okHttpClient.newCall(okRequest);
+    try {
+      Response okResponse = call.execute();
+      if (!okResponse.isSuccessful()) {
+        throw new HttpException(okRequest.urlString(), okResponse.code(), okResponse.message());
+      }
+      return new HttpResponse(okResponse);
+    } catch (IOException e) {
+      throw new IllegalStateException("Fail to request " + okRequest.urlString(), e);
+    }
   }
 
   public static class Builder {
+    private String url;
+    private String userAgent;
     private String login;
     private String password;
-    private String url;
-    private String proxyHost;
+    private Proxy proxy;
     private String proxyLogin;
     private String proxyPassword;
-    private int proxyPort = 0;
-
     private int connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLISECONDS;
     private int readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLISECONDS;
+    private final List<Interceptor> interceptors = new ArrayList<>();
 
-    private Builder() {
+    /**
+     * Optional User  Agent
+     */
+    public Builder userAgent(@Nullable String userAgent) {
+      this.userAgent = userAgent;
+      return this;
     }
 
     /**
@@ -95,43 +214,26 @@ public class HttpConnector implements WsConnector {
     }
 
     /**
-     * Optional login, for example "admin"
+     * Optional login/password, for example "admin"
      */
-    public Builder login(@Nullable String login) {
+    public Builder credentials(@Nullable String login, @Nullable String password) {
       this.login = login;
-      return this;
-    }
-
-    /**
-     * Optional password related to {@link #login(String)}, for example "admin"
-     */
-    public Builder password(@Nullable String password) {
       this.password = password;
       return this;
     }
 
     /**
-     * Host and port of the optional HTTP proxy
+     * Optional access token, for example {@code "ABCDE"}. Alternative to {@link #credentials(String, String)}
      */
-    public Builder proxy(@Nullable String proxyHost, int proxyPort) {
-      this.proxyHost = proxyHost;
-      this.proxyPort = proxyPort;
-      return this;
-    }
-
-    public Builder proxyLogin(@Nullable String proxyLogin) {
-      this.proxyLogin = proxyLogin;
-      return this;
-    }
-
-    public Builder proxyPassword(@Nullable String proxyPassword) {
-      this.proxyPassword = proxyPassword;
+    public Builder token(@Nullable String token) {
+      this.login = token;
+      this.password = null;
       return this;
     }
 
     /**
      * Sets a specified timeout value, in milliseconds, to be used when opening HTTP connection.
-     * A timeout of zero is interpreted as an infinite timeout. Default value is {@link HttpConnector#DEFAULT_CONNECT_TIMEOUT_MILLISECONDS}
+     * A timeout of zero is interpreted as an infinite timeout. Default value is {@link #DEFAULT_CONNECT_TIMEOUT_MILLISECONDS}
      */
     public Builder connectTimeoutMilliseconds(int i) {
       this.connectTimeoutMs = i;
@@ -140,20 +242,35 @@ public class HttpConnector implements WsConnector {
 
     /**
      * Sets the read timeout to a specified timeout, in milliseconds.
-     * A timeout of zero is interpreted as an infinite timeout. Default value is {@link HttpConnector#DEFAULT_READ_TIMEOUT_MILLISECONDS}
+     * A timeout of zero is interpreted as an infinite timeout. Default value is {@link #DEFAULT_READ_TIMEOUT_MILLISECONDS}
      */
     public Builder readTimeoutMilliseconds(int i) {
       this.readTimeoutMs = i;
       return this;
     }
 
+    public Builder proxy(@Nullable Proxy proxy) {
+      this.proxy = proxy;
+      return this;
+    }
+
+    public Builder proxyCredentials(@Nullable String proxyLogin, @Nullable String proxyPassword) {
+      this.proxyLogin = proxyLogin;
+      this.proxyPassword = proxyPassword;
+      return this;
+    }
+
     /**
-     * Build a new client
+     * Adds a OkHttp interceptor, for example to log request URLs or response errors.
+     * See https://github.com/square/okhttp/wiki/Interceptors
      */
+    public Builder interceptor(Interceptor interceptor) {
+      this.interceptors.add(interceptor);
+      return this;
+    }
+
     public HttpConnector build() {
-      if (url == null || "".equals(url)) {
-        throw new IllegalStateException("Server URL must be set");
-      }
+      checkArgument(!isNullOrEmpty(url), "Server URL is not defined");
       return new HttpConnector(this);
     }
 
index 09aa68e5995f1d90da5df1ab022596616cb9b7f9..393c6232f89e07609c554ae9fb6b9c6ad057b313 100644 (file)
 package org.sonarqube.ws.client;
 
 /**
- * @since 3.6
+ * @since 5.3
  */
 public class HttpException extends RuntimeException {
 
   private final String url;
-  private final int status;
+  private final int code;
 
-  public HttpException(String url, int status, String message) {
-    super(String.format("Error %d on %s : %s", status, url, message));
+  public HttpException(String url, int code, String message) {
+    super(String.format("Error %d on %s : %s", code, url, message));
     this.url = url;
-    this.status = status;
+    this.code = code;
   }
 
   public String url() {
     return url;
   }
 
-  public int status() {
-    return status;
+  /**
+   * @see java.net.HttpURLConnection constants
+   */
+  public int code() {
+    return code;
   }
 }
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpRequestFactory.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpRequestFactory.java
deleted file mode 100644 (file)
index cda4c54..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonarqube.ws.client;
-
-import com.github.kevinsawicki.http.HttpRequest;
-import com.google.common.base.Throwables;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.Message;
-import com.google.protobuf.Parser;
-import java.io.InputStream;
-import java.util.Arrays;
-import javax.annotation.Nullable;
-import org.sonarqube.ws.MediaTypes;
-
-import static java.net.HttpURLConnection.HTTP_CREATED;
-import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
-import static java.net.HttpURLConnection.HTTP_OK;
-
-/**
- * Not an API. Please do not use this class, except maybe for unit tests.
- */
-public class HttpRequestFactory {
-
-  private static final int[] RESPONSE_SUCCESS = {HTTP_OK, HTTP_CREATED, HTTP_NO_CONTENT};
-
-  private final String baseUrl;
-  private String login;
-  private String password;
-  private String proxyHost;
-  private String proxyLogin;
-  private String proxyPassword;
-  private int proxyPort;
-  private int connectTimeoutInMilliseconds;
-  private int readTimeoutInMilliseconds;
-
-  public HttpRequestFactory(String baseUrl) {
-    this.baseUrl = baseUrl;
-  }
-
-  public HttpRequestFactory setLogin(@Nullable String login) {
-    this.login = login;
-    return this;
-  }
-
-  public HttpRequestFactory setPassword(@Nullable String password) {
-    this.password = password;
-    return this;
-  }
-
-  public HttpRequestFactory setProxyHost(@Nullable String proxyHost) {
-    this.proxyHost = proxyHost;
-    return this;
-  }
-
-  public HttpRequestFactory setProxyLogin(@Nullable String proxyLogin) {
-    this.proxyLogin = proxyLogin;
-    return this;
-  }
-
-  public HttpRequestFactory setProxyPassword(@Nullable String proxyPassword) {
-    this.proxyPassword = proxyPassword;
-    return this;
-  }
-
-  public HttpRequestFactory setProxyPort(int proxyPort) {
-    this.proxyPort = proxyPort;
-    return this;
-  }
-
-  public HttpRequestFactory setConnectTimeoutInMilliseconds(int connectTimeoutInMilliseconds) {
-    this.connectTimeoutInMilliseconds = connectTimeoutInMilliseconds;
-    return this;
-  }
-
-  public HttpRequestFactory setReadTimeoutInMilliseconds(int readTimeoutInMilliseconds) {
-    this.readTimeoutInMilliseconds = readTimeoutInMilliseconds;
-    return this;
-  }
-
-  public String getBaseUrl() {
-    return baseUrl;
-  }
-
-  public String getLogin() {
-    return login;
-  }
-
-  public String getPassword() {
-    return password;
-  }
-
-  public String getProxyHost() {
-    return proxyHost;
-  }
-
-  public String getProxyLogin() {
-    return proxyLogin;
-  }
-
-  public String getProxyPassword() {
-    return proxyPassword;
-  }
-
-  public int getProxyPort() {
-    return proxyPort;
-  }
-
-  public int getConnectTimeoutInMilliseconds() {
-    return connectTimeoutInMilliseconds;
-  }
-
-  public int getReadTimeoutInMilliseconds() {
-    return readTimeoutInMilliseconds;
-  }
-
-  public String execute(WsRequest wsRequest) {
-    HttpRequest httpRequest = wsRequestToHttpRequest(wsRequest);
-    return execute(httpRequest);
-  }
-
-  public <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser) {
-    HttpRequest httpRequest = wsRequestToHttpRequest(wsRequest);
-    InputStream response = executeWithStream(httpRequest);
-    try {
-      return protobufParser.parseFrom(response);
-    } catch (InvalidProtocolBufferException e) {
-      Throwables.propagate(e);
-    }
-
-    throw new IllegalStateException("Uncatched exception when parsing protobuf response");
-  }
-
-  private HttpRequest wsRequestToHttpRequest(WsRequest wsRequest) {
-    HttpRequest httpRequest = WsRequest.Method.GET.equals(wsRequest.getMethod())
-      ? HttpRequest.get(buildUrl(wsRequest.getEndpoint()), wsRequest.getParams(), true)
-      : HttpRequest.post(buildUrl(wsRequest.getEndpoint()), wsRequest.getParams(), true);
-    httpRequest = prepare(httpRequest);
-    switch (wsRequest.getMediaType()) {
-      case PROTOBUF:
-        httpRequest.accept(MediaTypes.PROTOBUF);
-        break;
-      case JSON:
-        httpRequest.accept(MediaTypes.JSON);
-        break;
-      case TEXT:
-        httpRequest.accept(MediaTypes.TXT);
-        break;
-      default:
-        httpRequest.accept(MediaTypes.DEFAULT);
-        break;
-    }
-
-    return httpRequest;
-  }
-
-  private String buildUrl(String part) {
-    StringBuilder url = new StringBuilder();
-    url.append(baseUrl);
-    if (!part.startsWith("/")) {
-      url.append('/');
-    }
-    url.append(part);
-    return url.toString();
-  }
-
-  private static String execute(HttpRequest request) {
-    try {
-      checkSuccess(request);
-      return request.body(HttpRequest.CHARSET_UTF8);
-    } catch (HttpRequest.HttpRequestException e) {
-      throw new IllegalStateException("Fail to request " + request.url(), e);
-    }
-  }
-
-  private static InputStream executeWithStream(HttpRequest request) {
-    try {
-      checkSuccess(request);
-      return request.stream();
-    } catch (HttpRequest.HttpRequestException e) {
-      throw new IllegalStateException("Fail to request " + request.url(), e);
-    }
-  }
-
-  private static void checkSuccess(HttpRequest request) {
-    boolean isSuccess = Arrays.binarySearch(RESPONSE_SUCCESS, request.code()) >= 0;
-    if (!isSuccess) {
-      throw new HttpException(request.url().toString(), request.code(), request.body());
-    }
-  }
-
-  private HttpRequest prepare(HttpRequest request) {
-    if (proxyHost != null) {
-      request.useProxy(proxyHost, proxyPort);
-      if (proxyLogin != null) {
-        request.proxyBasic(proxyLogin, proxyPassword);
-      }
-    }
-    request
-      .acceptGzipEncoding()
-      .uncompress(true)
-      .acceptJson()
-      .acceptCharset(HttpRequest.CHARSET_UTF8)
-      .connectTimeout(connectTimeoutInMilliseconds)
-      .readTimeout(readTimeoutInMilliseconds)
-      .trustAllCerts()
-      .trustAllHosts();
-    if (login != null) {
-      request.basic(login, password);
-    }
-    return request;
-  }
-}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpResponse.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpResponse.java
new file mode 100644 (file)
index 0000000..b2e3f27
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import com.squareup.okhttp.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+
+class HttpResponse implements WsResponse {
+
+  private final Response okResponse;
+
+  HttpResponse(Response okResponse) {
+    this.okResponse = okResponse;
+  }
+
+  @Override
+  public String getRequestUrl() {
+    return okResponse.request().urlString();
+  }
+
+  @Override
+  public boolean hasContent() {
+    return okResponse.code() != HTTP_NO_CONTENT;
+  }
+
+  @Override
+  public String getContentType() {
+    return okResponse.header("Content-Type");
+  }
+
+  /**
+   * Get stream of bytes
+   */
+  @Override
+  public InputStream getContentStream() {
+    try {
+      return okResponse.body().byteStream();
+    } catch (IOException e) {
+      throw fail(e);
+    }
+  }
+
+  /**
+   * Get stream of characters, decoded with the charset
+   * of the Content-Type header. If that header is either absent or lacks a
+   * charset, this will attempt to decode the response body as UTF-8.
+   */
+  @Override
+  public Reader getContentReader() {
+    try {
+      return okResponse.body().charStream();
+    } catch (IOException e) {
+      throw fail(e);
+    }
+  }
+
+  @Override
+  public String getContent() {
+    try {
+      return okResponse.body().string();
+    } catch (IOException e) {
+      throw fail(e);
+    }
+  }
+
+  private RuntimeException fail(Exception e) {
+    throw new IllegalStateException("Fail to read response of " + getRequestUrl(), e);
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java
new file mode 100644 (file)
index 0000000..e9e69a7
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client;
+
+import org.sonarqube.ws.client.ce.ComputeEngineService;
+import org.sonarqube.ws.client.component.ComponentsService;
+import org.sonarqube.ws.client.issue.IssuesService;
+import org.sonarqube.ws.client.permission.PermissionsService;
+import org.sonarqube.ws.client.qualityprofile.QualityProfilesService;
+import org.sonarqube.ws.client.usertoken.UserTokensService;
+
+/**
+ * Entry point of the Java Client for SonarQube Web Services
+ *
+ * @since 5.3
+ */
+public class HttpWsClient implements WsClient {
+
+  private final ComputeEngineService ceWsClient;
+  private final PermissionsService permissionsService;
+  private final ComponentsService componentsService;
+  private final QualityProfilesService qualityProfilesService;
+  private final IssuesService issuesService;
+  private final UserTokensService userTokensService;
+  private final WsConnector wsConnector;
+
+  public HttpWsClient(WsConnector wsConnector) {
+    this.wsConnector = wsConnector;
+    this.ceWsClient = new ComputeEngineService(wsConnector);
+    this.permissionsService = new PermissionsService(wsConnector);
+    this.componentsService = new ComponentsService(wsConnector);
+    this.qualityProfilesService = new QualityProfilesService(wsConnector);
+    this.issuesService = new IssuesService(wsConnector);
+    this.userTokensService = new UserTokensService(wsConnector);
+  }
+
+  @Override
+  public WsConnector wsConnector() {
+    return wsConnector;
+  }
+
+  @Override
+  public PermissionsService permissions() {
+    return this.permissionsService;
+  }
+
+  @Override
+  public ComputeEngineService computeEngine() {
+    return ceWsClient;
+  }
+
+  @Override
+  public ComponentsService components() {
+    return componentsService;
+  }
+
+  @Override
+  public QualityProfilesService qualityProfiles() {
+    return qualityProfilesService;
+  }
+
+  @Override
+  public IssuesService issues() {
+    return issuesService;
+  }
+
+  @Override
+  public UserTokensService userTokens() {
+    return userTokensService;
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/MockWsResponse.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/MockWsResponse.java
new file mode 100644 (file)
index 0000000..00411fb
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import com.google.common.base.Throwables;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import org.apache.commons.io.IOUtils;
+import org.sonarqube.ws.MediaTypes;
+
+import static java.util.Objects.requireNonNull;
+
+public class MockWsResponse implements WsResponse {
+
+  private String requestUrl;
+  private byte[] content;
+  private String contentType;
+
+  @Override
+  public String getContentType() {
+    requireNonNull(contentType);
+    return contentType;
+  }
+
+  public MockWsResponse setContentType(String contentType) {
+    this.contentType = contentType;
+    return this;
+  }
+
+  public MockWsResponse setRequestUrl(String requestUrl) {
+    this.requestUrl = requestUrl;
+    return this;
+  }
+
+  public MockWsResponse setContent(InputStream is) {
+    try {
+      return setContent(IOUtils.toByteArray(is));
+    } catch (IOException e) {
+      throw Throwables.propagate(e);
+    }
+  }
+
+  public MockWsResponse setContent(byte[] b) {
+    this.content = b;
+    return this;
+  }
+
+  public MockWsResponse setContent(String s) {
+    this.content = s.getBytes(StandardCharsets.UTF_8);
+    return this;
+  }
+
+  @Override
+  public boolean hasContent() {
+    return content != null;
+  }
+
+  @Override
+  public String getRequestUrl() {
+    requireNonNull(requestUrl);
+    return requestUrl;
+  }
+
+  @Override
+  public InputStream getContentStream() {
+    requireNonNull(content);
+    return new ByteArrayInputStream(content);
+  }
+
+  @Override
+  public Reader getContentReader() {
+    requireNonNull(content);
+    return new StringReader(new String(content, StandardCharsets.UTF_8));
+  }
+
+  @Override
+  public String getContent() {
+    requireNonNull(content);
+    return new String(content, StandardCharsets.UTF_8);
+  }
+
+  public static MockWsResponse createJson(String json) {
+    return new MockWsResponse()
+      .setContentType(MediaTypes.JSON)
+      .setContentType(json);
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/PostRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/PostRequest.java
new file mode 100644 (file)
index 0000000..0c00389
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import java.io.File;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @since 5.3
+ */
+public class PostRequest extends BaseRequest<PostRequest> {
+
+  private final Map<String, Part> parts = new LinkedHashMap<>();
+
+  public PostRequest(String path) {
+    super(path);
+  }
+
+  @Override
+  public Method getMethod() {
+    return Method.POST;
+  }
+
+  public PostRequest setPart(String name, Part part) {
+    this.parts.put(name, part);
+    return this;
+  }
+
+  public Map<String, Part> getParts() {
+    return parts;
+  }
+
+  public static class Part {
+    private final String mediaType;
+    private final File file;
+
+    public Part(String mediaType, File file) {
+      this.mediaType = mediaType;
+      this.file = file;
+    }
+
+    public String getMediaType() {
+      return mediaType;
+    }
+
+    public File getFile() {
+      return file;
+    }
+  }
+
+}
index 39886b57a610e819d81b2b06de0f079616966f72..4b125627b5b0d37240e6b1cae3d1a1e7bc51fce2 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-
 package org.sonarqube.ws.client;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.protobuf.Message;
-import com.google.protobuf.Parser;
-import org.sonarqube.ws.client.component.ComponentsWsClient;
-import org.sonarqube.ws.client.issue.IssuesWsClient;
-import org.sonarqube.ws.client.permission.PermissionsWsClient;
-import org.sonarqube.ws.client.qualityprofile.QualityProfilesWsClient;
-import org.sonarqube.ws.client.usertoken.UserTokensWsClient;
-
-import static org.sonarqube.ws.client.WsRequest.MediaType.PROTOBUF;
+import org.sonarqube.ws.client.ce.ComputeEngineService;
+import org.sonarqube.ws.client.component.ComponentsService;
+import org.sonarqube.ws.client.issue.IssuesService;
+import org.sonarqube.ws.client.permission.PermissionsService;
+import org.sonarqube.ws.client.qualityprofile.QualityProfilesService;
+import org.sonarqube.ws.client.usertoken.UserTokensService;
 
 /**
- * Entry point of the Java Client for SonarQube Web Services.
- * <p/>
- * Example:
- * <pre>
- *   WsClient client = new WsClient(Connector);
- * </pre>
- *
- * @since 5.2
+ * @since 5.3
  */
-public class WsClient {
-
-  @VisibleForTesting
-  final WsConnector wsConnector;
-  private final PermissionsWsClient permissionsWsClient;
-  private final ComponentsWsClient componentsWsClient;
-  private final QualityProfilesWsClient qualityProfilesWsClient;
-  private final IssuesWsClient issuesWsClient;
-  private final UserTokensWsClient userTokensWsClient;
-
-  public WsClient(WsConnector wsConnector) {
-    this.wsConnector = wsConnector;
-    this.permissionsWsClient = new PermissionsWsClient(this);
-    this.componentsWsClient = new ComponentsWsClient(this);
-    this.qualityProfilesWsClient = new QualityProfilesWsClient(this);
-    this.issuesWsClient = new IssuesWsClient(this);
-    userTokensWsClient = new UserTokensWsClient(this);
-  }
-
-  public String execute(WsRequest wsRequest) {
-    return wsConnector.execute(wsRequest);
-  }
+public interface WsClient {
+  ComponentsService components();
 
-  public <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser) {
-    return wsConnector.execute(wsRequest.setMediaType(PROTOBUF), protobufParser);
-  }
+  ComputeEngineService computeEngine();
 
-  public PermissionsWsClient permissionsClient() {
-    return this.permissionsWsClient;
-  }
+  IssuesService issues();
 
-  public ComponentsWsClient componentsWsClient() {
-    return componentsWsClient;
-  }
+  PermissionsService permissions();
 
-  public QualityProfilesWsClient qualityProfilesWsClient() {
-    return qualityProfilesWsClient;
-  }
+  QualityProfilesService qualityProfiles();
 
-  public IssuesWsClient issuesWsClient() {
-    return issuesWsClient;
-  }
+  UserTokensService userTokens();
 
-  public UserTokensWsClient userTokensWsClient() {
-    return userTokensWsClient;
-  }
+  WsConnector wsConnector();
 }
index c4f282744eddb128b1b24ea44f0d474ef2a3604d..d7754d598fc7bd56eb4439db9a00ba9cf6c3fa16 100644 (file)
 
 package org.sonarqube.ws.client;
 
-import com.google.protobuf.Message;
-import com.google.protobuf.Parser;
-
+/**
+ * @since 5.3
+ */
 public interface WsConnector {
-  String execute(WsRequest wsRequest);
 
-  <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser);
+  /**
+   * @throws IllegalStateException if the request could not be executed due to
+   *     a connectivity problem or timeout. Because networks can
+   *     fail during an exchange, it is possible that the remote server
+   *     accepted the request before the failure
+   * @throws HttpException if the response code is not in range [200..300)
+   */
+  WsResponse call(WsRequest wsRequest);
+
+  /**
+   * Server base URL, always with trailing slash, for instance "http://localhost:9000/"
+   */
+  String baseUrl();
+
 }
index 9e4a91f13c481981e060ec9c084a54d686814dbb..dc635e963c74debc8e2718685fad730c8fec7282 100644 (file)
 
 package org.sonarqube.ws.client;
 
-import java.util.HashMap;
 import java.util.Map;
-import javax.annotation.Nullable;
 
-import static java.util.Objects.requireNonNull;
-import static org.sonarqube.ws.client.WsRequest.Method.GET;
-import static org.sonarqube.ws.client.WsRequest.Method.POST;
-
-public class WsRequest {
-  private final Map<String, Object> params = new HashMap<>();
-  private Method method = Method.GET;
-  private MediaType mimeType = MediaType.JSON;
-  private String endpoint;
-
-  private WsRequest(String endpoint) {
-    this.endpoint = endpoint;
-  }
-
-  public static WsRequest newPostRequest(String endpoint) {
-    return new WsRequest(endpoint)
-      .setMethod(POST);
-  }
-
-  public static WsRequest newGetRequest(String endpoint) {
-    return new WsRequest(endpoint)
-      .setMethod(GET);
-  }
-
-  public Method getMethod() {
-    return method;
-  }
-
-  private WsRequest setMethod(Method method) {
-    this.method = method;
-    return this;
-  }
-
-  public MediaType getMediaType() {
-    return mimeType;
-  }
-
-  public WsRequest setMediaType(MediaType type) {
-    requireNonNull(type);
-    this.mimeType = type;
-    return this;
-  }
+/**
+ * @since 5.3
+ */
+public interface WsRequest {
 
-  public WsRequest setParam(String key, @Nullable Object value) {
-    requireNonNull(key, "a WS parameter key cannot be null");
-    if (value != null) {
-      this.params.put(key, value);
-    } else {
-      this.params.remove(key);
-    }
+  Method getMethod();
 
-    return this;
-  }
+  String getPath();
 
-  public String getEndpoint() {
-    return endpoint;
-  }
+  String getMediaType();
 
-  public Map<String, Object> getParams() {
-    return params;
-  }
+  Map<String, String> getParams();
 
-  public enum Method {
+  enum Method {
     GET, POST
   }
-
-  public enum MediaType {
-    PROTOBUF, JSON, TEXT
-  }
 }
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsResponse.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsResponse.java
new file mode 100644 (file)
index 0000000..c6550ff
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * @since 5.3
+ */
+public interface WsResponse {
+
+  boolean hasContent();
+
+  String getContentType();
+
+  String getRequestUrl();
+
+  InputStream getContentStream();
+
+  Reader getContentReader();
+
+  String getContent();
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/ComputeEngineService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/ComputeEngineService.java
new file mode 100644 (file)
index 0000000..465b96d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.ce;
+
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.WsCe;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+public class ComputeEngineService extends BaseService {
+
+  public ComputeEngineService(WsConnector wsConnector) {
+    super(wsConnector, "api/ce");
+  }
+
+  public WsCe.SubmitResponse submit(SubmitWsRequest request) {
+    PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, request.getReport());
+    PostRequest post = new PostRequest(path("submit"))
+      .setParam("projectKey", request.getProjectKey())
+      .setParam("projectName", request.getProjectName())
+      .setParam("projectBranch", request.getProjectBranch())
+      .setPart("report", filePart);
+    return call(post, WsCe.SubmitResponse.parser());
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/SubmitWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/SubmitWsRequest.java
new file mode 100644 (file)
index 0000000..af414ee
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.ce;
+
+import java.io.File;
+import javax.annotation.CheckForNull;
+
+public class SubmitWsRequest {
+
+  private String projectKey;
+  private String projectName;
+  private String projectBranch;
+  private File report;
+
+  public String getProjectKey() {
+    return projectKey;
+  }
+
+  public SubmitWsRequest setProjectKey(String projectKey) {
+    this.projectKey = projectKey;
+    return this;
+  }
+
+  @CheckForNull
+  public String getProjectName() {
+    return projectName;
+  }
+
+  public SubmitWsRequest setProjectName(String projectName) {
+    this.projectName = projectName;
+    return this;
+  }
+
+  @CheckForNull
+  public String getProjectBranch() {
+    return projectBranch;
+  }
+
+  public SubmitWsRequest setProjectBranch(String projectBranch) {
+    this.projectBranch = projectBranch;
+    return this;
+  }
+
+  @CheckForNull
+  public File getReport() {
+    return report;
+  }
+
+  public SubmitWsRequest setReport(File report) {
+    this.report = report;
+    return this;
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/package-info.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/package-info.java
new file mode 100644 (file)
index 0000000..8b9789b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonarqube.ws.client.ce;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java
new file mode 100644 (file)
index 0000000..fcee519
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.component;
+
+import org.sonarqube.ws.WsComponents.SearchWsResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+public class ComponentsService extends BaseService {
+
+  public ComponentsService(WsConnector wsConnector) {
+    super(wsConnector, "api/components");
+  }
+
+  public SearchWsResponse search(SearchWsRequest request) {
+    GetRequest get = new GetRequest(path("search"))
+      .setParam("qualifiers", request.getQualifiers())
+      .setParam("p", request.getPage())
+      .setParam("ps", request.getPageSize())
+      .setParam("q", request.getQuery());
+    return call(get, SearchWsResponse.parser());
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsClient.java
deleted file mode 100644 (file)
index 1b081fe..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client.component;
-
-import org.sonarqube.ws.WsComponents.SearchWsResponse;
-import org.sonarqube.ws.client.WsClient;
-
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-
-public class ComponentsWsClient {
-  private static final String ENDPOINT = "api/components/";
-  private final WsClient wsClient;
-
-  public ComponentsWsClient(WsClient wsClient) {
-    this.wsClient = wsClient;
-  }
-
-  public SearchWsResponse search(SearchWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action("search"))
-        .setParam("qualifiers", request.getQualifiers())
-        .setParam("p", request.getPage())
-        .setParam("ps", request.getPageSize())
-        .setParam("q", request.getQuery()),
-      SearchWsResponse.parser());
-  }
-
-  private static String action(String action) {
-    return ENDPOINT + action;
-  }
-}
index 8fe92d8820fc636b08606056935247063ceceb4e..f28bc23033ddf7ca9eaf7f759d884a0a7ebe6e29 100644 (file)
@@ -26,7 +26,7 @@ import com.google.common.collect.Iterables;
 import java.util.List;
 
 /**
- * @since 3.7
+ * @since 5.3
  */
 public class IssueFilterParameters {
 
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesService.java
new file mode 100644 (file)
index 0000000..343481d
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.issue;
+
+import com.google.common.base.Joiner;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonarqube.ws.Issues.SearchWsResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ACTION_PLANS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASC;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASSIGNED;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASSIGNEES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.AUTHORS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENTS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_KEYS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_ROOTS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_ROOT_UUIDS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_UUIDS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_AFTER;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_AT;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_BEFORE;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_IN_LAST;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.DIRECTORIES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.FACET_MODE;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.FILE_UUIDS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ISSUES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.LANGUAGES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.MODULE_UUIDS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.ON_COMPONENT_ONLY;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.PLANNED;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.PROJECTS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.PROJECT_KEYS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.PROJECT_UUIDS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.REPORTERS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.RESOLUTIONS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.RESOLVED;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.RULES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.SEVERITIES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.STATUSES;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.TAGS;
+
+public class IssuesService extends BaseService {
+  private static final Joiner LIST_TO_PARAMS_STRING = Joiner.on(",").skipNulls();
+
+  public IssuesService(WsConnector wsConnector) {
+    super(wsConnector, "api/issues");
+  }
+
+  public SearchWsResponse search(SearchWsRequest request) {
+    return call(
+      new GetRequest(path("search"))
+        .setParam(ACTION_PLANS, listToParamList(request.getActionPlans()))
+        .setParam(ADDITIONAL_FIELDS, listToParamList(request.getAdditionalFields()))
+        .setParam(ASC, request.getAsc())
+        .setParam(ASSIGNED, request.getAssigned())
+        .setParam(ASSIGNEES, listToParamList(request.getAssignees()))
+        .setParam(AUTHORS, listToParamList(request.getAuthors()))
+        .setParam(COMPONENT_KEYS, listToParamList(request.getComponentKeys()))
+        .setParam(COMPONENT_ROOT_UUIDS, listToParamList(request.getComponentRootUuids()))
+        .setParam(COMPONENT_ROOTS, listToParamList(request.getComponentRoots()))
+        .setParam(COMPONENT_UUIDS, listToParamList(request.getComponentUuids()))
+        .setParam(COMPONENTS, listToParamList(request.getComponents()))
+        .setParam(CREATED_AFTER, request.getCreatedAfter())
+        .setParam(CREATED_AT, request.getCreatedAt())
+        .setParam(CREATED_BEFORE, request.getCreatedBefore())
+        .setParam(CREATED_IN_LAST, request.getCreatedInLast())
+        .setParam(DIRECTORIES, listToParamList(request.getDirectories()))
+        .setParam(FACET_MODE, request.getFacetMode())
+        .setParam("facets", listToParamList(request.getFacets()))
+        .setParam(FILE_UUIDS, listToParamList(request.getFileUuids()))
+        .setParam(ISSUES, listToParamList(request.getIssues()))
+        .setParam(LANGUAGES, listToParamList(request.getLanguages()))
+        .setParam(MODULE_UUIDS, listToParamList(request.getModuleUuids()))
+        .setParam(ON_COMPONENT_ONLY, request.getOnComponentOnly())
+        .setParam("p", request.getPage())
+        .setParam("ps", request.getPageSize())
+        .setParam(PLANNED, request.getPlanned())
+        .setParam(PROJECT_KEYS, listToParamList(request.getProjectKeys()))
+        .setParam(PROJECT_UUIDS, listToParamList(request.getProjectUuids()))
+        .setParam(PROJECTS, listToParamList(request.getProjects()))
+        .setParam(REPORTERS, listToParamList(request.getReporters()))
+        .setParam(RESOLUTIONS, listToParamList(request.getResolutions()))
+        .setParam(RESOLVED, request.getResolved())
+        .setParam(RULES, listToParamList(request.getRules()))
+        .setParam("s", request.getSort())
+        .setParam(SEVERITIES, listToParamList(request.getSeverities()))
+        .setParam(STATUSES, listToParamList(request.getStatuses()))
+        .setParam(TAGS, listToParamList(request.getTags())),
+      SearchWsResponse.parser());
+  }
+
+  @CheckForNull
+  private static String listToParamList(@Nullable List<String> strings) {
+    return strings == null
+      ? null
+      : LIST_TO_PARAMS_STRING.join(strings);
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsClient.java
deleted file mode 100644 (file)
index 7070897..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client.issue;
-
-import com.google.common.base.Joiner;
-import java.util.List;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonarqube.ws.Issues.SearchWsResponse;
-import org.sonarqube.ws.client.WsClient;
-
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ACTION_PLANS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ADDITIONAL_FIELDS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASC;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASSIGNED;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASSIGNEES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.AUTHORS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENTS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_KEYS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_ROOTS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_ROOT_UUIDS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_UUIDS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_AFTER;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_AT;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_BEFORE;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_IN_LAST;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.DIRECTORIES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.FACET_MODE;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.FILE_UUIDS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ISSUES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.LANGUAGES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.MODULE_UUIDS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.ON_COMPONENT_ONLY;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.PLANNED;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.PROJECTS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.PROJECT_KEYS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.PROJECT_UUIDS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.REPORTERS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.RESOLUTIONS;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.RESOLVED;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.RULES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.SEVERITIES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.STATUSES;
-import static org.sonarqube.ws.client.issue.IssueFilterParameters.TAGS;
-
-public class IssuesWsClient {
-  private static final Joiner LIST_TO_PARAMS_STRING = Joiner.on(",").skipNulls();
-  private final WsClient wsClient;
-
-  public IssuesWsClient(WsClient wsClient) {
-    this.wsClient = wsClient;
-  }
-
-  public SearchWsResponse search(SearchWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action("search"))
-        .setParam(ACTION_PLANS, listToParamList(request.getActionPlans()))
-        .setParam(ADDITIONAL_FIELDS, listToParamList(request.getAdditionalFields()))
-        .setParam(ASC, request.getAsc())
-        .setParam(ASSIGNED, request.getAssigned())
-        .setParam(ASSIGNEES, listToParamList(request.getAssignees()))
-        .setParam(AUTHORS, listToParamList(request.getAuthors()))
-        .setParam(COMPONENT_KEYS, listToParamList(request.getComponentKeys()))
-        .setParam(COMPONENT_ROOT_UUIDS, listToParamList(request.getComponentRootUuids()))
-        .setParam(COMPONENT_ROOTS, listToParamList(request.getComponentRoots()))
-        .setParam(COMPONENT_UUIDS, listToParamList(request.getComponentUuids()))
-        .setParam(COMPONENTS, listToParamList(request.getComponents()))
-        .setParam(CREATED_AFTER, request.getCreatedAfter())
-        .setParam(CREATED_AT, request.getCreatedAt())
-        .setParam(CREATED_BEFORE, request.getCreatedBefore())
-        .setParam(CREATED_IN_LAST, request.getCreatedInLast())
-        .setParam(DIRECTORIES, listToParamList(request.getDirectories()))
-        .setParam(FACET_MODE, request.getFacetMode())
-        .setParam("facets", listToParamList(request.getFacets()))
-        .setParam(FILE_UUIDS, listToParamList(request.getFileUuids()))
-        .setParam(ISSUES, listToParamList(request.getIssues()))
-        .setParam(LANGUAGES, listToParamList(request.getLanguages()))
-        .setParam(MODULE_UUIDS, listToParamList(request.getModuleUuids()))
-        .setParam(ON_COMPONENT_ONLY, request.getOnComponentOnly())
-        .setParam("p", request.getPage())
-        .setParam("ps", request.getPageSize())
-        .setParam(PLANNED, request.getPlanned())
-        .setParam(PROJECT_KEYS, listToParamList(request.getProjectKeys()))
-        .setParam(PROJECT_UUIDS, listToParamList(request.getProjectUuids()))
-        .setParam(PROJECTS, listToParamList(request.getProjects()))
-        .setParam(REPORTERS, listToParamList(request.getReporters()))
-        .setParam(RESOLUTIONS, listToParamList(request.getResolutions()))
-        .setParam(RESOLVED, request.getResolved())
-        .setParam(RULES, listToParamList(request.getRules()))
-        .setParam("s", request.getSort())
-        .setParam(SEVERITIES, listToParamList(request.getSeverities()))
-        .setParam(STATUSES, listToParamList(request.getStatuses()))
-        .setParam(TAGS, listToParamList(request.getTags())),
-      SearchWsResponse.parser());
-  }
-
-  private static String action(String action) {
-    return "api/issues/" + action;
-  }
-
-  @CheckForNull
-  private static String listToParamList(@Nullable List<String> strings) {
-    return strings == null
-      ? null
-      : LIST_TO_PARAMS_STRING.join(strings);
-  }
-}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsService.java
new file mode 100644 (file)
index 0000000..89f18f8
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.permission;
+
+import org.sonarqube.ws.WsPermissions;
+import org.sonarqube.ws.WsPermissions.CreateTemplateWsResponse;
+import org.sonarqube.ws.WsPermissions.SearchProjectPermissionsWsResponse;
+import org.sonarqube.ws.WsPermissions.SearchTemplatesWsResponse;
+import org.sonarqube.ws.WsPermissions.UpdateTemplateWsResponse;
+import org.sonarqube.ws.WsPermissions.UsersWsResponse;
+import org.sonarqube.ws.WsPermissions.WsSearchGlobalPermissionsResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_DESCRIPTION;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ID;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_NAME;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY_PATTERN;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_QUALIFIER;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME;
+import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_USER_LOGIN;
+
+public class PermissionsService extends BaseService {
+
+  public PermissionsService(WsConnector wsConnector) {
+    super(wsConnector, PermissionsWsParameters.CONTROLLER);
+  }
+
+  public WsPermissions.WsGroupsResponse groups(GroupsWsRequest request) {
+    GetRequest get = new GetRequest(path("groups"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
+      .setParam("p", request.getPage())
+      .setParam("ps", request.getPageSize())
+      .setParam("selected", request.getSelected())
+      .setParam("q", request.getQuery());
+    return call(get, WsPermissions.WsGroupsResponse.parser());
+  }
+
+  public void addGroup(AddGroupWsRequest request) {
+    call(new PostRequest(path("add_group"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
+      .setParam(PARAM_GROUP_ID, request.getGroupId())
+      .setParam(PARAM_GROUP_NAME, request.getGroupName()));
+  }
+
+  public void addGroupToTemplate(AddGroupToTemplateWsRequest request) {
+    call(new PostRequest(path("add_group_to_template"))
+      .setParam(PARAM_GROUP_ID, request.getGroupId())
+      .setParam(PARAM_GROUP_NAME, request.getGroupName())
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public void addUser(AddUserWsRequest request) {
+    call(new PostRequest(path("add_user"))
+      .setParam(PARAM_USER_LOGIN, request.getLogin())
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey()));
+  }
+
+  public void addUserToTemplate(AddUserToTemplateWsRequest request) {
+    call(new PostRequest(path("add_user_to_template"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_USER_LOGIN, request.getLogin())
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public void applyTemplate(ApplyTemplateWsRequest request) {
+    call(new PostRequest(path("apply_template"))
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public CreateTemplateWsResponse createTemplate(CreateTemplateWsRequest request) {
+    PostRequest post = new PostRequest(path("create_template"))
+      .setParam(PARAM_NAME, request.getName())
+      .setParam(PARAM_DESCRIPTION, request.getDescription())
+      .setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern());
+    return call(post, CreateTemplateWsResponse.parser());
+  }
+
+  public void deleteTemplate(DeleteTemplateWsRequest request) {
+    call(new PostRequest(path("delete_template"))
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public void removeGroup(RemoveGroupWsRequest request) {
+    call(new PostRequest(path("remove_group"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_GROUP_ID, request.getGroupId())
+      .setParam(PARAM_GROUP_NAME, request.getGroupName())
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey()));
+  }
+
+  public void removeGroupFromTemplate(RemoveGroupFromTemplateWsRequest request) {
+    call(new PostRequest(path("remove_group_from_template"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_GROUP_ID, request.getGroupId())
+      .setParam(PARAM_GROUP_NAME, request.getGroupName())
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public void removeUser(RemoveUserWsRequest request) {
+    call(new PostRequest(path("remove_user"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_USER_LOGIN, request.getLogin())
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey()));
+  }
+
+  public void removeUserFromTemplate(RemoveUserFromTemplateWsRequest request) {
+    call(new PostRequest(path("remove_user_from_template"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_USER_LOGIN, request.getLogin())
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public WsSearchGlobalPermissionsResponse searchGlobalPermissions() {
+    GetRequest get = new GetRequest(path("search_global_permissions"));
+    return call(get, WsSearchGlobalPermissionsResponse.parser());
+  }
+
+  public SearchProjectPermissionsWsResponse searchProjectPermissions(SearchProjectPermissionsWsRequest request) {
+    GetRequest get = new GetRequest(path("search_project_permissions"))
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
+      .setParam(PARAM_QUALIFIER, request.getQualifier())
+      .setParam("p", request.getPage())
+      .setParam("ps", request.getPageSize())
+      .setParam("q", request.getQuery());
+    return call(get, SearchProjectPermissionsWsResponse.parser());
+  }
+
+  public SearchTemplatesWsResponse searchTemplates(SearchTemplatesWsRequest request) {
+    GetRequest get = new GetRequest(path("search_templates"))
+      .setParam("q", request.getQuery());
+    return call(get, SearchTemplatesWsResponse.parser());
+  }
+
+  public void setDefaultTemplate(SetDefaultTemplateWsRequest request) {
+    call(new PostRequest(path("set_default_template"))
+      .setParam(PARAM_QUALIFIER, request.getQualifier())
+      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
+      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
+  }
+
+  public UpdateTemplateWsResponse updateTemplate(UpdateTemplateWsRequest request) {
+    return call(new PostRequest(path("update_template"))
+      .setParam(PARAM_DESCRIPTION, request.getDescription())
+      .setParam(PARAM_ID, request.getId())
+      .setParam(PARAM_NAME, request.getName())
+      .setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()), UpdateTemplateWsResponse.parser());
+  }
+
+  public UsersWsResponse users(UsersWsRequest request) {
+    return call(new GetRequest(path("users"))
+      .setParam(PARAM_PERMISSION, request.getPermission())
+      .setParam(PARAM_PROJECT_ID, request.getProjectId())
+      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
+      .setParam("selected", request.getSelected())
+      .setParam("p", request.getPage())
+      .setParam("ps", request.getPageSize())
+      .setParam("q", request.getQuery()), UsersWsResponse.parser());
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/permission/PermissionsWsClient.java
deleted file mode 100644 (file)
index bb14ee0..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client.permission;
-
-import org.sonarqube.ws.WsPermissions;
-import org.sonarqube.ws.WsPermissions.CreateTemplateWsResponse;
-import org.sonarqube.ws.WsPermissions.SearchProjectPermissionsWsResponse;
-import org.sonarqube.ws.WsPermissions.SearchTemplatesWsResponse;
-import org.sonarqube.ws.WsPermissions.UpdateTemplateWsResponse;
-import org.sonarqube.ws.WsPermissions.UsersWsResponse;
-import org.sonarqube.ws.WsPermissions.WsSearchGlobalPermissionsResponse;
-import org.sonarqube.ws.client.WsClient;
-
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-import static org.sonarqube.ws.client.WsRequest.newPostRequest;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_DESCRIPTION;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ID;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_NAME;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY_PATTERN;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_QUALIFIER;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME;
-import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_USER_LOGIN;
-
-public class PermissionsWsClient {
-  private final WsClient wsClient;
-
-  public PermissionsWsClient(WsClient wsClient) {
-    this.wsClient = wsClient;
-  }
-
-  public WsPermissions.WsGroupsResponse groups(GroupsWsRequest request) {
-    return wsClient.execute(newGetRequest(action("groups"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_PROJECT_ID, request.getProjectId())
-      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
-      .setParam("p", request.getPage())
-      .setParam("ps", request.getPageSize())
-      .setParam("selected", request.getSelected())
-      .setParam("q", request.getQuery()),
-      WsPermissions.WsGroupsResponse.parser());
-  }
-
-  public void addGroup(AddGroupWsRequest request) {
-    wsClient.execute(newPostRequest(action("add_group"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_PROJECT_ID, request.getProjectId())
-      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
-      .setParam(PARAM_GROUP_ID, request.getGroupId())
-      .setParam(PARAM_GROUP_NAME, request.getGroupName()));
-  }
-
-  public void addGroupToTemplate(AddGroupToTemplateWsRequest request) {
-    wsClient.execute(newPostRequest(action("add_group_to_template"))
-      .setParam(PARAM_GROUP_ID, request.getGroupId())
-      .setParam(PARAM_GROUP_NAME, request.getGroupName())
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public void addUser(AddUserWsRequest request) {
-    wsClient.execute(newPostRequest(action("add_user"))
-      .setParam(PARAM_USER_LOGIN, request.getLogin())
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_PROJECT_ID, request.getProjectId())
-      .setParam(PARAM_PROJECT_KEY, request.getProjectKey()));
-  }
-
-  public void addUserToTemplate(AddUserToTemplateWsRequest request) {
-    wsClient.execute(newPostRequest(action("add_user_to_template"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_USER_LOGIN, request.getLogin())
-      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public void applyTemplate(ApplyTemplateWsRequest request) {
-    wsClient.execute(newPostRequest(action("apply_template"))
-      .setParam(PARAM_PROJECT_ID, request.getProjectId())
-      .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
-      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public CreateTemplateWsResponse createTemplate(CreateTemplateWsRequest request) {
-    return wsClient.execute(newPostRequest(
-      action("create_template"))
-        .setParam(PARAM_NAME, request.getName())
-        .setParam(PARAM_DESCRIPTION, request.getDescription())
-        .setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()),
-      CreateTemplateWsResponse.parser());
-  }
-
-  public void deleteTemplate(DeleteTemplateWsRequest request) {
-    wsClient.execute(newPostRequest(action("delete_template"))
-      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public void removeGroup(RemoveGroupWsRequest request) {
-    wsClient.execute(newPostRequest(action("remove_group"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_GROUP_ID, request.getGroupId())
-      .setParam(PARAM_GROUP_NAME, request.getGroupName())
-      .setParam(PARAM_PROJECT_ID, request.getProjectId())
-      .setParam(PARAM_PROJECT_KEY, request.getProjectKey()));
-  }
-
-  public void removeGroupFromTemplate(RemoveGroupFromTemplateWsRequest request) {
-    wsClient.execute(newPostRequest(action("remove_group_from_template"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_GROUP_ID, request.getGroupId())
-      .setParam(PARAM_GROUP_NAME, request.getGroupName())
-      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public void removeUser(RemoveUserWsRequest request) {
-    wsClient.execute(newPostRequest(action("remove_user"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_USER_LOGIN, request.getLogin())
-      .setParam(PARAM_PROJECT_ID, request.getProjectId())
-      .setParam(PARAM_PROJECT_KEY, request.getProjectKey()));
-  }
-
-  public void removeUserFromTemplate(RemoveUserFromTemplateWsRequest request) {
-    wsClient.execute(newPostRequest(action("remove_user_from_template"))
-      .setParam(PARAM_PERMISSION, request.getPermission())
-      .setParam(PARAM_USER_LOGIN, request.getLogin())
-      .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-      .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public WsSearchGlobalPermissionsResponse searchGlobalPermissions() {
-    return wsClient.execute(
-      newGetRequest(action("search_global_permissions")),
-      WsSearchGlobalPermissionsResponse.parser());
-  }
-
-  public SearchProjectPermissionsWsResponse searchProjectPermissions(SearchProjectPermissionsWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action("search_project_permissions"))
-        .setParam(PARAM_PROJECT_ID, request.getProjectId())
-        .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
-        .setParam(PARAM_QUALIFIER, request.getQualifier())
-        .setParam("p", request.getPage())
-        .setParam("ps", request.getPageSize())
-        .setParam("q", request.getQuery()),
-      SearchProjectPermissionsWsResponse.parser());
-  }
-
-  public SearchTemplatesWsResponse searchTemplates(SearchTemplatesWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action("search_templates"))
-        .setParam("q", request.getQuery()),
-      SearchTemplatesWsResponse.parser());
-  }
-
-  public void setDefaultTemplate(SetDefaultTemplateWsRequest request) {
-    wsClient.execute(
-      newPostRequest(action("set_default_template"))
-        .setParam(PARAM_QUALIFIER, request.getQualifier())
-        .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
-        .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()));
-  }
-
-  public UpdateTemplateWsResponse updateTemplate(UpdateTemplateWsRequest request) {
-    return wsClient.execute(
-      newPostRequest(action("update_template"))
-        .setParam(PARAM_DESCRIPTION, request.getDescription())
-        .setParam(PARAM_ID, request.getId())
-        .setParam(PARAM_NAME, request.getName())
-        .setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()),
-      UpdateTemplateWsResponse.parser());
-  }
-
-  public UsersWsResponse users(UsersWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action("users"))
-        .setParam(PARAM_PERMISSION, request.getPermission())
-        .setParam(PARAM_PROJECT_ID, request.getProjectId())
-        .setParam(PARAM_PROJECT_KEY, request.getProjectKey())
-        .setParam("selected", request.getSelected())
-        .setParam("p", request.getPage())
-        .setParam("ps", request.getPageSize())
-        .setParam("q", request.getQuery()),
-      UsersWsResponse.parser());
-  }
-
-  private static String action(String action) {
-    return PermissionsWsParameters.ENDPOINT + "/" + action;
-  }
-}
index 1557e97b632a9dd20bbce617c0b48410e1c8657b..edd9f6b3a4aedc3ccaa31a7253fa8c287ee03663 100644 (file)
@@ -21,7 +21,7 @@
 package org.sonarqube.ws.client.permission;
 
 public class PermissionsWsParameters {
-  public static final String ENDPOINT = "api/permissions";
+  public static final String CONTROLLER = "api/permissions";
 
   public static final String PARAM_PERMISSION = "permission";
   public static final String PARAM_GROUP_NAME = "groupName";
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java
new file mode 100644 (file)
index 0000000..505ac3e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.qualityprofile;
+
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+public class QualityProfilesService extends BaseService {
+
+  public QualityProfilesService(WsConnector wsConnector) {
+    super(wsConnector, "api/qualityprofiles");
+  }
+
+  public SearchWsResponse search(SearchWsRequest request) {
+    return call(
+      new GetRequest(path("search"))
+        .setParam("defaults", request.getDefaults())
+        .setParam("language", request.getLanguage())
+        .setParam("profileName", request.getProfileName())
+        .setParam("projectKey", request.getProjectKey()),
+      SearchWsResponse.parser());
+  }
+
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesWsClient.java
deleted file mode 100644 (file)
index dcbdec4..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client.qualityprofile;
-
-import org.sonarqube.ws.QualityProfiles.SearchWsResponse;
-import org.sonarqube.ws.client.WsClient;
-
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-
-public class QualityProfilesWsClient {
-  private final WsClient wsClient;
-
-  public QualityProfilesWsClient(WsClient wsClient) {
-    this.wsClient = wsClient;
-  }
-
-  public SearchWsResponse search(SearchWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action("search"))
-        .setParam("defaults", request.getDefaults())
-        .setParam("language", request.getLanguage())
-        .setParam("profileName", request.getProfileName())
-        .setParam("projectKey", request.getProjectKey()),
-      SearchWsResponse.parser());
-  }
-
-  private static String action(String action) {
-    return "api/qualityprofiles/" + action;
-  }
-}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensService.java
new file mode 100644 (file)
index 0000000..ce9a39d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonarqube.ws.client.usertoken;
+
+import org.sonarqube.ws.WsUserTokens.GenerateWsResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_GENERATE;
+import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.CONTROLLER;
+import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.PARAM_LOGIN;
+import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.PARAM_NAME;
+
+public class UserTokensService extends BaseService {
+
+  public UserTokensService(WsConnector wsConnector) {
+    super(wsConnector, CONTROLLER);
+  }
+
+  public GenerateWsResponse generate(GenerateWsRequest request) {
+    return call(
+      new PostRequest(path(ACTION_GENERATE))
+        .setParam(PARAM_LOGIN, request.getLogin())
+        .setParam(PARAM_NAME, request.getName()),
+      GenerateWsResponse.parser());
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/UserTokensWsClient.java
deleted file mode 100644 (file)
index fdd7eec..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client.usertoken;
-
-import org.sonarqube.ws.WsComponents.SearchWsResponse;
-import org.sonarqube.ws.WsUserTokens.GenerateWsResponse;
-import org.sonarqube.ws.client.WsClient;
-
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-import static org.sonarqube.ws.client.WsRequest.newPostRequest;
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_GENERATE;
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_REVOKE;
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_SEARCH;
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.PARAM_LOGIN;
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.PARAM_NAME;
-import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.USER_TOKENS_ENDPOINT;
-
-public class UserTokensWsClient {
-  private static final String SLASH = "/";
-  private final WsClient wsClient;
-
-  public UserTokensWsClient(WsClient wsClient) {
-    this.wsClient = wsClient;
-  }
-
-  public GenerateWsResponse generate(GenerateWsRequest request) {
-    return wsClient.execute(
-      newPostRequest(action(ACTION_GENERATE))
-        .setParam(PARAM_LOGIN, request.getLogin())
-        .setParam(PARAM_NAME, request.getName()),
-      GenerateWsResponse.parser());
-  }
-
-  public void revoke(RevokeWsRequest request) {
-    wsClient.execute(
-      newPostRequest(action(ACTION_REVOKE))
-        .setParam(PARAM_LOGIN, request.getLogin())
-        .setParam(PARAM_NAME, request.getName()));
-  }
-
-  public SearchWsResponse search(SearchWsRequest request) {
-    return wsClient.execute(
-      newGetRequest(action(ACTION_SEARCH))
-        .setParam(PARAM_LOGIN, request.getLogin()),
-      SearchWsResponse.parser()
-    );
-  }
-
-  private static String action(String action) {
-    return USER_TOKENS_ENDPOINT + SLASH + action;
-  }
-}
index 203ee5243a851a5fa8f9413b462f1311c2cf3fcc..a82d6e0989cc1723167419043989ac49167b28fa 100644 (file)
@@ -21,7 +21,7 @@
 package org.sonarqube.ws.client.usertoken;
 
 public class UserTokensWsParameters {
-  public static final String USER_TOKENS_ENDPOINT = "api/user_tokens";
+  public static final String CONTROLLER = "api/user_tokens";
   public static final String ACTION_GENERATE = "generate";
   public static final String ACTION_REVOKE = "revoke";
   public static final String ACTION_SEARCH = "search";
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/BaseRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/BaseRequestTest.java
new file mode 100644 (file)
index 0000000..42fcf47
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonarqube.ws.MediaTypes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.data.MapEntry.entry;
+
+public class BaseRequestTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  FakeRequest underTest = new FakeRequest("api/foo");
+
+  @Test
+  public void test_defaults() {
+    assertThat(underTest.getMethod()).isEqualTo(WsRequest.Method.GET);
+    assertThat(underTest.getParams()).isEmpty();
+    assertThat(underTest.getMediaType()).isEqualTo(MediaTypes.JSON);
+    assertThat(underTest.getPath()).isEqualTo("api/foo");
+  }
+
+  @Test
+  public void setMediaType() {
+    underTest.setMediaType(MediaTypes.PROTOBUF);
+    assertThat(underTest.getMediaType()).isEqualTo(MediaTypes.PROTOBUF);
+  }
+
+  @Test
+  public void keep_order_of_params() {
+    assertThat(underTest.getParams()).isEmpty();
+
+    underTest.setParam("keyB", "b");
+    assertThat(underTest.getParams()).containsExactly(entry("keyB", "b"));
+
+    underTest.setParam("keyA", "a");
+    assertThat(underTest.getParams()).containsExactly(entry("keyB", "b"), entry("keyA", "a"));
+  }
+
+  @Test
+  public void null_param_value() {
+    underTest.setParam("key", null);
+    assertThat(underTest.getParams()).isEmpty();
+  }
+
+  @Test
+  public void fail_if_null_param_key() {
+    expectedException.expect(IllegalArgumentException.class);
+    underTest.setParam(null, "val");
+  }
+
+  private static class FakeRequest extends BaseRequest<FakeRequest> {
+    FakeRequest(String path) {
+      super(path);
+    }
+
+    @Override
+    public Method getMethod() {
+      return Method.GET;
+    }
+  }
+}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/BaseServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/BaseServiceTest.java
new file mode 100644 (file)
index 0000000..2d92809
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.Testing;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BaseServiceTest {
+
+  WsConnector wsConnector = mock(WsConnector.class);
+
+  @Test
+  public void test_call() throws Exception {
+    new BaseService(wsConnector, "api/issues") {
+
+      public void test() throws IOException {
+        GetRequest get = new GetRequest(path("issue")).setMediaType(MediaTypes.JSON);
+        when(wsConnector.call(get)).thenReturn(new MockWsResponse().setContent("ok"));
+
+        WsResponse response = call(get);
+
+        assertThat(response.getContent()).isEqualTo("ok");
+      }
+
+    }.test();
+  }
+
+  @Test
+  public void call_and_convert_protobuf() {
+    new BaseService(wsConnector, "api/issues") {
+
+      public void test() {
+        GetRequest get = new GetRequest(path("issue")).setParam("key", "ABC");
+        when(wsConnector.call(get)).thenReturn(newProtobufFakeResponse());
+
+        Testing.Fake message = call(get, Testing.Fake.parser());
+
+        assertThat(message.getLabel()).isEqualTo("ok");
+        assertThat(get.getPath()).isEqualTo("api/issues/issue");
+        // media type automatically set to protobuf
+        assertThat(get.getMediaType()).isEqualTo(MediaTypes.PROTOBUF);
+      }
+
+    }.test();
+  }
+
+  @Test
+  public void fail_to_parse_protobuf_response() {
+    new BaseService(wsConnector, "api/issues") {
+
+      public void test() {
+        GetRequest get = new GetRequest(path("issue")).setParam("key", "ABC");
+        when(wsConnector.call(get)).thenReturn(MockWsResponse.createJson("{}").setRequestUrl("http://local/api/issues/issue?key=ABC"));
+
+        try {
+          call(get, Testing.Fake.parser());
+          fail();
+        } catch (IllegalStateException e) {
+          assertThat(e).hasMessage("Fail to parse protobuf response of http://local/api/issues/issue?key=ABC");
+        }
+      }
+    }.test();
+  }
+
+  private static WsResponse newProtobufFakeResponse() {
+    Testing.Fake message = Testing.Fake.newBuilder().setLabel("ok").build();
+    return new MockWsResponse().setContent(message.toByteArray());
+  }
+}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/HttpConnectorTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/HttpConnectorTest.java
new file mode 100644 (file)
index 0000000..79362ea
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import com.squareup.okhttp.Interceptor;
+import com.squareup.okhttp.Response;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonarqube.ws.MediaTypes;
+
+import static com.squareup.okhttp.Credentials.basic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class HttpConnectorTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  MockWebServer server;
+  String serverUrl;
+
+  @Before
+  public void setUp() throws Exception {
+    server = new MockWebServer();
+    server.start();
+    serverUrl = server.url("").url().toString();
+  }
+
+  @Test
+  public void test_default_settings() throws Exception {
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build();
+    assertThat(underTest.baseUrl()).isEqualTo(serverUrl);
+    GetRequest request = new GetRequest("api/issues/search").setMediaType(MediaTypes.PROTOBUF);
+    WsResponse response = underTest.call(request);
+
+    // verify default timeouts on client
+    assertThat(underTest.okHttpClient().getConnectTimeout()).isEqualTo(HttpConnector.DEFAULT_CONNECT_TIMEOUT_MILLISECONDS);
+    assertThat(underTest.okHttpClient().getReadTimeout()).isEqualTo(HttpConnector.DEFAULT_READ_TIMEOUT_MILLISECONDS);
+
+    // verify response
+    assertThat(response.hasContent()).isTrue();
+    assertThat(response.getContent()).isEqualTo("hello, world!");
+
+    // verify the request received by server
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getMethod()).isEqualTo("GET");
+    assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search");
+    assertThat(recordedRequest.getHeader("Accept")).isEqualTo(MediaTypes.PROTOBUF);
+    assertThat(recordedRequest.getHeader("Accept-Charset")).isEqualTo("UTF-8");
+    assertThat(recordedRequest.getHeader("User-Agent")).startsWith("okhttp/");
+    // compression is handled by OkHttp
+    assertThat(recordedRequest.getHeader("Accept-Encoding")).isEqualTo("gzip");
+  }
+
+  @Test
+  public void use_basic_authentication() throws Exception {
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .credentials("theLogin", "thePassword")
+      .build();
+
+    GetRequest request = new GetRequest("api/issues/search");
+    underTest.call(request);
+
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theLogin", "thePassword"));
+  }
+
+  @Test
+  public void use_basic_authentication_with_null_password() throws Exception {
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .credentials("theLogin", null)
+      .build();
+
+    GetRequest request = new GetRequest("api/issues/search");
+    underTest.call(request);
+
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theLogin", ""));
+  }
+
+  /**
+   * Access token replaces the couple {login,password} and is sent through
+   * the login field
+   */
+  @Test
+  public void use_access_token() throws Exception {
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .token("theToken")
+      .build();
+
+    GetRequest request = new GetRequest("api/issues/search");
+    underTest.call(request);
+
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theToken", ""));
+  }
+
+  @Test
+  public void use_proxy_authentication() throws Exception {
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .proxyCredentials("theProxyLogin", "theProxyPassword")
+      .build();
+
+    GetRequest request = new GetRequest("api/issues/search");
+    underTest.call(request);
+
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getHeader("Proxy-Authorization")).isEqualTo(basic("theProxyLogin", "theProxyPassword"));
+  }
+
+  @Test
+  public void override_timeouts() {
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .readTimeoutMilliseconds(42)
+      .connectTimeoutMilliseconds(74)
+      .build();
+
+    assertThat(underTest.okHttpClient().getReadTimeout()).isEqualTo(42);
+    assertThat(underTest.okHttpClient().getConnectTimeout()).isEqualTo(74);
+  }
+
+  @Test
+  public void send_user_agent() throws Exception {
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .userAgent("Maven Plugin/2.3")
+      .build();
+
+    underTest.call(new GetRequest("api/issues/search"));
+
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("Maven Plugin/2.3");
+  }
+
+  @Test
+  public void fail_if_unknown_implementation_of_request() {
+    HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build();
+    try {
+      underTest.call(mock(WsRequest.class));
+      fail();
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageContaining("Unsupported implementation: ");
+    }
+  }
+
+  @Test
+  public void send_post_request() throws Exception {
+    answerHelloWorld();
+    PostRequest request = new PostRequest("api/issues/search")
+      .setParam("severity", "MAJOR")
+      .setMediaType(MediaTypes.PROTOBUF);
+
+    HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build();
+    WsResponse response = underTest.call(request);
+
+    // verify response
+    assertThat(response.hasContent()).isTrue();
+    assertThat(response.getContent()).isEqualTo("hello, world!");
+
+    // verify the request received by server
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getMethod()).isEqualTo("POST");
+    assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search?severity=MAJOR");
+  }
+
+  @Test
+  public void upload_file() throws Exception {
+    answerHelloWorld();
+    File reportFile = temp.newFile();
+    FileUtils.write(reportFile, "the report content");
+    PostRequest request = new PostRequest("api/report/upload")
+      .setParam("project", "theKey")
+      .setPart("report", new PostRequest.Part(MediaTypes.TXT, reportFile))
+      .setMediaType(MediaTypes.PROTOBUF);
+
+    HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build();
+    WsResponse response = underTest.call(request);
+
+    assertThat(response.hasContent()).isTrue();
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getMethod()).isEqualTo("POST");
+    assertThat(recordedRequest.getPath()).isEqualTo("/api/report/upload?project=theKey");
+    String body = IOUtils.toString(recordedRequest.getBody().inputStream());
+    assertThat(body)
+      .contains("Content-Disposition: form-data; name=\"report\"")
+      .contains("Content-Type: text/plain")
+      .contains("the report content");
+  }
+
+  @Test
+  public void http_error() throws Exception {
+    server.enqueue(new MockResponse().setResponseCode(404));
+    PostRequest request = new PostRequest("api/issues/search");
+    HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build();
+
+    try {
+      underTest.call(request);
+      fail();
+    } catch (HttpException e) {
+      assertThat(e.code()).isEqualTo(404);
+
+    }
+  }
+
+  @Test
+  public void intercept_request_and_response() {
+    final AtomicBoolean called = new AtomicBoolean(false);
+    Interceptor interceptor = new Interceptor() {
+      @Override
+      public Response intercept(Chain chain) throws IOException {
+        called.set(true);
+        return chain.proceed(chain.request());
+      }
+    };
+
+    answerHelloWorld();
+    HttpConnector underTest = new HttpConnector.Builder()
+      .url(serverUrl)
+      .interceptor(interceptor)
+      .build();
+    underTest.call(new GetRequest(""));
+
+    assertThat(called.get()).isTrue();
+  }
+
+  @Test
+  public void support_base_url_ending_with_slash() throws Exception {
+    assertThat(serverUrl).endsWith("/");
+    HttpConnector underTest = new HttpConnector.Builder().url(StringUtils.removeEnd(serverUrl, "/")).build();
+    GetRequest request = new GetRequest("api/issues/search");
+
+    answerHelloWorld();
+    WsResponse response = underTest.call(request);
+
+    assertThat(response.hasContent()).isTrue();
+  }
+
+  @Test
+  public void support_base_url_with_context() {
+    // just to be sure
+    assertThat(serverUrl).endsWith("/");
+    HttpConnector underTest = new HttpConnector.Builder().url(serverUrl + "sonar").build();
+
+    GetRequest request = new GetRequest("api/issues/search");
+    answerHelloWorld();
+    assertThat(underTest.call(request).getRequestUrl()).isEqualTo(serverUrl + "sonar/api/issues/search");
+
+    request = new GetRequest("/api/issues/search");
+    answerHelloWorld();
+    assertThat(underTest.call(request).getRequestUrl()).isEqualTo(serverUrl + "sonar/api/issues/search");
+  }
+
+  private void answerHelloWorld() {
+    server.enqueue(new MockResponse().setBody("hello, world!"));
+  }
+}
index 3eab476fec93513dbcd66ceedacc802ea2fcf7d7..2db49ea358b17645aec48fa601eab36a5bed3122 100644 (file)
@@ -27,7 +27,7 @@ public class HttpExceptionTest {
   @Test
   public void test_exception() throws Exception {
     HttpException exception = new HttpException("http://localhost:9000/api/search", 500, "Not found");
-    assertThat(exception.status()).isEqualTo(500);
+    assertThat(exception.code()).isEqualTo(500);
     assertThat(exception.url()).isEqualTo("http://localhost:9000/api/search");
     assertThat(exception.getMessage()).isEqualTo("Error 500 on http://localhost:9000/api/search : Not found");
   }
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/HttpRequestFactoryTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/HttpRequestFactoryTest.java
deleted file mode 100644 (file)
index a256004..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonarqube.ws.client;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-
-public class HttpRequestFactoryTest {
-  @Rule
-  public MockHttpServerInterceptor httpServer = new MockHttpServerInterceptor();
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Test
-  public void test_get() {
-    httpServer.stubStatusCode(200).stubResponseBody("{'issues': []}");
-
-    HttpRequestFactory factory = new HttpRequestFactory(httpServer.url());
-    String json = factory.execute(newGetRequest("/api/issues"));
-
-    assertThat(json).isEqualTo("{'issues': []}");
-    assertThat(httpServer.requestedPath()).isEqualTo("/api/issues");
-  }
-
-  @Test
-  public void should_throw_illegal_state_exc_if_connect_exception() {
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Fail to request http://localhost:1/api/issues");
-
-    HttpRequestFactory factory = new HttpRequestFactory("http://localhost:1");
-    factory.execute(newGetRequest("/api/issues"));
-  }
-
-  @Test
-  public void test_authentication() {
-    httpServer.stubStatusCode(200).stubResponseBody("{}");
-
-    HttpRequestFactory factory = new HttpRequestFactory(httpServer.url()).setLogin("karadoc").setPassword("legrascestlavie");
-    String json = factory.execute(newGetRequest("/api/issues"));
-
-    assertThat(json).isEqualTo("{}");
-    assertThat(httpServer.requestedPath()).isEqualTo("/api/issues");
-    assertThat(httpServer.requestHeaders().get("Authorization")).isEqualTo("Basic a2FyYWRvYzpsZWdyYXNjZXN0bGF2aWU=");
-  }
-
-  @Test
-  public void test_proxy() throws Exception {
-    expectedException.expect(IllegalStateException.class);
-
-    HttpRequestFactory factory = new HttpRequestFactory(httpServer.url())
-      .setProxyHost("localhost").setProxyPort(1)
-      .setProxyLogin("john").setProxyPassword("smith");
-    factory.execute(newGetRequest("/api/issues"));
-  }
-
-  @Test
-  public void beginning_slash_is_optional() throws Exception {
-    HttpRequestFactory factory = new HttpRequestFactory(httpServer.url());
-    factory.execute(newGetRequest("api/foo"));
-    assertThat(httpServer.requestedPath()).isEqualTo("/api/foo");
-
-    factory.execute(newGetRequest("/api/bar"));
-    assertThat(httpServer.requestedPath()).isEqualTo("/api/bar");
-  }
-}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/MockHttpServer.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/MockHttpServer.java
deleted file mode 100644 (file)
index c24be3e..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonarqube.ws.client;
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-
-import static org.apache.commons.io.IOUtils.write;
-
-public class MockHttpServer {
-  private Server server;
-  private String responseBody;
-  private byte[] binaryResponseBody;
-  private int responseStatus = HttpURLConnection.HTTP_OK;
-  private String requestPath;
-  private Map requestHeaders = new HashMap(), requestParams = new HashMap();
-  private String contentType;
-
-  public void start() throws Exception {
-    // 0 is random available port
-    server = new Server(0);
-    server.setHandler(getMockHandler());
-    server.start();
-  }
-
-  public Handler getMockHandler() {
-    Handler handler = new AbstractHandler() {
-      @Override
-      public void handle(String target, Request baseRequest, HttpServletRequest httpServletRequest, HttpServletResponse response) throws IOException, ServletException {
-        requestPath = baseRequest.getUri().toString();
-        requestHeaders.clear();
-        Enumeration names = baseRequest.getHeaderNames();
-        while (names.hasMoreElements()) {
-          String headerName = (String) names.nextElement();
-          requestHeaders.put(headerName, baseRequest.getHeader(headerName));
-        }
-        requestParams.clear();
-        names = baseRequest.getParameterNames();
-        while (names.hasMoreElements()) {
-          String headerName = (String) names.nextElement();
-          requestParams.put(headerName, baseRequest.getParameter(headerName));
-        }
-        response.setStatus(responseStatus);
-        response.setContentType("application/json;charset=utf-8");
-        if (responseBody != null) {
-          write(responseBody, response.getOutputStream());
-        } else {
-          write(binaryResponseBody, response.getOutputStream());
-        }
-        baseRequest.setHandled(true);
-      }
-    };
-    return handler;
-  }
-
-  public void stop() {
-    try {
-      if (server != null) {
-        server.stop();
-      }
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to stop HTTP server", e);
-    }
-  }
-
-  public MockHttpServer doReturnBody(String responseBody) {
-    this.responseBody = responseBody;
-    return this;
-  }
-
-  public MockHttpServer doReturnBody(byte[] responseBody) {
-    this.binaryResponseBody = responseBody;
-    return this;
-  }
-
-  public MockHttpServer doReturnStatus(int status) {
-    this.responseStatus = status;
-    return this;
-  }
-
-  public MockHttpServer doReturnContentType(String contentType) {
-    this.contentType = contentType;
-    return this;
-  }
-
-  public String requestPath() {
-    return requestPath;
-  }
-
-  public Map requestHeaders() {
-    return requestHeaders;
-  }
-
-  public Map requestParams() {
-    return requestParams;
-  }
-
-  public int getPort() {
-    return server.getConnectors()[0].getLocalPort();
-  }
-}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/MockHttpServerInterceptor.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/MockHttpServerInterceptor.java
deleted file mode 100644 (file)
index cd36126..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client;
-
-import org.junit.rules.ExternalResource;
-
-import java.util.Map;
-
-public final class MockHttpServerInterceptor extends ExternalResource {
-
-  private MockHttpServer server;
-
-  @Override
-  protected final void before() throws Throwable {
-    server = new MockHttpServer();
-    server.start();
-  }
-
-  @Override
-  protected void after() {
-    server.stop();
-  }
-
-  public MockHttpServerInterceptor stubResponseBody(String body) {
-    server.doReturnBody(body);
-    return this;
-  }
-
-  public MockHttpServerInterceptor stubStatusCode(int status) {
-    server.doReturnStatus(status);
-    return this;
-  }
-
-  public String requestedPath() {
-    return server.requestPath();
-  }
-
-  public Map requestHeaders() {
-    return server.requestHeaders();
-  }
-
-  public Map requestParams() {
-    return server.requestParams();
-  }
-
-  public int port() {
-    return server.getPort();
-  }
-
-  public String url() {
-    return "http://localhost:" + port();
-  }
-}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/PostRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/PostRequestTest.java
new file mode 100644 (file)
index 0000000..3693cdf
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.ws.client;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonarqube.ws.MediaTypes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PostRequestTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Test
+  public void post_is_post() {
+    PostRequest request = new PostRequest("api/issues/search");
+    assertThat(request.getMethod()).isEqualTo(WsRequest.Method.POST);
+  }
+
+  @Test
+  public void empty_parts_and_params_by_default() {
+    PostRequest request = new PostRequest("api/issues/search");
+    assertThat(request.getParts()).isEmpty();
+    assertThat(request.getParams()).isEmpty();
+  }
+
+  @Test
+  public void add_part() throws IOException {
+    PostRequest request = new PostRequest("api/issues/search");
+    File reportFile = temp.newFile();
+    request.setPart("report", new PostRequest.Part(MediaTypes.JSON, reportFile));
+
+    assertThat(request.getParts()).hasSize(1);
+    PostRequest.Part part = request.getParts().get("report");
+    assertThat(part.getMediaType()).isEqualTo(MediaTypes.JSON);
+    assertThat(part.getFile()).isSameAs(reportFile);
+  }
+}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/WsClientTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/WsClientTest.java
deleted file mode 100644 (file)
index 13ff1fa..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client;
-
-import com.google.common.net.HttpHeaders;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonarqube.ws.MediaTypes;
-import org.sonarqube.ws.WsComponents;
-
-import static java.net.HttpURLConnection.HTTP_OK;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonarqube.ws.client.HttpConnector.newDefaultHttpConnector;
-import static org.sonarqube.ws.client.HttpConnector.newHttpConnector;
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-
-public class WsClientTest {
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  MockHttpServer server;
-
-  WsClient underTest;
-
-  @Before
-  public void setUp() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-
-    underTest = new WsClient(newDefaultHttpConnector("http://localhost:" + server.getPort()));
-  }
-
-  @After
-  public void stopServer() {
-    if (server != null) {
-      server.stop();
-    }
-  }
-
-  @Test
-  public void return_protobuf_response() throws Exception {
-    server.doReturnBody(
-      WsComponents.SearchWsResponse
-        .newBuilder()
-        .addComponents(WsComponents.SearchWsResponse.Component.getDefaultInstance())
-        .build()
-        .toByteArray());
-    server.doReturnStatus(HTTP_OK);
-    server.doReturnContentType(MediaTypes.PROTOBUF);
-
-    WsComponents.SearchWsResponse response = underTest.execute(
-      newGetRequest("api/components/search")
-        .setMediaType(WsRequest.MediaType.PROTOBUF),
-      WsComponents.SearchWsResponse.parser());
-
-    assertThat(response.getComponentsCount()).isEqualTo(1);
-    assertThat(server.requestHeaders().get(HttpHeaders.ACCEPT))
-      .isEqualTo(MediaTypes.PROTOBUF);
-  }
-
-  @Test
-  public void return_json_response() throws Exception {
-    String expectedResponse = "{\"key\":value}";
-    server.doReturnBody(expectedResponse);
-    server.doReturnStatus(HTTP_OK);
-    server.doReturnContentType(MediaTypes.JSON);
-
-    String response = underTest.execute(newGetRequest("api/components/search"));
-
-    assertThat(response).isEqualTo(expectedResponse);
-    assertThat(server.requestHeaders().get(HttpHeaders.ACCEPT)).isEqualTo(MediaTypes.JSON);
-  }
-
-  @Test
-  public void url_should_not_be_null() {
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Server URL must be set");
-
-    new WsClient(newHttpConnector().build());
-  }
-
-  @Test
-  public void url_should_not_be_empty() {
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Server URL must be set");
-
-    new WsClient(newDefaultHttpConnector(""));
-  }
-
-  @Test
-  public void test_default_configuration() throws Exception {
-    underTest = new WsClient(newDefaultHttpConnector("http://localhost:9000"));
-
-    HttpRequestFactory requestFactory = ((HttpConnector) underTest.wsConnector).requestFactory;
-    assertThat(requestFactory.getBaseUrl()).isEqualTo("http://localhost:9000");
-    assertThat(requestFactory.getLogin()).isNull();
-    assertThat(requestFactory.getPassword()).isNull();
-    assertThat(requestFactory.getConnectTimeoutInMilliseconds()).isEqualTo(HttpConnector.DEFAULT_CONNECT_TIMEOUT_MILLISECONDS);
-    assertThat(requestFactory.getReadTimeoutInMilliseconds()).isEqualTo(HttpConnector.DEFAULT_READ_TIMEOUT_MILLISECONDS);
-    assertThat(requestFactory.getProxyHost()).isNull();
-    assertThat(requestFactory.getProxyPort()).isEqualTo(0);
-    assertThat(requestFactory.getProxyLogin()).isNull();
-    assertThat(requestFactory.getProxyPassword()).isNull();
-  }
-
-  @Test
-  public void test_custom_configuration() throws Exception {
-    underTest = new WsClient(newHttpConnector()
-      .url("http://localhost:9000")
-      .login("eric")
-      .password("pass")
-      .connectTimeoutMilliseconds(12345)
-      .readTimeoutMilliseconds(6789)
-      .proxy("localhost", 2052)
-      .proxyLogin("proxyLogin")
-      .proxyPassword("proxyPass")
-      .build());
-
-    HttpRequestFactory requestFactory = ((HttpConnector) underTest.wsConnector).requestFactory;
-    assertThat(requestFactory.getBaseUrl()).isEqualTo("http://localhost:9000");
-    assertThat(requestFactory.getLogin()).isEqualTo("eric");
-    assertThat(requestFactory.getPassword()).isEqualTo("pass");
-    assertThat(requestFactory.getConnectTimeoutInMilliseconds()).isEqualTo(12345);
-    assertThat(requestFactory.getReadTimeoutInMilliseconds()).isEqualTo(6789);
-    assertThat(requestFactory.getProxyHost()).isEqualTo("localhost");
-    assertThat(requestFactory.getProxyPort()).isEqualTo(2052);
-    assertThat(requestFactory.getProxyLogin()).isEqualTo("proxyLogin");
-    assertThat(requestFactory.getProxyPassword()).isEqualTo("proxyPass");
-  }
-}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/WsRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/WsRequestTest.java
deleted file mode 100644 (file)
index 28b5c04..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonarqube.ws.client;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonarqube.ws.client.WsRequest.Method;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonarqube.ws.client.WsRequest.newGetRequest;
-import static org.sonarqube.ws.client.WsRequest.newPostRequest;
-
-public class WsRequestTest {
-
-  static final String ENDPOINT = "api/issues/search";
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  WsRequest underTest;
-
-  @Test
-  public void get_request() {
-    underTest = newGetRequest("api/issues/search");
-
-    assertThat(underTest.getMethod()).isEqualTo(Method.GET);
-  }
-
-  @Test
-  public void post_request() {
-    underTest = newPostRequest("api/issues/search");
-
-    assertThat(underTest.getMethod()).isEqualTo(Method.POST);
-  }
-
-  @Test
-  public void set_non_null_param() {
-    underTest = newGetRequest("api/issues/search")
-      .setParam("key", "value");
-
-    assertThat(underTest.getParams().get("key")).isEqualTo("value");
-  }
-
-  @Test
-  public void set_null_param_remove_existing_param() {
-    underTest = newGetRequest(ENDPOINT)
-      .setParam("key", "value")
-      .setParam("key", null);
-
-    assertThat(underTest.getParams().get("key")).isNull();
-  }
-
-  @Test
-  public void fail_if_key_is_null() {
-    expectedException.expect(NullPointerException.class);
-    expectedException.expectMessage("a WS parameter key cannot be null");
-
-    underTest = newGetRequest(ENDPOINT)
-      .setParam(null, "value");
-  }
-}
diff --git a/sonar-ws/src/test/protobuf/ws-testing.proto b/sonar-ws/src/test/protobuf/ws-testing.proto
new file mode 100644 (file)
index 0000000..9e802d9
--- /dev/null
@@ -0,0 +1,29 @@
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2015 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube 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.
+//
+// SonarQube 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 this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+syntax = "proto2";
+
+package sonarqube.ws.testing;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "Testing";
+option optimize_for = SPEED;
+
+message Fake {
+  optional string label = 1;
+}