diff --git a/pom.xml b/pom.xml
index f5e23c19..892468fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,7 @@
- 3.4.1
+ 3.6.0-SNAPSHOT3.3.03.0.1.Final
@@ -206,5 +206,6 @@
zanata-client-commandszanata-maven-pluginzanata-rest-client
+ rest-client
-
+
\ No newline at end of file
diff --git a/rest-client/pom.xml b/rest-client/pom.xml
new file mode 100644
index 00000000..7e845ab0
--- /dev/null
+++ b/rest-client/pom.xml
@@ -0,0 +1,102 @@
+
+
+ 4.0.0
+
+ org.zanata
+ client
+ 3.6.0-SNAPSHOT
+
+ rest-client
+ rest-client
+ http://maven.apache.org
+
+ UTF-8
+
+
+
+ org.zanata
+ zanata-common-api
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+
+
+ org.jboss.resteasy
+ resteasy-jaxb-provider
+
+
+ org.jboss.resteasy
+ jaxrs-api
+
+
+ org.jboss.resteasy
+ resteasy-multipart-provider
+
+
+
+
+ com.sun.jersey
+ jersey-core
+ 1.17.1
+
+
+ com.sun.jersey
+ jersey-client
+ 1.17.1
+
+
+ com.sun.jersey.contribs
+ jersey-multipart
+ 1.17.1
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ log4j
+ log4j
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+ com.google.guava
+ guava
+
+
+ commons-codec
+ commons-codec
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-core
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+
+
+
+
+ src/test/resources
+
+
+
+
diff --git a/rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderFilter.java b/rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderFilter.java
new file mode 100644
index 00000000..9be0d28e
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderFilter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.zanata.rest.client;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.Provider;
+
+import org.zanata.rest.RestConstant;
+
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.filter.ClientFilter;
+
+@Provider
+public class ApiKeyHeaderFilter extends ClientFilter {
+ private String apiKey;
+ private String username;
+ private String ver;
+
+ public ApiKeyHeaderFilter(String username, String apiKey, String ver) {
+ this.username = username;
+ this.apiKey = apiKey;
+ this.ver = ver;
+ }
+
+ @Override
+ public ClientResponse handle(ClientRequest cr)
+ throws ClientHandlerException {
+ MultivaluedMap headers = cr.getHeaders();
+ headers.add(RestConstant.HEADER_USERNAME, username);
+ headers.add(RestConstant.HEADER_API_KEY, apiKey);
+ headers.add(RestConstant.HEADER_VERSION_NO, ver);
+ return getNext().handle(cr);
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
diff --git a/rest-client/src/main/java/org/zanata/rest/client/CacheResponseFilter.java b/rest-client/src/main/java/org/zanata/rest/client/CacheResponseFilter.java
new file mode 100644
index 00000000..bee5a842
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/CacheResponseFilter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.zanata.rest.client;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+import com.sun.jersey.api.client.filter.ClientFilter;
+
+/**
+ * This is a workaround that jersey client don't support put/post returning
+ * response.
+ *
+ * @author Patrick Huang pahuang@redhat.com
+ */
+public class CacheResponseFilter extends ClientFilter {
+ private Optional cachedClientResponse = Optional.absent();
+
+ @Override
+ public ClientResponse handle(ClientRequest cr)
+ throws ClientHandlerException {
+ ClientResponse response = getNext().handle(cr);
+ response.bufferEntity();
+ cachedClientResponse = Optional.of(response);
+ return response;
+ }
+
+ public T getEntity(Class type) {
+ checkState();
+ return cachedClientResponse.get().getEntity(type);
+ }
+
+ public T getEntity(GenericType genericType) {
+ checkState();
+ return cachedClientResponse.get().getEntity(genericType);
+ }
+
+ private void checkState() {
+ Preconditions.checkState(cachedClientResponse.isPresent(),
+ "No cached ClientResponse. Did you forget to add this filter?");
+ }
+}
diff --git a/rest-client/src/main/java/org/zanata/rest/client/FileResourceClient.java b/rest-client/src/main/java/org/zanata/rest/client/FileResourceClient.java
new file mode 100644
index 00000000..380d49ba
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/FileResourceClient.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.zanata.rest.client;
+
+import java.net.URI;
+import javax.ws.rs.core.MediaType;
+
+import org.zanata.rest.DocumentFileUploadForm;
+import org.zanata.rest.StringSet;
+import org.zanata.rest.dto.ChunkUploadResponse;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.multipart.FormDataMultiPart;
+
+/**
+ * @author Patrick Huang pahuang@redhat.com
+ */
+public class FileResourceClient {
+ private final RestClientFactory factory;
+
+ private final URI baseUri;
+
+ public FileResourceClient(RestClientFactory restClientFactory) {
+ this.factory = restClientFactory;
+ baseUri = restClientFactory.getBaseUri();
+
+ }
+
+ public StringSet acceptedFileTypes() {
+ String types = factory.getClient()
+ .resource(baseUri)
+ .path("file").path("accepted_types")
+ .get(String.class);
+ return new StringSet(types);
+ }
+
+ public ChunkUploadResponse uploadSourceFile(
+ String projectSlug,
+ String iterationSlug, String docId,
+ DocumentFileUploadForm documentFileUploadForm) {
+ CacheResponseFilter filter = new CacheResponseFilter();
+ Client client = factory.getClient();
+ client.addFilter(filter);
+ WebResource.Builder builder = client
+ .resource(baseUri)
+ .path("file").path("source").path(projectSlug)
+ .path(iterationSlug)
+ .queryParam("docId", docId)
+ .type(MediaType.MULTIPART_FORM_DATA_TYPE);
+ FormDataMultiPart form =
+ prepareFormDataMultiPart(documentFileUploadForm);
+
+ builder.post(form);
+ ChunkUploadResponse chunkUploadResponse =
+ filter.getEntity(ChunkUploadResponse.class);
+ client.removeFilter(filter);
+ return chunkUploadResponse;
+ }
+
+ private FormDataMultiPart prepareFormDataMultiPart(
+ DocumentFileUploadForm documentFileUploadForm) {
+ FormDataMultiPart form =
+ new FormDataMultiPart()
+ .field("file", documentFileUploadForm
+ .getFileStream(),
+ MediaType.APPLICATION_OCTET_STREAM_TYPE);
+ addBodyPartIfPresent(form, "adapterParams",
+ documentFileUploadForm.getAdapterParams());
+ addBodyPartIfPresent(form, "type", documentFileUploadForm.getFileType());
+ addBodyPartIfPresent(form, "first", documentFileUploadForm.getFirst());
+ addBodyPartIfPresent(form, "hash", documentFileUploadForm.getHash());
+ addBodyPartIfPresent(form, "last", documentFileUploadForm.getLast());
+ addBodyPartIfPresent(form, "size", documentFileUploadForm.getSize());
+ addBodyPartIfPresent(form, "uploadId",
+ documentFileUploadForm.getUploadId());
+ return form;
+ }
+
+ public ChunkUploadResponse uploadTranslationFile(
+ String projectSlug,
+ String iterationSlug, String locale, String docId,
+ String mergeType,
+ DocumentFileUploadForm documentFileUploadForm) {
+ CacheResponseFilter filter = new CacheResponseFilter();
+ Client client = factory.getClient();
+ client.addFilter(filter);
+ WebResource.Builder builder = client.resource(baseUri)
+ .path("file").path("translation")
+ .path(projectSlug)
+ .path(iterationSlug)
+ .path(locale)
+ .queryParam("docId", docId)
+ .queryParam("merge", mergeType)
+ .type(MediaType.MULTIPART_FORM_DATA_TYPE);
+ FormDataMultiPart form =
+ prepareFormDataMultiPart(documentFileUploadForm);
+
+ builder.post(form);
+ ChunkUploadResponse chunkUploadResponse =
+ filter.getEntity(ChunkUploadResponse.class);
+ client.removeFilter(filter);
+ return chunkUploadResponse;
+ }
+
+ private static FormDataMultiPart addBodyPartIfPresent(
+ FormDataMultiPart form, String field, T value) {
+ if (value != null) {
+ return form.field(field, value.toString());
+ }
+ return form;
+ }
+}
diff --git a/rest-client/src/main/java/org/zanata/rest/client/RestClientFactory.java b/rest-client/src/main/java/org/zanata/rest/client/RestClientFactory.java
new file mode 100644
index 00000000..35540a83
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/RestClientFactory.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.zanata.rest.client;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.zanata.rest.dto.VersionInfo;
+import com.google.common.base.Throwables;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.client.urlconnection.HTTPSProperties;
+import com.sun.jersey.multipart.impl.MultiPartWriter;
+
+/**
+ * @author Patrick Huang pahuang@redhat.com
+ */
+public class RestClientFactory {
+ private static final Logger log =
+ LoggerFactory.getLogger(RestClientFactory.class);
+ private String clientVersion;
+ private VersionInfo clientApiVersion;
+
+ private Client client;
+ private URI baseURI;
+
+ public RestClientFactory(URI base, String username, String apiKey,
+ VersionInfo clientApiVersion, boolean logHttp,
+ boolean sslCertDisabled) {
+ baseURI = base;
+ this.clientApiVersion = clientApiVersion;
+ clientVersion = clientApiVersion.getVersionNo();
+ DefaultClientConfig clientConfig =
+ new DefaultClientConfig(MultiPartWriter.class);
+
+ sslConfiguration(sslCertDisabled, clientConfig);
+
+ client = Client.create(clientConfig);
+ client.addFilter(
+ new ApiKeyHeaderFilter(username, apiKey, clientVersion));
+ client.addFilter(new TraceDebugInterceptor(logHttp));
+ }
+
+ private static void sslConfiguration(boolean sslCertDisabled,
+ ClientConfig clientConfig) {
+ if (!sslCertDisabled) {
+ return;
+ }
+ try {
+ final SSLContext sslContext = SSLContext.getInstance("TLS");
+
+ // Create a trust manager that does not validate certificate chains
+ // against our server
+ final TrustManager[] trustAllCerts;
+ trustAllCerts =
+ new TrustManager[] { new AcceptAllX509TrustManager() };
+ sslContext.init(null, trustAllCerts, new SecureRandom());
+ HttpsURLConnection
+ .setDefaultSSLSocketFactory(sslContext
+ .getSocketFactory());
+ clientConfig.getProperties().put(
+ HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
+ new HTTPSProperties(
+ new HostnameVerifier() {
+ @Override
+ public boolean verify(String s,
+ SSLSession sslSession) {
+ // whatever your matching policy states
+ return true;
+ }
+ }, sslContext
+ ));
+ } catch (Exception e) {
+ log.warn("error creating SSL client", e);
+ Throwables.propagate(e);
+ }
+ }
+
+ public VersionInfo getServerVersionInfo() {
+ return client.resource(getBaseUri()).path("version")
+ .get(VersionInfo.class);
+ }
+
+ private URL getBaseUrl() {
+ try {
+ return new URL(fixBase(baseURI).toString() + getUrlPrefix());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected URI getBaseUri() {
+ try {
+ return getBaseUrl().toURI();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static URI fixBase(URI base) {
+ if (base != null) {
+ String baseString = base.toString();
+ if (!baseString.endsWith("/")) {
+ try {
+ URI result = new URI(baseString + "/");
+ log.warn("Appending '/' to base URL '{}': using '{}'",
+ baseString, result);
+ return result;
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return base;
+ }
+
+ protected String getUrlPrefix() {
+ return "rest/";
+ }
+
+ protected Client getClient() {
+ return client;
+ }
+
+ private static class AcceptAllX509TrustManager implements X509TrustManager {
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ public void
+ checkClientTrusted(X509Certificate[] certs, String authType)
+ throws CertificateException {
+ }
+
+ public void
+ checkServerTrusted(X509Certificate[] certs, String authType)
+ throws CertificateException {
+ }
+ }
+}
diff --git a/rest-client/src/main/java/org/zanata/rest/client/SourceDocResourceClient.java b/rest-client/src/main/java/org/zanata/rest/client/SourceDocResourceClient.java
new file mode 100644
index 00000000..b6dfb552
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/SourceDocResourceClient.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.zanata.rest.client;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import org.zanata.rest.dto.resource.ResourceMeta;
+import com.sun.jersey.api.client.GenericType;
+import com.sun.jersey.api.client.WebResource;
+
+/**
+ * @author Patrick Huang
+ * pahuang@redhat.com
+ */
+public class SourceDocResourceClient {
+ private final RestClientFactory factory;
+ private final String project;
+ private final String projectVersion;
+ private final URI baseUri;
+
+ public SourceDocResourceClient(RestClientFactory factory, String project, String projectVersion) {
+ this.factory = factory;
+ this.project = project;
+ this.projectVersion = projectVersion;
+ baseUri = factory.getBaseUri();
+ }
+
+ public List getResourceMeta(Set extensions) {
+ WebResource webResource =
+ factory.getClient().resource(baseUri)
+ .path("projects").path("p")
+ .path(project)
+ .path("iterations").path("i")
+ .path(projectVersion)
+ .path("r");
+ if (extensions != null) {
+ webResource = webResource.queryParam("ext", extensions.toString());
+ }
+ return webResource
+ .get(new GenericType>() {});
+ }
+}
diff --git a/rest-client/src/main/java/org/zanata/rest/client/StatisticsResourceClient.java b/rest-client/src/main/java/org/zanata/rest/client/StatisticsResourceClient.java
new file mode 100644
index 00000000..4a452a2e
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/StatisticsResourceClient.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.zanata.rest.client;
+
+import java.net.URI;
+import java.util.Arrays;
+import javax.ws.rs.DefaultValue;
+
+import org.zanata.rest.dto.stats.ContainerTranslationStatistics;
+import org.zanata.rest.dto.stats.contribution.ContributionStatistics;
+import org.zanata.rest.service.StatisticsResource;
+import com.sun.jersey.api.client.WebResource;
+
+/**
+ * @author Patrick Huang
+ * pahuang@redhat.com
+ */
+public class StatisticsResourceClient implements StatisticsResource {
+ private final RestClientFactory factory;
+ private final URI baseUri;
+
+ public StatisticsResourceClient(RestClientFactory factory) {
+ this.factory = factory;
+ baseUri = factory.getBaseUri();
+ }
+
+ @Override
+ public ContainerTranslationStatistics getStatistics(String projectSlug,
+ String iterationSlug, @DefaultValue("false") boolean includeDetails,
+ @DefaultValue("false") boolean includeWordStats, String[] locales) {
+ WebResource webResource =
+ factory.getClient().resource(baseUri).path("stats")
+ .path("proj")
+ .path(projectSlug)
+ .path("iter")
+ .path(iterationSlug)
+ .queryParam("detail", String.valueOf(includeDetails))
+ .queryParam("word", String.valueOf(includeWordStats))
+ .queryParam("locale", Arrays.toString(locales));
+ return webResource.get(ContainerTranslationStatistics.class);
+ }
+
+ @Override
+ public ContainerTranslationStatistics getStatistics(String projectSlug,
+ String iterationSlug, String docId,
+ @DefaultValue("false") boolean includeWordStats, String[] locales) {
+ //TODO implement
+ throw new UnsupportedOperationException("Implement me!");
+ //return null;
+ }
+
+ @Override
+ public ContributionStatistics getContributionStatistics(String projectSlug,
+ String versionSlug, String username, String dateRange) {
+ //TODO implement
+ throw new UnsupportedOperationException("Implement me!");
+ //return null;
+ }
+}
diff --git a/rest-client/src/main/java/org/zanata/rest/client/TraceDebugInterceptor.java b/rest-client/src/main/java/org/zanata/rest/client/TraceDebugInterceptor.java
new file mode 100644
index 00000000..d1a2f2e5
--- /dev/null
+++ b/rest-client/src/main/java/org/zanata/rest/client/TraceDebugInterceptor.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014, Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.zanata.rest.client;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.ext.Provider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.zanata.rest.RestConstant;
+
+import com.google.common.base.Charsets;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.filter.ClientFilter;
+
+/**
+ * Performs logging of Requests on the client side. This interceptor
+ * logs at the level TRACE, unless the option logHttp is set, in which case it
+ * will log as INFO.
+ *
+ * @author Patrick Huang
+ * pahuang@redhat.com
+ *
+ */
+@Provider
+public class TraceDebugInterceptor extends ClientFilter {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(TraceDebugInterceptor.class);
+
+ private boolean logHttp;
+
+ @Override
+ public ClientResponse handle(ClientRequest cr)
+ throws ClientHandlerException {
+ if (!logHttp && !log.isTraceEnabled()) {
+ return getNext().handle(cr);
+ }
+ log(">> REST Request: " + cr.getMethod() + " => "
+ + cr.getURI());
+
+ // Log before sending a request
+ for (String key : cr.getHeaders().keySet()) {
+ String headerVal =
+ cr.getHeaders().get(key).toString();
+ if (key.equals(RestConstant.HEADER_API_KEY)) {
+ headerVal =
+ this.maskHeaderValues(
+ cr.getHeaders()
+ .get(key));
+ }
+
+ log(">> Header: " + key + " = " + headerVal);
+ }
+ log(">> body: " + cr.getEntity());
+
+
+
+ ClientResponse response = getNext().handle(cr);
+
+ // log after a response has been received
+ log("<< REST Response: " + response.getStatus()
+ + ":" + response.getClientResponseStatus());
+ for (String key : response.getHeaders().keySet()) {
+ log("<< Header: " + key + " = " +
+ response.getHeaders().get(key));
+ }
+ response.bufferEntity();
+ log(">> Body: " + getPayloadAsString(response));
+ return response;
+ }
+
+ // this is jersey implementation specific
+ private String getPayloadAsString(ClientResponse response) {
+ ByteArrayInputStream entityInputStream = null;
+ try {
+ entityInputStream =
+ (ByteArrayInputStream) response.getEntityInputStream();
+ int available = entityInputStream.available();
+ byte[] data = new byte[available];
+ entityInputStream.read(data);
+ return new String(data, 0, available, Charsets.UTF_8);
+ } catch (Exception e) {
+ log.warn("can't read response payload");
+ return "[error reading response]";
+ } finally {
+ if (entityInputStream != null) {
+ entityInputStream.reset();
+ }
+ }
+
+ }
+
+ public TraceDebugInterceptor(boolean logHttp) {
+ this.logHttp = logHttp;
+ }
+
+ private void log(String msg) {
+ if (logHttp) {
+ log.info(msg);
+ } else {
+ log.trace(msg);
+ }
+ }
+
+ /**
+ * Masks a list of header values so they are not displayed as clear text in
+ * the logs.
+ */
+ private String maskHeaderValues(List