diff --git a/demos/pom.xml b/demos/pom.xml
index 31b5105286..50209cbeae 100644
--- a/demos/pom.xml
+++ b/demos/pom.xml
@@ -37,6 +37,7 @@
getpath
googleappengine
perftest
+ runatcloud-tweetstore
speedtest-azurequeue
speedtest-sqs
simpledb
diff --git a/demos/runatcloud-tweetstore/pom.xml b/demos/runatcloud-tweetstore/pom.xml
new file mode 100644
index 0000000000..93f30642fc
--- /dev/null
+++ b/demos/runatcloud-tweetstore/pom.xml
@@ -0,0 +1,274 @@
+
+
+
+ 4.0.0
+
+ org.jclouds
+ jclouds-demos-project
+ 1.0-SNAPSHOT
+
+ jclouds-demo-runatcloud-tweetstore
+ war
+ jclouds TweetStore for RUN@cloud
+ jclouds TweetStore for CloudBees' RUN@cloud using Guice for Dependency Injection
+
+
+ tweetstore
+ run
+ localhost
+ 8088
+ cloudfiles-us,aws-s3,azureblob
+ jclouds-tweetstore
+
+
+
+
+ ${project.groupId}
+ jclouds-blobstore
+ ${project.version}
+
+
+ org.twitter4j
+ twitter4j-core
+ [2.1,)
+
+
+ ${project.groupId}
+ jclouds-blobstore
+ ${project.version}
+ test-jar
+ test
+
+
+ org.jclouds.provider
+ aws-s3
+ ${project.version}
+ runtime
+
+
+ org.jclouds.provider
+ cloudfiles-us
+ ${project.version}
+ runtime
+
+
+ org.jclouds.provider
+ azureblob
+ ${project.version}
+ runtime
+
+
+ com.google.inject.extensions
+ guice-servlet
+ 3.0
+
+
+ displaytag
+ displaytag
+ 1.2
+ runtime
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+
+ org.slf4j
+ slf4j-jdk14
+ 1.5.6
+ runtime
+
+
+ jstl
+ javax.servlet
+ 1.1.2
+ runtime
+
+
+ standard
+ taglibs
+ 1.1.2
+ runtime
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+ provided
+
+
+
+
+ net.stax
+ stax-appserver
+ 1.0.20110131-SNAPSHOT
+ test
+
+
+
+
+
+ bees-plugins-snapshots
+ http://repository-cloudbees.forge.cloudbees.com/public-snapshot/
+
+ false
+
+
+ true
+
+
+
+
+
+ ${project.artifactId}
+
+
+ maven-surefire-plugin
+
+
+ integration
+ integration-test
+
+ test
+
+
+
+
+ bees.address
+ ${bees.address}
+
+
+ bees.port
+ ${bees.port}
+
+
+ bees.environment
+ ${bees.environment}
+
+
+ bees.basedir
+ ${project.build.directory}/bees
+
+
+ warfile
+ ${project.build.directory}/${project.artifactId}
+
+
+
+
+
+
+
+ com.cloudbees
+ bees-maven-plugin
+ 1.0-SNAPSHOT
+
+
+
+
+
+
+ live
+
+
+
+ maven-surefire-plugin
+
+
+ integration
+ integration-test
+
+ test
+
+
+
+
+ test.twitter.identity
+ ${test.twitter.identity}
+
+
+ test.twitter.credential
+ ${test.twitter.credential}
+
+
+ test.azureblob.identity
+ ${test.azureblob.identity}
+
+
+ test.azureblob.credential
+ ${test.azureblob.credential}
+
+
+ test.cloudfiles-us.identity
+ ${test.cloudfiles-us.identity}
+
+
+ test.cloudfiles-us.credential
+ ${test.cloudfiles-us.credential}
+
+
+ test.aws-s3.identity
+ ${test.aws-s3.identity}
+
+
+ test.aws-s3.credential
+ ${test.aws-s3.credential}
+
+
+ bees.address
+ ${bees.address}
+
+
+ bees.port
+ ${bees.port}
+
+
+ bees.environment
+ ${bees.environment}
+
+
+ jclouds.tweetstore.blobstores
+ ${jclouds.tweetstore.blobstores}
+
+
+ jclouds.tweetstore.container
+ ${jclouds.tweetstore.container}
+
+
+ bees.basedir
+ ${project.build.directory}/bees
+
+
+ warfile
+ ${project.build.directory}/${project.artifactId}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/runatcloud-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java b/demos/runatcloud-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java
new file mode 100644
index 0000000000..3af47b0c2f
--- /dev/null
+++ b/demos/runatcloud-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java
@@ -0,0 +1,154 @@
+/**
+ *
+ * Copyright (C) 2011 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ */
+package org.jclouds.demo.tweetstore.config;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.servlet.ServletContextEvent;
+
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.BlobStoreContextFactory;
+import org.jclouds.demo.tweetstore.config.utils.HttpRequestTask;
+import org.jclouds.demo.tweetstore.config.utils.HttpRequestTask.Factory;
+import org.jclouds.demo.tweetstore.config.utils.TaskQueue;
+import org.jclouds.demo.tweetstore.controller.AddTweetsController;
+import org.jclouds.demo.tweetstore.controller.StoreTweetsController;
+import org.jclouds.http.HttpRequest;
+
+import twitter4j.Twitter;
+import twitter4j.TwitterFactory;
+import twitter4j.conf.Configuration;
+import twitter4j.conf.ConfigurationBuilder;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.io.Closeables;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.google.inject.servlet.ServletModule;
+
+/**
+ * Setup Logging and create Injector for use in testing S3.
+ *
+ * @author Adrian Cole
+ */
+public class GuiceServletConfig extends GuiceServletContextListener {
+ public static final String PROPERTY_BLOBSTORE_CONTEXTS = "blobstore.contexts";
+
+ private Map providerTypeToBlobStoreMap;
+ private Twitter twitterClient;
+ private String container;
+ private TaskQueue queue;
+
+ @Override
+ public void contextInitialized(ServletContextEvent servletContextEvent) {
+ BlobStoreContextFactory blobStoreContextFactory = new BlobStoreContextFactory();
+
+ Properties props = loadJCloudsProperties(servletContextEvent);
+
+ Set modules = ImmutableSet.of();
+ // shared across all blobstores and used to retrieve tweets
+ try {
+ Configuration twitterConf = new ConfigurationBuilder()
+ .setUser(props.getProperty("twitter.identity"))
+ .setPassword(props.getProperty("twitter.credential")).build();
+ twitterClient = new TwitterFactory(twitterConf).getInstance();
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("properties for twitter not configured properly in "
+ + props.toString(), e);
+ }
+ // common namespace for storing tweets
+ container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), PROPERTY_TWEETSTORE_CONTAINER);
+
+ // instantiate and store references to all blobstores by provider name
+ providerTypeToBlobStoreMap = Maps.newHashMap();
+ for (String hint : Splitter.on(',').split(checkNotNull(props.getProperty(PROPERTY_BLOBSTORE_CONTEXTS), PROPERTY_BLOBSTORE_CONTEXTS))) {
+ providerTypeToBlobStoreMap.put(hint, blobStoreContextFactory.createContext(hint, modules, props));
+ }
+
+ // get a queue for submitting store tweet requests
+ queue = TaskQueue.builder().name("twitter").period(MINUTES).build();
+ Factory taskFactory = HttpRequestTask.factory(props, "twitter");
+ // submit a job to store tweets for each configured blobstore
+ for (String name : providerTypeToBlobStoreMap.keySet()) {
+ queue.add(taskFactory.create(HttpRequest.builder()
+ .endpoint(URI.create("http://localhost:8080" + servletContextEvent.getServletContext().getContextPath() + "/store/do"))
+ .headers(ImmutableMultimap.of("context", name, "X-RUN@cloud-QueueName", "twitter"))
+ .method("GET").build()));
+ }
+
+ super.contextInitialized(servletContextEvent);
+ }
+
+ private Properties loadJCloudsProperties(
+ ServletContextEvent servletContextEvent) {
+ InputStream input = servletContextEvent.getServletContext()
+ .getResourceAsStream("/WEB-INF/jclouds.properties");
+ Properties props = new Properties();
+ try {
+ props.load(input);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ Closeables.closeQuietly(input);
+ }
+ return props;
+ }
+
+ @Override
+ protected Injector getInjector() {
+ return Guice.createInjector(new ServletModule() {
+ @Override
+ protected void configureServlets() {
+ bind(new TypeLiteral