Skip to content
Browse files

initial commit.

  • Loading branch information...
1 parent a663114 commit 71f048de430066c89e3937043bff5bc785b33af6 @joshlong joshlong committed May 8, 2012
Showing with 21,381 additions and 0 deletions.
  1. +6 −0 README
  2. +18 −0 cf-workers-batch/manifest.yml
  3. +170 −0 cf-workers-batch/pom.xml
  4. +3 −0 cf-workers-batch/redeploy.sh
  5. +211 −0 cf-workers-batch/src/main/java/org/cloudfoundry/workers/stocks/batch/BatchConfiguration.java
  6. +32 −0 cf-workers-batch/src/main/java/org/cloudfoundry/workers/stocks/batch/Main.java
  7. +66 −0 cf-workers-batch/src/main/java/org/cloudfoundry/workers/stocks/batch/NightlyStockSymbolRecorder.java
  8. +19 −0 cf-workers-batch/src/main/resources/batch.xml
  9. +80 −0 cf-workers-batch/src/main/resources/batch_h2.sql
  10. +79 −0 cf-workers-batch/src/main/resources/batch_psql.sql
  11. +23 −0 cf-workers-batch/src/main/resources/stocks_h2.sql
  12. +36 −0 cf-workers-batch/src/main/resources/stocks_psql.sql
  13. +66 −0 cf-workers-core/pom.xml
  14. +71 −0 ...kers-core/src/main/java/org/cloudfoundry/workers/stocks/GoogleFinanceStockSymbolLookupClient.java
  15. +47 −0 cf-workers-core/src/main/java/org/cloudfoundry/workers/stocks/MockStockSymbolLookupClient.java
  16. +107 −0 cf-workers-core/src/main/java/org/cloudfoundry/workers/stocks/StockSymbolLookup.java
  17. +35 −0 cf-workers-core/src/main/java/org/cloudfoundry/workers/stocks/StockSymbolLookupClient.java
  18. +84 −0 ...ore/src/main/java/org/cloudfoundry/workers/stocks/YahooPipesQuotesApiStockSymbolLookupClient.java
  19. +5 −0 cf-workers-integration-service/TODO.TXT
  20. +18 −0 cf-workers-integration-service/manifest.yml
  21. +139 −0 cf-workers-integration-service/pom.xml
  22. +3 −0 cf-workers-integration-service/redeploy.sh
  23. +32 −0 ...s-integration-service/src/main/java/org/cloudfoundry/workers/stocks/integration/service/Main.java
  24. +122 −0 ...rvice/src/main/java/org/cloudfoundry/workers/stocks/integration/service/ServiceConfiguration.java
  25. +23 −0 cf-workers-integration-service/src/main/resources/symbol-lookup-gateway-service.xml
  26. +50 −0 cf-workers-integration-webclient/README
  27. +16 −0 cf-workers-integration-webclient/manifest.yml
  28. +211 −0 cf-workers-integration-webclient/pom.xml
  29. +4 −0 cf-workers-integration-webclient/redeploy.sh
  30. +111 −0 ...bclient/src/main/java/org/cloudfoundry/workers/stocks/integration/client/ClientConfiguration.java
  31. +41 −0 ...-integration-webclient/src/main/java/org/cloudfoundry/workers/stocks/integration/client/Main.java
  32. +41 −0 ...ebclient/src/main/java/org/cloudfoundry/workers/stocks/integration/client/StockClientGateway.java
  33. +26 −0 ...s-integration-webclient/src/main/java/org/cloudfoundry/workers/stocks/web/StockApiController.java
  34. +23 −0 ...-integration-webclient/src/main/java/org/cloudfoundry/workers/stocks/web/StockViewController.java
  35. +49 −0 ...-integration-webclient/src/main/java/org/cloudfoundry/workers/stocks/web/WebMvcConfiguration.java
  36. +29 −0 cf-workers-integration-webclient/src/main/resources/client.xml
  37. +9 −0 cf-workers-integration-webclient/src/main/resources/log4j.properties
  38. 0 cf-workers-integration-webclient/src/main/resources/messages.properties
  39. +103 −0 cf-workers-integration-webclient/src/main/webapp/WEB-INF/views/stocks.jsp
  40. +36 −0 cf-workers-integration-webclient/src/main/webapp/WEB-INF/web.xml
  41. +4,960 −0 cf-workers-integration-webclient/src/main/webapp/web/assets/bootstrap/bootstrap.css
  42. BIN cf-workers-integration-webclient/src/main/webapp/web/assets/img/glyphicons-halflings.png
  43. +13,556 −0 cf-workers-integration-webclient/src/main/webapp/web/assets/js/angular-1.0.0rc6.js
  44. +4 −0 cf-workers-integration-webclient/src/main/webapp/web/assets/js/jquery-1.7.2.min.js
  45. +5 −0 cf-workers-integration-webclient/src/main/webapp/web/views/stocks.css
  46. +50 −0 cf-workers-integration-webclient/src/main/webapp/web/views/stocks.js
  47. +562 −0 pom.xml
View
6 README
@@ -0,0 +1,6 @@
+BUILD
+* To build this, run the Apache Maven 2 tool at the base of the code base (where the top most pom.xml file is): mvn clean install
+* there are shell scripts (should be named *redeploy.sh* in each folder) that show you how to compile the code and
+ deploy them to Cloud Foundry. Because each module has already a manifest.yml, they don't need to tweaked, except when it comes to the URL of the application. Obviously, there's a high
+ degree the URL you want is already taken by somebody else, so choose wisely.
+* For the integration demos (-webclient, and -service), you need to install the -service first, then kick open the -webclient version)
View
18 cf-workers-batch/manifest.yml
@@ -0,0 +1,18 @@
+---
+applications:
+ target/appassembler/:
+ name: batch
+ framework:
+ name: standalone
+ info:
+ mem: 64M
+ description: Standalone Application
+ exec:
+ runtime: java
+ command: bin/main
+ url:
+ mem: 512M
+ instances: 1
+ services:
+ stock_batch:
+ type: postgresql
View
170 cf-workers-batch/pom.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>cf-workers-batch</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>cf-workers-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-orm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-jdbc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aspects</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.cloudfoundry</groupId><artifactId>cloudfoundry-runtime</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.amqp</groupId>
+ <artifactId>spring-rabbit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.rabbitmq</groupId>
+ <artifactId>amqp-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.amqp</groupId>
+ <artifactId>spring-amqp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.integration</groupId>
+ <artifactId>spring-integration-amqp</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.integration</groupId>
+ <artifactId>spring-integration-core</artifactId>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.springframework.batch</groupId>
+ <artifactId>spring-batch-integration</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.batch</groupId>
+ <artifactId>spring-batch-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+
+
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ </dependency>
+
+ </dependencies>
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>appassembler-maven-plugin</artifactId>
+ <version>1.1.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>assemble</goal>
+ </goals>
+ <configuration>
+ <assembledirectory>target</assembledirectory>
+ <programs>
+ <program>
+ <mainClass>org.cloudfoundry.workers.stocks.batch.Main</mainClass>
+ </program>
+ </programs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
View
3 cf-workers-batch/redeploy.sh
@@ -0,0 +1,3 @@
+mvn clean install
+vmc delete batch
+vmc --path target/appassembler push
View
211 cf-workers-batch/src/main/java/org/cloudfoundry/workers/stocks/batch/BatchConfiguration.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.batch;
+
+
+import org.cloudfoundry.runtime.env.CloudEnvironment;
+import org.cloudfoundry.runtime.env.RdbmsServiceInfo;
+import org.cloudfoundry.runtime.service.relational.RdbmsServiceCreator;
+import org.cloudfoundry.workers.stocks.GoogleFinanceStockSymbolLookupClient;
+import org.cloudfoundry.workers.stocks.StockSymbolLookup;
+import org.cloudfoundry.workers.stocks.StockSymbolLookupClient;
+import org.cloudfoundry.workers.stocks.YahooPipesQuotesApiStockSymbolLookupClient;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
+import org.springframework.batch.core.configuration.support.MapJobRegistry;
+import org.springframework.batch.core.launch.support.SimpleJobLauncher;
+import org.springframework.batch.core.repository.JobRepository;
+import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
+import org.springframework.batch.item.ItemProcessor;
+import org.springframework.batch.item.ItemReader;
+import org.springframework.batch.item.database.ItemSqlParameterSourceProvider;
+import org.springframework.batch.item.database.JdbcBatchItemWriter;
+import org.springframework.batch.item.database.JdbcCursorItemReader;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportResource;
+import org.springframework.context.annotation.Scope;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.SqlParameterSource;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.jdbc.datasource.init.DataSourceInitializer;
+import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.util.Assert;
+import org.springframework.web.client.RestTemplate;
+
+import javax.sql.DataSource;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Collection;
+import java.util.Date;
+
+/**
+ * Configures a Spring Batch job that looks at the ticker symbols
+ * in the 'STOCKS' table, and then retreives
+ * the information for all of the records for that day.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+@Configuration
+@ImportResource("/batch.xml")
+@EnableScheduling
+public class BatchConfiguration {
+
+ @Bean
+ @Autowired
+ @Qualifier("analyseStocks")
+ public NightlyStockSymbolRecorder recorder(Job job) throws Throwable {
+ return new NightlyStockSymbolRecorder(jobLauncher(), job);
+ }
+
+ @Bean
+ public CloudEnvironment cloudEnvironment() {
+ return new CloudEnvironment();
+ }
+
+ @Bean
+ public DataSourceInitializer dataSourceInitializer() {
+ DataSourceInitializer dsi = new DataSourceInitializer();
+ dsi.setDataSource(this.dataSource());
+ ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
+ String[] scripts = "/batch_psql.sql,/stocks_psql.sql".split(",");
+ for (String s : scripts)
+ resourceDatabasePopulator.addScript(new ClassPathResource(s));
+ dsi.setDatabasePopulator(resourceDatabasePopulator);
+ dsi.setEnabled(true);
+ return dsi;
+ }
+
+ @Bean
+ public DataSource dataSource() {
+
+ Collection<RdbmsServiceInfo> servicesInfosForTheDbms = this.cloudEnvironment().getServiceInfos(RdbmsServiceInfo.class);
+ Assert.isTrue(servicesInfosForTheDbms.size() > 0, "there must be at least one RDBMS bound!");
+ RdbmsServiceInfo rdbmsServiceInfo = servicesInfosForTheDbms.iterator().next();
+ RdbmsServiceCreator rdbmsServiceCreator = new RdbmsServiceCreator();
+ return rdbmsServiceCreator.createService(rdbmsServiceInfo);
+ }
+
+ @Bean // sets up infrastructure and scope
+ public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() throws Exception {
+ JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
+ jobRegistryBeanPostProcessor.setJobRegistry(this.mapJobRegistry());
+ return jobRegistryBeanPostProcessor;
+ }
+
+ @Bean
+ public PlatformTransactionManager transactionManager() {
+ return new DataSourceTransactionManager(dataSource());
+ }
+
+ @Bean
+ public MapJobRegistry mapJobRegistry() throws Exception {
+ return new MapJobRegistry();
+ }
+
+ @Bean(name = "jobRepository")
+ public JobRepository jobRepository() throws Exception {
+ JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
+ jobRepositoryFactoryBean.setDataSource(this.dataSource());
+ jobRepositoryFactoryBean.setTransactionManager(this.transactionManager());
+ jobRepositoryFactoryBean.afterPropertiesSet();
+ return (JobRepository) jobRepositoryFactoryBean.getObject();
+ }
+
+ @Bean
+ public SimpleJobLauncher jobLauncher() throws Exception {
+ SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
+ simpleJobLauncher.setJobRepository(this.jobRepository());
+ return simpleJobLauncher;
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @Bean
+ public StockSymbolLookupClient lookupClient() {
+ return new YahooPipesQuotesApiStockSymbolLookupClient(restTemplate());
+ }
+
+ @Bean
+ public ItemReader<String> reader() {
+
+ RowMapper<String> rowMapper = new RowMapper<String>() {
+ @Override
+ public String mapRow(ResultSet rs, int rowNum) throws SQLException {
+ return rs.getString("symbol");
+ }
+ };
+
+ JdbcCursorItemReader<String> readerOfSymbols = new JdbcCursorItemReader<String>();
+ readerOfSymbols.setSql("SELECT SYMBOL FROM STOCKS");
+ readerOfSymbols.setRowMapper(rowMapper);
+ readerOfSymbols.setDataSource(dataSource());
+ return readerOfSymbols;
+ }
+
+ @Bean
+ public ItemProcessor<String, StockSymbolLookup> processor() {
+ final StockSymbolLookupClient stockSymbolLookupClient = this.lookupClient();
+ return new ItemProcessor<String, StockSymbolLookup>() {
+ @Override
+ public StockSymbolLookup process(String tickerSymbol) throws Exception {
+ try {
+ return stockSymbolLookupClient.lookupSymbol(tickerSymbol);
+ } catch (Throwable throwable) {
+ throw new RuntimeException(throwable);
+ }
+ }
+ };
+
+ }
+
+ @Bean(name = "writer")
+ @Scope("step")
+ public JdbcBatchItemWriter<StockSymbolLookup> writer(final @Value("#{jobParameters['date']}") Date dateOfAnalysis) {
+ JdbcBatchItemWriter<StockSymbolLookup> jdbcBatchItemWriter = new JdbcBatchItemWriter<StockSymbolLookup>();
+ jdbcBatchItemWriter.setSql("INSERT INTO STOCKS_DATA( DATE_ANALYSED, HIGH_PRICE, LOW_PRICE, CLOSING_PRICE, SYMBOL) VALUES ( :da, :hp, :lp, :cp, :s ) ");
+ jdbcBatchItemWriter.setDataSource(this.dataSource());
+ jdbcBatchItemWriter.setItemSqlParameterSourceProvider(new ItemSqlParameterSourceProvider<StockSymbolLookup>() {
+ @Override
+ public SqlParameterSource createSqlParameterSource(StockSymbolLookup item) {
+ return new MapSqlParameterSource()
+ .addValue("da", new java.sql.Date(dateOfAnalysis.getTime()), Types.DATE)
+ .addValue("hp", item.getHighPrice(), Types.DOUBLE)
+ .addValue("lp", item.getLowPrice(), Types.DOUBLE)
+ .addValue("s", item.getTicker())
+ .addValue("si", item.getId(), Types.BIGINT) // stock id has to be the same as in the Symbol lookup
+ .addValue("cp", item.getLastValueWhileOpen(), Types.DOUBLE);
+
+ }
+ });
+
+ return jdbcBatchItemWriter;
+ }
+
+
+}
View
32 cf-workers-batch/src/main/java/org/cloudfoundry/workers/stocks/batch/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.batch;
+
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * Launches a batch job for a given date.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+public class Main {
+ public static void main (String [] args) throws Throwable {
+ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BatchConfiguration.class);
+ applicationContext.start();
+ }
+}
View
66 ...batch/src/main/java/org/cloudfoundry/workers/stocks/batch/NightlyStockSymbolRecorder.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.batch;
+
+import org.springframework.batch.core.*;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import java.util.Date;
+import java.util.logging.Logger;
+
+/**
+ * Simple example of a jl that runs in a background thread and does some
+ * long-running work (in this case, looking up stock information)
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+public class NightlyStockSymbolRecorder {
+
+ private Logger logger = Logger.getLogger(getClass().getName());
+
+ private JobLauncher jobLauncher;
+ private Job job;
+
+ // @Scheduled(cron = "0 23 ? * MON-FRI")
+ // every weekday at 11 PM / 23h00
+
+ // set to every 10s for testing.
+ @Scheduled(fixedRate = 10 * 1000)
+ public void runNightlyStockPriceRecorder() throws Throwable {
+ JobParameters params = new JobParametersBuilder()
+ .addDate("date", new Date())
+ .toJobParameters();
+
+ JobExecution jobExecution = jobLauncher.run(job, params);
+ BatchStatus batchStatus = jobExecution.getStatus();
+ while (batchStatus.isRunning()) {
+ logger.info("Still running...");
+ Thread.sleep(1000);
+ }
+ logger.info(String.format("Exit status: %s", jobExecution.getExitStatus().getExitCode()));
+ JobInstance jobInstance = jobExecution.getJobInstance();
+ logger.info(String.format("job instance Id: %d", jobInstance.getId()));
+ }
+
+ public NightlyStockSymbolRecorder(JobLauncher jl, Job jobToRun) {
+ this.jobLauncher = jl;
+ this.job = jobToRun;
+ }
+
+
+}
View
19 cf-workers-batch/src/main/resources/batch.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/batch"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+ ">
+
+
+ <job id="analyseStocks" job-repository="jobRepository">
+ <step id="step1">
+ <tasklet transaction-manager="transactionManager">
+ <chunk reader="reader" processor="processor" writer="writer" commit-interval="2"/>
+ </tasklet>
+ </step>
+ </job>
+
+</beans:beans>
View
80 cf-workers-batch/src/main/resources/batch_h2.sql
@@ -0,0 +1,80 @@
+-- Autogenerated: do not edit this file
+
+
+CREATE TABLE BATCH_JOB_INSTANCE (
+ JOB_INSTANCE_ID BIGINT IDENTITY NOT NULL PRIMARY KEY ,
+ VERSION BIGINT ,
+ JOB_NAME VARCHAR(100) NOT NULL,
+ JOB_KEY VARCHAR(32) NOT NULL,
+ constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION (
+ JOB_EXECUTION_ID BIGINT IDENTITY NOT NULL PRIMARY KEY ,
+ VERSION BIGINT ,
+ JOB_INSTANCE_ID BIGINT NOT NULL,
+ CREATE_TIME TIMESTAMP NOT NULL,
+ START_TIME TIMESTAMP DEFAULT NULL ,
+ END_TIME TIMESTAMP DEFAULT NULL ,
+ STATUS VARCHAR(10) ,
+ EXIT_CODE VARCHAR(100) ,
+ EXIT_MESSAGE VARCHAR(2500) ,
+ LAST_UPDATED TIMESTAMP,
+ constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
+ references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
+) ;
+
+CREATE TABLE BATCH_JOB_PARAMS (
+ JOB_INSTANCE_ID BIGINT NOT NULL ,
+ TYPE_CD VARCHAR(6) NOT NULL ,
+ KEY_NAME VARCHAR(100) NOT NULL ,
+ STRING_VAL VARCHAR(250) ,
+ DATE_VAL TIMESTAMP DEFAULT NULL ,
+ LONG_VAL BIGINT ,
+ DOUBLE_VAL DOUBLE PRECISION ,
+ constraint JOB_INST_PARAMS_FK foreign key (JOB_INSTANCE_ID)
+ references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
+) ;
+
+CREATE TABLE BATCH_STEP_EXECUTION (
+ STEP_EXECUTION_ID BIGINT IDENTITY NOT NULL PRIMARY KEY ,
+ VERSION BIGINT NOT NULL,
+ STEP_NAME VARCHAR(100) NOT NULL,
+ JOB_EXECUTION_ID BIGINT NOT NULL,
+ START_TIME TIMESTAMP NOT NULL ,
+ END_TIME TIMESTAMP DEFAULT NULL ,
+ STATUS VARCHAR(10) ,
+ COMMIT_COUNT BIGINT ,
+ READ_COUNT BIGINT ,
+ FILTER_COUNT BIGINT ,
+ WRITE_COUNT BIGINT ,
+ READ_SKIP_COUNT BIGINT ,
+ WRITE_SKIP_COUNT BIGINT ,
+ PROCESS_SKIP_COUNT BIGINT ,
+ ROLLBACK_COUNT BIGINT ,
+ EXIT_CODE VARCHAR(100) ,
+ EXIT_MESSAGE VARCHAR(2500) ,
+ LAST_UPDATED TIMESTAMP,
+ constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
+ STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
+ SHORT_CONTEXT VARCHAR(2500) NOT NULL,
+ SERIALIZED_CONTEXT LONGVARCHAR ,
+ constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
+ references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
+ JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
+ SHORT_CONTEXT VARCHAR(2500) NOT NULL,
+ SERIALIZED_CONTEXT LONGVARCHAR ,
+ constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;
+CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ;
+CREATE SEQUENCE BATCH_JOB_SEQ;
View
79 cf-workers-batch/src/main/resources/batch_psql.sql
@@ -0,0 +1,79 @@
+-- Autogenerated: do not edit this file
+
+CREATE TABLE BATCH_JOB_INSTANCE (
+ JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY ,
+ VERSION BIGINT ,
+ JOB_NAME VARCHAR(100) NOT NULL,
+ JOB_KEY VARCHAR(32) NOT NULL,
+ constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION (
+ JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
+ VERSION BIGINT ,
+ JOB_INSTANCE_ID BIGINT NOT NULL,
+ CREATE_TIME TIMESTAMP NOT NULL,
+ START_TIME TIMESTAMP DEFAULT NULL ,
+ END_TIME TIMESTAMP DEFAULT NULL ,
+ STATUS VARCHAR(10) ,
+ EXIT_CODE VARCHAR(100) ,
+ EXIT_MESSAGE VARCHAR(2500) ,
+ LAST_UPDATED TIMESTAMP,
+ constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
+ references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
+) ;
+
+CREATE TABLE BATCH_JOB_PARAMS (
+ JOB_INSTANCE_ID BIGINT NOT NULL ,
+ TYPE_CD VARCHAR(6) NOT NULL ,
+ KEY_NAME VARCHAR(100) NOT NULL ,
+ STRING_VAL VARCHAR(250) ,
+ DATE_VAL TIMESTAMP DEFAULT NULL ,
+ LONG_VAL BIGINT ,
+ DOUBLE_VAL DOUBLE PRECISION ,
+ constraint JOB_INST_PARAMS_FK foreign key (JOB_INSTANCE_ID)
+ references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
+) ;
+
+CREATE TABLE BATCH_STEP_EXECUTION (
+ STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
+ VERSION BIGINT NOT NULL,
+ STEP_NAME VARCHAR(100) NOT NULL,
+ JOB_EXECUTION_ID BIGINT NOT NULL,
+ START_TIME TIMESTAMP NOT NULL ,
+ END_TIME TIMESTAMP DEFAULT NULL ,
+ STATUS VARCHAR(10) ,
+ COMMIT_COUNT BIGINT ,
+ READ_COUNT BIGINT ,
+ FILTER_COUNT BIGINT ,
+ WRITE_COUNT BIGINT ,
+ READ_SKIP_COUNT BIGINT ,
+ WRITE_SKIP_COUNT BIGINT ,
+ PROCESS_SKIP_COUNT BIGINT ,
+ ROLLBACK_COUNT BIGINT ,
+ EXIT_CODE VARCHAR(100) ,
+ EXIT_MESSAGE VARCHAR(2500) ,
+ LAST_UPDATED TIMESTAMP,
+ constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
+ STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
+ SHORT_CONTEXT VARCHAR(2500) NOT NULL,
+ SERIALIZED_CONTEXT TEXT ,
+ constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
+ references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
+) ;
+
+CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
+ JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
+ SHORT_CONTEXT VARCHAR(2500) NOT NULL,
+ SERIALIZED_CONTEXT TEXT ,
+ constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
+ references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
+) ;
+
+CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ MAXVALUE 9223372036854775807 NO CYCLE;
+CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ MAXVALUE 9223372036854775807 NO CYCLE;
+CREATE SEQUENCE BATCH_JOB_SEQ MAXVALUE 9223372036854775807 NO CYCLE;
View
23 cf-workers-batch/src/main/resources/stocks_h2.sql
@@ -0,0 +1,23 @@
+
+CREATE TABLE PUBLIC.stocks (
+ symbol VARCHAR(10) NOT NULL,
+ CONSTRAINT stocks_pk PRIMARY KEY (symbol)
+);
+
+
+CREATE TABLE PUBLIC.stocks_data (
+ id serial,
+ date_analysed DATE NOT NULL,
+ high_price DECIMAL NOT NULL,
+ low_price DECIMAL NOT NULL,
+ closing_price DECIMAL NOT NULL,
+ symbol VARCHAR(10) NOT NULL,
+ CONSTRAINT stocks_data_pk PRIMARY KEY (id)
+);
+
+
+ALTER TABLE PUBLIC.stocks_data ADD CONSTRAINT stocks_data_stocks_fk
+FOREIGN KEY (symbol)
+REFERENCES PUBLIC.stocks (symbol)
+ON DELETE NO ACTION
+ON UPDATE NO ACTION;
View
36 cf-workers-batch/src/main/resources/stocks_psql.sql
@@ -0,0 +1,36 @@
+--
+-- DDL for the stock ticker aggregation application
+--
+
+
+-- setup the tables for this batch job
+CREATE TABLE PUBLIC.stocks (
+ symbol VARCHAR(10) NOT NULL,
+ CONSTRAINT stocks_pk PRIMARY KEY (symbol)
+);
+
+CREATE TABLE PUBLIC.stocks_data (
+ id serial,
+ date_analysed DATE NOT NULL,
+ high_price NUMERIC NOT NULL,
+ low_price NUMERIC NOT NULL,
+ closing_price NUMERIC NOT NULL,
+ symbol VARCHAR(10) NOT NULL,
+ CONSTRAINT stocks_data_pk PRIMARY KEY (id)
+);
+
+
+ALTER TABLE PUBLIC.stocks_data ADD CONSTRAINT stocks_data_stocks_fk
+FOREIGN KEY (symbol)
+REFERENCES PUBLIC.stocks (symbol)
+ON DELETE NO ACTION
+ON UPDATE NO ACTION
+NOT DEFERRABLE;
+
+
+-- seed the table with some sample tickers
+INSERT INTO STOCKS(symbol) values('GOOG');
+INSERT INTO STOCKS(symbol) values('MSFT');
+INSERT INTO STOCKS(symbol) values('ORCL');
+INSERT INTO STOCKS(symbol) values('ADBE');
+INSERT INTO STOCKS(symbol) values('VMW');
View
66 cf-workers-core/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>cf-workers-core</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId></dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>appassembler-maven-plugin</artifactId>
+ <version>1.1.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>assemble</goal>
+ </goals>
+ <configuration>
+ <assembledirectory>target</assembledirectory>
+ <programs>
+ <program>
+ <mainClass>org.cloudfoundry.workers.stocks.scheduling.Main</mainClass>
+ </program>
+ </programs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
View
71 ...e/src/main/java/org/cloudfoundry/workers/stocks/GoogleFinanceStockSymbolLookupClient.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.logging.Logger;
+
+/**
+ * Client API to the Google Finance stock symbol JSON feed.
+ *
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ *
+ */
+public class GoogleFinanceStockSymbolLookupClient implements StockSymbolLookupClient {
+
+ private String urlTemplateForSymbol = "http://www.google.com/finance/info?infotype=infoquoteall&q={symbol}";
+
+ private RestTemplate restTemplate;
+
+
+ public GoogleFinanceStockSymbolLookupClient(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ public GoogleFinanceStockSymbolLookupClient(String urlTemplateForSymbol, RestTemplate restTemplate) {
+ this.urlTemplateForSymbol = urlTemplateForSymbol;
+ this.restTemplate = restTemplate;
+ }
+
+ public StockSymbolLookup lookupSymbol(String symbol) throws Throwable {
+ String response = restTemplate.getForObject(this.urlTemplateForSymbol, String.class, symbol);
+ logger.info("response:" + response);
+ response = response.substring(response.indexOf("{"));
+ response = response.substring(0, response.lastIndexOf("}") + 1).trim();
+ JsonNode node = new ObjectMapper().readTree(response);
+ return convertJsonNodeInToSymbolLookup(node);
+ }
+
+ private StockSymbolLookup convertJsonNodeInToSymbolLookup(JsonNode jsonNode) throws Throwable {
+ Number id = jsonNode.get("id").getValueAsLong();
+ Double changeWhileOpen = jsonNode.get("c").getValueAsDouble();
+ String ticker = jsonNode.get("t").getValueAsText();
+ String exchange = jsonNode.get("e").getValueAsText();
+ Double highPrice = jsonNode.get("hi").getValueAsDouble();
+ Double lowPrice = jsonNode.get("l").getValueAsDouble();
+ Double lastValueWhileOpen = jsonNode.get("l").getValueAsDouble();
+ StockSymbolLookup lookup = new StockSymbolLookup(id, changeWhileOpen, ticker, exchange, highPrice, lowPrice, lastValueWhileOpen);
+ logger.info("service: retrieved stock information: "+ ToStringBuilder.reflectionToString(lookup));
+ return lookup;
+ }
+ private Logger logger = Logger .getLogger(getClass().getName());
+}
View
47 ...rkers-core/src/main/java/org/cloudfoundry/workers/stocks/MockStockSymbolLookupClient.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks;
+
+import org.springframework.util.StringUtils;
+
+import java.util.Random;
+
+/**
+ * returns a mock implementation of the {@link StockSymbolLookup}
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ *
+ */
+public class MockStockSymbolLookupClient implements StockSymbolLookupClient {
+
+ private Double randomDouble() {
+ return new Random().nextDouble();
+ }
+
+ private Long randomLong() {
+ return new Random().nextLong();
+ }
+
+ private StockSymbolLookup fabricateForSymbol(String ticker, String exchange) {
+ return new StockSymbolLookup(randomLong(), randomDouble(), ticker,
+ StringUtils.hasText(exchange) ? exchange : "NYSE", randomDouble(), randomDouble(), randomDouble());
+ }
+
+ @Override
+ public StockSymbolLookup lookupSymbol(String symbol) throws Throwable {
+ return fabricateForSymbol(symbol, null);
+ }
+}
View
107 cf-workers-core/src/main/java/org/cloudfoundry/workers/stocks/StockSymbolLookup.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks;
+
+
+/**
+ * A simple entity to wrap the lookup result data.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ *
+ */
+public class StockSymbolLookup {
+
+ private Number id;
+ private Double changeWhileOpen;
+ private String ticker;
+ private String exchange;
+ private Double highPrice;
+ private Double lowPrice;
+ private Double lastValueWhileOpen;
+
+ public StockSymbolLookup(){}
+ public StockSymbolLookup(Number id, Double changeWhileOpen, String ticker, String exchange, Double highPrice, Double lowPrice, Double lastValueWhileOpen) {
+ this.id = id;
+ this.changeWhileOpen = changeWhileOpen;
+ this.ticker = ticker;
+ this.exchange = exchange;
+ this.highPrice = highPrice;
+ this.lowPrice = lowPrice;
+ this.lastValueWhileOpen = lastValueWhileOpen;
+ }
+
+ public Number getId() {
+ return id;
+ }
+
+ public void setId(Number id) {
+ this.id = id;
+ }
+
+ public Double getChangeWhileOpen() {
+ return changeWhileOpen;
+ }
+
+ public void setChangeWhileOpen(Double changeWhileOpen) {
+ this.changeWhileOpen = changeWhileOpen;
+ }
+
+ public String getTicker() {
+ return ticker;
+ }
+
+ public void setTicker(String ticker) {
+ this.ticker = ticker;
+ }
+
+ public String getExchange() {
+ return exchange;
+ }
+
+ public void setExchange(String exchange) {
+ this.exchange = exchange;
+ }
+
+ public Double getHighPrice() {
+ return highPrice;
+ }
+
+ public void setHighPrice(Double highPrice) {
+ this.highPrice = highPrice;
+ }
+
+ public Double getLowPrice() {
+ return lowPrice;
+ }
+
+ public void setLowPrice(Double lowPrice) {
+ this.lowPrice = lowPrice;
+ }
+
+ public Double getLastValueWhileOpen() {
+ return lastValueWhileOpen;
+ }
+
+ public void setLastValueWhileOpen(Double lastValueWhileOpen) {
+ this.lastValueWhileOpen = lastValueWhileOpen;
+ }
+
+ @Override
+ public String toString() {
+ return (String.format("ticker: %s, exchange: %s, highPrice: %s, lowPrice: %s, changeWhileOpen: %s, ID: %s, last value while open: %s",
+ ticker, exchange, highPrice, lowPrice, changeWhileOpen, id, lastValueWhileOpen));
+ }
+}
View
35 cf-workers-core/src/main/java/org/cloudfoundry/workers/stocks/StockSymbolLookupClient.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks;
+
+/**
+ * Base contract. Let's be honest: you probably don't need this interface. However, I developed the initial revision of this
+ * code while on a plane, and didn't have a connection to test everything with,
+ * so being able to mock it out was pretty handy...
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+public interface StockSymbolLookupClient {
+
+ /**
+ * given the ticker symbol, lookup the information about the stock
+ *
+ * @param symbol the symbol to lookup
+ * @return a {@link StockSymbolLookup} containing information about the ticker including its closing price
+ * @throws Throwable
+ */
+ StockSymbolLookup lookupSymbol (String symbol) throws Throwable;
+}
View
84 ...main/java/org/cloudfoundry/workers/stocks/YahooPipesQuotesApiStockSymbolLookupClient.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * This version relies on the following RESTful YQL query:
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ *
+ */
+public class YahooPipesQuotesApiStockSymbolLookupClient implements StockSymbolLookupClient {
+
+ private RestTemplate restTemplate ;
+
+ private String url = "http://query.yahooapis.com/v1/public/yql?q={q}&format={format}&env={env}&callback={callback}";
+
+ private Logger logger = Logger.getLogger(YahooPipesQuotesApiStockSymbolLookupClient.class.getName());
+
+ public YahooPipesQuotesApiStockSymbolLookupClient(RestTemplate restTemplate ){
+ this.restTemplate = restTemplate;
+ }
+
+ private Map<String, String> buildParamsForUrl(String q, String format, String env, String cb) {
+ Map<String, String> maps = new HashMap<String, String>();
+ maps.put("q", q);
+ maps.put("format", format);
+ maps.put("env", env);
+ maps.put("callback", cb);
+ return maps;
+ }
+
+ private Map<String, String> buildStockUrl(String symbol) {
+ String q = String.format("select * from yahoo.finance.quotes where symbol in (\"%s\")", symbol);
+ return buildParamsForUrl(q, "json", "store://datatables.org/alltableswithkeys", "cbfunc");
+ }
+
+ @Override
+ public StockSymbolLookup lookupSymbol(String symbol) throws Throwable {
+ String response = restTemplate.getForObject(url, String.class, buildStockUrl(symbol));
+ response = response.substring(response.indexOf("(") + 1);
+ response = response.substring(0, response.lastIndexOf(")")).trim();
+ logger.info(response);
+ JsonNode jsonNode = new ObjectMapper().readTree(response);
+ return convertJsonNodeToStockSymbolLookup(jsonNode);
+ }
+
+ private StockSymbolLookup convertJsonNodeToStockSymbolLookup(JsonNode jsonNode) {
+ JsonNode jsonNode1 =jsonNode.get("query").get("results").get("quote");
+ logger.info(ToStringBuilder.reflectionToString(jsonNode1));
+ String symbol = jsonNode1.get("symbol").getValueAsText();
+ String range = jsonNode1.get("DaysRange").getValueAsText();
+ String parts[]=range.split("\\s+-\\s+") ;
+ Double low=Double.parseDouble(parts[0]), high=Double.parseDouble(parts[1]);
+ String exchange = jsonNode1.get("StockExchange").getValueAsText();
+ Double lastTradedPrice = jsonNode1.get("LastTradePriceOnly").getValueAsDouble();
+ return new StockSymbolLookup(null, high - low, symbol,exchange, high,low, lastTradedPrice);
+ }
+
+
+
+}
View
5 cf-workers-integration-service/TODO.TXT
@@ -0,0 +1,5 @@
+- split client / service into 2 separate modules that are both deployed into cloud foundry (maybe the client gets used by a web application or something)
+- restore the 'real' stock ticker service and remove the mock one
+- restore the client itself, theres no need for the 'invoker' bean (redundant!)
+- can the common infrastructure bits shared between cleint and service be extracted out to a commmon config type?
+- use the cloud foundry runtime to dynamically load the rabbit configuration from the cloud so that these apps run corrctly on cloud foundry
View
18 cf-workers-integration-service/manifest.yml
@@ -0,0 +1,18 @@
+---
+applications:
+ target/appassembler/:
+ name: integration-service
+ framework:
+ name: standalone
+ info:
+ mem: 64M
+ description: Standalone Application
+ exec:
+ runtime: java
+ command: bin/main
+ url:
+ mem: 512M
+ instances: 1
+ services:
+ stock_rabbitmq:
+ type: rabbitmq
View
139 cf-workers-integration-service/pom.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>cf-workers-integration-service</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>cf-workers-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aspects</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.cloudfoundry</groupId>
+ <artifactId>cloudfoundry-runtime</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.amqp</groupId>
+ <artifactId>spring-rabbit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.rabbitmq</groupId>
+ <artifactId>amqp-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.amqp</groupId>
+ <artifactId>spring-amqp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.integration</groupId>
+ <artifactId>spring-integration-amqp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.integration</groupId>
+ <artifactId>spring-integration-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.batch</groupId>
+ <artifactId>spring-batch-integration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.batch</groupId>
+ <artifactId>spring-batch-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>appassembler-maven-plugin</artifactId>
+ <version>1.1.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>assemble</goal>
+ </goals>
+ <configuration>
+ <assembledirectory>target</assembledirectory>
+ <programs>
+ <program>
+ <mainClass>org.cloudfoundry.workers.stocks.integration.service.Main</mainClass>
+ </program>
+ </programs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
View
3 cf-workers-integration-service/redeploy.sh
@@ -0,0 +1,3 @@
+mvn clean install;
+vmc delete integration-service;
+vmc --path target/appassembler/ push
View
32 ...ation-service/src/main/java/org/cloudfoundry/workers/stocks/integration/service/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.integration.service;
+
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * Kicks off the service that responds to requests.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+public class Main {
+ public static void main (String args []) throws Throwable {
+ AnnotationConfigApplicationContext annotationConfigApplicationContext
+ = new AnnotationConfigApplicationContext( ServiceConfiguration.class ) ;
+ }
+}
View
122 ...c/main/java/org/cloudfoundry/workers/stocks/integration/service/ServiceConfiguration.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.integration.service;
+
+import org.cloudfoundry.runtime.env.CloudEnvironment;
+import org.cloudfoundry.runtime.env.RabbitServiceInfo;
+import org.cloudfoundry.runtime.service.messaging.RabbitServiceCreator;
+import org.cloudfoundry.workers.stocks.GoogleFinanceStockSymbolLookupClient;
+import org.cloudfoundry.workers.stocks.MockStockSymbolLookupClient;
+import org.cloudfoundry.workers.stocks.StockSymbolLookupClient;
+import org.cloudfoundry.workers.stocks.YahooPipesQuotesApiStockSymbolLookupClient;
+import org.springframework.amqp.core.*;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitAdmin;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;
+import org.springframework.amqp.support.converter.JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportResource;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.util.Assert;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Collection;
+
+
+/**
+ * Configuration for the service that actually does the stock symbol lookup.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+@ImportResource("/symbol-lookup-gateway-service.xml")
+@Configuration
+public class ServiceConfiguration {
+
+ private String stocks = "tickers";
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @Bean
+ public StockSymbolLookupClient client() {
+ return new YahooPipesQuotesApiStockSymbolLookupClient(restTemplate());
+ }
+
+ @Bean
+ public RabbitTemplate rabbitTemplate() {
+ RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
+ rabbitTemplate.setMessageConverter(mc());
+ return rabbitTemplate;
+ }
+
+ @Bean
+ public RabbitTransactionManager amqpTransactionManager() {
+ return new RabbitTransactionManager(this.connectionFactory());
+ }
+
+ @Bean
+ public MessageConverter mc() {
+ return new JsonMessageConverter();
+ }
+
+ @Bean
+ public CloudEnvironment cloudEnvironment() {
+ return new CloudEnvironment();
+ }
+
+ @Bean
+ public ConnectionFactory connectionFactory() {
+ CloudEnvironment cloudEnvironment = this.cloudEnvironment();
+ Collection<RabbitServiceInfo> rabbitServiceInfoList = cloudEnvironment.getServiceInfos(RabbitServiceInfo.class);
+ Assert.isTrue(rabbitServiceInfoList.size() > 0, "the rabbitService infos collection should be > 0");
+ RabbitServiceInfo rabbitServiceInfo = rabbitServiceInfoList.iterator().next();
+ RabbitServiceCreator rabbitServiceCreator = new RabbitServiceCreator();
+ return rabbitServiceCreator.createService(rabbitServiceInfo);
+ }
+
+ @Bean
+ public AmqpAdmin amqpAdmin() {
+ return new RabbitAdmin(this.connectionFactory());
+ }
+
+ @Bean
+ public Queue customerQueue() {
+ Queue q = new Queue(this.stocks);
+ amqpAdmin().declareQueue(q);
+ return q;
+ }
+
+ @Bean
+ public DirectExchange customerExchange() {
+ DirectExchange directExchange = new DirectExchange(stocks);
+ this.amqpAdmin().declareExchange(directExchange);
+ return directExchange;
+ }
+
+ @Bean
+ public Binding marketDataBinding() {
+ return BindingBuilder.bind(customerQueue()).to(customerExchange()).with(this.stocks);
+ }
+
+}
View
23 cf-workers-integration-service/src/main/resources/symbol-lookup-gateway-service.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/integration"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:amqp="http://www.springframework.org/schema/integration/amqp"
+ xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
+ http://www.springframework.org/schema/integration/amqp http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
+
+
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+
+ <channel id="inboundSymbolsRequests"/>
+
+ <amqp:inbound-gateway request-channel="inboundSymbolsRequests"
+ queue-names="tickers"
+ message-converter="mc"
+ connection-factory="connectionFactory"/>
+
+
+ <service-activator ref="client" input-channel="inboundSymbolsRequests" requires-reply="true"/>
+
+</beans:beans>
View
50 cf-workers-integration-webclient/README
@@ -0,0 +1,50 @@
+This is a simple application that demonstrates how to use Spring 3.1 on Cloud Foundry.
+This application builds a transactional service that talks to an RDBMS and is fronted by
+an Spring MVC controller which handles RESTful API calls.
+
+Deployment is easy, and there are a lot of options:
+
+From an Eclipse environment like the SpringSource Tool Suite equipped with the m2e and Cloud Foundry WTP connector support:
+1) Import the project into Eclipse using the m2e / m2eclipse plugin - File > Import > Maven > Existing Maven Projects.
+2) Setup a Cloud Foundry WTP server pointing to the Cloud Foundry cloud you want to target
+3) Drag and drop the application onto the Cloud Foundry WTP instance, and specify that you need a Redis service and a PostgreSQL service and 512M of RAM.
+
+You can use the vmc command line tool, too.
+0) you need to change the name of the application as specified in your manifest.yml file, if there's already an existing application deployed under the same name on the Cloud Foundry instance
+1) Run 'mvn clean install' on the command line from the root of the project to create a binary.
+2) From the root of the project, run 'vmc --path target/springmvc31-1.0.0 push'
+
+You should also be able to deploy the project using the Maven Cloud Foundry plugin, which is already configured. You need to specify
+connectivity information in your ~/.m2/settings.xml file, as described in http://blog.springsource.org/2011/09/22/rapid-cloud-foundry-deployments-with-maven/
+0) you need to change the name of the application as specified in your manifest.yml file, if there's already an existing application deployed under the same name on the Cloud Foundry instance
+1) from the root of the project, run 'mvn clean install'
+2) then run 'mvn cf:push'
+
+
+
+Here are some SQL statements to setup the database:
+
+ H2
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Juergen' , 'Hoeller', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Mark' , 'Fisher', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Chris' , 'Richardson', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Josh' , 'Long', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Dave' , 'Syer', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Matt' , 'Quinlan', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Gunnar' , 'Hillert', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Dave' , 'McCrory', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Raja' , 'Rao', NOW()) ;
+ insert into customer (firstname ,lastname ,signupdate ) values( 'Monica' , 'Wilkinson', NOW()) ;
+
+
+ PostgreSQL
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Mark', 'Fisher', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Juergen', 'Hoeller', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Chris', 'Richardson', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Dave', 'Syer', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Matt', 'Quinlan', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Gunnar', 'Hiller', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Josh', 'Long', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Dave', 'McCrory', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Raja', 'Rao', NOW()) ;
+ INSERT INTO customer(id, firstname, lastname, signupdate) values( nextval( 'hibernate_sequence') , 'Monica', 'Wilkinson', NOW()) ;
View
16 cf-workers-integration-webclient/manifest.yml
@@ -0,0 +1,16 @@
+---
+applications:
+ target/cf-workers-integration-webclient-1.0-SNAPSHOT:
+ name: integration-webclient
+ framework:
+ name: java_web
+ info:
+ mem: 512M
+ description: Java Web Application
+ exec:
+ url: ${name}.${target-base}
+ mem: 512M
+ instances: 1
+ services:
+ stock_rabbitmq:
+ type: rabbitmq
View
211 cf-workers-integration-webclient/pom.xml
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>cf-workers-integration-webclient</artifactId>
+ <packaging>war</packaging>
+ <properties>
+ <java-version>1.6</java-version>
+ <org.springframework-version>3.1.0.RELEASE</org.springframework-version>
+ </properties>
+ <dependencies>
+
+ <dependency>
+ <groupId>org.cloudfoundry.workers</groupId>
+ <artifactId>cf-workers-core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>jstl</artifactId>
+ <version>1.2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.cloudfoundry</groupId>
+ <artifactId>cloudfoundry-runtime</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aspects</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.amqp</groupId>
+ <artifactId>spring-rabbit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.rabbitmq</groupId>
+ <artifactId>amqp-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.amqp</groupId>
+ <artifactId>spring-amqp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.integration</groupId>
+ <artifactId>spring-integration-amqp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.integration</groupId>
+ <artifactId>spring-integration-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.batch</groupId>
+ <artifactId>spring-batch-integration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.batch</groupId>
+ <artifactId>spring-batch-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>3.0.20100224</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+ <repositories>
+ <!-- For testing against latest Spring snapshots -->
+ <repository>
+ <id>org.springframework.maven.snapshot</id>
+ <name>Spring Maven Snapshot Repository</name>
+ <url>http://maven.springframework.org/snapshot</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ <!-- For developing against latest Spring milestones -->
+ <repository>
+ <id>org.springframework.maven.milestone</id>
+ <name>Spring Maven Milestone Repository</name>
+ <url>http://maven.springframework.org/milestone</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ <!-- For JPA and Hibernate artifacts -->
+ <repository>
+ <id>JBoss Repo</id>
+ <url>https://repository.jboss.org/nexus/content/repositories/releases</url>
+ <name>JBoss Repo</name>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>repository.springframework.maven.milestone</id>
+ <name>Spring Framework Maven Milestone Repository</name>
+ <url>http://maven.springframework.org/milestone</url>
+ </pluginRepository>
+ </pluginRepositories>
+ <build>
+
+ <plugins>
+ <plugin>
+ <groupId>org.cloudfoundry</groupId>
+ <artifactId>cf-maven-plugin</artifactId>
+ <version>1.0.0.M1</version>
+ <configuration>
+ <server>mycloudfoundry-instance</server>
+ <target>http://api.cloudfoundry.com</target>
+ <url>cfdevnexus.cloudfoundry.com</url>
+ <memory>512</memory>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>${java-version}</source>
+ <target>${java-version}</target>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.1.1</version>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
View
4 cf-workers-integration-webclient/redeploy.sh
@@ -0,0 +1,4 @@
+mvn clean install
+vmc delete integration-webclient
+vmc --path target/cf-workers-integration-webclient-1.0-SNAPSHOT push
+
View
111 ...src/main/java/org/cloudfoundry/workers/stocks/integration/client/ClientConfiguration.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.integration.client;
+
+import org.cloudfoundry.runtime.env.CloudEnvironment;
+import org.cloudfoundry.runtime.env.RabbitServiceInfo;
+import org.cloudfoundry.runtime.service.messaging.RabbitServiceCreator;
+import org.springframework.amqp.core.*;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitAdmin;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;
+import org.springframework.amqp.support.converter.JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportResource;
+import org.springframework.core.env.Environment;
+import org.springframework.util.Assert;
+
+import java.util.Collection;
+
+
+/**
+ * Configures the service's gateway client which in turn communicates with
+ * the remote service through a Spring Integration gateway implementation.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+@ImportResource("classpath:/client.xml")
+@Configuration
+public class ClientConfiguration {
+
+ private String tickers = "tickers";
+
+ @Autowired
+ private Environment environment;
+
+ @Bean
+ public RabbitTemplate amqpTemplate() {
+ RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
+ rabbitTemplate.setMessageConverter(mc());
+ return rabbitTemplate;
+ }
+
+ @Bean
+ public RabbitTransactionManager amqpTransactionManager() {
+ return new RabbitTransactionManager(this.connectionFactory());
+ }
+
+ @Bean
+ public MessageConverter mc() {
+ return new JsonMessageConverter();
+ }
+
+ @Bean
+ public ConnectionFactory connectionFactory() {
+
+ CloudEnvironment cloudEnvironment = this.cloudEnvironment();
+ Collection<RabbitServiceInfo> rabbitServiceInfoList = cloudEnvironment.getServiceInfos(RabbitServiceInfo.class);
+ Assert.isTrue(rabbitServiceInfoList.size() > 0 , "the rabbitService infos collection should be > 0");
+ RabbitServiceInfo rabbitServiceInfo = rabbitServiceInfoList.iterator().next();
+ RabbitServiceCreator rabbitServiceCreator = new RabbitServiceCreator();
+ return rabbitServiceCreator.createService(rabbitServiceInfo);
+ }
+
+ @Bean
+ public CloudEnvironment cloudEnvironment() {
+ return new CloudEnvironment();
+ }
+
+ @Bean
+ public AmqpAdmin amqpAdmin() {
+ return new RabbitAdmin(this.connectionFactory());
+ }
+
+ @Bean
+ public Queue customerQueue() {
+ Queue q = new Queue(this.tickers);
+ amqpAdmin().declareQueue(q);
+ return q;
+ }
+
+ @Bean
+ public DirectExchange customerExchange() {
+ DirectExchange directExchange = new DirectExchange(tickers);
+ this.amqpAdmin().declareExchange(directExchange);
+ return directExchange;
+ }
+
+ @Bean
+ public Binding marketDataBinding() {
+ return BindingBuilder.bind(customerQueue()).to(customerExchange()).with(this.tickers);
+ }
+
+}
View
41 ...tion-webclient/src/main/java/org/cloudfoundry/workers/stocks/integration/client/Main.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.integration.client;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.cloudfoundry.workers.stocks.StockSymbolLookup;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import java.util.logging.Logger;
+
+/**
+ * Simple client that sends meesages to a service using the Spring Integration {@link org.springframework.integration.annotation.Gateway} support.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ *
+ */
+public class Main {
+
+ public static void main(String args[]) throws Throwable {
+ AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ClientConfiguration.class);
+ StockClientGateway clientGateway = annotationConfigApplicationContext.getBean(StockClientGateway.class);
+ Logger log = Logger.getLogger(Main.class.getName());
+ String symbol = "VMW";
+ StockSymbolLookup lookup = clientGateway.lookup(symbol);
+ log.info("client: retrieved stock information: "+ ToStringBuilder.reflectionToString(lookup));
+ }
+}
View
41 .../src/main/java/org/cloudfoundry/workers/stocks/integration/client/StockClientGateway.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2011 the original author or authors.
+ *
+ * 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.cloudfoundry.workers.stocks.integration.client;
+
+import org.cloudfoundry.workers.stocks.StockSymbolLookup;
+
+/**
+ *
+ * This is an interface that we'll proxy out using Spring Integration's
+ * {@link org.springframework.integration.annotation.Gateway gateway}
+ * mechanism to show how to do request/reply semantics using messaging. We
+ * could also implement the service interface if we wanted. This just
+ * shoes that the client contract need not be coupled to the service contract.
+ *
+ * @author Josh Long (josh.long@springsource.com)
+ */
+public interface StockClientGateway {
+
+ /**
+ * This will invoke the actual Spring Integration messaging code and return the results.
+ * From the client's perspective, the call is synchornous. From the provider perspective,
+ * however, the call is asynchronous. Code on the client need not
+ *
+ * @param symbol the stock symbol lookup result
+ * @return the result of the query
+ */
+ StockSymbolLookup lookup(String symbol);
+}
View
26 ...ation-webclient/src/main/java/org/cloudfoundry/workers/stocks/web/StockApiController.java
@@ -0,0 +1,26 @@
+package org.cloudfoundry.workers.stocks.web;
+
+import org.cloudfoundry.workers.stocks.StockSymbolLookup;
+import org.cloudfoundry.workers.stocks.integration.client.StockClientGateway;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@RequestMapping("/stocks/")
+@Controller
+public class StockApiController {
+
+ @Autowired
+ private StockClientGateway stockClientGateway ;
+
+ @ResponseBody
+ @RequestMapping(value = "/{ticker}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
+ public StockSymbolLookup customerById(@PathVariable("ticker") String ticker ) {
+ return stockClientGateway.lookup(ticker);
+ }
+
+}
View
23 ...tion-webclient/src/main/java/org/cloudfoundry/workers/stocks/web/StockViewController.java
@@ -0,0 +1,23 @@
+package org.cloudfoundry.workers.stocks.web;
+
+import org.cloudfoundry.workers.stocks.integration.client.StockClientGateway;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Controller
+public class StockViewController {
+
+
+ @RequestMapping(value = "/", method = RequestMethod.GET)
+ public String customer(HttpServletRequest httpServletRequest, Model model) {
+ String ctxPath = httpServletRequest.getContextPath();
+ model.addAttribute("context", ctxPath );
+ return "stocks";
+ }
+}
View
49 ...tion-webclient/src/main/java/org/cloudfoundry/workers/stocks/web/WebMvcConfiguration.java
@@ -0,0 +1,49 @@
+package org.cloudfoundry.workers.stocks.web;
+
+import org.cloudfoundry.workers.stocks.integration.client.ClientConfiguration;
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.view.InternalResourceViewResolver;
+import org.springframework.web.servlet.view.JstlView;
+
+
+@Configuration
+@EnableWebMvc
+@Import( ClientConfiguration.class)
+public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
+
+ @Bean
+ public InternalResourceViewResolver internalResourceViewResolver() {
+ InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
+ internalResourceViewResolver.setViewClass(JstlView.class);
+ internalResourceViewResolver.setPrefix("/WEB-INF/views/");
+ internalResourceViewResolver.setSuffix(".jsp");
+ return internalResourceViewResolver;
+ }
+
+ @Bean
+ public MessageSource messageSource() {
+ String[] baseNames = "messages".split(",");
+ ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
+ resourceBundleMessageSource.setBasenames(baseNames);
+ return resourceBundleMessageSource;
+ }
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/web/**").addResourceLocations("/web/");
+ }
+
+
+ @Override
+ public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
+ configurer.enable();
+ }
+}
View
29 cf-workers-integration-webclient/src/main/resources/client.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/integration"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:amqp="http://www.springframework.org/schema/integration/amqp"
+ xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
+ http://www.springframework.org/schema/integration/amqp http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+
+
+ <channel id="outboundSymbolsReplies"/>
+ <channel id="outboundSymbolsRequests"/>
+
+ <gateway
+ service-interface="org.cloudfoundry.workers.stocks.integration.client.StockClientGateway"
+ default-request-channel="outboundSymbolsRequests"
+ default-reply-channel="outboundSymbolsReplies"
+ />
+
+ <amqp:outbound-gateway
+ request-channel="outboundSymbolsRequests"
+ reply-channel="outboundSymbolsReplies"
+ routing-key="tickers"
+ amqp-template="amqpTemplate"
+ />
+
+
+</beans:beans>
View
9 cf-workers-integration-webclient/src/main/resources/log4j.properties
@@ -0,0 +1,9 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+# Root logger option
+log4j.rootLogger=INFO,stdout
+
View
0 cf-workers-integration-webclient/src/main/resources/messages.properties
No changes.
View
103 cf-workers-integration-webclient/src/main/webapp/WEB-INF/views/stocks.jsp
@@ -0,0 +1,103 @@
+<!doctype html>
+<html ng-app>
+<head>
+
+ <script src="${context}/web/assets/js/jquery-1.7.2.min.js"></script>
+ <script src="${context}/web/assets/js/angular-1.0.0rc6.js"></script>
+ <link rel="stylesheet" href="${context}/web/assets/bootstrap/bootstrap.css">
+ <script src="${context}/web/views/stocks.js"></script>
+ <link rel="stylesheet" href="${context}/web/views/stocks.css"/>
+</head>
+<body>
+ <script language = "javascript" type = "text/javascript">
+ <!--
+ $(function(){
+ utils.setup( '${context}');
+ })
+ // utils.setup( '${context}');
+ //-->
+ </script>
+
+<div ng-controller="StockCtrl">
+ <div>
+ <form class="well form-search" ng-submit="lookupStock()">
+ <label> Search by ticker symbol</label>
+ <input type="text" ng-model="ticker" class="input-medium search-query" width="5" size="5" placeholder="stock ticker symbol">
+ <button type="submit" class="btn btn-primary" ng-click="lookupStock()" >
+ <a class="icon-search"></a>
+ </button>
+ </form>
+ </div>
+
+ <!-- http://stocks-web.cloudfoundry.com/stocks/vmw
+ {"id":718288,"exchange":"NYSE","changeWhileOpen":-3.34,"ticker":"VMW","highPrice":107.38,"lowPrice":104.26,"lastValueWhileOpen":104.26}
+ -->
+
+ <form class="form-horizontal" ng-submit="updateCustomer">
+ <fieldset>
+ <legend> Information on {{stock.ticker}}
+ </legend>
+
+
+ <div class="control-group">
+ <span class="control-label" >Ticker:</span>
+ <div class="controls">
+ {{stock.ticker}}
+ </div>
+ </div>
+
+
+ <div class="control-group">
+ <span class="control-label" >ID:</span>
+ <div class="controls