Issue 211: added gae, but waiting for service to go live

This commit is contained in:
Adrian Cole 2010-03-20 16:22:17 -07:00
parent 1ca3919442
commit 38b2bef023
9 changed files with 199 additions and 108 deletions

View File

@ -101,7 +101,7 @@
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-tools-api</artifactId> <artifactId>appengine-tools-api</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${appengine.home}/lib/appengine-tools-api.jar</systemPath> <systemPath>${appengine.home}/lib/appengine-tools-api.jar</systemPath>
</dependency> </dependency>

View File

@ -156,12 +156,12 @@
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-api-labs</artifactId> <artifactId>appengine-api-labs</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-tools-api</artifactId> <artifactId>appengine-tools-api</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${appengine.home}/lib/appengine-tools-api.jar</systemPath> <systemPath>${appengine.home}/lib/appengine-tools-api.jar</systemPath>
</dependency> </dependency>

View File

@ -146,12 +146,12 @@
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-api-labs</artifactId> <artifactId>appengine-api-labs</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-tools-api</artifactId> <artifactId>appengine-tools-api</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${appengine.home}/lib/appengine-tools-api.jar</systemPath> <systemPath>${appengine.home}/lib/appengine-tools-api.jar</systemPath>
</dependency> </dependency>

View File

@ -44,26 +44,27 @@
<artifactId>jclouds-joda</artifactId> <artifactId>jclouds-joda</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-bouncycastle</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-api</artifactId> <artifactId>appengine-api</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-api-stubs</artifactId> <artifactId>appengine-api-stubs</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>1.3.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.appengine</groupId> <groupId>com.google.appengine</groupId>
<artifactId>appengine-local-runtime</artifactId> <artifactId>appengine-local-runtime</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -0,0 +1,172 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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<HttpResponse> 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<HTTPResponse, HttpResponse> {
@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<HttpRequest, HTTPRequest> {
/**
* 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;
}
}
}

View File

@ -33,7 +33,6 @@ import javax.ws.rs.core.HttpHeaders;
import org.jclouds.Constants; import org.jclouds.Constants;
import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; 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.annotations.VisibleForTesting;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables; import com.google.common.io.Closeables;
import com.google.common.util.concurrent.ListenableFuture;
/** /**
* Google App Engine version of {@link HttpCommandExecutorService} * Google App Engine version of {@link HttpCommandExecutorService}
@ -66,7 +64,7 @@ import com.google.common.util.concurrent.ListenableFuture;
@SingleThreaded @SingleThreaded
@Singleton @Singleton
public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorService<HTTPRequest> { public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorService<HTTPRequest> {
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; private final URLFetchService urlFetchService;
@ -78,12 +76,6 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
this.urlFetchService = urlFetchService; this.urlFetchService = urlFetchService;
} }
@Override
public ListenableFuture<HttpResponse> submit(HttpCommand command) {
convertHostHeaderToEndPoint(command);
return super.submit(command);
}
/** /**
* byte [] content is replayable and the only content type supportable by GAE. As such, we * byte [] content is replayable and the only content type supportable by GAE. As such, we
* convert the original request content to a byte array. * convert the original request content to a byte array.
@ -149,22 +141,6 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
return gaeRequest; 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. * nothing to clean up.
*/ */

View File

@ -22,7 +22,6 @@ import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.concurrent.config.ConfiguresExecutorService; import org.jclouds.concurrent.config.ConfiguresExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.date.joda.config.JodaDateServiceModule; import org.jclouds.date.joda.config.JodaDateServiceModule;
import org.jclouds.encryption.bouncycastle.config.BouncyCastleEncryptionServiceModule;
import org.jclouds.gae.GaeHttpCommandExecutorService; import org.jclouds.gae.GaeHttpCommandExecutorService;
import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.TransformingHttpCommandExecutorService; import org.jclouds.http.TransformingHttpCommandExecutorService;
@ -51,7 +50,6 @@ public class GoogleAppEngineConfigurationModule extends ExecutorServiceModule {
@Override @Override
protected void configure() { protected void configure() {
super.configure(); super.configure();
install(new BouncyCastleEncryptionServiceModule());
install(new JodaDateServiceModule()); install(new JodaDateServiceModule());
bind(HttpCommandExecutorService.class).to(GaeHttpCommandExecutorService.class); bind(HttpCommandExecutorService.class).to(GaeHttpCommandExecutorService.class);
bind(TransformingHttpCommandExecutorService.class).to( bind(TransformingHttpCommandExecutorService.class).to(

View File

@ -20,9 +20,7 @@ package org.jclouds.gae;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.File;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -30,12 +28,10 @@ import java.util.concurrent.TimeoutException;
import org.jclouds.gae.config.GoogleAppEngineConfigurationModule; import org.jclouds.gae.config.GoogleAppEngineConfigurationModule;
import org.jclouds.http.BaseHttpCommandExecutorServiceTest; import org.jclouds.http.BaseHttpCommandExecutorServiceTest;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import org.testng.v6.Maps;
import com.google.appengine.tools.development.ApiProxyLocalImpl; import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.apphosting.api.ApiProxy; import com.google.appengine.tools.development.testing.LocalURLFetchServiceTestConfig;
import com.google.inject.Module; import com.google.inject.Module;
/** /**
@ -79,21 +75,9 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
super.testPostBinder(); 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 @BeforeMethod
void setupApiProxy() { void setupApiProxy() {
ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment()); new LocalServiceTestHelper(new LocalURLFetchServiceTestConfig()).setUp();
ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")) {
});
} }
@Override @Override
@ -177,11 +161,11 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
} }
@Override @Override
@Test(enabled = false) @Test(invocationCount = 50, timeOut = 3000)
public void testGetStringWithHeader() throws MalformedURLException, ExecutionException, public void testGetStringWithHeader() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException { InterruptedException, TimeoutException {
// GAE does not support sending headers in their test stub as of version setupApiProxy();
// 1.2.0 super.testGetStringWithHeader();
} }
@Override @Override
@ -192,54 +176,14 @@ public class GaeHttpCommandExecutorServiceIntegrationTest extends
super.testHead(); super.testHead();
} }
@Test(enabled = false) @Override
@Test(invocationCount = 50, timeOut = 3000)
public void testRequestFilter() throws MalformedURLException, ExecutionException, public void testRequestFilter() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException { InterruptedException, TimeoutException {
// GAE does not support sending headers in their test stub as of version setupApiProxy();
// 1.2.0 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<String, Object> getAttributes() {
return Maps.newHashMap();
}
}
protected Module createConnectionModule() { protected Module createConnectionModule() {
return new GoogleAppEngineConfigurationModule(); return new GoogleAppEngineConfigurationModule();
} }

View File

@ -138,7 +138,7 @@ public class GaeHttpCommandExecutorServiceTest {
assert gaeRequest.getPayload() == null; assert gaeRequest.getPayload() == null;
assertEquals(gaeRequest.getHeaders().size(), 2);// content length, user agent assertEquals(gaeRequest.getHeaders().size(), 2);// content length, user agent
assertEquals(gaeRequest.getHeaders().get(0).getName(), HttpHeaders.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 @Test