From d1cdaca6396cac8344108c69f22099337cad1ec1 Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Mon, 1 Dec 2014 14:21:22 +1000 Subject: [PATCH] rhbz1124630 - move client interface to server to avoid massive change in testing --- functional-test/pom.xml | 89 ++-- .../concurrentedit/ConcurrentAccessTest.java | 52 +-- .../feature/misc/RateLimitRestAndUITest.java | 187 ++++---- .../zanata/util/SampleDataResourceClient.java | 71 ++- .../org/zanata/util/ZanataRestCaller.java | 225 ++++----- pom.xml | 4 +- zanata-war/pom.xml | 7 +- .../rest/client/ApiKeyHeaderDecorator.java | 70 +++ .../zanata/rest/client/IAccountResource.java | 27 ++ .../client/IAsynchronousProcessResource.java | 15 + .../rest/client/ICopyTransResource.java | 14 + .../org/zanata/rest/client/IFileResource.java | 101 +++++ .../zanata/rest/client/IGlossaryResource.java | 60 +++ .../client/IProjectIterationResource.java | 40 ++ .../zanata/rest/client/IProjectResource.java | 39 ++ .../zanata/rest/client/IProjectsResource.java | 50 ++ .../rest/client/ISourceDocResource.java | 108 +++++ .../rest/client/IStatisticsResource.java | 14 + .../rest/client/ITranslatedDocResource.java | 95 ++++ .../client/ITranslationMemoryResource.java | 14 + .../zanata/rest/client/IVersionResource.java | 48 ++ .../rest/client/TraceDebugInterceptor.java | 122 +++++ .../rest/client/ZanataProxyFactory.java | 429 ++++++++++++++++++ .../org/zanata/rest/client/package-info.java | 6 + 24 files changed, 1563 insertions(+), 324 deletions(-) create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IAccountResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IAsynchronousProcessResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/ICopyTransResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IFileResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IGlossaryResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IProjectIterationResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IProjectResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IProjectsResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/ISourceDocResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IStatisticsResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/ITranslatedDocResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/ITranslationMemoryResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/IVersionResource.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/TraceDebugInterceptor.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/ZanataProxyFactory.java create mode 100644 zanata-war/src/test/java/org/zanata/rest/client/package-info.java diff --git a/functional-test/pom.xml b/functional-test/pom.xml index 4a046aea1e..d971a6d79c 100644 --- a/functional-test/pom.xml +++ b/functional-test/pom.xml @@ -207,65 +207,57 @@ 2.4 - - - org.codehaus.jackson - jackson-mapper-asl - - org.zanata zanata-common-api + + + org.jboss.resteasy + jaxrs-api + + + org.jboss.resteasy + resteasy-jaxrs + + + org.jboss.resteasy + resteasy-jaxb-provider + + org.codehaus.jackson jackson-core-asl - - org.jboss.resteasy - jaxrs-api - ${resteasy.scope} - org.zanata zanata-rest-client compile - - - org.jboss.resteasy - resteasy-jaxrs - javassist - javassist - - - javax.annotation - jsr250-api + org.apache.httpcomponents + httpclient + - org.zanata - zanata-common-util - - - org.jboss.resteasy - resteasy-jaxb-provider + org.apache.httpcomponents + httpclient + 4.3.1 - javax.xml.bind - jaxb-api - - - org.jboss.logging - jboss-logging + commons-logging + commons-logging - + + org.zanata + zanata-common-util + org.zanata @@ -327,6 +319,33 @@ joda-time + + com.jayway.awaitility + awaitility + 1.6.3 + + + cglib + cglib-nodep + + + org.objenesis + objenesis + + + test + + + cglib + cglib-nodep + test + + + org.objenesis + objenesis + test + + org.codehaus.groovy groovy-all @@ -855,7 +874,6 @@ mysql:mysql-connector-java org.codehaus.groovy:groovy-all - org.jboss.resteasy:resteasy-jaxb-provider org.projectlombok:lombok @@ -996,3 +1014,4 @@ + diff --git a/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java b/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java index 45a4a3c749..a414cea34b 100644 --- a/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java +++ b/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java @@ -20,6 +20,10 @@ */ package org.zanata.feature.concurrentedit; +import static org.assertj.core.api.Assertions.assertThat; +import static org.zanata.util.ZanataRestCaller.buildSourceResource; +import static org.zanata.util.ZanataRestCaller.buildTextFlow; + import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; @@ -29,31 +33,26 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; -import org.jboss.resteasy.client.ClientRequest; +import javax.ws.rs.core.MediaType; + import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.Feature; -import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; +import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.rest.dto.resource.Resource; import org.zanata.util.AddUsersRule; import org.zanata.util.Constants; import org.zanata.util.PropertiesHolder; import org.zanata.util.ZanataRestCaller; + import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.Lists; - -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.zanata.util.ZanataRestCaller.buildSourceResource; -import static org.zanata.util.ZanataRestCaller.buildTextFlow; -import static org.zanata.util.ZanataRestCaller.checkStatusAndReleaseConnection; -import static org.zanata.util.ZanataRestCaller.getStatusAndReleaseConnection; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.WebResource; /** * @author Patrick Huang input) { } } - private static ClientRequest clientRequestAsAdmin(String path) { - ClientRequest clientRequest = - new ClientRequest( - PropertiesHolder.getProperty(Constants.zanataInstance - .value()) + path); - clientRequest.header("X-Auth-User", "admin"); - clientRequest.header("X-Auth-Token", - PropertiesHolder.getProperty(Constants.zanataApiKey.value())); - clientRequest.header("Content-Type", "application/xml"); - return clientRequest; + private static WebResource.Builder clientRequestAsAdmin(String path) { + return Client + .create() + .resource(PropertiesHolder.getProperty(Constants.zanataInstance + .value()) + path) + .header("X-Auth-User", "admin") + .header("X-Auth-Token", + PropertiesHolder.getProperty(Constants.zanataApiKey + .value())) + .header("Content-Type", "application/xml"); } } diff --git a/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java b/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java index 1b1c030046..2c95d81e9f 100644 --- a/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java +++ b/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java @@ -20,6 +20,8 @@ */ package org.zanata.feature.misc; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; @@ -27,17 +29,17 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; + import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import org.jboss.resteasy.client.ClientRequest; -import org.jboss.resteasy.client.core.BaseClientResponse; +import lombok.extern.slf4j.Slf4j; + import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.Feature; -import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; +import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.page.administration.AdministrationPage; import org.zanata.page.administration.ServerConfigurationPage; import org.zanata.util.AddUsersRule; @@ -45,22 +47,23 @@ import org.zanata.util.PropertiesHolder; import org.zanata.util.ZanataRestCaller; import org.zanata.workflow.LoginWorkFlow; + import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import lombok.extern.slf4j.Slf4j; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.zanata.util.ZanataRestCaller.checkStatusAndReleaseConnection; -import static org.zanata.util.ZanataRestCaller.getStatusAndReleaseConnection; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.api.client.WebResource; /** * @author Patrick Huang pahuang@redhat.com */ -@Feature(summary = "The system can be set to rate consecutive REST access calls", - tcmsTestPlanIds = 5315, tcmsTestCaseIds = 0) +@Feature( + summary = "The system can be set to rate consecutive REST access calls", + tcmsTestPlanIds = 5315, tcmsTestCaseIds = 0) @Category(DetailedTest.class) @Slf4j public class RateLimitRestAndUITest extends ZanataTestCase { @@ -71,7 +74,7 @@ public class RateLimitRestAndUITest extends ZanataTestCase { private static final String TRANSLATOR = "translator"; private static final String TRANSLATOR_API = PropertiesHolder.getProperty(Constants.zanataTranslatorKey - .value()); + .value()); private String maxConcurrentPathParam = "c/max.concurrent.req.per.apikey"; private String maxActivePathParam = "c/max.active.req.per.apikey"; @@ -90,9 +93,9 @@ public void canConfigureRateLimitByWebUI() { AdministrationPage administrationPage = serverConfigPage.inputMaxConcurrent(5).inputMaxActive(3).save(); - //RHBZ1160651 - //assertThat(administrationPage.getNotificationMessage()) - // .isEqualTo("Configuration was successfully updated."); + // RHBZ1160651 + // assertThat(administrationPage.getNotificationMessage()) + // .isEqualTo("Configuration was successfully updated."); serverConfigPage = administrationPage.goToServerConfigPage(); @@ -104,96 +107,93 @@ public void canConfigureRateLimitByWebUI() { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void canCallServerConfigurationRestService() throws Exception { - ClientRequest clientRequest = + WebResource.Builder clientRequest = clientRequestAsAdmin("rest/configurations/" + maxConcurrentPathParam); - clientRequest.body("text/plain", "1"); + clientRequest.entity("1", "text/plain"); // can put - Response putResponse = clientRequest.put(); - - assertThat(getStatusAndReleaseConnection(putResponse)).isEqualTo(201); + clientRequest.put(); // can get single configuration - Response getResponse = + String rateLimitConfig = clientRequestAsAdmin( - "rest/configurations/" + maxConcurrentPathParam).get(); + "rest/configurations/" + maxConcurrentPathParam).get( + String.class); - assertThat(getResponse.getStatus()).isEqualTo(200); - String rateLimitConfig = - ((BaseClientResponse) getResponse) - .getEntity(String.class); assertThat(rateLimitConfig) .contains("max.concurrent.req.per.apikey"); assertThat(rateLimitConfig).contains("1"); // can get all configurations - Response getAllResponse = - clientRequestAsAdmin("rest/configurations/").get(); - BaseClientResponse baseClientResponse = - (BaseClientResponse) getAllResponse; - - String configurations = baseClientResponse.getEntity(String.class); + String configurations = + clientRequestAsAdmin("rest/configurations/").get(String.class); log.info("result {}", configurations); - assertThat(getStatusAndReleaseConnection(getAllResponse)) - .isEqualTo(200); assertThat(configurations).isNotNull(); } - private static ClientRequest clientRequestAsAdmin(String path) { - ClientRequest clientRequest = - new ClientRequest( - PropertiesHolder.getProperty(Constants.zanataInstance - .value()) + path); - clientRequest.header("X-Auth-User", "admin"); - clientRequest.header("X-Auth-Token", - PropertiesHolder.getProperty(Constants.zanataApiKey.value())); - clientRequest.header("Content-Type", "application/xml"); - return clientRequest; + private static WebResource.Builder clientRequestAsAdmin(String path) { + return Client + .create() + .resource(PropertiesHolder.getProperty(Constants.zanataInstance + .value()) + path) + .header("X-Auth-User", "admin") + .header("X-Auth-Token", + PropertiesHolder.getProperty(Constants.zanataApiKey + .value())) + .header("Content-Type", "application/xml"); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void serverConfigurationRestServiceOnlyAvailableToAdmin() throws Exception { // all request should be rejected - Response response = - clientRequestAsTranslator("rest/configurations/").get(); - assertThat(getStatusAndReleaseConnection(response)).isEqualTo(401); + ClientResponse response = + clientRequestAsTranslator("rest/configurations/").get( + ClientResponse.class); + assertThat(response.getStatus()).isEqualTo(401); - Response response1 = + ClientResponse response1 = clientRequestAsTranslator( - "rest/configurations/c/email.admin.addr").get(); - assertThat(getStatusAndReleaseConnection(response1)).isEqualTo(401); - - ClientRequest request = - clientRequestAsTranslator("rest/configurations/c/email.admin.addr"); - request.body("text/plain", "admin@email.com"); - Response response2 = request.put(); - assertThat(getStatusAndReleaseConnection(response2)).isEqualTo(401); + "rest/configurations/c/email.admin.addr").get( + ClientResponse.class); + assertThat(response1.getStatus()).isEqualTo(401); + + WebResource.Builder request = + clientRequestAsTranslator( + "rest/configurations/c/email.admin.addr"); + request.entity("admin@email.com", "text/plain"); + try { + request.put(); + } catch (UniformInterfaceException e) { + assertThat(e.getResponse().getStatus()).isEqualTo(401); + } } - private static ClientRequest clientRequestAsTranslator(String path) { - ClientRequest clientRequest = - new ClientRequest( - PropertiesHolder.getProperty(Constants.zanataInstance - .value()) + path); - clientRequest.header("X-Auth-User", TRANSLATOR); - clientRequest.header("X-Auth-Token", TRANSLATOR_API); - clientRequest.header("Content-Type", "application/xml"); - return clientRequest; + private static WebResource.Builder clientRequestAsTranslator(String path) { + return Client.create().resource( + PropertiesHolder.getProperty(Constants.zanataInstance + .value()) + path) + .header("X-Auth-User", TRANSLATOR) + .header("X-Auth-Token", TRANSLATOR_API) + .header("Content-Type", "application/xml"); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void canOnlyDealWithKnownConfiguration() throws Exception { - ClientRequest clientRequest = + WebResource.Builder clientRequest = clientRequestAsAdmin("rest/configurations/c/abc"); - Response putResponse = clientRequest.put(); - assertThat(getStatusAndReleaseConnection(putResponse)).isEqualTo(400); + try { + clientRequest.put(); + } catch (UniformInterfaceException e) { + assertThat(e.getResponse().getStatus()).isEqualTo(400); + } - Response getResponse = - clientRequestAsAdmin("rest/configurations/c/abc").get(); - assertThat(getStatusAndReleaseConnection(getResponse)).isEqualTo(404); + ClientResponse getResponse = + clientRequestAsAdmin("rest/configurations/c/abc").get( + ClientResponse.class); + assertThat(getResponse.getStatus()).isEqualTo(404); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) @@ -204,12 +204,12 @@ public void canLimitConcurrentRestRequestsPerAPIKey() throws Exception { new ZanataRestCaller(TRANSLATOR, TRANSLATOR_API) .createProjectAndVersion(projectSlug, iterationSlug, "gettext"); - ClientRequest clientRequest = + WebResource.Builder clientRequest = clientRequestAsAdmin("rest/configurations/" + maxConcurrentPathParam); - clientRequest.body(MediaType.TEXT_PLAIN_TYPE, "2"); + clientRequest.entity("2", MediaType.TEXT_PLAIN_TYPE); - checkStatusAndReleaseConnection(clientRequest.put()); + clientRequest.put(); // prepare to fire multiple REST requests final AtomicInteger atomicInteger = new AtomicInteger(1); @@ -260,19 +260,20 @@ ImmutableList.> builder() @Test(timeout = 5000) public void exceptionWillReleaseSemaphore() throws Exception { // Given: max active is set to 1 - ClientRequest configRequest = + WebResource.Builder configRequest = clientRequestAsAdmin("rest/configurations/" + maxActivePathParam); - configRequest.body(MediaType.TEXT_PLAIN_TYPE, "1"); - checkStatusAndReleaseConnection(configRequest.put()); + configRequest.entity("1", MediaType.TEXT_PLAIN_TYPE); + configRequest.put(); // When: multiple requests that will result in a mapped exception - ClientRequest clientRequest = - clientRequestAsAdmin("rest/test/data/sample/dummy?exception=org.zanata.rest.NoSuchEntityException"); - getStatusAndReleaseConnection(clientRequest.get()); - getStatusAndReleaseConnection(clientRequest.get()); - getStatusAndReleaseConnection(clientRequest.get()); - getStatusAndReleaseConnection(clientRequest.get()); + WebResource.Builder clientRequest = + clientRequestAsAdmin( + "rest/test/data/sample/dummy?exception=org.zanata.rest.NoSuchEntityException"); + clientRequest.get(ClientResponse.class); + clientRequest.get(ClientResponse.class); + clientRequest.get(ClientResponse.class); + clientRequest.get(ClientResponse.class); // Then: request that result in exception should still release // semaphore. i.e. no permit leak @@ -282,19 +283,20 @@ public void exceptionWillReleaseSemaphore() throws Exception { @Test(timeout = 5000) public void unmappedExceptionWillAlsoReleaseSemaphore() throws Exception { // Given: max active is set to 1 - ClientRequest configRequest = + WebResource.Builder configRequest = clientRequestAsAdmin("rest/configurations/" + maxActivePathParam); - configRequest.body(MediaType.TEXT_PLAIN_TYPE, "1"); - checkStatusAndReleaseConnection(configRequest.put()); + configRequest.entity("1", MediaType.TEXT_PLAIN_TYPE); + configRequest.put(); // When: multiple requests that will result in an unmapped exception - ClientRequest clientRequest = - clientRequestAsAdmin("rest/test/data/sample/dummy?exception=java.lang.RuntimeException"); - getStatusAndReleaseConnection(clientRequest.get()); - getStatusAndReleaseConnection(clientRequest.get()); - getStatusAndReleaseConnection(clientRequest.get()); - getStatusAndReleaseConnection(clientRequest.get()); + WebResource.Builder clientRequest = + clientRequestAsAdmin( + "rest/test/data/sample/dummy?exception=java.lang.RuntimeException"); + clientRequest.get(ClientResponse.class); + clientRequest.get(ClientResponse.class); + clientRequest.get(ClientResponse.class); + clientRequest.get(ClientResponse.class); // Then: request that result in exception should still release // semaphore. i.e. no permit leak @@ -338,3 +340,4 @@ public Integer apply(Future input) { }); } } + diff --git a/functional-test/src/test/java/org/zanata/util/SampleDataResourceClient.java b/functional-test/src/test/java/org/zanata/util/SampleDataResourceClient.java index c73a2585fa..852c52c878 100644 --- a/functional-test/src/test/java/org/zanata/util/SampleDataResourceClient.java +++ b/functional-test/src/test/java/org/zanata/util/SampleDataResourceClient.java @@ -20,13 +20,8 @@ */ package org.zanata.util; -import javax.ws.rs.core.Response; - -import org.hamcrest.Matchers; -import org.jboss.resteasy.client.ClientRequest; -import org.jboss.resteasy.client.ClientResponse; - -import static org.hamcrest.MatcherAssert.assertThat; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.WebResource; /** * @author Patrick Huang DONE_STATUS = + EnumSet.of(ProcessStatus.ProcessStatusCode.Failed, + ProcessStatus.ProcessStatusCode.Finished, + ProcessStatus.ProcessStatusCode.NotAccepted); /** * Rest client as user admin. @@ -73,8 +78,11 @@ public ZanataRestCaller() { /** * Rest client as given user. - * @param username username - * @param apiKey user api key + * + * @param username + * username + * @param apiKey + * user api key */ public ZanataRestCaller(String username, String apiKey) { try { @@ -82,9 +90,9 @@ public ZanataRestCaller(String username, String apiKey) { String baseUrl = PropertiesHolder.getProperty(Constants.zanataInstance .value()); - zanataProxyFactory = - new ZanataProxyFactory(new URI(baseUrl), username, apiKey, - versionInfo, false, false); + restClientFactory = + new RestClientFactory(new URI(baseUrl), username, apiKey, + versionInfo, true, false); } catch (URISyntaxException e) { throw Throwables.propagate(e); } @@ -92,72 +100,50 @@ public ZanataRestCaller(String username, String apiKey) { public void createProjectAndVersion(String projectSlug, String iterationSlug, String projectType) { - IProjectResource projectResource = - zanataProxyFactory.getProject(projectSlug); + ProjectClient projectResource = + restClientFactory.getProjectClient(projectSlug); Project project = new Project(); project.setDefaultType(projectType); project.setId(projectSlug); project.setName(projectSlug); - ClientResponse response = projectResource.put(project); - checkStatusAndReleaseConnection(response); + projectResource.put(project); ProjectIteration iteration = new ProjectIteration(); iteration.setId(iterationSlug); - IProjectIterationResource projectIteration = - zanataProxyFactory.getProjectIteration(projectSlug, + ProjectIterationClient projectIteration = + restClientFactory.getProjectIterationClient(projectSlug, iterationSlug); - ClientResponse iterationResponse = projectIteration.put(iteration); - checkStatusAndReleaseConnection(iterationResponse); + projectIteration.put(iteration); } public void deleteSourceDoc(String projectSlug, String iterationSlug, String resourceName) { - ClientResponse response = zanataProxyFactory - .getSourceDocResource(projectSlug, iterationSlug) + restClientFactory + .getSourceDocResourceClient(projectSlug, iterationSlug) .deleteResource(resourceName); - checkStatusAndReleaseConnection(response); } public int postSourceDocResource(String projectSlug, String iterationSlug, Resource resource, boolean copytrans) { - ClientResponse response = zanataProxyFactory - .getSourceDocResource(projectSlug, iterationSlug).post( - resource, - Collections.emptySet(), copytrans); - int status = response.getStatus(); - response.releaseConnection(); - return status; + asyncPushSource(projectSlug, iterationSlug, resource, copytrans); + return 201; } - public ContainerTranslationStatistics getStatistics(String projectSlug, String iterationSlug, + public ContainerTranslationStatistics getStatistics(String projectSlug, + String iterationSlug, String[] locales) { - return zanataProxyFactory.getStatisticsResource() + return restClientFactory.getStatisticsClient() .getStatistics(projectSlug, iterationSlug, false, false, locales); } public void putSourceDocResource(String projectSlug, String iterationSlug, String idNoSlash, Resource resource, boolean copytrans) { - ClientResponse response = - zanataProxyFactory.getSourceDocResource(projectSlug, - iterationSlug).putResource(idNoSlash, resource, - Collections.emptySet(), copytrans); - checkStatusAndReleaseConnection(response); - } - - public static int checkStatusAndReleaseConnection( - Response response) { - ClientResponse clientResponse = (ClientResponse) response; - ClientUtility.checkResult(clientResponse); - clientResponse.releaseConnection(); - return response.getStatus(); - } - - public static int getStatusAndReleaseConnection(Response response) { - ((BaseClientResponse) response).releaseConnection(); - return response.getStatus(); + restClientFactory.getSourceDocResourceClient(projectSlug, + iterationSlug).putResource(idNoSlash, resource, + Collections.emptySet(), copytrans); } public static Resource buildSourceResource(String name, @@ -181,23 +167,20 @@ public static TextFlow buildTextFlow(String resId, String... contents) { public void postTargetDocResource(String projectSlug, String iterationSlug, String idNoSlash, LocaleId localeId, TranslationsResource translationsResource, String mergeType) { - - ClientResponse response = zanataProxyFactory - .getTranslatedDocResource(projectSlug, iterationSlug) - .putTranslations(idNoSlash, localeId, - translationsResource, - Collections.emptySet(), mergeType); - checkStatusAndReleaseConnection(response); + asyncPushTarget(projectSlug, iterationSlug, idNoSlash, localeId, + translationsResource, mergeType); } - public static TranslationsResource buildTranslationResource(TextFlowTarget... targets) { + public static TranslationsResource buildTranslationResource( + TextFlowTarget... targets) { TranslationsResource resource = new TranslationsResource(); resource.setRevision(0); resource.getTextFlowTargets().addAll(Lists.newArrayList(targets)); return resource; } - public static TextFlowTarget buildTextFlowTarget(String resId, String... contents) { + public static TextFlowTarget buildTextFlowTarget(String resId, + String... contents) { TextFlowTarget target = new TextFlowTarget(resId); target.setRevision(0); target.setTextFlowRevision(0); @@ -206,76 +189,58 @@ public static TextFlowTarget buildTextFlowTarget(String resId, String... content return target; } - public void runCopyTrans(String projectSlug, String iterationSlug, - String docId) { - ICopyTransResource resource = - zanataProxyFactory.getCopyTransResource(); - CopyTransStatus copyTransStatus = - resource.startCopyTrans(projectSlug, - iterationSlug, docId); - log.info("copyTrans started: {}-{}-{}", projectSlug, iterationSlug, docId); - while (copyTransStatus.isInProgress()) { - try { - Thread.sleep(1000); - log.debug("copyTrans completion: {}", copyTransStatus.getPercentageComplete()); - copyTransStatus = resource.getCopyTransStatus(projectSlug, iterationSlug, docId); - } catch (InterruptedException e) { - throw Throwables.propagate(e); - } - } - log.info("copyTrans completed: {}-{}-{}", projectSlug, iterationSlug, docId); + public void runCopyTrans(final String projectSlug, + final String iterationSlug, + final String docId) { + final CopyTransClient copyTransClient = + restClientFactory.getCopyTransClient(); + copyTransClient.startCopyTrans(projectSlug, iterationSlug, docId); + log.info("copyTrans started: {}-{}-{}", projectSlug, iterationSlug, + docId); + Awaitility.await().pollInterval(Duration.ONE_SECOND) + .until(new Callable() { + @Override + public Boolean call() throws Exception { + return !copyTransClient.getCopyTransStatus(projectSlug, + iterationSlug, docId).isInProgress(); + } + }); + log.info("copyTrans completed: {}-{}-{}", projectSlug, iterationSlug, + docId); } public void asyncPushSource(String projectSlug, String iterationSlug, Resource sourceResource, boolean copyTrans) { - IAsynchronousProcessResource resource = - zanataProxyFactory.getAsynchronousProcessResource(); - ProcessStatus processStatus = resource.startSourceDocCreationOrUpdate( - sourceResource.getName(), projectSlug, iterationSlug, - sourceResource, - Sets.newHashSet(), false); - processStatus = waitUntilFinished(resource, processStatus); + AsyncProcessClient asyncProcessClient = + restClientFactory.getAsyncProcessClient(); + ProcessStatus processStatus = + asyncProcessClient.startSourceDocCreationOrUpdate( + sourceResource.getName(), projectSlug, iterationSlug, + sourceResource, + Sets. newHashSet(), false); + processStatus = waitUntilFinished(asyncProcessClient, processStatus); log.info("finished async source push ({}-{}): {}", projectSlug, iterationSlug, processStatus.getStatusCode()); if (copyTrans) { - log.info("start copyTrans for {} - {}", projectSlug, iterationSlug); - ICopyTransResource copyTransResource = - zanataProxyFactory.getCopyTransResource(); - CopyTransStatus copyTransStatus = - copyTransResource - .startCopyTrans(projectSlug, iterationSlug, - sourceResource.getName()); - while (copyTransStatus.isInProgress()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw Throwables.propagate(e); - } - copyTransStatus = copyTransResource.getCopyTransStatus(projectSlug, iterationSlug, sourceResource.getName()); - } - log.info("finish copyTrans for {} - {}", projectSlug, iterationSlug); + runCopyTrans(projectSlug, iterationSlug, sourceResource.getName()); } } private ProcessStatus waitUntilFinished( - IAsynchronousProcessResource resource, + final AsyncProcessClient asyncProcessClient, ProcessStatus processStatus) { - EnumSet doneStatus = - EnumSet.of(ProcessStatus.ProcessStatusCode.Failed, - ProcessStatus.ProcessStatusCode.Finished, - ProcessStatus.ProcessStatusCode.NotAccepted); - String processId = processStatus.getUrl(); - while (!doneStatus.contains(processStatus.getStatusCode())) { - log.debug("{} percent completed {}, messages: {}", processId, - processStatus.getPercentageComplete(), - processStatus.getMessages()); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw Throwables.propagate(e); - } - processStatus = resource.getProcessStatus(processId); - } + final String processId = processStatus.getUrl(); + Awaitility.await() + .pollInterval(Duration.ONE_SECOND) + .until(new Callable() { + @Override + public Boolean call() throws Exception { + return DONE_STATUS.contains( + asyncProcessClient.getProcessStatus(processId) + .getStatusCode()); + } + }); + if (processStatus.getStatusCode().equals( ProcessStatus.ProcessStatusCode.Failed)) { throw new RuntimeException(processStatus.getStatusCode().toString()); @@ -284,17 +249,19 @@ private ProcessStatus waitUntilFinished( } public void asyncPushTarget(String projectSlug, String iterationSlug, - String docId, LocaleId localeId, TranslationsResource transResource, + String docId, LocaleId localeId, + TranslationsResource transResource, String mergeType, boolean assignCreditToUploader) { - IAsynchronousProcessResource resource = - zanataProxyFactory.getAsynchronousProcessResource(); + AsyncProcessClient asyncProcessClient = + restClientFactory.getAsyncProcessClient(); ProcessStatus processStatus = - resource.startTranslatedDocCreationOrUpdate(docId, projectSlug, + asyncProcessClient.startTranslatedDocCreationOrUpdate(docId, + projectSlug, iterationSlug, localeId, transResource, - Collections.emptySet(), mergeType, - assignCreditToUploader); - processStatus = waitUntilFinished(resource, processStatus); + Collections. emptySet(), mergeType, assignCreditToUploader); + processStatus = waitUntilFinished(asyncProcessClient, processStatus); log.info("finished async translation({}-{}) push: {}", projectSlug, iterationSlug, processStatus.getStatusCode()); } } + diff --git a/pom.xml b/pom.xml index b5b3966b84..94c2740360 100644 --- a/pom.xml +++ b/pom.xml @@ -43,8 +43,8 @@ 3.7.0-SNAPSHOT - 3.3.1 - 3.3.1 + 3.4.1 + 3.6.0-SNAPSHOT 3.6.0 4.5.0.Final diff --git a/zanata-war/pom.xml b/zanata-war/pom.xml index 3a8e6f1bcb..2c1e3f8908 100644 --- a/zanata-war/pom.xml +++ b/zanata-war/pom.xml @@ -109,8 +109,6 @@ org.opensymphony.quartz:quartz org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-api - - org.jboss.resteasy:resteasy-multipart-provider com.lowagie:itext @@ -164,6 +162,7 @@ com.ning.maven.plugins maven-duplicate-finder-plugin + false com.google.gwt @@ -2075,10 +2074,12 @@ org.apache.httpcomponents httpcore + 4.2.4 org.apache.httpcomponents httpclient + 4.2.5 commons-logging @@ -2292,3 +2293,5 @@ + + diff --git a/zanata-war/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java b/zanata-war/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java new file mode 100644 index 0000000000..805f7ab5b6 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java @@ -0,0 +1,70 @@ +package org.zanata.rest.client; + +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.annotations.interception.ClientInterceptor; +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.spi.interception.ClientExecutionContext; +import org.jboss.resteasy.spi.interception.ClientExecutionInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zanata.rest.RestConstant; + +@Provider +@ClientInterceptor +public class ApiKeyHeaderDecorator implements ClientExecutionInterceptor { + private static Logger log = LoggerFactory + .getLogger(ApiKeyHeaderDecorator.class); + private String apiKey; + private String username; + private String ver; + + public ApiKeyHeaderDecorator() { + } + + public ApiKeyHeaderDecorator(String username, String apiKey, String ver) { + this.username = username; + this.apiKey = apiKey; + this.ver = ver; + } + + @SuppressWarnings("rawtypes") + @Override + public ClientResponse execute(ClientExecutionContext ctx) throws Exception { + ctx.getRequest().getHeadersAsObjects() + .add(RestConstant.HEADER_USERNAME, username); + ctx.getRequest().getHeadersAsObjects() + .add(RestConstant.HEADER_API_KEY, apiKey); + ctx.getRequest().getHeadersAsObjects() + .add(RestConstant.HEADER_VERSION_NO, ver); + try { + return ctx.proceed(); + } catch (Error e) { + // NB Seam/RestEasy doesn't log these exceptions fully for some + // reason + log.warn("error processing request", e); + throw e; + } catch (Exception e) { + // NB Seam/RestEasy doesn't log these exceptions fully for some + // reason + log.warn("exception processing request", e); + throw e; + } + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IAccountResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IAccountResource.java new file mode 100644 index 0000000000..208e53f07f --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IAccountResource.java @@ -0,0 +1,27 @@ +package org.zanata.rest.client; + +import org.jboss.resteasy.client.ClientResponse; +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.Account; +import org.zanata.rest.service.AccountResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Produces; + +// TODO remove the template parameters from AccountResource's Path +//@Path(AccountResource.SERVICE_PATH) +public interface IAccountResource extends AccountResource { + + @GET + @Produces({ MediaTypes.APPLICATION_ZANATA_ACCOUNT_XML, + MediaTypes.APPLICATION_ZANATA_ACCOUNT_JSON }) + public ClientResponse get(); + + @PUT + @Consumes({ MediaTypes.APPLICATION_ZANATA_ACCOUNT_XML, + MediaTypes.APPLICATION_ZANATA_ACCOUNT_JSON }) + public ClientResponse put(Account account); + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IAsynchronousProcessResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IAsynchronousProcessResource.java new file mode 100644 index 0000000000..155b6d663f --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IAsynchronousProcessResource.java @@ -0,0 +1,15 @@ +package org.zanata.rest.client; + +import org.zanata.rest.service.AsynchronousProcessResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path(AsynchronousProcessResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface IAsynchronousProcessResource extends + AsynchronousProcessResource { +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/ICopyTransResource.java b/zanata-war/src/test/java/org/zanata/rest/client/ICopyTransResource.java new file mode 100644 index 0000000000..f8ccdae798 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/ICopyTransResource.java @@ -0,0 +1,14 @@ +package org.zanata.rest.client; + +import org.zanata.rest.service.CopyTransResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path(CopyTransResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface ICopyTransResource extends CopyTransResource { +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IFileResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IFileResource.java new file mode 100644 index 0000000000..034b980fc9 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IFileResource.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012, 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 org.jboss.resteasy.annotations.providers.multipart.MultipartForm; +import org.jboss.resteasy.client.ClientResponse; +import org.zanata.rest.DocumentFileUploadForm; +import org.zanata.rest.dto.ChunkUploadResponse; +import org.zanata.rest.service.FileResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +/** + * REST client interface for file upload and download. + * + * @author David Mason, damason@redhat.com + * @see DocumentFileUploadForm + * @see FileResource + */ +@Path(FileResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_OCTET_STREAM }) +@Consumes({ MediaType.APPLICATION_OCTET_STREAM }) +public interface IFileResource extends FileResource { + + @Override + @GET + @Path(ACCEPTED_TYPES_RESOURCE) + @Produces(MediaType.TEXT_PLAIN) + // /file/accepted_types + public + ClientResponse acceptedFileTypes(); + + @Override + @POST + @Path(SOURCE_UPLOAD_TEMPLATE) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_XML) + public ClientResponse uploadSourceFile( + @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @QueryParam("docId") String docId, + @MultipartForm DocumentFileUploadForm uploadForm); + + @Override + @POST + @Path(TRANSLATION_UPLOAD_TEMPLATE) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_XML) + public ClientResponse uploadTranslationFile( + @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @PathParam("locale") String localeId, + @QueryParam("docId") String docId, + @QueryParam("merge") String merge, + @MultipartForm DocumentFileUploadForm uploadForm); + + @Override + @GET + @Path(SOURCE_DOWNLOAD_TEMPLATE) + public ClientResponse downloadSourceFile( + @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @PathParam("fileType") String fileType, + @QueryParam("docId") String docId); + + @Override + @GET + @Path(FILE_DOWNLOAD_TEMPLATE) + public ClientResponse downloadTranslationFile( + @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @PathParam("locale") String locale, + @PathParam("fileType") String fileExtension, + @QueryParam("docId") String docId); +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IGlossaryResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IGlossaryResource.java new file mode 100644 index 0000000000..89fbe21369 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IGlossaryResource.java @@ -0,0 +1,60 @@ +package org.zanata.rest.client; + +import org.codehaus.enunciate.jaxrs.TypeHint; +import org.jboss.resteasy.client.ClientResponse; +import org.zanata.common.LocaleId; +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.Glossary; +import org.zanata.rest.service.GlossaryResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path(GlossaryResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, + MediaTypes.APPLICATION_ZANATA_GLOSSARY_XML, + MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, + MediaTypes.APPLICATION_ZANATA_GLOSSARY_XML, + MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON }) +public interface IGlossaryResource extends GlossaryResource { + public static final String SERVICE_PATH = "/glossary"; + + @Override + @GET + @Produces({ MediaTypes.APPLICATION_ZANATA_GLOSSARY_XML, + MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + @TypeHint(Glossary.class) + public ClientResponse getEntries(); + + @Override + @GET + @Path("/{locale}") + @Produces({ MediaTypes.APPLICATION_ZANATA_GLOSSARY_XML, + MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + @TypeHint(Glossary.class) + public ClientResponse get(@PathParam("locale") LocaleId locale); + + @Override + @PUT + public ClientResponse put(Glossary glossary); + + @Override + @DELETE + @Path("/{locale}") + public ClientResponse deleteGlossary( + @PathParam("locale") LocaleId locale); + + @Override + @DELETE + public ClientResponse deleteGlossaries(); + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IProjectIterationResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IProjectIterationResource.java new file mode 100644 index 0000000000..2d79afbf8c --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IProjectIterationResource.java @@ -0,0 +1,40 @@ +package org.zanata.rest.client; + +import org.jboss.resteasy.client.ClientResponse; +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.ProjectIteration; +import org.zanata.rest.service.ProjectIterationResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +//TODO remove the template parameters from ProjectIterationResource's Path +//@Path(ProjectIterationResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface IProjectIterationResource extends ProjectIterationResource { + + @Override + @GET + @Produces({ MediaTypes.APPLICATION_ZANATA_PROJECT_ITERATION_XML, + MediaTypes.APPLICATION_ZANATA_PROJECT_ITERATION_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public ClientResponse get(); + + @Override + @PUT + @Consumes({ MediaTypes.APPLICATION_ZANATA_PROJECT_ITERATION_XML, + MediaTypes.APPLICATION_ZANATA_PROJECT_ITERATION_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public ClientResponse put(ProjectIteration project); + + @GET + @Path("/config") + @Produces({ MediaType.APPLICATION_XML }) + @Override + ClientResponse sampleConfiguration(); +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IProjectResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IProjectResource.java new file mode 100644 index 0000000000..b394642d78 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IProjectResource.java @@ -0,0 +1,39 @@ +package org.zanata.rest.client; + +import org.jboss.resteasy.client.ClientResponse; +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.Project; +import org.zanata.rest.service.ProjectResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +//TODO remove the template parameters from ProjectResource's Path +//@Path(ProjectResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface IProjectResource extends ProjectResource { + + @HEAD + @Produces({ MediaTypes.APPLICATION_ZANATA_PROJECT_XML, + MediaTypes.APPLICATION_ZANATA_PROJECT_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public ClientResponse head(); + + @GET + @Produces({ MediaTypes.APPLICATION_ZANATA_PROJECT_XML, + MediaTypes.APPLICATION_ZANATA_PROJECT_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public ClientResponse get(); + + @PUT + @Consumes({ MediaTypes.APPLICATION_ZANATA_PROJECT_XML, + MediaTypes.APPLICATION_ZANATA_PROJECT_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public ClientResponse put(Project project); +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IProjectsResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IProjectsResource.java new file mode 100644 index 0000000000..6eb9b3298e --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IProjectsResource.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010, 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 org.jboss.resteasy.client.ClientResponse; +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.Project; +import org.zanata.rest.service.ProjectsResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Rest client interface for project lists. + * + */ +@Path(ProjectsResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface IProjectsResource extends ProjectsResource { + + @Override + @GET + @Produces({ MediaTypes.APPLICATION_ZANATA_PROJECTS_XML, + MediaTypes.APPLICATION_ZANATA_PROJECTS_JSON, + MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public ClientResponse get(); + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/ISourceDocResource.java b/zanata-war/src/test/java/org/zanata/rest/client/ISourceDocResource.java new file mode 100644 index 0000000000..5a6c5633b2 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/ISourceDocResource.java @@ -0,0 +1,108 @@ +/* + * Copyright 2010, 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 org.jboss.resteasy.annotations.providers.jaxb.Wrapped; +import org.jboss.resteasy.client.ClientResponse; +import org.zanata.common.Namespaces; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.ResourceMeta; +import org.zanata.rest.service.SourceDocResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import java.util.List; +import java.util.Set; + +/** + * Client interface for Source document resources. + * + * @author Carlos Munoz camunoz@redhat.com + */ +//TODO remove the template parameters from SourceDocResource's Path +//@Path(SourceDocResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface ISourceDocResource extends SourceDocResource { + @Override + @GET + public ClientResponse> get( + @QueryParam("ext") Set extensions); + + @Override + @POST + public ClientResponse post(Resource messageBody, + @QueryParam("ext") Set extensions, + @QueryParam("copyTrans") @DefaultValue("true") boolean copytrans); + + @Override + @GET + @Path(RESOURCE_SLUG_TEMPLATE) + public ClientResponse getResource( + @PathParam("id") String idNoSlash, + @QueryParam("ext") Set extensions); + + @Override + @PUT + @Path(RESOURCE_SLUG_TEMPLATE) + public ClientResponse putResource( + @PathParam("id") String idNoSlash, Resource resource, + @QueryParam("ext") Set extensions, + @QueryParam("copyTrans") @DefaultValue("true") boolean copytrans); + + @PUT + @Path(RESOURCE_SLUG_TEMPLATE) + @Deprecated + public ClientResponse putResource( + @PathParam("id") String idNoSlash, Resource resource, + @QueryParam("ext") Set extensions); + + @Override + @DELETE + @Path(RESOURCE_SLUG_TEMPLATE) + public ClientResponse deleteResource( + @PathParam("id") String idNoSlash); + + @Override + @GET + @Path(RESOURCE_SLUG_TEMPLATE + "/meta") + public ClientResponse getResourceMeta( + @PathParam("id") String idNoSlash, + @QueryParam("ext") Set extensions); + + @Override + @PUT + @Path(RESOURCE_SLUG_TEMPLATE + "/meta") + public ClientResponse putResourceMeta( + @PathParam("id") String idNoSlash, ResourceMeta messageBody, + @QueryParam("ext") Set extensions); + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IStatisticsResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IStatisticsResource.java new file mode 100644 index 0000000000..0f6b90db14 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IStatisticsResource.java @@ -0,0 +1,14 @@ +package org.zanata.rest.client; + +import org.zanata.rest.service.StatisticsResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path(StatisticsResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface IStatisticsResource extends StatisticsResource { +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/ITranslatedDocResource.java b/zanata-war/src/test/java/org/zanata/rest/client/ITranslatedDocResource.java new file mode 100644 index 0000000000..af5c18395b --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/ITranslatedDocResource.java @@ -0,0 +1,95 @@ +/* + * Copyright 2010, 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 org.jboss.resteasy.client.ClientResponse; +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.service.TranslatedDocResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import java.util.Set; + +import static org.zanata.rest.service.SourceDocResource.RESOURCE_SLUG_TEMPLATE; + +/** + * Client Interface for the Translation Resources service. + */ +//TODO remove the template parameters from TranslatedDocResource's Path +//@Path(TranslatedDocResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface ITranslatedDocResource extends TranslatedDocResource { + @GET + @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + @Deprecated + public ClientResponse getTranslations( + @PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + @QueryParam("ext") Set extensions); + + @Override + @GET + @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + public ClientResponse getTranslations( + @PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + @QueryParam("ext") Set extensions, + @QueryParam("skeletons") boolean skeletons, + @HeaderParam(HttpHeaders.IF_NONE_MATCH) String eTag); + + @Override + @DELETE + @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + public ClientResponse deleteTranslations( + @PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale); + + @Override + @PUT + @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + public ClientResponse putTranslations( + @PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + TranslationsResource messageBody, + @QueryParam("ext") Set extensions, + @QueryParam("merge") @DefaultValue("auto") String merge); + + @PUT + @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + @Deprecated + public ClientResponse putTranslations( + @PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + TranslationsResource messageBody, + @QueryParam("ext") Set extensions); +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/ITranslationMemoryResource.java b/zanata-war/src/test/java/org/zanata/rest/client/ITranslationMemoryResource.java new file mode 100644 index 0000000000..c742aac625 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/ITranslationMemoryResource.java @@ -0,0 +1,14 @@ +package org.zanata.rest.client; + +import org.zanata.rest.service.TranslationMemoryResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path(TranslationMemoryResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML /* , "application/x-tmx" */}) +@Consumes({ MediaType.APPLICATION_XML /* , "application/x-tmx" */}) +public interface ITranslationMemoryResource extends TranslationMemoryResource { +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/IVersionResource.java b/zanata-war/src/test/java/org/zanata/rest/client/IVersionResource.java new file mode 100644 index 0000000000..773fd200a9 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/IVersionResource.java @@ -0,0 +1,48 @@ +/* + * Copyright 2010, 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 org.jboss.resteasy.client.ClientResponse; +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.VersionInfo; +import org.zanata.rest.service.VersionResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Rest service client interface for application version. + * + */ +@Path(VersionResource.SERVICE_PATH) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) +public interface IVersionResource extends VersionResource { + @Override + @GET + @Produces({ MediaTypes.APPLICATION_ZANATA_VERSION_XML, + MediaTypes.APPLICATION_ZANATA_VERSION_JSON }) + public ClientResponse get(); + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/TraceDebugInterceptor.java b/zanata-war/src/test/java/org/zanata/rest/client/TraceDebugInterceptor.java new file mode 100644 index 0000000000..1f2ad88fcd --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/TraceDebugInterceptor.java @@ -0,0 +1,122 @@ +/* + * Copyright 2010, 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.util.ArrayList; +import java.util.List; + +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.annotations.interception.ClientInterceptor; +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.spi.interception.ClientExecutionContext; +import org.jboss.resteasy.spi.interception.ClientExecutionInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zanata.rest.RestConstant; + +/** + * Performs logging of Resteasy 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 Carlos Munoz camunoz@redhat.com + * + */ +@Provider +@ClientInterceptor +public class TraceDebugInterceptor implements ClientExecutionInterceptor { + + private static final Logger log = LoggerFactory + .getLogger(TraceDebugInterceptor.class); + + private boolean logHttp; + + public TraceDebugInterceptor() { + this(true); + } + + public TraceDebugInterceptor(boolean logHttp) { + this.logHttp = logHttp; + } + + private void log(String msg) { + if (logHttp) { + log.info(msg); + } else { + log.trace(msg); + } + } + + @SuppressWarnings("rawtypes") + @Override + public ClientResponse execute(ClientExecutionContext ctx) throws Exception { + if (!logHttp && !log.isTraceEnabled()) { + return ctx.proceed(); + } + + log(">> REST Request: " + ctx.getRequest().getHttpMethod() + " => " + + ctx.getRequest().getUri()); + + // Log before sending a request + for (String key : ctx.getRequest().getHeaders().keySet()) { + String headerVal = + ctx.getRequest().getHeaders().get(key).toString(); + if (key.equals(RestConstant.HEADER_API_KEY)) { + headerVal = + this.maskHeaderValues(ctx.getRequest().getHeaders() + .get(key)); + } + + log(">> Header: " + key + " = " + headerVal); + } + log(">> Body: " + ctx.getRequest().getBody()); + + ClientResponse result = ctx.proceed(); + + // log after a response has been received + log("<< REST Response: " + result.getResponseStatus().getStatusCode() + + ":" + result.getResponseStatus()); + for (Object key : result.getHeaders().keySet()) { + log("<< Header: " + key + " = " + result.getHeaders().get(key)); + } + + return result; + } + + /** + * Masks a list of header values so they are not displayed as clear text in + * the logs. + */ + private String maskHeaderValues(List headerValues) { + List maskedList = new ArrayList(headerValues.size()); + + for (String actualValue : headerValues) { + maskedList.add(actualValue.replaceAll(".", "*")); // mask all + // characters with + // stars + } + + return maskedList.toString(); + } + +} diff --git a/zanata-war/src/test/java/org/zanata/rest/client/ZanataProxyFactory.java b/zanata-war/src/test/java/org/zanata/rest/client/ZanataProxyFactory.java new file mode 100644 index 0000000000..9d075cef0c --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/ZanataProxyFactory.java @@ -0,0 +1,429 @@ +package org.zanata.rest.client; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.UnknownHostException; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.lang.SystemUtils; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.HttpParams; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.jboss.resteasy.client.ClientExecutor; +import org.jboss.resteasy.client.ClientRequestFactory; +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.client.core.ClientInterceptorRepository; +import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor; +import org.jboss.resteasy.client.exception.ResteasyIOException; +import org.jboss.resteasy.plugins.providers.RegisterBuiltin; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zanata.rest.RestConstant; +import org.zanata.rest.dto.VersionInfo; + +import com.google.common.base.Throwables; + +// TODO fix deprecation warnings +public class ZanataProxyFactory { + private static final Logger log = LoggerFactory + .getLogger(ZanataProxyFactory.class); + + static { + ResteasyProviderFactory instance = + ResteasyProviderFactory.getInstance(); + RegisterBuiltin.register(instance); + + if (!SystemUtils.isJavaVersionAtLeast(1.7f)) { + log.warn("Please upgrade to Java 1.7 or later, or you may have trouble connecting to some secure hosts."); + } + } + + private VersionInfo clientApiVersion; + + private String clientVersion; + private String serverVersion; + + private ClientRequestFactory crf; + + /** + * see org.zanata.client.commands.init.InitCommand.MockZanataProxyFactory + */ + protected ZanataProxyFactory() { + } + + /** + * This will by default pass a null ClientExecutor to the + * ClientRequestFactory which in turn create default client executor. If + * sslCertDisabled is true, we will create our customized ClientExecutor. + * + * @param base + * base url + * @param username + * username + * @param apiKey + * api key + * @param clientApiVersion + * client API version + * @param logHttp + * whether to log http output + * @param sslCertDisabled + * whether to disable SSL certificate verification + */ + public ZanataProxyFactory(URI base, String username, String apiKey, + VersionInfo clientApiVersion, boolean logHttp, + boolean sslCertDisabled) { + this(base, username, apiKey, clientApiVersion, logHttp, + sslCertDisabled, true); + } + + /** + * Same as + * org.zanata.rest.client.ZanataProxyFactory#ZanataProxyFactory(java. + * net.URI, java.lang.String, java.lang.String, + * org.zanata.rest.dto.VersionInfo, boolean, boolean) except it allows to + * configure whether to do an eager REST version check to the server. + * + * @param eagerVersionCheck + * whether or not to perform an eager REST version call + */ + public ZanataProxyFactory(URI base, String username, String apiKey, + VersionInfo clientApiVersion, boolean logHttp, + boolean sslCertDisabled, boolean eagerVersionCheck) { + this.clientApiVersion = clientApiVersion; + ClientExecutor clientExecutor = createClientExecutor(sslCertDisabled); + + crf = new ClientRequestFactory(clientExecutor, null, fixBase(base)); + // This is not doing anything. + // There is no easy way to handle redirect with resteasy at the moment. + // See https://issues.jboss.org/browse/RESTEASY-1075 + // See org.zanata.rest.client.ClientUtility.checkResult(org.jboss.resteasy.client.ClientResponse, java.net.URI)() + crf.setFollowRedirects(true); + registerPrefixInterceptor(new TraceDebugInterceptor(logHttp)); + registerPrefixInterceptor(new ApiKeyHeaderDecorator(username, apiKey, + clientApiVersion.getVersionNo())); + } + + + private void warnMismatchAPIVersion(String clientScm, String serverScm) { + if (!serverVersion.equals(clientVersion)) { + log.warn("client API version is {}, but server API version is {}", + clientVersion, serverVersion); + } else if (serverVersion.contains(RestConstant.SNAPSHOT_VERSION) + && !serverScm.equalsIgnoreCase(clientScm)) { + log.warn( + "client API SCM id is {}, but server API SCM id is {}", + clientScm, serverScm); + } + } + + private static ClientExecutor createClientExecutor(boolean sslCertDisabled) { + try { + final SSLContext sslContext = SSLContext.getInstance("TLS"); + + // Create a trust manager that does not validate certificate chains + // against our server + final TrustManager[] trustAllCerts; + if (sslCertDisabled) { + trustAllCerts = + new TrustManager[]{ new AcceptAllX509TrustManager() }; + } else { + trustAllCerts = null; + } + + sslContext.init(null, trustAllCerts, new SecureRandom()); + + // NB: This factory is a workaround to enable SNI with + // httpcomponents-client 4.2; not needed for 4.3 + SSLSocketFactory factory; + if (sslCertDisabled) { + // avoid triggering the problem described here: + // https://stackoverflow.com/questions/7615645/ssl-handshake-alert-unrecognized-name-error-since-upgrade-to-java-1-7-0 + factory = new SSLSocketFactory(sslContext); + } else { + factory = new SSLSocketFactory(sslContext) { + @Override + public Socket connectSocket(Socket socket, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpParams params) + throws IOException, UnknownHostException, + ConnectTimeoutException { + if (socket instanceof SSLSocket) { + try { + PropertyUtils.setProperty(socket, "host", + remoteAddress.getHostName()); + } catch (Exception ex) { + log.warn( + "Unable to enable SNI; you may have trouble connecting to some secure hosts. Please ensure that you are running Java 1.7 or later."); + } + } + return super.connectSocket(socket, remoteAddress, + localAddress, params); + } + }; + } + + HttpClient client = new DefaultHttpClient(); + + if (sslCertDisabled) { + X509HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; + factory.setHostnameVerifier(hostnameVerifier); + } + + ClientConnectionManager manager = client.getConnectionManager(); + manager.getSchemeRegistry().register( + new Scheme("https", 443, factory)); + return new ApacheHttpClient4Executor(client); + + } catch (Exception e) { + log.warn("error creating SSL client", e); + } + return null; + } + + /** + * Returns the Base url to be used for rest Requests. + */ + private URL getBaseUrl() { + try { + return new URL(fixBase(crf.getBase()).toString() + getUrlPrefix()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + private URI getBaseUri() { + try { + return getBaseUrl().toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + protected String getUrlPrefix() { + return "rest/"; + } + + public T createProxy(Class clazz, URI baseUri) { + log.debug("{} proxy uri: {}", clazz.getSimpleName(), baseUri); + T proxy = crf.createProxy(clazz, baseUri); + // CacheFactory.makeCacheable(proxy); + return proxy; + } + + /** + * Returns a client proxy, provided all information is on the proxied + * interface. (i.e. The interface is marked with a {@link javax.ws.rs.Path} + * annotation.) + * + * @param clazz + * Client interface to proxy. + * @return Client proxy for the class. + * @see {@link ZanataProxyFactory#createProxy(Class, java.net.URI)} + */ + public T createProxy(Class clazz) { + return createProxy(clazz, getBaseUri()); + } + + 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; + } + + public IGlossaryResource getGlossaryResource() { + return createProxy(IGlossaryResource.class); + } + + public IAccountResource getAccount(String username) { + return createProxy(IAccountResource.class, getAccountURI(username)); + } + + public URI getAccountURI(String username) { + try { + URL url = new URL(getBaseUrl(), "accounts/u/" + username); + return url.toURI(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public IProjectResource getProject(String proj) { + return createProxy(IProjectResource.class, getProjectURI(proj)); + } + + public URI getProjectURI(String proj) { + try { + URL url = new URL(getBaseUrl(), "projects/p/" + proj); + return url.toURI(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public IProjectIterationResource getProjectIteration(String proj, + String iter) { + return createProxy(IProjectIterationResource.class, + getProjectIterationURI(proj, iter)); + } + + public URI getProjectIterationURI(String proj, String iter) { + try { + URL url = + new URL(getBaseUrl(), "projects/p/" + proj + + "/iterations/i/" + iter); + return url.toURI(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + // NB IProjectsResource is not currently used in Java + public IProjectsResource getProjectsResource() { + return createProxy(IProjectsResource.class); + } + + public ITranslatedDocResource getTranslatedDocResource(String projectSlug, + String versionSlug) { + return createProxy(ITranslatedDocResource.class, + getResourceURI(projectSlug, versionSlug)); + } + + public ISourceDocResource getSourceDocResource(String projectSlug, + String versionSlug) { + return createProxy(ISourceDocResource.class, + getResourceURI(projectSlug, versionSlug)); + } + + public IFileResource getFileResource() { + return createProxy(IFileResource.class); + } + + public IStatisticsResource getStatisticsResource() { + return createProxy(IStatisticsResource.class); + } + + public ICopyTransResource getCopyTransResource() { + return createProxy(ICopyTransResource.class); + } + + public IAsynchronousProcessResource getAsynchronousProcessResource() { + return createProxy(IAsynchronousProcessResource.class); + } + + public URI getResourceURI(String projectSlug, String versionSlug) { + String spec = + "projects/p/" + projectSlug + "/iterations/i/" + versionSlug + + "/r"; + try { + return new URL(getBaseUrl(), spec).toURI(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + String msg = + "URI Syntax error. Please make sure your project (project ID) and version are correct."; + log.error(msg); + log.error("part of your url: {}", spec); + throw new RuntimeException(msg); + } + } + + /** + * @see org.jboss.resteasy.client.core.ClientInterceptorRepositoryImpl#registerInterceptor(Object) + * @param interceptor + */ + public void registerPrefixInterceptor(Object interceptor) { + ClientInterceptorRepository repo = getPrefixInterceptors(); + repo.registerInterceptor(interceptor); + } + + /** + * Workaround for signature incompatibility between RESTEasy 2.x and 3.x + * @return + */ + private ClientInterceptorRepository getPrefixInterceptors() { + try { + Method m = crf.getClass().getMethod("getPrefixInterceptors"); + return (ClientInterceptorRepository) m.invoke(crf); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected IVersionResource createIVersionResource() { + return createProxy(IVersionResource.class, getBaseUri()); + } + + /** + * Compares a given version identifier with the server version. + * + * @param version + * The version to against which to compare the server version. + * @return A positive integer if the server version is greater than the + * given version. A negative integer if the server version is less + * than the given version. 0 if both versions are the same. + */ + public int compareToServerVersion(String version) { + DefaultArtifactVersion srvVersion = + new DefaultArtifactVersion(serverVersion); + DefaultArtifactVersion providedVersion = + new DefaultArtifactVersion(version); + + return srvVersion.compareTo(providedVersion); + } + + 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/zanata-war/src/test/java/org/zanata/rest/client/package-info.java b/zanata-war/src/test/java/org/zanata/rest/client/package-info.java new file mode 100644 index 0000000000..3f961049a3 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/rest/client/package-info.java @@ -0,0 +1,6 @@ +/** + * This package is copied from zanata-api and zanata-rest-client just so + * we don't have to change all our REST tests. + */ +package org.zanata.rest.client; +