Add REST client
In order to query the remote server, a REST client is added. It is
the one that comes with Gerrit's acceptance tests with a modified
constructor in order to avoid more dependencies.
The BUCK build is only supported in Gerrit tree. The support for
standalone BUCK build is already prepared, but will only work with
Gerrit 2.11 where the 'STANDALONE_MODE' flag is available.
Change-Id: Ie12e02a6552643eea122e5fabd71bbd4a3f7ba3e
diff --git a/BUCK b/BUCK
index 2b74c47..39591cb 100644
--- a/BUCK
+++ b/BUCK
@@ -1,5 +1,15 @@
include_defs('//bucklets/gerrit_plugin.bucklet')
+# TODO: support standalone Buck build with 2.11
+#if STANDALONE_MODE:
+# HTTP_LIB = '//lib/http:http_lib'
+# GSON = '//lib/gson:gson'
+#else:
+# HTTP_LIB = '//plugins/importer/lib/http:http_lib'
+# GSON = '//plugins/importer/lib/gson:gson'
+HTTP_LIB = '//plugins/importer/lib/http:http_lib'
+GSON = '//plugins/importer/lib/gson:gson'
+
gerrit_plugin(
name = 'importer',
srcs = glob(['src/main/java/**/*.java']),
@@ -11,6 +21,10 @@
'Gerrit-Module: com.googlesource.gerrit.plugins.importer.Module',
'Gerrit-SshModule: com.googlesource.gerrit.plugins.importer.SshModule',
],
+ deps = [
+ HTTP_LIB,
+ GSON,
+ ],
)
# this is required for bucklets/tools/eclipse/project.py to work
diff --git a/lib/commons/BUCK b/lib/commons/BUCK
new file mode 100644
index 0000000..ba42a1f
--- /dev/null
+++ b/lib/commons/BUCK
@@ -0,0 +1,41 @@
+include_defs('//bucklets/maven_jar.bucklet')
+
+java_library(
+ name = 'commons_lib',
+ deps = [
+ ':codec',
+ ':io',
+ ':lang',
+ ],
+ visibility = ['PUBLIC'],
+)
+
+EXCLUDE = [
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE.txt'
+]
+
+maven_jar(
+ name = 'codec',
+ id = 'commons-codec:commons-codec:1.4',
+ sha1 = '4216af16d38465bbab0f3dff8efa14204f7a399a',
+ license = 'Apache2.0',
+ exclude = EXCLUDE,
+)
+
+maven_jar(
+ name = 'io',
+ id = 'commons-io:commons-io:1.4',
+ sha1 = 'a8762d07e76cfde2395257a5da47ba7c1dbd3dce',
+ license = 'Apache2.0',
+ exclude = EXCLUDE,
+)
+
+maven_jar(
+ name = 'lang',
+ id = 'commons-lang:commons-lang:2.5',
+ sha1 = 'b0236b252e86419eef20c31a44579d2aee2f0a69',
+ license = 'Apache2.0',
+ exclude = EXCLUDE,
+)
+
diff --git a/lib/gson/BUCK b/lib/gson/BUCK
new file mode 100644
index 0000000..8892994
--- /dev/null
+++ b/lib/gson/BUCK
@@ -0,0 +1,8 @@
+include_defs('//bucklets/maven_jar.bucklet')
+
+maven_jar(
+ name = 'gson',
+ id = 'com.google.code.gson:gson:2.1',
+ sha1 = '2e66da15851f9f5b5079228f856c2f090ba98c38',
+ license = 'Apache2.0',
+)
diff --git a/lib/http/BUCK b/lib/http/BUCK
new file mode 100644
index 0000000..0b9d990
--- /dev/null
+++ b/lib/http/BUCK
@@ -0,0 +1,43 @@
+include_defs('//bucklets/gerrit_plugin.bucklet')
+include_defs('//bucklets/maven_jar.bucklet')
+
+# TODO: support standalone Buck build with 2.11
+#if STANDALONE_MODE:
+# COMMONS = '//lib/commons:commons_lib'
+# LOG = '//lib/log:jcl-over-slf4j'
+#else:
+# COMMONS = '//plugins/importer/lib/commons:commons_lib'
+# LOG = '//plugins/importer/lib/log:jcl-over-slf4j'
+COMMONS = '//plugins/importer/lib/commons:commons_lib'
+LOG = '//plugins/importer/lib/log:jcl-over-slf4j'
+
+java_library(
+ name = 'http_lib',
+ exported_deps = [
+ ':httpclient',
+ ':httpcore',
+ ],
+ visibility = ['PUBLIC'],
+)
+
+maven_jar(
+ name = 'httpclient',
+ id = 'org.apache.httpcomponents:httpclient:4.3.4',
+ bin_sha1 = 'a9a1fef2faefed639ee0d0fba5b3b8e4eb2ff2d8',
+ src_sha1 = '7a14aafed8c5e2c4e360a2c1abd1602efa768b1f',
+ license = 'Apache2.0',
+ deps = [
+ COMMONS,
+ ':httpcore',
+ LOG,
+ ],
+)
+
+maven_jar(
+ name = 'httpcore',
+ id = 'org.apache.httpcomponents:httpcore:4.3.2',
+ bin_sha1 = '31fbbff1ddbf98f3aa7377c94d33b0447c646b6e',
+ src_sha1 = '4809f38359edeea9487f747e09aa58ec8d3a54c5',
+ license = 'Apache2.0',
+)
+
diff --git a/lib/log/BUCK b/lib/log/BUCK
new file mode 100644
index 0000000..fc994d6
--- /dev/null
+++ b/lib/log/BUCK
@@ -0,0 +1,8 @@
+include_defs('//bucklets/maven_jar.bucklet')
+
+maven_jar(
+ name = 'jcl-over-slf4j',
+ id = 'org.slf4j:jcl-over-slf4j:1.7.7',
+ sha1 = '56003dcd0a31deea6391b9e2ef2f2dc90b205a92',
+ license = 'slf4j',
+)
diff --git a/pom.xml b/pom.xml
index 2a881fb..3a9818a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,24 @@
<encoding>UTF-8</encoding>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <createDependencyReducedPom>false</createDependencyReducedPom>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
@@ -75,5 +93,15 @@
<version>${Gerrit-ApiVersion}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.3.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <version>4.3.2</version>
+ </dependency>
</dependencies>
</project>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/HttpResponse.java b/src/main/java/com/googlesource/gerrit/plugins/importer/HttpResponse.java
new file mode 100755
index 0000000..4cd58dd
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/HttpResponse.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.importer;
+
+import com.google.common.base.Preconditions;
+
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.RawParseUtils;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+
+public class HttpResponse {
+
+ protected org.apache.http.HttpResponse response;
+ protected Reader reader;
+
+ HttpResponse(org.apache.http.HttpResponse response) {
+ this.response = response;
+ }
+
+ public Reader getReader() throws IllegalStateException, IOException {
+ if (reader == null && response.getEntity() != null) {
+ reader = new InputStreamReader(response.getEntity().getContent());
+ }
+ return reader;
+ }
+
+ public void consume() throws IllegalStateException, IOException {
+ Reader reader = getReader();
+ if (reader != null) {
+ while (reader.read() != -1);
+ }
+ }
+
+ public int getStatusCode() {
+ return response.getStatusLine().getStatusCode();
+ }
+
+ public String getEntityContent() throws IOException {
+ Preconditions.checkNotNull(response, "Response is not initialized.");
+ Preconditions.checkNotNull(response.getEntity(),
+ "Response.Entity is not initialized.");
+ ByteBuffer buf = IO.readWholeStream(
+ response.getEntity().getContent(),
+ 1024);
+ return RawParseUtils.decode(
+ buf.array(),
+ buf.arrayOffset(),
+ buf.limit())
+ .trim();
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/HttpSession.java b/src/main/java/com/googlesource/gerrit/plugins/importer/HttpSession.java
new file mode 100755
index 0000000..bce528b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/HttpSession.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.importer;
+
+import com.google.common.base.CharMatcher;
+
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class HttpSession {
+
+ protected final String url;
+ private final String user;
+ private final String pass;
+ private HttpClient client;
+
+ public HttpSession(String url, String user, String pass) {
+ this.url = CharMatcher.is('/').trimTrailingFrom(url);
+ this.user = user;
+ this.pass = pass;
+ }
+
+ public HttpResponse get(String path) throws IOException {
+ HttpGet get = new HttpGet(url + path);
+ return new HttpResponse(getClient().execute(get));
+ }
+
+ protected HttpClient getClient() {
+ if (client == null) {
+ URI uri = URI.create(url);
+ BasicCredentialsProvider creds = new BasicCredentialsProvider();
+ creds.setCredentials(new AuthScope(uri.getHost(), uri.getPort()),
+ new UsernamePasswordCredentials(user, pass));
+ client = HttpClientBuilder
+ .create()
+ .setDefaultCredentialsProvider(creds)
+ .setMaxConnPerRoute(10)
+ .setMaxConnTotal(1024)
+ .build();
+ }
+ return client;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/RestResponse.java b/src/main/java/com/googlesource/gerrit/plugins/importer/RestResponse.java
new file mode 100755
index 0000000..512db6e
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/RestResponse.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.importer;
+
+import static com.google.gerrit.httpd.restapi.RestApiServlet.JSON_MAGIC;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+public class RestResponse extends HttpResponse {
+
+ RestResponse(org.apache.http.HttpResponse response) {
+ super(response);
+ }
+
+ @Override
+ public Reader getReader() throws IllegalStateException, IOException {
+ if (reader == null && response.getEntity() != null) {
+ reader = new InputStreamReader(response.getEntity().getContent());
+ reader.skip(JSON_MAGIC.length);
+ }
+ return reader;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/RestSession.java b/src/main/java/com/googlesource/gerrit/plugins/importer/RestSession.java
new file mode 100755
index 0000000..f02ecf7
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/RestSession.java
@@ -0,0 +1,117 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.importer;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.gerrit.extensions.restapi.RawInput;
+import com.google.gerrit.server.OutputFormat;
+
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.message.BasicHeader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+public class RestSession extends HttpSession {
+
+ public RestSession(String url, String user, String pass) {
+ super(url, user, pass);
+ }
+
+ @Override
+ public RestResponse get(String endPoint) throws IOException {
+ HttpGet get = new HttpGet(url + "/a" + endPoint);
+ return new RestResponse(getClient().execute(get));
+ }
+
+ public RestResponse put(String endPoint) throws IOException {
+ return put(endPoint, null);
+ }
+
+ public RestResponse put(String endPoint, Object content) throws IOException {
+ HttpPut put = new HttpPut(url + "/a" + endPoint);
+ if (content != null) {
+ put.addHeader(new BasicHeader("Content-Type", "application/json"));
+ put.setEntity(new StringEntity(
+ OutputFormat.JSON_COMPACT.newGson().toJson(content),
+ Charsets.UTF_8.name()));
+ }
+ return new RestResponse(getClient().execute(put));
+ }
+
+ public RestResponse putRaw(String endPoint, RawInput stream) throws IOException {
+ Preconditions.checkNotNull(stream);
+ HttpPut put = new HttpPut(url + "/a" + endPoint);
+ put.addHeader(new BasicHeader("Content-Type", stream.getContentType()));
+ put.setEntity(new BufferedHttpEntity(
+ new InputStreamEntity(
+ stream.getInputStream(),
+ stream.getContentLength())));
+ return new RestResponse(getClient().execute(put));
+ }
+
+ public RestResponse post(String endPoint) throws IOException {
+ return post(endPoint, null);
+ }
+
+ public RestResponse post(String endPoint, Object content) throws IOException {
+ HttpPost post = new HttpPost(url + "/a" + endPoint);
+ if (content != null) {
+ post.addHeader(new BasicHeader("Content-Type", "application/json"));
+ post.setEntity(new StringEntity(
+ OutputFormat.JSON_COMPACT.newGson().toJson(content),
+ Charsets.UTF_8.name()));
+ }
+ return new RestResponse(getClient().execute(post));
+ }
+
+ public RestResponse delete(String endPoint) throws IOException {
+ HttpDelete delete = new HttpDelete(url + "/a" + endPoint);
+ return new RestResponse(getClient().execute(delete));
+ }
+
+
+ public static RawInput newRawInput(final String content) throws IOException {
+ Preconditions.checkNotNull(content);
+ Preconditions.checkArgument(!content.isEmpty());
+ return new RawInput() {
+ byte bytes[] = content.getBytes(StandardCharsets.UTF_8);
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(bytes);
+ }
+
+ @Override
+ public String getContentType() {
+ return "application/octet-stream";
+ }
+
+ @Override
+ public long getContentLength() {
+ return bytes.length;
+ }
+ };
+ }
+}