diff --git a/aws/demos/googleappengine/pom.xml b/aws/demos/googleappengine/pom.xml
index 620404c015..4f191ded92 100644
--- a/aws/demos/googleappengine/pom.xml
+++ b/aws/demos/googleappengine/pom.xml
@@ -101,7 +101,7 @@
com.google.appengine
appengine-tools-api
- 1.3.0
+ 1.3.2
system
${appengine.home}/lib/appengine-tools-api.jar
diff --git a/demos/gae-tweetstore-spring/pom.xml b/demos/gae-tweetstore-spring/pom.xml
index 2226397c0e..0e8b66ded5 100644
--- a/demos/gae-tweetstore-spring/pom.xml
+++ b/demos/gae-tweetstore-spring/pom.xml
@@ -156,12 +156,12 @@
com.google.appengine
appengine-api-labs
- 1.3.0
+ 1.3.2
com.google.appengine
appengine-tools-api
- 1.3.0
+ 1.3.2
system
${appengine.home}/lib/appengine-tools-api.jar
diff --git a/demos/gae-tweetstore/pom.xml b/demos/gae-tweetstore/pom.xml
index d84f067236..9ebf86a837 100644
--- a/demos/gae-tweetstore/pom.xml
+++ b/demos/gae-tweetstore/pom.xml
@@ -146,12 +146,12 @@
com.google.appengine
appengine-api-labs
- 1.3.0
+ 1.3.2
com.google.appengine
appengine-tools-api
- 1.3.0
+ 1.3.2
system
${appengine.home}/lib/appengine-tools-api.jar
diff --git a/extensions/gae/pom.xml b/extensions/gae/pom.xml
index 3c72cbc8b2..5108126cd6 100644
--- a/extensions/gae/pom.xml
+++ b/extensions/gae/pom.xml
@@ -44,26 +44,27 @@
jclouds-joda
${project.version}
-
- ${project.groupId}
- jclouds-bouncycastle
- ${project.version}
-
com.google.appengine
appengine-api
- 1.3.0
+ 1.3.2
com.google.appengine
appengine-api-stubs
- 1.3.0
+ 1.3.2
+ test
+
+
+ com.google.appengine
+ appengine-testing
+ 1.3.2
test
com.google.appengine
appengine-local-runtime
- 1.3.0
+ 1.3.2
test
diff --git a/extensions/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java b/extensions/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java
new file mode 100644
index 0000000000..58841b6652
--- /dev/null
+++ b/extensions/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java
@@ -0,0 +1,172 @@
+/**
+ *
+ * Copyright (C) 2009 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+package org.jclouds.gae;
+
+import static com.google.appengine.api.urlfetch.FetchOptions.Builder.disallowTruncate;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.core.HttpHeaders;
+
+import org.jclouds.concurrent.ConcurrentUtils;
+import org.jclouds.concurrent.SingleThreaded;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpCommandExecutorService;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.Payload;
+import org.jclouds.http.payloads.ByteArrayPayload;
+import org.jclouds.http.payloads.FilePayload;
+import org.jclouds.http.payloads.InputStreamPayload;
+import org.jclouds.http.payloads.StringPayload;
+
+import com.google.appengine.api.urlfetch.FetchOptions;
+import com.google.appengine.api.urlfetch.HTTPHeader;
+import com.google.appengine.api.urlfetch.HTTPMethod;
+import com.google.appengine.api.urlfetch.HTTPRequest;
+import com.google.appengine.api.urlfetch.HTTPResponse;
+import com.google.appengine.api.urlfetch.URLFetchService;
+import com.google.appengine.repackaged.com.google.common.base.Throwables;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closeables;
+import com.google.common.util.concurrent.Executors;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Google App Engine version of {@link HttpCommandExecutorService} using their fetchAsync call
+ *
+ * @author Adrian Cole
+ */
+@SingleThreaded
+@Singleton
+public class AsyncGaeHttpCommandExecutorService implements HttpCommandExecutorService {
+ public static final String USER_AGENT = "jclouds/1.0 urlfetch/1.3.2";
+
+ private final URLFetchService urlFetchService;
+ private final ConvertToGaeRequest convertToGaeRequest;
+ private final ConvertToJcloudsResponse convertToJcloudsResponse;
+
+ @Inject
+ public AsyncGaeHttpCommandExecutorService(URLFetchService urlFetchService,
+ ConvertToGaeRequest convertToGaeRequest,
+ ConvertToJcloudsResponse convertToJcloudsResponse) {
+ this.urlFetchService = urlFetchService;
+ this.convertToGaeRequest = convertToGaeRequest;
+ this.convertToJcloudsResponse = convertToJcloudsResponse;
+ }
+
+ @Override
+ public ListenableFuture submit(HttpCommand command) {
+ // TODO: this needs to handle retrying and filtering
+ return Futures.compose(ConcurrentUtils.makeListenable(urlFetchService
+ .fetchAsync(convertToGaeRequest.apply(command.getRequest())), Executors
+ .sameThreadExecutor()), convertToJcloudsResponse);
+ }
+
+ @Singleton
+ public static class ConvertToJcloudsResponse implements Function {
+
+ @Override
+ public HttpResponse apply(HTTPResponse gaeResponse) {
+ HttpResponse response = new HttpResponse();
+ response.setStatusCode(gaeResponse.getResponseCode());
+ for (HTTPHeader header : gaeResponse.getHeaders()) {
+ response.getHeaders().put(header.getName(), header.getValue());
+ }
+ if (gaeResponse.getContent() != null) {
+ response.setContent(new ByteArrayInputStream(gaeResponse.getContent()));
+ }
+ return response;
+ }
+ }
+
+ @Singleton
+ public static class ConvertToGaeRequest implements Function {
+ /**
+ * byte [] content is replayable and the only content type supportable by GAE. As such, we
+ * convert the original request content to a byte array.
+ */
+ @VisibleForTesting
+ void changeRequestContentToBytes(HttpRequest request) {
+ Payload content = request.getPayload();
+ if (content == null || content instanceof ByteArrayPayload) {
+ return;
+ } else if (content instanceof StringPayload) {
+ String string = ((StringPayload) content).getRawContent();
+ request.setPayload(string.getBytes());
+ } else if (content instanceof InputStreamPayload || content instanceof FilePayload) {
+ InputStream i = content.getContent();
+ try {
+ try {
+ request.setPayload(ByteStreams.toByteArray(i));
+ } catch (IOException e) {
+ Throwables.propagate(e);
+ }
+ } finally {
+ Closeables.closeQuietly(i);
+ }
+ } else {
+ throw new UnsupportedOperationException("Content not supported " + content.getClass());
+ }
+
+ }
+
+ @Override
+ public HTTPRequest apply(HttpRequest request) {
+ URL url = null;
+ try {
+ url = request.getEndpoint().toURL();
+ } catch (MalformedURLException e) {
+ Throwables.propagate(e);
+ }
+
+ FetchOptions options = disallowTruncate();
+ options.followRedirects();
+
+ HTTPRequest gaeRequest = new HTTPRequest(url, HTTPMethod.valueOf(request.getMethod()
+ .toString()), options);
+
+ for (String header : request.getHeaders().keySet()) {
+ for (String value : request.getHeaders().get(header)) {
+ gaeRequest.addHeader(new HTTPHeader(header, value));
+ }
+ }
+ gaeRequest.addHeader(new HTTPHeader(HttpHeaders.USER_AGENT, USER_AGENT));
+
+ if (request.getPayload() != null) {
+ changeRequestContentToBytes(request);
+ gaeRequest.setPayload(((ByteArrayPayload) request.getPayload()).getRawContent());
+ } else {
+ gaeRequest.addHeader(new HTTPHeader(HttpHeaders.CONTENT_LENGTH, "0"));
+ }
+ return gaeRequest;
+ }
+
+ }
+
+}
diff --git a/extensions/gae/src/main/java/org/jclouds/gae/GaeHttpCommandExecutorService.java b/extensions/gae/src/main/java/org/jclouds/gae/GaeHttpCommandExecutorService.java
index 2681a702d6..c63cd38c2c 100644
--- a/extensions/gae/src/main/java/org/jclouds/gae/GaeHttpCommandExecutorService.java
+++ b/extensions/gae/src/main/java/org/jclouds/gae/GaeHttpCommandExecutorService.java
@@ -33,7 +33,6 @@ import javax.ws.rs.core.HttpHeaders;
import org.jclouds.Constants;
import org.jclouds.concurrent.SingleThreaded;
-import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
@@ -56,7 +55,6 @@ import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
-import com.google.common.util.concurrent.ListenableFuture;
/**
* Google App Engine version of {@link HttpCommandExecutorService}
@@ -66,7 +64,7 @@ import com.google.common.util.concurrent.ListenableFuture;
@SingleThreaded
@Singleton
public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorService {
- public static final String USER_AGENT = "jclouds/1.0 urlfetch/1.3.0";
+ public static final String USER_AGENT = "jclouds/1.0 urlfetch/1.3.2";
private final URLFetchService urlFetchService;
@@ -78,12 +76,6 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
this.urlFetchService = urlFetchService;
}
- @Override
- public ListenableFuture submit(HttpCommand command) {
- convertHostHeaderToEndPoint(command);
- return super.submit(command);
- }
-
/**
* byte [] content is replayable and the only content type supportable by GAE. As such, we
* convert the original request content to a byte array.
@@ -149,22 +141,6 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
return gaeRequest;
}
- /**
- * As host headers are not supported in GAE/J v1.2.1, we'll change the hostname of the
- * destination to the same value as the host header
- *
- * @param command
- */
- @VisibleForTesting
- public static void convertHostHeaderToEndPoint(HttpCommand command) {
- HttpRequest request = command.getRequest();
- String hostHeader = request.getFirstHeaderOrNull(HttpHeaders.HOST);
- if (hostHeader != null) {
- command.changeSchemeHostAndPortTo(request.getEndpoint().getScheme(), hostHeader, request
- .getEndpoint().getPort());
- }
- }
-
/**
* nothing to clean up.
*/
diff --git a/extensions/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java b/extensions/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java
index db05b66f8e..96541bd21c 100644
--- a/extensions/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java
+++ b/extensions/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java
@@ -22,7 +22,6 @@ import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.concurrent.config.ConfiguresExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.date.joda.config.JodaDateServiceModule;
-import org.jclouds.encryption.bouncycastle.config.BouncyCastleEncryptionServiceModule;
import org.jclouds.gae.GaeHttpCommandExecutorService;
import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.TransformingHttpCommandExecutorService;
@@ -51,7 +50,6 @@ public class GoogleAppEngineConfigurationModule extends ExecutorServiceModule {
@Override
protected void configure() {
super.configure();
- install(new BouncyCastleEncryptionServiceModule());
install(new JodaDateServiceModule());
bind(HttpCommandExecutorService.class).to(GaeHttpCommandExecutorService.class);
bind(TransformingHttpCommandExecutorService.class).to(
diff --git a/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java b/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java
index 31e0fa8638..7de962323b 100644
--- a/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java
+++ b/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceIntegrationTest.java
@@ -20,9 +20,7 @@ package org.jclouds.gae;
import static org.testng.Assert.assertEquals;
-import java.io.File;
import java.net.MalformedURLException;
-import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@@ -30,12 +28,10 @@ import java.util.concurrent.TimeoutException;
import org.jclouds.gae.config.GoogleAppEngineConfigurationModule;
import org.jclouds.http.BaseHttpCommandExecutorServiceTest;
import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
-import org.testng.v6.Maps;
-import com.google.appengine.tools.development.ApiProxyLocalImpl;
-import com.google.apphosting.api.ApiProxy;
+import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
+import com.google.appengine.tools.development.testing.LocalURLFetchServiceTestConfig;
import com.google.inject.Module;
/**
@@ -79,21 +75,9 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
super.testPostBinder();
}
- @BeforeTest
- void validateExecutor() {
- // ExecutorService executorService = injector.getInstance(ExecutorService.class);
- // assert executorService.getClass().isAnnotationPresent(SingleThreadCompatible.class) :
- // Arrays
- // .asList(executorService.getClass().getAnnotations()).toString()
- // + executorService.getClass().getName();
-
- }
-
@BeforeMethod
void setupApiProxy() {
- ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment());
- ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")) {
- });
+ new LocalServiceTestHelper(new LocalURLFetchServiceTestConfig()).setUp();
}
@Override
@@ -177,11 +161,11 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
}
@Override
- @Test(enabled = false)
+ @Test(invocationCount = 50, timeOut = 3000)
public void testGetStringWithHeader() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException {
- // GAE does not support sending headers in their test stub as of version
- // 1.2.0
+ setupApiProxy();
+ super.testGetStringWithHeader();
}
@Override
@@ -192,54 +176,14 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
super.testHead();
}
- @Test(enabled = false)
+ @Override
+ @Test(invocationCount = 50, timeOut = 3000)
public void testRequestFilter() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException {
- // GAE does not support sending headers in their test stub as of version
- // 1.2.0
+ setupApiProxy();
+ super.testRequestFilter();
}
-
- class TestEnvironment implements ApiProxy.Environment {
- public String getAppId() {
- return "Unit Tests";
- }
-
- public String getVersionId() {
- return "1.0";
- }
-
- public void setDefaultNamespace(String s) {
- }
-
- public String getRequestNamespace() {
- return null;
- }
-
- public String getDefaultNamespace() {
- return null;
- }
-
- public String getAuthDomain() {
- return null;
- }
-
- public boolean isLoggedIn() {
- return false;
- }
-
- public String getEmail() {
- return null;
- }
-
- public boolean isAdmin() {
- return false;
- }
-
- public Map getAttributes() {
- return Maps.newHashMap();
- }
- }
-
+
protected Module createConnectionModule() {
return new GoogleAppEngineConfigurationModule();
}
diff --git a/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceTest.java b/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceTest.java
index d857fe9581..7c7936ac35 100644
--- a/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceTest.java
+++ b/extensions/gae/src/test/java/org/jclouds/gae/GaeHttpCommandExecutorServiceTest.java
@@ -138,7 +138,7 @@ public class GaeHttpCommandExecutorServiceTest {
assert gaeRequest.getPayload() == null;
assertEquals(gaeRequest.getHeaders().size(), 2);// content length, user agent
assertEquals(gaeRequest.getHeaders().get(0).getName(), HttpHeaders.USER_AGENT);
- assertEquals(gaeRequest.getHeaders().get(0).getValue(), "jclouds/1.0 urlfetch/1.3.0");
+ assertEquals(gaeRequest.getHeaders().get(0).getValue(), "jclouds/1.0 urlfetch/1.3.2");
}
@Test