diff --git a/demos/gae-tweetstore-spring/README.txt b/demos/gae-tweetstore-spring/README.txt index baa5639aa9..5ea882839c 100644 --- a/demos/gae-tweetstore-spring/README.txt +++ b/demos/gae-tweetstore-spring/README.txt @@ -24,9 +24,11 @@ It should not be regarded as a sample of how to write a web application using Sp however! The original jclouds-demo-gae-tweetstore has been modified in as few places as possible; it has not been rewritten in the style of a Spring MVC application. -This sample uses the Google App Engine for Java SDK located at http://googleappengine.googlecode.com/files/appengine-java-sdk-1.2.5.zip +This sample uses the Google App Engine for Java SDK located at +http://code.google.com/p/googleappengine/downloads/list -Please unzip the above file and modify your maven settings.xml like below before attempting to run 'mvn -Plive install' +Please unzip the above file and modify your maven settings.xml like below before +attempting to run 'mvn -Plive install' appengine diff --git a/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringAppConfig.java b/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringAppConfig.java deleted file mode 100644 index a1f8b0ac4c..0000000000 --- a/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringAppConfig.java +++ /dev/null @@ -1,164 +0,0 @@ -/** - * - * Copyright (C) 2009 Cloud Conscious, LLC. - * - * ==================================================================== - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ==================================================================== - */ -package org.jclouds.demo.tweetstore.config; - -import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_BLOBSTORE_CONTEXTBUILDERS; -import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.util.Map; -import java.util.Properties; - -import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.blobstore.BlobStoreContextBuilder; -import org.jclouds.gae.config.GaeHttpCommandExecutorServiceModule; -import org.jclouds.twitter.TwitterClient; -import org.jclouds.twitter.TwitterContextFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ResourceLoaderAware; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ResourceLoader; - -import com.google.appengine.api.labs.taskqueue.Queue; -import com.google.appengine.api.labs.taskqueue.QueueFactory; -import com.google.appengine.api.labs.taskqueue.TaskOptions.Method; -import com.google.appengine.repackaged.com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import com.google.common.io.Closeables; - -/** - * Reads properties and creates resources for servlets. - * - * @author Andrew Phillips - * @see SpringServletConfig - */ -@Configuration -public class SpringAppConfig extends LoggingConfig implements InitializingBean, DisposableBean, ResourceLoaderAware { - /* - * The call to TwitterContextFactory.createContext in initialize() - * must be carried out before the servlet context loads, otherwise the - * GAE will throw an access exception. - * For this reason, this code cannot be in the default servlet context - * loaded by the DispatcherServlet, but is executed in the root application - * context (which is processed by a listener). - * - * Note that none of the common annotations (such as @PostConstruct) - * will work here because Spring does not detect JSR-250 support (since it - * cannot load javax.annotation.Resource). - */ - Map> providerTypeToBlobStoreMap; - TwitterClient twitterClient; - String container; - - private final Properties props = new Properties(); - - /* (non-Javadoc) - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ - @SuppressWarnings("unchecked") - @Override - public void afterPropertiesSet() throws Exception { - logger.trace("About to initialize properties."); - - // shared across all blobstores and used to retrieve tweets - twitterClient = TwitterContextFactory.createContext(props, - new GaeHttpCommandExecutorServiceModule()).getApi(); - - // common namespace for storing tweets - container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), - PROPERTY_TWEETSTORE_CONTAINER); - ImmutableList contextBuilderClassNames = ImmutableList.of( - checkNotNull(props.getProperty(PROPERTY_BLOBSTORE_CONTEXTBUILDERS), - PROPERTY_BLOBSTORE_CONTEXTBUILDERS) - .split(",")); - - // instantiate and store references to all blobstores by provider name - providerTypeToBlobStoreMap = Maps.newHashMap(); - for (String className : contextBuilderClassNames) { - Class> builderClass; - Constructor> constructor; - String name; - BlobStoreContext context; - try { - builderClass = (Class>) Class.forName(className); - name = builderClass.getSimpleName().replaceAll("BlobStoreContextBuilder", ""); - constructor = builderClass.getConstructor(Properties.class); - context = constructor.newInstance(props) - .withModules(new GaeHttpCommandExecutorServiceModule()) - .buildContext(); - } catch (Exception e) { - throw new RuntimeException("error instantiating " + className, e); - } - providerTypeToBlobStoreMap.put(name, context); - } - - // get a queue for submitting store tweet requests - Queue queue = QueueFactory.getQueue("twitter"); - // submit a job to store tweets for each configured blobstore - for (String name : providerTypeToBlobStoreMap.keySet()) { - queue.add(url("/store/do").header("context", name).method(Method.GET)); - } - - logger.trace("Properties initialized. TwitterClient: '%s', container: '%s', provider types: '%s'", - twitterClient, container, providerTypeToBlobStoreMap.keySet()); - } - - /* (non-Javadoc) - * @see org.springframework.beans.factory.DisposableBean#destroy() - */ - @Override - public void destroy() throws Exception { - logger.trace("About to close contexts."); - - for (BlobStoreContext context : providerTypeToBlobStoreMap.values()) { - context.close(); - } - - logger.trace("Contexts closed."); - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.context.ResourceLoaderAware#setResourceLoader(org - * .springframework.core.io.ResourceLoader) - */ - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - logger.trace("About to read properties from '%s'", "/WEB-INF/jclouds.properties"); - - InputStream input = null; - try { - input = resourceLoader.getResource("/WEB-INF/jclouds.properties").getInputStream(); - props.load(input); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - Closeables.closeQuietly(input); - } - - logger.trace("Properties successfully read."); - } -} diff --git a/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringServletConfig.java b/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringServletConfig.java index 0934254514..1dcb2cb610 100644 --- a/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringServletConfig.java +++ b/demos/gae-tweetstore-spring/src/main/java/org/jclouds/demo/tweetstore/config/SpringServletConfig.java @@ -18,18 +18,31 @@ */ package org.jclouds.demo.tweetstore.config; +import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url; import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_BLOBSTORE_CONTEXTBUILDERS; +import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; import java.util.Map; +import java.util.Properties; -import javax.inject.Inject; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.BlobStoreContextBuilder; import org.jclouds.demo.tweetstore.controller.AddTweetsController; import org.jclouds.demo.tweetstore.controller.StoreTweetsController; import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses; +import org.jclouds.gae.config.GaeHttpCommandExecutorServiceModule; +import org.jclouds.twitter.TwitterClient; +import org.jclouds.twitter.TwitterContextFactory; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,7 +52,12 @@ import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.handler.SimpleServletHandlerAdapter; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; +import com.google.appengine.api.labs.taskqueue.Queue; +import com.google.appengine.api.labs.taskqueue.QueueFactory; +import com.google.appengine.api.labs.taskqueue.TaskOptions.Method; +import com.google.appengine.repackaged.com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; +import com.google.common.io.Closeables; /** * Creates servlets (using resources from the {@link SpringAppConfig}) and mappings. @@ -51,20 +69,84 @@ import com.google.common.collect.Maps; public class SpringServletConfig extends LoggingConfig implements ServletConfigAware { private ServletConfig servletConfig; - @Inject - private SpringAppConfig appConfig; + private Map> providerTypeToBlobStoreMap; + private TwitterClient twitterClient; + private String container; + + @SuppressWarnings("unchecked") + @PostConstruct + public void initialize() { + Properties props = loadJCloudsProperties(); + logger.trace("About to initialize members."); + + // shared across all blobstores and used to retrieve tweets + twitterClient = TwitterContextFactory.createContext(props, + new GaeHttpCommandExecutorServiceModule()).getApi(); + + // common namespace for storing tweets + container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), + PROPERTY_TWEETSTORE_CONTAINER); + ImmutableList contextBuilderClassNames = ImmutableList.of( + checkNotNull(props.getProperty(PROPERTY_BLOBSTORE_CONTEXTBUILDERS), + PROPERTY_BLOBSTORE_CONTEXTBUILDERS) + .split(",")); + + // instantiate and store references to all blobstores by provider name + providerTypeToBlobStoreMap = Maps.newHashMap(); + for (String className : contextBuilderClassNames) { + Class> builderClass; + Constructor> constructor; + String name; + BlobStoreContext context; + try { + builderClass = (Class>) Class.forName(className); + name = builderClass.getSimpleName().replaceAll("BlobStoreContextBuilder", ""); + constructor = builderClass.getConstructor(Properties.class); + context = constructor.newInstance(props) + .withModules(new GaeHttpCommandExecutorServiceModule()) + .buildContext(); + } catch (Exception e) { + throw new RuntimeException("error instantiating " + className, e); + } + providerTypeToBlobStoreMap.put(name, context); + } + + // get a queue for submitting store tweet requests + Queue queue = QueueFactory.getQueue("twitter"); + // submit a job to store tweets for each configured blobstore + for (String name : providerTypeToBlobStoreMap.keySet()) { + queue.add(url("/store/do").header("context", name).method(Method.GET)); + } + logger.trace("Members initialized. TwitterClient: '%s', container: '%s', provider types: '%s'", + twitterClient, container, providerTypeToBlobStoreMap.keySet()); + } + private Properties loadJCloudsProperties() { + logger.trace("About to read properties from '%s'", "/WEB-INF/jclouds.properties"); + Properties props = new Properties(); + InputStream input = servletConfig.getServletContext() + .getResourceAsStream("/WEB-INF/jclouds.properties"); + try { + props.load(input); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + Closeables.closeQuietly(input); + } + logger.trace("Properties successfully read."); + return props; + } @Bean public StoreTweetsController storeTweetsController() { - StoreTweetsController controller = new StoreTweetsController(appConfig.providerTypeToBlobStoreMap, - appConfig.container, appConfig.twitterClient); + StoreTweetsController controller = new StoreTweetsController(providerTypeToBlobStoreMap, + container, twitterClient); injectServletConfig(controller); return controller; } @Bean public AddTweetsController addTweetsController() { - AddTweetsController controller = new AddTweetsController(appConfig.providerTypeToBlobStoreMap, + AddTweetsController controller = new AddTweetsController(providerTypeToBlobStoreMap, serviceToStoredTweetStatuses()); injectServletConfig(controller); return controller; @@ -72,20 +154,17 @@ public class SpringServletConfig extends LoggingConfig implements ServletConfigA private void injectServletConfig(Servlet servlet) { logger.trace("About to inject servlet config '%s'", servletConfig); - try { servlet.init(checkNotNull(servletConfig)); } catch (ServletException exception) { throw new BeanCreationException("Unable to instantiate " + servlet, exception); } - logger.trace("Successfully injected servlet config."); } @Bean ServiceToStoredTweetStatuses serviceToStoredTweetStatuses() { - return new ServiceToStoredTweetStatuses(appConfig.providerTypeToBlobStoreMap, - appConfig.container); + return new ServiceToStoredTweetStatuses(providerTypeToBlobStoreMap, container); } @Bean @@ -107,6 +186,15 @@ public class SpringServletConfig extends LoggingConfig implements ServletConfigA public HandlerAdapter servletHandlerAdapter() { return new SimpleServletHandlerAdapter(); } + + @PreDestroy + public void destroy() throws Exception { + logger.trace("About to close contexts."); + for (BlobStoreContext context : providerTypeToBlobStoreMap.values()) { + context.close(); + } + logger.trace("Contexts closed."); + } /* (non-Javadoc) * @see org.springframework.web.context.ServletConfigAware#setServletConfig(javax.servlet.ServletConfig) diff --git a/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/dispatcher-servlet.xml b/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/dispatcher-servlet.xml index 5c4755de94..60d498a2be 100644 --- a/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/dispatcher-servlet.xml +++ b/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/dispatcher-servlet.xml @@ -9,6 +9,10 @@ tries to load javax.annotation.Resource --> + + + + \ No newline at end of file diff --git a/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/web.xml b/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/web.xml index 306086fd93..ca16b8e186 100644 --- a/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/web.xml +++ b/demos/gae-tweetstore-spring/src/main/webapp/WEB-INF/web.xml @@ -22,21 +22,7 @@ - jclouds-demo-gae-tweetstore-spring - - - - contextClass - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - - contextConfigLocation - org.jclouds.demo.tweetstore.config.SpringAppConfig - - - - org.springframework.web.context.ContextLoaderListener - + jclouds-tweetstore-spring