From 683ddbf5255c4bc2cf6153d20d867a8baca6de2b Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 24 Jul 2013 14:56:30 +0100 Subject: [PATCH] Add Reactor autoconfiguration * Make Rector @Autowirable * Create a ConsumerBeanPostProcessor so users can add @On and @Reply to bean methods * Added groovy auto compiler and script sample [#53955419] [bs-250] --- pom.xml | 6 ++ spring-autoconfigure/pom.xml | 5 ++ .../reactor/ReactorAutoConfiguration.java | 57 ++++++++++++++++ .../main/resources/META-INF/spring.factories | 1 + .../ReactorAutoConfigurationTests.java | 41 ++++++++++++ spring-cli/samples/reactor.groovy | 21 ++++++ .../ReactorCompilerAutoConfiguration.java | 67 +++++++++++++++++++ ...ork.cli.compiler.CompilerAutoConfiguration | 1 + .../resources/META-INF/springcli.properties | 3 +- .../cli/SampleIntegrationTests.java | 12 ++++ spring-cli/src/test/resources/logback.xml | 2 +- .../spring-zero-sample-web-ui/pom.xml | 4 ++ 12 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 spring-autoconfigure/src/main/java/org/springframework/autoconfigure/reactor/ReactorAutoConfiguration.java create mode 100644 spring-autoconfigure/src/test/java/org/springframework/autoconfigure/reactor/ReactorAutoConfigurationTests.java create mode 100644 spring-cli/samples/reactor.groovy create mode 100644 spring-cli/src/main/java/org/springframework/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java diff --git a/pom.xml b/pom.xml index 9cd0fbe58c..e21afb6da1 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ 7.0.42 8.1.9.v20130131 1.7.3 + 1.0.0.M1 http://github.com/SpringSource/spring-bootstrap @@ -432,6 +433,11 @@ hsqldb 2.2.9 + + org.projectreactor + reactor-spring + ${reactor.version} + org.mockito mockito-core diff --git a/spring-autoconfigure/pom.xml b/spring-autoconfigure/pom.xml index fa7a2a6d09..5dc7022bed 100644 --- a/spring-autoconfigure/pom.xml +++ b/spring-autoconfigure/pom.xml @@ -110,6 +110,11 @@ thymeleaf-extras-springsecurity3 true + + org.projectreactor + reactor-spring + true + ${project.groupId} diff --git a/spring-autoconfigure/src/main/java/org/springframework/autoconfigure/reactor/ReactorAutoConfiguration.java b/spring-autoconfigure/src/main/java/org/springframework/autoconfigure/reactor/ReactorAutoConfiguration.java new file mode 100644 index 0000000000..dd98cdadef --- /dev/null +++ b/spring-autoconfigure/src/main/java/org/springframework/autoconfigure/reactor/ReactorAutoConfiguration.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2013 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.springframework.autoconfigure.reactor; + +import org.springframework.autoconfigure.AutoConfigureAfter; +import org.springframework.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.bootstrap.context.condition.ConditionalOnClass; +import org.springframework.bootstrap.context.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +import reactor.core.Environment; +import reactor.core.Reactor; +import reactor.spring.context.ConsumerBeanPostProcessor; + +/** + * @author Dave Syer + */ +@Configuration +@ConditionalOnClass(Reactor.class) +@ConditionalOnMissingBean(Reactor.class) +@AutoConfigureAfter(WebMvcAutoConfiguration.class) +public class ReactorAutoConfiguration { + + @Bean + public Environment reactorEnvironment() { + return new Environment(); // TODO: use Spring Environment to configure? + } + + @Bean + public Reactor rootReactor() { + return reactorEnvironment().getRootReactor(); + } + + @Bean + @Order(Ordered.LOWEST_PRECEDENCE) + protected ConsumerBeanPostProcessor reactorConsumerBeanPostProcessor() { + return new ConsumerBeanPostProcessor(); + } + +} diff --git a/spring-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-autoconfigure/src/main/resources/META-INF/spring.factories index 8c5efd0348..ae06447374 100644 --- a/spring-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-autoconfigure/src/main/resources/META-INF/spring.factories @@ -7,6 +7,7 @@ org.springframework.autoconfigure.data.JpaRepositoriesAutoConfiguration,\ org.springframework.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ +org.springframework.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.autoconfigure.web.ServerPropertiesAutoConfiguration,\ diff --git a/spring-autoconfigure/src/test/java/org/springframework/autoconfigure/reactor/ReactorAutoConfigurationTests.java b/spring-autoconfigure/src/test/java/org/springframework/autoconfigure/reactor/ReactorAutoConfigurationTests.java new file mode 100644 index 0000000000..c53a418191 --- /dev/null +++ b/spring-autoconfigure/src/test/java/org/springframework/autoconfigure/reactor/ReactorAutoConfigurationTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2013 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.springframework.autoconfigure.reactor; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import reactor.core.Reactor; + +import static org.junit.Assert.assertNotNull; + +/** + * @author Dave Syer + */ +public class ReactorAutoConfigurationTests { + + private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + @Test + public void reactorIsAvailable() { + this.context.register(ReactorAutoConfiguration.class); + this.context.refresh(); + assertNotNull(this.context.getBean(Reactor.class)); + this.context.close(); + } + +} diff --git a/spring-cli/samples/reactor.groovy b/spring-cli/samples/reactor.groovy new file mode 100644 index 0000000000..9b1ebd84bc --- /dev/null +++ b/spring-cli/samples/reactor.groovy @@ -0,0 +1,21 @@ +package org.test + +@EnableReactor +@Log +class Runner implements CommandLineRunner { + + @Autowired + Reactor reactor + + void run(String... args) { + reactor.notify("hello", Event.wrap("Phil")) + log.info "Notified Phil" + } + + @On(reactor="reactor", selector="hello") + void receive(Event event) { + log.info "Hello ${event.data}" + } +} + + diff --git a/spring-cli/src/main/java/org/springframework/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java b/spring-cli/src/main/java/org/springframework/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java new file mode 100644 index 0000000000..69d029aa4a --- /dev/null +++ b/spring-cli/src/main/java/org/springframework/cli/compiler/autoconfigure/ReactorCompilerAutoConfiguration.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2013 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.springframework.cli.compiler.autoconfigure; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.control.customizers.ImportCustomizer; +import org.springframework.cli.compiler.AstUtils; +import org.springframework.cli.compiler.CompilerAutoConfiguration; +import org.springframework.cli.compiler.DependencyCustomizer; + +/** + * {@link CompilerAutoConfiguration} for the Recator. + * + * @author Dave Syer + */ +public class ReactorCompilerAutoConfiguration extends CompilerAutoConfiguration { + + @Override + public boolean matches(ClassNode classNode) { + return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableReactor"); + } + + @Override + public void applyDependencies(DependencyCustomizer dependencies) { + dependencies + .ifAnyMissingClasses("org.reactor.Reactor") + .add("org.projectreactor", "reactor-spring", + dependencies.getProperty("reactor.version"), false) + .add("org.projectreactor", "reactor-core", + dependencies.getProperty("reactor.version")); + } + + @Override + public void applyImports(ImportCustomizer imports) { + imports.addImports("reactor.core.Reactor", "reactor.event.Event", + "reactor.spring.context.annotation.On", + "reactor.spring.context.annotation.Reply", + EnableReactor.class.getCanonicalName()); + } + + @Target(ElementType.TYPE) + @Documented + @Retention(RetentionPolicy.RUNTIME) + public static @interface EnableReactor { + + } +} diff --git a/spring-cli/src/main/resources/META-INF/services/org.springframework.cli.compiler.CompilerAutoConfiguration b/spring-cli/src/main/resources/META-INF/services/org.springframework.cli.compiler.CompilerAutoConfiguration index c42fcbfc78..208fd86164 100644 --- a/spring-cli/src/main/resources/META-INF/services/org.springframework.cli.compiler.CompilerAutoConfiguration +++ b/spring-cli/src/main/resources/META-INF/services/org.springframework.cli.compiler.CompilerAutoConfiguration @@ -1,5 +1,6 @@ org.springframework.cli.compiler.autoconfigure.SpringCompilerAutoConfiguration org.springframework.cli.compiler.autoconfigure.SpringMvcCompilerAutoConfiguration org.springframework.cli.compiler.autoconfigure.SpringBatchCompilerAutoConfiguration +org.springframework.cli.compiler.autoconfigure.ReactorCompilerAutoConfiguration org.springframework.cli.compiler.autoconfigure.SpringIntegrationCompilerAutoConfiguration org.springframework.cli.compiler.autoconfigure.SpringSecurityCompilerAutoConfiguration diff --git a/spring-cli/src/main/resources/META-INF/springcli.properties b/spring-cli/src/main/resources/META-INF/springcli.properties index 92129e177f..353e4c6b8f 100644 --- a/spring-cli/src/main/resources/META-INF/springcli.properties +++ b/spring-cli/src/main/resources/META-INF/springcli.properties @@ -5,4 +5,5 @@ spring.security.version: ${spring.security.version} spring.integration.version: ${spring.integration.version} groovy.version: ${groovy.version} jetty.version: ${jetty.version} -tomcat.version: ${tomcat.version} \ No newline at end of file +tomcat.version: ${tomcat.version} +reactor.version: ${reactor.version} \ No newline at end of file diff --git a/spring-cli/src/test/java/org/springframework/cli/SampleIntegrationTests.java b/spring-cli/src/test/java/org/springframework/cli/SampleIntegrationTests.java index dedd2a1fb1..10f1ec8918 100644 --- a/spring-cli/src/test/java/org/springframework/cli/SampleIntegrationTests.java +++ b/spring-cli/src/test/java/org/springframework/cli/SampleIntegrationTests.java @@ -117,6 +117,18 @@ public class SampleIntegrationTests { output.contains("completed with the following parameters")); } + @Test + public void reactorSample() throws Exception { + start("samples/reactor.groovy", "Phil"); + String output = getOutput(); + int count = 0; + while (!output.contains("Hello Phil") && count++ < 5) { + Thread.sleep(200); + output = getOutput(); + } + assertTrue("Wrong output: " + output, output.contains("Hello Phil")); + } + @Test public void jobWebSample() throws Exception { start("samples/job.groovy", "samples/web.groovy", "foo=bar"); diff --git a/spring-cli/src/test/resources/logback.xml b/spring-cli/src/test/resources/logback.xml index 8232e3a3cb..407f13b26a 100644 --- a/spring-cli/src/test/resources/logback.xml +++ b/spring-cli/src/test/resources/logback.xml @@ -14,6 +14,6 @@ - + diff --git a/spring-zero-samples/spring-zero-sample-web-ui/pom.xml b/spring-zero-samples/spring-zero-sample-web-ui/pom.xml index 6ae465bd3e..fe64c8eba5 100644 --- a/spring-zero-samples/spring-zero-sample-web-ui/pom.xml +++ b/spring-zero-samples/spring-zero-sample-web-ui/pom.xml @@ -26,6 +26,10 @@ nz.net.ultraq.thymeleaf thymeleaf-layout-dialect +