From a2c89935923ccae7413333c145916299fee2d94e Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 17 May 2012 00:42:08 -0700 Subject: [PATCH] updated gae demo to demonstrate use of views and GAE ThreadFactory --- demos/googleappengine/pom.xml | 14 +- .../GetAllResourcesController.java | 175 ++++++++++++++++++ .../GetAllStatusController.java | 93 ---------- .../config/GuiceServletConfig.java | 113 +++++++---- .../domain/ResourceResult.java | 128 +++++++++++++ .../googleappengine/domain/StatusResult.java | 105 ----------- ... => BlobStoreContextToAsyncResources.java} | 27 +-- ...ComputeServiceContextToAsyncResources.java | 69 +++++++ ... => ResourceMetadataToResourceResult.java} | 36 ++-- .../functions/ViewToAsyncResources.java | 59 ++++++ .../googleappengine/functions/ViewToId.java | 31 ++++ .../src/main/webapp/WEB-INF/jsp/resources.jsp | 104 +++++++++++ .../src/main/webapp/WEB-INF/jsp/status.jsp | 36 ---- .../googleappengine/src/main/webapp/index.jsp | 2 +- .../functest/GoogleAppEngineLiveTest.java | 56 ++++-- 15 files changed, 715 insertions(+), 333 deletions(-) create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java delete mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java delete mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java rename demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/{BlobStoreContextToStatusResult.java => BlobStoreContextToAsyncResources.java} (59%) create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java rename demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/{ComputeServiceContextToStatusResult.java => ResourceMetadataToResourceResult.java} (56%) create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java create mode 100644 demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp delete mode 100644 demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp diff --git a/demos/googleappengine/pom.xml b/demos/googleappengine/pom.xml index b78ce3452b..ce378227d5 100644 --- a/demos/googleappengine/pom.xml +++ b/demos/googleappengine/pom.xml @@ -40,8 +40,6 @@ 1.6.5 localhost 8088 - FIXME_IDENTITY - FIXME_CREDENTIAL @@ -57,13 +55,13 @@ org.jclouds.provider - hpcloud-objectstorage + aws-s3 ${project.version} runtime org.jclouds.provider - hpcloud-compute + hpcloud-objectstorage ${project.version} runtime @@ -369,8 +367,12 @@ - ${test.hpcloud.identity} - ${test.hpcloud.credential} + + ${test.aws-s3.identity} + ${test.aws-s3.credential} + ${test.hpcloud-objectstorage.identity} + ${test.hpcloud-objectstorage.credential} ${appengine.sdk.root} ${devappserver.address} ${devappserver.port} diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java new file mode 100644 index 0000000000..1be0d9ca74 --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java @@ -0,0 +1,175 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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.samples.googleappengine; + +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.size; +import static com.google.common.collect.Iterables.transform; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jclouds.View; +import org.jclouds.domain.ResourceMetadata; +import org.jclouds.logging.Logger; +import org.jclouds.samples.googleappengine.domain.ResourceResult; +import org.jclouds.samples.googleappengine.functions.ResourceMetadataToResourceResult; +import org.jclouds.samples.googleappengine.functions.ViewToAsyncResources; +import org.jclouds.samples.googleappengine.functions.ViewToId; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +/** + * Shows an example of how to list all resources from all views! + * + * @author Adrian Cole + */ +@Singleton +public class GetAllResourcesController extends HttpServlet { + private static final long serialVersionUID = 1L; + + private final ListeningExecutorService currentRequestExecutorService; + private final Iterable views; + private final ViewToAsyncResources viewToAsyncResources; + private final ResourceMetadataToResourceResult resourceMetadataToStatusResult; + private final Provider remainingMillis; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + GetAllResourcesController(ListeningExecutorService currentRequestExecutorService, Iterable views, + ViewToAsyncResources viewToAsyncResources, ResourceMetadataToResourceResult resourceMetadataToStatusResult, + Provider remainingMillis) { + this.currentRequestExecutorService = currentRequestExecutorService; + this.views = views; + this.viewToAsyncResources = viewToAsyncResources; + this.resourceMetadataToStatusResult = resourceMetadataToStatusResult; + this.remainingMillis = remainingMillis; + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + try { + addResourcesToRequest(request); + RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/WEB-INF/jsp/resources.jsp"); + dispatcher.forward(request, response); + } catch (Exception e) { + logger.error(e, "Error listing resources"); + throw new ServletException(e); + } + } + + private void addResourcesToRequest(HttpServletRequest request) { + Stopwatch watch = new Stopwatch().start(); + logger.info("ready to list views: %s", transform(views, ViewToId.INSTANCE)); + Iterable>>> asyncResources = transform(views, + viewToAsyncResources); + logger.info("launched %s tasks with %sms remaining", size(asyncResources), remainingMillis.get()); + + Set>> done = allResourcesWithinDeadline(asyncResources); + logger.info("%s tasks completed in %sms with %sms remaining", size(done), watch.stop().elapsedMillis(), + remainingMillis.get()); + + Iterable> flattened = concat(done); + + Set results = FluentIterable.from(flattened).transform(resourceMetadataToStatusResult) + .toImmutableSet(); + + request.setAttribute("resources", results); + } + + private Set>> allResourcesWithinDeadline( + Iterable>>> asyncResources) { + Builder>> resourcesWeCanList = addToBuilderOnComplete(asyncResources); + + // only serve resources that made it by the timeout + blockUntilAllDoneOrCancelOnTimeout(asyncResources); + + return resourcesWeCanList.build(); + } + + private Builder>> addToBuilderOnComplete( + Iterable>>> asyncResources) { + + final Builder>> resourcesWeCanList = ImmutableSet + .>> builder(); + + for (final ListenableFuture>> asyncResource : asyncResources) { + Futures.addCallback(asyncResource, new FutureCallback>>() { + public void onSuccess(Iterable> result) { + if (result != null) + resourcesWeCanList.add(result); + } + + public void onFailure(Throwable t) { + if (!(t instanceof CancellationException)) + logger.info("exception getting resource %s: %s", asyncResource, t.getMessage()); + } + }, currentRequestExecutorService); + + } + return resourcesWeCanList; + } + + private void blockUntilAllDoneOrCancelOnTimeout( + Iterable>>> asyncResources) { + List>>> remaining = Lists + .newArrayList(asyncResources); + + while (remaining.size() > 0) { + ListenableFuture resource = remaining.remove(0); + if (remainingMillis.get() <= 0) { + if (!resource.isDone()) + resource.cancel(true); + continue; + } + + try { + resource.get(remainingMillis.get(), TimeUnit.MILLISECONDS); + } catch (Exception e) { + logger.info("exception getting resource %s: %s", resource, e.getMessage()); + if (!resource.isDone()) + resource.cancel(true); + } + } + } + +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java deleted file mode 100644 index 2f6e76089b..0000000000 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you 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.samples.googleappengine; - -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import javax.annotation.Resource; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.compute.ComputeServiceContext; -import org.jclouds.logging.Logger; -import org.jclouds.samples.googleappengine.functions.BlobStoreContextToStatusResult; -import org.jclouds.samples.googleappengine.functions.ComputeServiceContextToStatusResult; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; - -/** - * Shows an example of how to use {@link BlobStoreContext} and {@link ComputeServiceContext} - * injected with Guice. - * - * @author Adrian Cole - */ -@Singleton -public class GetAllStatusController extends HttpServlet { - private static final long serialVersionUID = 1L; - - private final Iterable blobsStoreContexts; - private final Iterable computeServiceContexts; - private final BlobStoreContextToStatusResult blobStoreContextToContainerResult; - private final ComputeServiceContextToStatusResult computeServiceContextToContainerResult; - - @Resource - protected Logger logger = Logger.NULL; - - @Inject - GetAllStatusController(Iterable blobsStoreContexts, - Iterable computeServiceContexts, - BlobStoreContextToStatusResult blobStoreContextToContainerResult, - ComputeServiceContextToStatusResult computeServiceContextToContainerResult) { - this.blobsStoreContexts = blobsStoreContexts; - this.computeServiceContexts = computeServiceContexts; - this.blobStoreContextToContainerResult = blobStoreContextToContainerResult; - this.computeServiceContextToContainerResult = computeServiceContextToContainerResult; - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - try { - addStatusResultsToRequest(request); - RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/WEB-INF/jsp/status.jsp"); - dispatcher.forward(request, response); - } catch (Exception e) { - logger.error(e, "Error listing status"); - throw new ServletException(e); - } - } - - private void addStatusResultsToRequest(HttpServletRequest request) throws InterruptedException, ExecutionException, - TimeoutException { - request.setAttribute( - "status", - ImmutableSet.copyOf(Iterables.concat( - Iterables.transform(blobsStoreContexts, blobStoreContextToContainerResult), - Iterables.transform(computeServiceContexts, computeServiceContextToContainerResult)))); - } - -} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java index 5b195a08a1..cdc671b8ac 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java @@ -18,6 +18,11 @@ */ package org.jclouds.samples.googleappengine.config; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.get; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.io.Closeables.closeQuietly; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN; @@ -26,59 +31,90 @@ import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT import java.io.IOException; import java.io.InputStream; import java.util.Properties; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import javax.inject.Singleton; import javax.servlet.ServletContextEvent; import org.jclouds.ContextBuilder; -import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.View; import org.jclouds.gae.config.AsyncGoogleAppEngineConfigurationModule; -import org.jclouds.samples.googleappengine.GetAllStatusController; +import org.jclouds.logging.jdk.config.JDKLoggingModule; +import org.jclouds.providers.ProviderMetadata; +import org.jclouds.providers.Providers; +import org.jclouds.samples.googleappengine.GetAllResourcesController; +import com.google.appengine.api.ThreadManager; +import com.google.apphosting.api.ApiProxy; +import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; -import com.google.common.io.Closeables; +import com.google.common.reflect.TypeToken; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; +import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.google.inject.servlet.GuiceServletContextListener; import com.google.inject.servlet.ServletModule; /** - * Setup Logging and create {@link Injector} for use in testing Amazon EC2 and S3. + * Setup Logging and create {@link Injector} for use in testing Views * * @author Adrian Cole */ public class GuiceServletConfig extends GuiceServletContextListener { - private Iterable blobsStoreContexts; - private Iterable computeServiceContexts; + private Iterable views; @Override public void contextInitialized(ServletContextEvent servletContextEvent) { - Properties overrides = loadJCloudsProperties(servletContextEvent); + final Properties overrides = loadJCloudsProperties(servletContextEvent); + // until there's a global skip image parse option + overrides.setProperty("jclouds.ec2.ami-query", ""); + overrides.setProperty("jclouds.ec2.cc-ami-query", ""); + + // ensure requests don't take longer than GAE timeout overrides.setProperty(TIMEOUT_NODE_TERMINATED, "25000"); overrides.setProperty(TIMEOUT_NODE_RUNNING, "25000"); overrides.setProperty(TIMEOUT_SCRIPT_COMPLETE, "25000"); overrides.setProperty(TIMEOUT_PORT_OPEN, "25000"); - // note that this module hooks into the async urlfetchservice - ImmutableSet modules = ImmutableSet. of(new AsyncGoogleAppEngineConfigurationModule()); + // correct the classloader so that extensions can be found + Thread.currentThread().setContextClassLoader(Providers.class.getClassLoader()); - blobsStoreContexts = ImmutableSet.of( - ContextBuilder.newBuilder("hpcloud-objectstorage") - .modules(modules) - .overrides(overrides) - .buildView(BlobStoreContext.class)); - computeServiceContexts = ImmutableSet.of( - ContextBuilder.newBuilder("hpcloud-compute") - .modules(modules) - .overrides(overrides) - .buildView(ComputeServiceContext.class)); + Iterable identityInProperties = providersWeHaveIdentitiesFor(overrides); + + final ImmutableSet modules = ImmutableSet. of(new AsyncGoogleAppEngineConfigurationModule()); + views = transform(identityInProperties, new Function() { + + @Override + public View apply(ProviderMetadata input) { + TypeToken defaultView = get(input.getApiMetadata().getViews(), 0); + return ContextBuilder.newBuilder(input).modules(modules).overrides(overrides).buildView(defaultView); + } + + }); super.contextInitialized(servletContextEvent); } + protected Iterable providersWeHaveIdentitiesFor(final Properties overrides) { + // there's a chance serviceloader is being lazy, and we don't want + // ConcurrentModificationException, so copy into a set. + return ImmutableSet.copyOf(filter(Providers.all(), new Predicate() { + + @Override + public boolean apply(ProviderMetadata input) { + return overrides.containsKey(input.getId() + ".identity"); + } + + })); + } + private Properties loadJCloudsProperties(ServletContextEvent servletContextEvent) { InputStream input = servletContextEvent.getServletContext().getResourceAsStream("/WEB-INF/jclouds.properties"); Properties props = new Properties(); @@ -87,35 +123,44 @@ public class GuiceServletConfig extends GuiceServletContextListener { } catch (IOException e) { throw new RuntimeException(e); } finally { - Closeables.closeQuietly(input); + closeQuietly(input); } return props; } @Override protected Injector getInjector() { - return Guice.createInjector(new ServletModule() { + return Guice.createInjector(new JDKLoggingModule(), new ServletModule() { @Override protected void configureServlets() { - bind(new TypeLiteral>() { - }).toInstance(GuiceServletConfig.this.blobsStoreContexts); - bind(new TypeLiteral>() { - }).toInstance(GuiceServletConfig.this.computeServiceContexts); - serve("*.check").with(GetAllStatusController.class); + bind(new TypeLiteral>() { + }).toInstance(GuiceServletConfig.this.views); + serve("*.check").with(GetAllResourcesController.class); requestInjection(this); } - } - ); + @SuppressWarnings("unused") + @Provides + long remainingMillis() { + // leave 100ms for any post processing + return ApiProxy.getCurrentEnvironment().getRemainingMillis() - 100; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ListeningExecutorService currentRequestExecutorService() { + ThreadFactory factory = checkNotNull(ThreadManager.currentRequestThreadFactory(), + "ThreadManager.currentRequestThreadFactory()"); + return MoreExecutors.listeningDecorator(Executors.newCachedThreadPool(factory)); + } + }); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { - for (BlobStoreContext context : blobsStoreContexts) { - context.close(); - } - for (ComputeServiceContext context : computeServiceContexts) { - context.close(); + for (View view : views) { + view.unwrap().close(); } super.contextDestroyed(servletContextEvent); } diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java new file mode 100644 index 0000000000..fe09faa27d --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java @@ -0,0 +1,128 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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.samples.googleappengine.domain; + +import static com.google.common.base.Objects.equal; + +import com.google.common.base.Objects; + +/** + * + * @author Adrian Cole + */ +public class ResourceResult { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + protected String provider; + protected String location; + protected String type; + protected String id; + protected String name; + + public Builder provider(String provider) { + this.provider = provider; + return this; + } + + public Builder location(String location) { + this.location = location; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public ResourceResult build() { + return new ResourceResult(provider, location, type, id, name); + } + + } + + private final String provider; + private final String location; + private final String type; + private final String id; + private final String name; + + protected ResourceResult(String provider, String location, String type, String id, String name) { + this.provider = provider; + this.type = type; + this.location = location; + this.id = id; + this.name = name; + } + + public String getProvider() { + return provider; + } + + public String getLocation() { + return location; + } + + public String getType() { + return type; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ResourceResult that = ResourceResult.class.cast(o); + return equal(this.provider, that.provider) && equal(this.location, that.location) && equal(this.type, that.type) + && equal(this.id, that.id) && equal(this.name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(provider, location, type, id, name); + } + + @Override + public String toString() { + return Objects.toStringHelper("").add("provider", provider).add("location", location).add("type", type) + .add("id", id).add("name", name).toString(); + } +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java deleted file mode 100644 index 26138971ce..0000000000 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you 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.samples.googleappengine.domain; - -import java.io.Serializable; - -/** - * - * @author Adrian Cole - */ -public class StatusResult implements Comparable, Serializable { - /** The serialVersionUID */ - private static final long serialVersionUID = -3257496189689220018L; - private final String service; - private final String host; - private final String name; - private final String status; - - public StatusResult(String service, String host, String name, String status) { - this.service = service; - this.host = host; - this.name = name; - this.status = status; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((host == null) ? 0 : host.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((service == null) ? 0 : service.hashCode()); - result = prime * result + ((status == null) ? 0 : status.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - StatusResult other = (StatusResult) obj; - if (host == null) { - if (other.host != null) - return false; - } else if (!host.equals(other.host)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (service == null) { - if (other.service != null) - return false; - } else if (!service.equals(other.service)) - return false; - if (status == null) { - if (other.status != null) - return false; - } else if (!status.equals(other.status)) - return false; - return true; - } - - public int compareTo(StatusResult o) { - return (this == o) ? 0 : getService().compareTo(o.getService()); - } - - public String getHost() { - return host; - } - - public String getName() { - return name; - } - - public String getStatus() { - return status; - } - - public String getService() { - return service; - } - -} diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToAsyncResources.java similarity index 59% rename from demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java rename to demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToAsyncResources.java index a98f28ff91..518ea8b1ec 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToAsyncResources.java @@ -18,41 +18,28 @@ */ package org.jclouds.samples.googleappengine.functions; -import java.net.URI; - import javax.annotation.Resource; import javax.inject.Singleton; import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.domain.ResourceMetadata; import org.jclouds.logging.Logger; -import org.jclouds.samples.googleappengine.domain.StatusResult; import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; /** * * @author Adrian Cole */ @Singleton -public class BlobStoreContextToStatusResult implements Function { - +public class BlobStoreContextToAsyncResources implements + Function>>> { @Resource protected Logger logger = Logger.NULL; - public StatusResult apply(BlobStoreContext in) { - String host = URI.create(in.unwrap().getProviderMetadata().getEndpoint()).getHost(); - String status; - String name = "not found"; - try { - long start = System.currentTimeMillis(); - - name = String.format("%d containers", in.getBlobStore().list().size()); - - status = ((System.currentTimeMillis() - start) + "ms"); - } catch (Exception e) { - logger.error(e, "Error listing context %s", in); - status = (e.getMessage()); - } - return new StatusResult(in.unwrap().getId(), host, name, status); + public ListenableFuture>> apply(BlobStoreContext in) { + logger.info("listing containers on %s: ", in.unwrap().getId()); + return in.getAsyncBlobStore().list(); } } \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java new file mode 100644 index 0000000000..fed3897fb1 --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java @@ -0,0 +1,69 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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.samples.googleappengine.functions; + +import java.util.concurrent.Callable; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.ResourceMetadata; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.inject.Inject; + +/** + * ComputeService doesn't currently have an Async counterpart + * + * @author Adrian Cole + */ +@Singleton +public class ComputeServiceContextToAsyncResources implements + Function>>> { + + @Resource + protected Logger logger = Logger.NULL; + + private final ListeningExecutorService currentRequestExecutorService; + + @Inject + public ComputeServiceContextToAsyncResources(ListeningExecutorService currentRequestExecutorService) { + this.currentRequestExecutorService = currentRequestExecutorService; + } + + public ListenableFuture>> apply(final ComputeServiceContext in) { + return currentRequestExecutorService.submit(new Callable>>() { + + @Override + public Iterable> call() throws Exception { + logger.info("listing nodes on %s: ", in.unwrap().getId()); + return in.getComputeService().listNodes(); + } + + @Override + public String toString() { + return in.toString(); + } + }); + } +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToStatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ResourceMetadataToResourceResult.java similarity index 56% rename from demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToStatusResult.java rename to demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ResourceMetadataToResourceResult.java index 08aaeb2b55..1ef82b85dc 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToStatusResult.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ResourceMetadataToResourceResult.java @@ -18,14 +18,14 @@ */ package org.jclouds.samples.googleappengine.functions; -import java.net.URI; - import javax.annotation.Resource; import javax.inject.Singleton; -import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.Location; +import org.jclouds.domain.ResourceMetadata; import org.jclouds.logging.Logger; -import org.jclouds.samples.googleappengine.domain.StatusResult; +import org.jclouds.samples.googleappengine.domain.ResourceResult; +import org.jclouds.samples.googleappengine.domain.ResourceResult.Builder; import com.google.common.base.Function; @@ -34,25 +34,21 @@ import com.google.common.base.Function; * @author Adrian Cole */ @Singleton -public class ComputeServiceContextToStatusResult implements Function { +public class ResourceMetadataToResourceResult implements Function, ResourceResult> { @Resource protected Logger logger = Logger.NULL; - public StatusResult apply(ComputeServiceContext in) { - String host = URI.create(in.unwrap().getProviderMetadata().getEndpoint()).getHost(); - String status; - String name = "not found"; - try { - long start = System.currentTimeMillis(); - - name = String.format("%d nodes", in.getComputeService().listNodes().size()); - - status = ((System.currentTimeMillis() - start) + "ms"); - } catch (Exception e) { - logger.error(e, "Error listing context %s", in); - status = (e.getMessage()); - } - return new StatusResult(in.unwrap().getId(), host, name, status); + public ResourceResult apply(ResourceMetadata in) { + Builder builder = ResourceResult.builder(); + Location provider = in.getLocation(); + while (provider.getParent() != null) + provider = provider.getParent(); + builder.provider(provider.getId()); + builder.location(in.getLocation().getId()); + builder.type(in.getType().toString().toLowerCase()); + builder.id(in.getProviderId()); + builder.name(in.getName()); + return builder.build(); } } \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java new file mode 100644 index 0000000000..bd1030c213 --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java @@ -0,0 +1,59 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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.samples.googleappengine.functions; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.View; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.ResourceMetadata; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class ViewToAsyncResources implements Function>>> { + private final BlobStoreContextToAsyncResources blobStoreContextToAsyncResources; + private final ComputeServiceContextToAsyncResources computeServiceContextToAsyncResources; + + @Inject + public ViewToAsyncResources(BlobStoreContextToAsyncResources blobStoreContextToAsyncResources, + ComputeServiceContextToAsyncResources computeServiceContextToAsyncResources) { + this.blobStoreContextToAsyncResources = blobStoreContextToAsyncResources; + this.computeServiceContextToAsyncResources = computeServiceContextToAsyncResources; + } + + + @Override + public ListenableFuture>> apply(View input) { + if (input instanceof BlobStoreContext) { + return blobStoreContextToAsyncResources.apply(BlobStoreContext.class.cast(input)); + } else if (input instanceof ComputeServiceContext) { + return computeServiceContextToAsyncResources.apply(ComputeServiceContext.class.cast(input)); + } + throw new UnsupportedOperationException("unknown view type: " + input); + } + +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java new file mode 100644 index 0000000000..ea9f2da92d --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java @@ -0,0 +1,31 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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.samples.googleappengine.functions; + +import org.jclouds.View; + +import com.google.common.base.Function; + +public enum ViewToId implements Function { + INSTANCE; + @Override + public String apply(View input) { + return input.unwrap().getId(); + } +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp b/demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp new file mode 100644 index 0000000000..1a8a21bfd2 --- /dev/null +++ b/demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp @@ -0,0 +1,104 @@ +<%-- + + Licensed to jclouds, Inc. (jclouds) under one or more + contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. jclouds licenses this file + to you 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. + +--%> +<%@ page buffer="100kb"%> +<%@ taglib uri="http://displaytag.sf.net" prefix="display"%> + + +jclouds: multi-cloud library + + + +

Resource List

+ + + + +
+
+ + + + + + + +
+
+ + diff --git a/demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp b/demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp deleted file mode 100644 index 8cb3960523..0000000000 --- a/demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp +++ /dev/null @@ -1,36 +0,0 @@ -<%-- - - Licensed to jclouds, Inc. (jclouds) under one or more - contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. jclouds licenses this file - to you 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. - ---%> -<%@ page buffer="20kb"%> -<%@ taglib uri="http://displaytag.sf.net" prefix="display"%> - - -jclouds: multi-cloud library - - -

Status List

- - - - - - - - diff --git a/demos/googleappengine/src/main/webapp/index.jsp b/demos/googleappengine/src/main/webapp/index.jsp index b3db1e7d8f..38308a0f33 100644 --- a/demos/googleappengine/src/main/webapp/index.jsp +++ b/demos/googleappengine/src/main/webapp/index.jsp @@ -25,6 +25,6 @@

Welcome!

Click -here to get status of cloud services. +here to list all your cloud resources! diff --git a/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java b/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java index 93127e406e..c23e14d66a 100644 --- a/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java +++ b/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java @@ -18,21 +18,27 @@ */ package org.jclouds.samples.googleappengine.functest; -import static com.google.common.base.Preconditions.checkNotNull; - import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.Map; import java.util.Properties; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.compute.ComputeService; +import org.jclouds.util.Maps2; import org.jclouds.util.Strings2; import org.testng.annotations.BeforeTest; import org.testng.annotations.Parameters; import org.testng.annotations.Test; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; + /** * Starts up the Google App Engine for Java Development environment and deploys an application which - * tests Amazon EC2 and S3. + * tests {@link ComputeService} and {@link BlobStore}. * * @author Adrian Cole */ @@ -47,23 +53,37 @@ public class GoogleAppEngineLiveTest { public void startDevAppServer(final String warfile, final String address, final String port) throws Exception { url = new URL(String.format("http://%s:%s", address, port)); + Properties props = new Properties(); - String identity = checkNotNull(System.getProperty("test.hpcloud.identity"), - "test.hpcloud.identity"); - String credential = checkNotNull(System.getProperty("test.hpcloud.credential"), - "test.hpcloud.credential"); - - /** - * Since both objectstorage and compute use the same credentials, we can - * take a shortcut and specify both here: - */ - props.setProperty("jclouds.identity", identity); - props.setProperty("jclouds.credential", credential); - + props.putAll(stripTestPrefix(selectPropertiesForIdentityAndCredential())); server = new GoogleDevServer(); server.writePropertiesAndStartServer(address, port, warfile, props); } + Map stripTestPrefix(Map identityCrendential) { + return Maps2.transformKeys(identityCrendential, new Function() { + + @Override + public String apply(String arg0) { + return arg0.replace("test.", ""); + } + + }); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map selectPropertiesForIdentityAndCredential() { + return Maps.filterKeys((Map) System.getProperties(), new Predicate() { + + @Override + public boolean apply(String input) { + // TODO Auto-generated method stub + return input.matches("^test\\.[a-z0-9-]+\\.(identity|credential)$"); + } + + }); + } + @Test public void shouldPass() throws InterruptedException, IOException { InputStream i = url.openStream(); @@ -73,15 +93,15 @@ public class GoogleAppEngineLiveTest { @Test(invocationCount = 5, enabled = true) public void testGuiceJCloudsSerial() throws InterruptedException, IOException { - URL gurl = new URL(url, "/guice/status.check"); + URL gurl = new URL(url, "/guice/resources.check"); InputStream i = gurl.openStream(); String string = Strings2.toStringAndClose(i); assert string.indexOf("List") >= 0 : string; } - @Test(invocationCount = 10, enabled = true, threadPoolSize = 3) + @Test(invocationCount = 10, enabled = false, threadPoolSize = 3) public void testGuiceJCloudsParallel() throws InterruptedException, IOException { - URL gurl = new URL(url, "/guice/status.check"); + URL gurl = new URL(url, "/guice/resources.check"); InputStream i = gurl.openStream(); String string = Strings2.toStringAndClose(i); assert string.indexOf("List") >= 0 : string;