updated gae demo to demonstrate use of views and GAE ThreadFactory

This commit is contained in:
Adrian Cole 2012-05-17 00:42:08 -07:00
parent 7854d85f13
commit a2c8993592
15 changed files with 715 additions and 333 deletions

View File

@ -40,8 +40,6 @@
<appengine.sdk.version>1.6.5</appengine.sdk.version>
<devappserver.address>localhost</devappserver.address>
<devappserver.port>8088</devappserver.port>
<test.hpcloud.identity>FIXME_IDENTITY</test.hpcloud.identity>
<test.hpcloud.credential>FIXME_CREDENTIAL</test.hpcloud.credential>
</properties>
<dependencies>
@ -57,13 +55,13 @@
</dependency>
<dependency>
<groupId>org.jclouds.provider</groupId>
<artifactId>hpcloud-objectstorage</artifactId>
<artifactId>aws-s3</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jclouds.provider</groupId>
<artifactId>hpcloud-compute</artifactId>
<artifactId>hpcloud-objectstorage</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
@ -369,8 +367,12 @@
</goals>
<configuration>
<systemPropertyVariables>
<test.hpcloud.identity>${test.hpcloud.identity}</test.hpcloud.identity>
<test.hpcloud.credential>${test.hpcloud.credential}</test.hpcloud.credential>
<!-- note you can add support for new clouds by adding more entries here
after adding maven dependency -->
<test.aws-s3.identity>${test.aws-s3.identity}</test.aws-s3.identity>
<test.aws-s3.credential>${test.aws-s3.credential}</test.aws-s3.credential>
<test.hpcloud-objectstorage.identity>${test.hpcloud-objectstorage.identity}</test.hpcloud-objectstorage.identity>
<test.hpcloud-objectstorage.credential>${test.hpcloud-objectstorage.credential}</test.hpcloud-objectstorage.credential>
<appengine.sdk.root>${appengine.sdk.root}</appengine.sdk.root>
<devappserver.address>${devappserver.address}</devappserver.address>
<devappserver.port>${devappserver.port}</devappserver.port>

View File

@ -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<View> views;
private final ViewToAsyncResources viewToAsyncResources;
private final ResourceMetadataToResourceResult resourceMetadataToStatusResult;
private final Provider<Long> remainingMillis;
@Resource
protected Logger logger = Logger.NULL;
@Inject
GetAllResourcesController(ListeningExecutorService currentRequestExecutorService, Iterable<View> views,
ViewToAsyncResources viewToAsyncResources, ResourceMetadataToResourceResult resourceMetadataToStatusResult,
Provider<Long> 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<ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> asyncResources = transform(views,
viewToAsyncResources);
logger.info("launched %s tasks with %sms remaining", size(asyncResources), remainingMillis.get());
Set<Iterable<? extends ResourceMetadata<?>>> done = allResourcesWithinDeadline(asyncResources);
logger.info("%s tasks completed in %sms with %sms remaining", size(done), watch.stop().elapsedMillis(),
remainingMillis.get());
Iterable<ResourceMetadata<?>> flattened = concat(done);
Set<ResourceResult> results = FluentIterable.from(flattened).transform(resourceMetadataToStatusResult)
.toImmutableSet();
request.setAttribute("resources", results);
}
private Set<Iterable<? extends ResourceMetadata<?>>> allResourcesWithinDeadline(
Iterable<ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> asyncResources) {
Builder<Iterable<? extends ResourceMetadata<?>>> resourcesWeCanList = addToBuilderOnComplete(asyncResources);
// only serve resources that made it by the timeout
blockUntilAllDoneOrCancelOnTimeout(asyncResources);
return resourcesWeCanList.build();
}
private Builder<Iterable<? extends ResourceMetadata<?>>> addToBuilderOnComplete(
Iterable<ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> asyncResources) {
final Builder<Iterable<? extends ResourceMetadata<?>>> resourcesWeCanList = ImmutableSet
.<Iterable<? extends ResourceMetadata<?>>> builder();
for (final ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>> asyncResource : asyncResources) {
Futures.addCallback(asyncResource, new FutureCallback<Iterable<? extends ResourceMetadata<?>>>() {
public void onSuccess(Iterable<? extends ResourceMetadata<?>> 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<ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> asyncResources) {
List<ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> 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);
}
}
}
}

View File

@ -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<BlobStoreContext> blobsStoreContexts;
private final Iterable<ComputeServiceContext> computeServiceContexts;
private final BlobStoreContextToStatusResult blobStoreContextToContainerResult;
private final ComputeServiceContextToStatusResult computeServiceContextToContainerResult;
@Resource
protected Logger logger = Logger.NULL;
@Inject
GetAllStatusController(Iterable<BlobStoreContext> blobsStoreContexts,
Iterable<ComputeServiceContext> 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))));
}
}

View File

@ -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<BlobStoreContext> blobsStoreContexts;
private Iterable<ComputeServiceContext> computeServiceContexts;
private Iterable<View> 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<Module> modules = ImmutableSet.<Module> 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<ProviderMetadata> identityInProperties = providersWeHaveIdentitiesFor(overrides);
final ImmutableSet<Module> modules = ImmutableSet.<Module> of(new AsyncGoogleAppEngineConfigurationModule());
views = transform(identityInProperties, new Function<ProviderMetadata, View>() {
@Override
public View apply(ProviderMetadata input) {
TypeToken<? extends View> defaultView = get(input.getApiMetadata().getViews(), 0);
return ContextBuilder.newBuilder(input).modules(modules).overrides(overrides).buildView(defaultView);
}
});
super.contextInitialized(servletContextEvent);
}
protected Iterable<ProviderMetadata> 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<ProviderMetadata>() {
@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<Iterable<BlobStoreContext>>() {
}).toInstance(GuiceServletConfig.this.blobsStoreContexts);
bind(new TypeLiteral<Iterable<ComputeServiceContext>>() {
}).toInstance(GuiceServletConfig.this.computeServiceContexts);
serve("*.check").with(GetAllStatusController.class);
bind(new TypeLiteral<Iterable<View>>() {
}).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);
}

View File

@ -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();
}
}

View File

@ -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<StatusResult>, 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;
}
}

View File

@ -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<BlobStoreContext, StatusResult> {
public class BlobStoreContextToAsyncResources implements
Function<BlobStoreContext, ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> {
@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<? extends Iterable<? extends ResourceMetadata<?>>> apply(BlobStoreContext in) {
logger.info("listing containers on %s: ", in.unwrap().getId());
return in.getAsyncBlobStore().list();
}
}

View File

@ -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<ComputeServiceContext, ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> {
@Resource
protected Logger logger = Logger.NULL;
private final ListeningExecutorService currentRequestExecutorService;
@Inject
public ComputeServiceContextToAsyncResources(ListeningExecutorService currentRequestExecutorService) {
this.currentRequestExecutorService = currentRequestExecutorService;
}
public ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>> apply(final ComputeServiceContext in) {
return currentRequestExecutorService.submit(new Callable<Iterable<? extends ResourceMetadata<?>>>() {
@Override
public Iterable<? extends ResourceMetadata<?>> call() throws Exception {
logger.info("listing nodes on %s: ", in.unwrap().getId());
return in.getComputeService().listNodes();
}
@Override
public String toString() {
return in.toString();
}
});
}
}

View File

@ -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<ComputeServiceContext, StatusResult> {
public class ResourceMetadataToResourceResult implements Function<ResourceMetadata<?>, 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();
}
}

View File

@ -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<View, ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>>> {
private final BlobStoreContextToAsyncResources blobStoreContextToAsyncResources;
private final ComputeServiceContextToAsyncResources computeServiceContextToAsyncResources;
@Inject
public ViewToAsyncResources(BlobStoreContextToAsyncResources blobStoreContextToAsyncResources,
ComputeServiceContextToAsyncResources computeServiceContextToAsyncResources) {
this.blobStoreContextToAsyncResources = blobStoreContextToAsyncResources;
this.computeServiceContextToAsyncResources = computeServiceContextToAsyncResources;
}
@Override
public ListenableFuture<? extends Iterable<? extends ResourceMetadata<?>>> 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);
}
}

View File

@ -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<View, String> {
INSTANCE;
@Override
public String apply(View input) {
return input.unwrap().getId();
}
}

View File

@ -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"%>
<html>
<head>
<title>jclouds: multi-cloud library</title>
<style type="text/css">
<!--
table.staticheader {
text-decoration: none;
border: 1px solid #CCC;
width: 98%;
}
table.staticheader th {
padding: 3px 3px 3px 3px !important;
text-align:center;
}
table.staticheader td {
padding: 3px 3px 3px 3px !important;
}
table.staticheader thead tr {
position: relative;
height: 10px;
background-color: #D7E5F3;
}
table.staticheader tbody {
height:800px;
overflow-x:hidden;
overflow-y: auto;
overflow:scroll;
}
table.staticheader tbody tr {
height: auto;
white-space: nowrap;
}
table.staticheader tbody tr.odd {
background-color: #eee
}
table.staticheader tbody tr.tableRowEven,tr.even {
background-color: #ddd
}
table.staticheader tbody tr td:last-child {
padding-right: 20px;
}
table.staticheader tbody td {
padding: 2px 4px 2px 4px !important;
}
div.TableContainer {
height: 800px;
overflow-x:hidden;
overflow-y:auto;
}
-->
</style>
</head>
<body>
<h2>Resource List</h2>
<table width="100%" border="0">
<tr>
<td>
<div class="TableContainer">
<display:table name="resources" defaultsort="1" cellpadding="5" cellspacing="1" class="staticheader">
<display:column property="provider" title="Provider" />
<display:column property="location" title="Location" />
<display:column property="type" title="Type" />
<display:column property="id" title="Id" />
<display:column property="name" title="Name" />
</display:table>
</div>
</td>
</tr>
</table>
</body>
</html>

View File

@ -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"%>
<html>
<head>
<title>jclouds: multi-cloud library</title>
</head>
<body>
<h2>Status List</h2>
<display:table name="status" >
<display:column property="service" title="Service" />
<display:column property="host" title="Host" />
<display:column property="name" title="Item" />
<display:column property="status" title="Status" />
</display:table>
</body>
</html>

View File

@ -25,6 +25,6 @@
<body>
<h2>Welcome!</h2>
Click
<a href="/guice/status.check">here</a> to get status of cloud services.
<a href="/guice/resources.check">here</a> to list all your cloud resources!
</body>
</html>

View File

@ -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<String, String> stripTestPrefix(Map<String, String> identityCrendential) {
return Maps2.transformKeys(identityCrendential, new Function<String, String>() {
@Override
public String apply(String arg0) {
return arg0.replace("test.", "");
}
});
}
@SuppressWarnings({ "unchecked", "rawtypes" })
Map<String, String> selectPropertiesForIdentityAndCredential() {
return Maps.filterKeys((Map) System.getProperties(), new Predicate<String>() {
@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;