Browse Source
- If RabbitTemplate is on the classpath, turn on autodetection. - Create a RabbitTemplate, a Rabbit ConnectionFactory, and a RabbitAdmin is spring.rabbitmq.dynamic:true - Enable some **spring.rabbitmq** properties like host, port, username, password, and dynamic - Add tests to verify functionality - Add Groovy CLI functionality. Base it on @EnableRabbitMessaging. Add spring-amqp to the path. - Create rabbit.groovy test to prove it all works. - Make Queue and TopicExchange top-level Spring beans in rabbit.groovy test scriptpull/64/head
10 changed files with 401 additions and 5 deletions
@ -0,0 +1,140 @@ |
|||||||
|
/* |
||||||
|
* 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.boot.autoconfigure.amqp; |
||||||
|
|
||||||
|
import org.springframework.amqp.core.AmqpAdmin; |
||||||
|
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; |
||||||
|
import org.springframework.amqp.rabbit.core.RabbitAdmin; |
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link EnableAutoConfiguration Auto-configuration} for {@link RabbitTemplate}. |
||||||
|
* |
||||||
|
* @author Greg Turnquist |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
@ConditionalOnClass({ RabbitTemplate.class }) |
||||||
|
@EnableConfigurationProperties |
||||||
|
public class RabbitTemplateAutoConfiguration { |
||||||
|
|
||||||
|
@Bean |
||||||
|
@ConditionalOnExpression("${spring.rabbitmq.dynamic:true}") |
||||||
|
@ConditionalOnMissingBean(AmqpAdmin.class) |
||||||
|
public AmqpAdmin amqpAdmin(CachingConnectionFactory connectionFactory) { |
||||||
|
return new RabbitAdmin(connectionFactory); |
||||||
|
} |
||||||
|
|
||||||
|
@Configuration |
||||||
|
@ConditionalOnMissingBean(RabbitTemplate.class) |
||||||
|
protected static class RabbitTemplateCreator { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
CachingConnectionFactory connectionFactory; |
||||||
|
|
||||||
|
@Bean |
||||||
|
public RabbitTemplate rabbitTemplate() { |
||||||
|
return new RabbitTemplate(this.connectionFactory); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Configuration |
||||||
|
@ConditionalOnMissingBean(CachingConnectionFactory.class) |
||||||
|
@EnableConfigurationProperties(RabbitConnectionFactoryProperties.class) |
||||||
|
protected static class RabbitConnectionFactoryCreator { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private RabbitConnectionFactoryProperties config; |
||||||
|
|
||||||
|
@Bean |
||||||
|
public CachingConnectionFactory connectionFactory() { |
||||||
|
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(this.config.getHost()); |
||||||
|
connectionFactory.setPort(this.config.getPort()); |
||||||
|
if (this.config.getUsername() != null) { |
||||||
|
connectionFactory.setUsername(this.config.getUsername()); |
||||||
|
} |
||||||
|
if (this.config.getPassword() != null) { |
||||||
|
connectionFactory.setPassword(this.config.getPassword()); |
||||||
|
} |
||||||
|
return connectionFactory; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@ConfigurationProperties(name = "spring.rabbitmq") |
||||||
|
public static class RabbitConnectionFactoryProperties { |
||||||
|
|
||||||
|
private String host = "localhost"; |
||||||
|
|
||||||
|
private int port = 5672; |
||||||
|
|
||||||
|
private String username; |
||||||
|
|
||||||
|
private String password; |
||||||
|
|
||||||
|
private boolean dynamic = true; |
||||||
|
|
||||||
|
public String getHost() { |
||||||
|
return host; |
||||||
|
} |
||||||
|
|
||||||
|
public void setHost(String host) { |
||||||
|
this.host = host; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPort() { |
||||||
|
return port; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPort(int port) { |
||||||
|
this.port = port; |
||||||
|
} |
||||||
|
|
||||||
|
public String getUsername() { |
||||||
|
return username; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUsername(String username) { |
||||||
|
this.username = username; |
||||||
|
} |
||||||
|
|
||||||
|
public String getPassword() { |
||||||
|
return password; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPassword(String password) { |
||||||
|
this.password = password; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isDynamic() { |
||||||
|
return dynamic; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDynamic(boolean dynamic) { |
||||||
|
this.dynamic = dynamic; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,106 @@ |
|||||||
|
/* |
||||||
|
* 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.boot.autoconfigure.amqp; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.amqp.core.AmqpAdmin; |
||||||
|
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; |
||||||
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory; |
||||||
|
import org.springframework.amqp.rabbit.core.RabbitAdmin; |
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate; |
||||||
|
import org.springframework.boot.TestUtils; |
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals; |
||||||
|
import static junit.framework.Assert.assertNotNull; |
||||||
|
import static junit.framework.Assert.fail; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link RabbitTemplateAutoConfiguration}. |
||||||
|
* |
||||||
|
* @author Greg Turnquist |
||||||
|
*/ |
||||||
|
public class RabbitTemplateAutoconfigurationTests { |
||||||
|
|
||||||
|
private AnnotationConfigApplicationContext context; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDefaultRabbitTemplate() { |
||||||
|
this.context = new AnnotationConfigApplicationContext(); |
||||||
|
this.context.register(TestConfiguration.class, RabbitTemplateAutoConfiguration.class); |
||||||
|
this.context.refresh(); |
||||||
|
RabbitTemplate rabbitTemplate = this.context.getBean(RabbitTemplate.class); |
||||||
|
CachingConnectionFactory connectionFactory = this.context.getBean(CachingConnectionFactory.class); |
||||||
|
RabbitAdmin amqpAdmin = this.context.getBean(RabbitAdmin.class); |
||||||
|
assertNotNull(rabbitTemplate); |
||||||
|
assertNotNull(connectionFactory); |
||||||
|
assertNotNull(amqpAdmin); |
||||||
|
assertEquals(rabbitTemplate.getConnectionFactory(), connectionFactory); |
||||||
|
assertEquals(connectionFactory.getHost(), "localhost"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testRabbitTemplateWithOverrides() { |
||||||
|
this.context = new AnnotationConfigApplicationContext(); |
||||||
|
this.context.register(TestConfiguration.class, RabbitTemplateAutoConfiguration.class); |
||||||
|
TestUtils.addEnviroment(this.context, "spring.rabbitmq.host:remote-server", |
||||||
|
"spring.rabbitmq.port:9000", "spring.rabbitmq.username:alice", "spring.rabbitmq.password:secret"); |
||||||
|
this.context.refresh(); |
||||||
|
CachingConnectionFactory connectionFactory = this.context.getBean(CachingConnectionFactory.class); |
||||||
|
assertEquals(connectionFactory.getHost(), "remote-server"); |
||||||
|
assertEquals(connectionFactory.getPort(), 9000); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testConnectionFactoryBackoff() { |
||||||
|
this.context = new AnnotationConfigApplicationContext(); |
||||||
|
this.context.register(TestConfiguration2.class, RabbitTemplateAutoConfiguration.class); |
||||||
|
this.context.refresh(); |
||||||
|
RabbitTemplate rabbitTemplate = this.context.getBean(RabbitTemplate.class); |
||||||
|
CachingConnectionFactory connectionFactory = this.context.getBean(CachingConnectionFactory.class); |
||||||
|
assertEquals(rabbitTemplate.getConnectionFactory(), connectionFactory); |
||||||
|
assertEquals(connectionFactory.getHost(), "otherserver"); |
||||||
|
assertEquals(connectionFactory.getPort(), 8001); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testStaticQueues() { |
||||||
|
this.context = new AnnotationConfigApplicationContext(); |
||||||
|
this.context.register(TestConfiguration.class, RabbitTemplateAutoConfiguration.class); |
||||||
|
TestUtils.addEnviroment(this.context, "spring.rabbitmq.dynamic:false"); |
||||||
|
this.context.refresh(); |
||||||
|
try { |
||||||
|
this.context.getBean(AmqpAdmin.class); |
||||||
|
fail("There should NOT be an AmqpAdmin bean when dynamic is switch to false"); |
||||||
|
} catch (Exception e) {} |
||||||
|
} |
||||||
|
|
||||||
|
@Configuration |
||||||
|
protected static class TestConfiguration { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Configuration |
||||||
|
protected static class TestConfiguration2 { |
||||||
|
@Bean |
||||||
|
ConnectionFactory aDifferentConnectionFactory() { |
||||||
|
return new CachingConnectionFactory("otherserver", 8001); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
package org.test |
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch |
||||||
|
|
||||||
|
@Log |
||||||
|
@Configuration |
||||||
|
@EnableRabbitMessaging |
||||||
|
class RabbitExample implements CommandLineRunner { |
||||||
|
|
||||||
|
private CountDownLatch latch = new CountDownLatch(1) |
||||||
|
|
||||||
|
@Autowired |
||||||
|
RabbitTemplate rabbitTemplate |
||||||
|
|
||||||
|
private String queueName = "spring-boot" |
||||||
|
|
||||||
|
@Bean |
||||||
|
Queue queue() { |
||||||
|
new Queue(queueName, false) |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
TopicExchange exchange() { |
||||||
|
new TopicExchange("spring-boot-exchange") |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The queue and topic exchange cannot be inlined inside this method and have |
||||||
|
* dynamic creation with Spring AMQP work properly. |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
Binding binding(Queue queue, TopicExchange exchange) { |
||||||
|
BindingBuilder |
||||||
|
.bind(queue) |
||||||
|
.to(exchange) |
||||||
|
.with("spring-boot") |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
SimpleMessageListenerContainer container(CachingConnectionFactory connectionFactory) { |
||||||
|
return new SimpleMessageListenerContainer( |
||||||
|
connectionFactory: connectionFactory, |
||||||
|
queueNames: [queueName], |
||||||
|
messageListener: new MessageListenerAdapter(new Receiver(latch:latch), "receive") |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
void run(String... args) { |
||||||
|
log.info "Sending RabbitMQ message..." |
||||||
|
rabbitTemplate.convertAndSend(queueName, "Greetings from Spring Boot via RabbitMQ") |
||||||
|
latch.await() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Log |
||||||
|
class Receiver { |
||||||
|
CountDownLatch latch |
||||||
|
|
||||||
|
def receive(String message) { |
||||||
|
log.info "Received ${message}" |
||||||
|
latch.countDown() |
||||||
|
} |
||||||
|
} |
||||||
@ -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.boot.cli.compiler.autoconfigure; |
||||||
|
|
||||||
|
import org.codehaus.groovy.ast.ClassNode; |
||||||
|
import org.codehaus.groovy.control.CompilationFailedException; |
||||||
|
import org.codehaus.groovy.control.customizers.ImportCustomizer; |
||||||
|
import org.springframework.boot.cli.compiler.AstUtils; |
||||||
|
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration; |
||||||
|
import org.springframework.boot.cli.compiler.DependencyCustomizer; |
||||||
|
|
||||||
|
import java.lang.annotation.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link CompilerAutoConfiguration} for Spring Rabbit. |
||||||
|
* |
||||||
|
* @author Greg Turnquist |
||||||
|
*/ |
||||||
|
public class RabbitCompilerAutoConfiguration extends CompilerAutoConfiguration { |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean matches(ClassNode classNode) { |
||||||
|
// Slightly weird detection algorithm because there is no @Enable annotation for
|
||||||
|
// Integration
|
||||||
|
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbitMessaging"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void applyDependencies(DependencyCustomizer dependencies) |
||||||
|
throws CompilationFailedException { |
||||||
|
dependencies.add("org.springframework.amqp", "spring-rabbit", |
||||||
|
dependencies.getProperty("spring-rabbit.version")); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { |
||||||
|
imports.addStarImports("org.springframework.amqp.rabbit.core", |
||||||
|
"org.springframework.amqp.rabbit.connection", |
||||||
|
"org.springframework.amqp.rabbit.listener", |
||||||
|
"org.springframework.amqp.rabbit.listener.adapter", |
||||||
|
"org.springframework.amqp.core").addImports(EnableRabbitMessaging.class.getCanonicalName()); |
||||||
|
} |
||||||
|
|
||||||
|
@Target(ElementType.TYPE) |
||||||
|
@Documented |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public static @interface EnableRabbitMessaging { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue