I am trying to connect to an ActiveMQ broker running on my local machine using Spring Boot as followed from the this link
In the link, an in-memory broker is used but I am more keen on using a broker server, which in my case I am using ActiveMQ
I am specifying the activemq broker to Spring through the following configurations in the file application.properties present in the class path
spring.activemq.broker-url=stomp://localhost:61611
spring.activemq.password=password
spring.activemq.user=admin
Below are the dependencies from my pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-stomp -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-stomp</artifactId>
</dependency>
</dependencies>
Below is the log for ActiveMQ after I have started the server on my local machine
INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$1#5419f379: startup date [Sun May 07 07:17:36 IST 2017]; root of context hierarchy
INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[F:\Softwares\Browser\apache-activemq-5.14.5-bin\apache-activemq-5.14.5\bin\..\data\kahadb]
INFO | KahaDB is version 6
INFO | Recovering from the journal #1:2260893
INFO | Recovery replayed 3154 operations from the journal in 0.309 seconds.
INFO | PListStore:[F:\Softwares\Browser\apache-activemq-5.14.5-bin\apache-activemq-5.14.5\bin\..\data\localhost\tmp_storage] started
INFO | Apache ActiveMQ 5.14.5 (localhost, ID:FOOTBALL_FIELD-63581-1494121658754-0:1) is starting
INFO | Listening for connections at: tcp://0.0.0.0:61610?maximumConnections=1000&wireFormat.maxFrameSize=104857600
INFO | Connector openwire started
INFO | Listening for connections at: amqp://0.0.0.0:5670?maximumConnections=1000&wireFormat.maxFrameSize=104857600
INFO | Connector amqp started
INFO | Listening for connections at: stomp://0.0.0.0:61611?maximumConnections=1000&wireFormat.maxFrameSize=104857600
INFO | Connector stomp started
INFO | Listening for connections at: mqtt://0.0.0.0:1880?maximumConnections=1000&wireFormat.maxFrameSize=104857600
INFO | Connector mqtt started
WARN | ServletContext#o.e.j.s.ServletContextHandler#338c99c8{/,null,STARTING} has uncovered http methods for path: /
INFO | Listening for connections at ws://127.0.0.1:61612?maximumConnections=1000&wireFormat.maxFrameSize=104857600
INFO | Connector ws started
INFO | Apache ActiveMQ 5.14.5 (localhost, ID:FOOTBALL_FIELD-63581-1494121658754-0:1) started
Application.java
#SpringBootApplication
#EnableJms
public class Application {
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
// You could still override some of Boot's default if necessary.
return factory;
}
#Bean // Serialize message content to json using TextMessage
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
public static void main(String[] args) {
// Launch the application
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
// Send a message with a POJO - the template reuse the message converter
System.out.println("Sending an email message.");
jmsTemplate.convertAndSend("mailbox", new Email("info#example.com", "Hello"));
}
}
Requesting DTO Class Email
public class Email {
private String to;
private String body;
//getter setters for fields
//constructors
#Override
public String toString() {
return String.format("Email{to=%s, body=%s}", getTo(), getBody());
}
}
Reponse DTO Class ** **
#Component
public class Receiver {
#JmsListener(destination = "mailbox", containerFactory = "myFactory")
public void receiveMessage(Email email) {
System.out.println("Received <" + email + ">");
}
}
Now when I execute the Spring Boot application, I am getting an error , the stacktrace of which is below
Exception in thread "main" org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is javax.jms.JMSException: Could not create Transport. Reason: java.lang.IllegalArgumentException: Invalid connect parameters: {wireFormat.host=localhost}
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:316)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:169)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:487)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:570)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:658)
at hello.Application.main(Application.java:49)
Caused by: javax.jms.JMSException: Could not create Transport. Reason: java.lang.IllegalArgumentException: Invalid connect parameters: {wireFormat.host=localhost}
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:36)
at org.apache.activemq.ActiveMQConnectionFactory.createTransport(ActiveMQConnectionFactory.java:333)
at org.apache.activemq.ActiveMQConnectionFactory.createActiveMQConnection(ActiveMQConnectionFactory.java:346)
at org.apache.activemq.ActiveMQConnectionFactory.createActiveMQConnection(ActiveMQConnectionFactory.java:304)
at org.apache.activemq.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:244)
at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:180)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:474)
... 3 more
Caused by: java.lang.IllegalArgumentException: Invalid connect parameters: {wireFormat.host=localhost}
at org.apache.activemq.transport.TransportFactory.doConnect(TransportFactory.java:126)
at org.apache.activemq.transport.TransportFactory.connect(TransportFactory.java:65)
at org.apache.activemq.ActiveMQConnectionFactory.createTransport(ActiveMQConnectionFactory.java:331)
As a result of this, I am unable to connect to the ActiveMQ broker running on my local machine through Spring Boot.
My requirement is to build a JMS Sender and Receiver over STOMP through ActiveMQ broker in Spring Boot Only. But I am unable to connect to the STOMP URL of the instance f ActiveMQ running on my local.
UPDATE : 11.05.2017 :
After a lot of debugging, I managed to find out that at line # 120 of org.apache.activemq.transport.TransportFactory class has this code Transport transport = createTransport(location, wf) is calling the org.apache.activemq.transport.tcp.TcpTransportFactory implementation of org.apache.activemq.transport.TransportFactory instead of org.apache.activemq.transport.stomp.StompTransportFactory or as a matter of fact any of StompTransportFactory implementations available in activemq-stomp.jar being present in classpath. I want to know if any body can tell me how is the appropriate TransportFactory implementation is selected.
I am stuck on this for almost 3 days. Any type of help or suggestion would be appreciated :)
Cheers!
Is there any reason that you want to use STOMP in Java in place of using the normal ActiveMQ Java client and protocols like VM or TCP ?
STOMP is designed for plain text... your exception is due to the fact that you are using the ActiveMQ client to speak STOMP which it cannot do and to do this you need some other libraries for that maybe spring stomp support but it is not exactly what you try to do http://docs.spring.io/spring-integration/reference/html/stomp.html
if you need STOMP you can use this lib and configure the correspondant beans https://github.com/fusesource/stompjms
UPDATE
As i see in this test https://github.com/fusesource/stompjms/blob/master/stompjms-activemq-test/src/test/java/org/fusesource/stomp/activemq/ActiveMQJmsStompTest.java
It is easy to integrate this library to any java or spring client since it implements JMS API.
You have to define a bean for the ConnectionFactory :
add to your application.properties
jms.broker.url=tcp://localhost:61611
And update your configuration :
#Value("${jms.broker.url}")
public String host;
#Bean
public ConnectionFactory connectionFactory() throws Exception {
StompJmsConnectionFactory result = new StompJmsConnectionFactory();
result.setBrokerURI(host);
return result;
}
Related
when I restart redis cause
java.util.concurrent.ExecutionException: io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.
Why is this a problem
use version like this
#Configuration
public class RedisConfig {
#Autowired
private RedisProperties redisProperties;
#Bean(destroyMethod = "close")
public StatefulRedisConnection<String, Object> StatefulRedisConnection() {
RedisURI redisUri = RedisURI.builder().withPassword(redisProperties.getPassword())
.withSentinel(redisProperties.getSentinel().getNodes().get(0).split(":")[0],
Integer.valueOf(redisProperties.getSentinel().getNodes().get(0).split(":")[1]))
.withSentinelMasterId(redisProperties.getSentinel().getMaster())
.withDatabase(redisProperties.getDatabase()).build();
RedisClient redisClient = RedisClient.create(redisUri);
return redisClient.connect(new SerializedObjectCodec());
}
}
public class CacheImpl implements Cache {
#Autowired
private StatefulRedisConnection connect;
public Boolean addCourseInfosCache() {
RedisAsyncCommands<String, Object> commands = connect.async();
// disable auto-flushing
commands.setAutoFlushCommands(false);
List<RedisFuture<?>> futures = Lists.newArrayList();
commands.flushCommands();
}
}
Lettuce leverages a custom syntax for Redis URIs. This is the schema:
redis :// [password#] host [: port] [/ database]
[? [timeout=timeout[d|h|m|s|ms|us|ns]]
[&_database=database_]]
There are four URI schemes:
redis – a standalone Redis server
rediss – a standalone Redis server via an SSL connection
redis-socket – a standalone Redis server via a Unix domain socket
redis-sentinel – a Redis Sentinel server
The Redis database instance can be specified as part of the URL path or as an additional parameter. If both are supplied, the parameter has higher precedence.
Print your redis uri connection string and check with your inputs.
You can upgrade Lettuce version and try:
RedisURI redisUri = RedisURI.Builder.sentinel("sentinelhost1", "mymaster").withPassword("abc").build();
For me, problem because version of spring and redis (2.1.4), after upgrade newer version, it work normal Please use with:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.9.RELEASE</version>
</dependency>
I use redis sentinel and my properties is:
spring.redis.database=0
spring.redis.sentinel.master=
spring.redis.sentinel.nodes=
spring.redis.sentinel.password=
spring.redis.password=
spring.redis.timeout=60000
I wrote simple sample to read text from console and send it to the rabbitMq server:
#Configuration
#EnableIntegration
#IntegrationComponentScan
public class Config {
#Autowired
private AmqpTemplate amqpTemplate;
#Bean
public IntegrationFlow fromConsoleToRabbitFlow() {
return IntegrationFlows.from(consoleSource(), c -> c.id("consoleInput")
.poller(Pollers.fixedRate(1000))
.autoStartup(true)
).channel("consoleOutputChannel")
.handle(Amqp.outboundAdapter(amqpTemplate).routingKey("my_spring_integration_queue"))
.get();
}
public MessageSource<String> consoleSource() {
return CharacterStreamReadingMessageSource.stdin();
}
}
It looks like almost working solution but I can't find my_spring_integration_queue in rabbitmq admin console:
But I can't find anything related to 'my_spring_integration_queue' on other tab. Where can I find it ?
I expect that application will create queue if it doesn't exist. I was not able to find a method for send into the queur so I used .routingKey method. I also tried .exchangeName method but it lead to the:
32019-08-27 13:26:15.972 ERROR 16372 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'my_spring_integration_queue' in vhost '/', class-id=60, method-id=40)
P.S.
the Queue tab looks like this:
You either need to add the queue manually or use a RabbitAdmin #Bean to automatically declare it for you - the admin will find all beans of type Queue and declare them.
If you are using Spring Boot, it will automatically configure an admin bean for you so you just need the Queue #Bean.
See Configuring the Broker.
I have a requirement to send a message to a RabbitMQ instance with a JPA entity after persisting/flushing it, which lead me to configure the rabbitTemplate as channelTransacted.
The consumer is external, but, to create an integration test, I have added an embedded broker (Apache QPid) and a listener to be able to check that the messages are sent.
As indicated by the documentation, I seem to have ran into a deadlock:
If we have producers and consumers in the same application, we may end up with a deadlock when producers are blocking the connection (because there are no resources on the Broker any more) and consumers cannot free them (because the connection is blocked). [...]
A separate CachingConnectionFactory is not possible for transactional producers that execute on a consumer thread, since they should reuse the Channel associated with the consumer transactions.
If I set rabbitTemplate.channelTransacted = false, the listener gets invoked fine, otherwise harness.getNextInvocationDataFor just waits until it times out.
What I'm hoping is that there's still a way to do this kind of integration test and that perhaps I configured something wrong.
I've tried with both the simple and the direct listener types, didn't make any difference:
queues:
transactions: 'transactions'
spring:
rabbitmq:
host: rabbitmq
username: guest
password: guest
dynamic: true # automatically create queues and exchanges on the RabbitMQ server
template:
routing-key: ${queues.transactions}
retry.enabled: true
# mandatory: true # interesting only for cases where a reply is expected
# publisher-confirms: true # does not work in transacted mode
publisher-returns: true # required to get notifications in case of send problems
# used for integration tests
listener:
type: direct
direct:
retry:
enabled: true
stateless: false # needed when transacted mode is enabled
max-attempts: 1 # since this is used just for integration tests, we don't want more
I'm using Spring Boot 2.1.3 with the spring-boot-starter-amqp, which pulls in spring-rabbit-2.1.4 and Apache Qpid 7.1.1 as the embedded broker for the test:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
#AutoConfigureTestDatabase
#Transactional
#ActiveProfiles("test")
public class SalesTransactionGatewayTest {
private static final String TRANSACTIONS_LISTENER = "transactions";
#TestConfiguration
#RabbitListenerTest(spy = false, capture = true)
public static class Config {
#Bean
public SystemLauncher broker() throws Exception {
SystemLauncher broker = new SystemLauncher();
Map<String, Object> attributes = new HashMap<>();
attributes.put(SystemConfig.TYPE, "Memory");
attributes.put(SystemConfig.INITIAL_CONFIGURATION_LOCATION, "classpath:qpid-config.json");
attributes.put(SystemConfig.STARTUP_LOGGED_TO_SYSTEM_OUT, false);
broker.startup(attributes);
return broker;
}
#Bean
public Listener listener() {
return new Listener();
}
}
public static class Listener {
#RabbitListener(id = TRANSACTIONS_LISTENER, queues = "${queues.transactions}")
public void receive(SalesTransaction transaction) {
Logger.getLogger(Listener.class.getName()).log(Level.INFO, "Received tx: {0}", transaction);
}
}
#Before
public void setUp() {
// this makes the test work, setting it to `true` makes it deadlock
rabbitTemplate.setChannelTransacted(false);
}
#Test
public void shouldBeSentToGateway() throws Exception {
SalesTransaction savedTransaction = service.saveTransaction(salesTransaction);
InvocationData invocationData = this.harness.getNextInvocationDataFor(TRANSACTIONS_LISTENER, 10, TimeUnit.SECONDS);
assertNotNull(invocationData);
assertEquals(salesTransaction, invocationData.getArguments()[0]);
}
}
11:02:56.844 [SimpleAsyncTaskExecutor-1] INFO org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer - SimpleConsumer [queue=transactions, consumerTag=sgen_1 identity=16ef3497] started
Mar 25, 2019 11:02:57 AM AmqpSalesTransactionGateway send
INFO: Sending transaction: 01a92a56-c93b-4d02-af66-66ef007c2817 w/ status COMPLETED
11:02:57.961 [main] INFO org.springframework.amqp.rabbit.connection.CachingConnectionFactory - Attempting to connect to: [localhost:5672]
11:02:57.972 [main] INFO org.springframework.amqp.rabbit.connection.CachingConnectionFactory - Created new connection: rabbitConnectionFactory.publisher#6d543ec2:0/SimpleConnection#79dd79eb [delegate=amqp://guest#127.0.0.1:5672/, localPort= 56501]
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertNotNull(Assert.java:712)
at org.junit.Assert.assertNotNull(Assert.java:722)
I have a spring boot application and I am trying to configure listeners to already existing queues. Following is what I configured in my application.yml file. I have also annotated my config class with #EnableRabbit and listener with #RabbitListener with appropriate configuration referring spring documentation.
Please note that every property has a valid default value, I have removed them before posting them here.
spring:
rabbitmq:
host: ${rmq_host}
port: ${rmq_port}
virtualHost: ${rmq_virtual_host}
requestedHeartbeat: ${rmq_requested_heartbeat_seconds}
listener:
simple:
concurrency: ${rmq_listener_config_concurrent_users}
autoStartup: ${rmq_listener_config_auto_startup}
acknowledge-mode: ${rmq_listener_config_ack_mode}
ssl:
enabled: ${rmq_ssl_enabled:true}
keyStore: ${rmq_ssl_keystore}
keyStorePassword: ${rmq_ssl_keystore_password}
trustStore: ${rmq_ssl_truststore}
trustStorePassword: ${rmq_ssl_truststore_password}
With this configuration when I try to start the application it throws below exception.
org.springframework.amqp.rabbit.listener.exception.FatalListenerStartupException: Authentication failure
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:532)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1389)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.amqp.AmqpAuthenticationException: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:65)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:368)
at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:565)
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils$1.createConnection(ConnectionFactoryUtils.java:90)
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.doGetTransactionalResourceHolder(ConnectionFactoryUtils.java:140)
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.getTransactionalResourceHolder(ConnectionFactoryUtils.java:76)
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:527)
... 2 common frames omitted
Caused by: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:342)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:909)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:859)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:799)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:352)
... 7 common frames omitted
While writing producers, I observed the same error which was resolved by setting sasl configurations to EXTERNAL but I could do it then as I was writing configuration beans on my own, here I am trying to use out of the box support from spring.
OK, so here is what i found. There is an open defect on spring-boot which mentions that due to unavailability of configuring sasl while using spring boot, it always uses PLAIN mode of authentication and there is a workaround which made my code work as expected. Below is the configuration class I added.
#Configuration
public class RabbitMQConfig {
#Autowired
private RabbitProperties rabbitProperties;
#Autowired
private CachingConnectionFactory cachingConnectionFactory;
#PostConstruct
public void init() {
if ( rabbitProperties.getSsl().isEnabled() && rabbitProperties.getSsl().getKeyStore() != null ) {
cachingConnectionFactory.getRabbitConnectionFactory().setSaslConfig( DefaultSaslConfig.EXTERNAL );
}
}
}
I am new to spring boot and am trying to write a consumer using spring boot
Below is my snippet:
application.properties
spring.activemq.broker-url=tcp://localhost:8161
spring.activemq.user=admin
spring.activemq.password=admin
SampleActiveMQApplication.java
#SpringBootApplication
#EnableJms
public class SampleActiveMQApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceAddPayeeApplication.class, args);
}
}
Consumer.java
#Component
public class Consumer {
#JmsListener(destination = "queue/msgQueue")
public void receiveQueue(String text) {
System.out.println("inside consumer");
System.out.println(text);
}
}
I am getting the below error
2018-04-23 07:08:08.277 WARN 9196 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Setup of JMS message listener invoker failed for destination 'queue/msgQueue' - trying to recover. Cause: Disposed due to prior exception
2018-04-23 07:08:08.292 ERROR 9196 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Could not refresh JMS Connection for destination 'queue/msgQueue' - retrying using FixedBackOff{interval=5000, currentAttempts=0, maxAttempts=unlimited}. Cause: Cannot send, channel has already failed: tcp://127.0.0.1:8161
Please assist:
Are you sure that your broker listens on your specified port 8161? By default, Active-mq admin management consoles listens on port 8161 which is accessible over the browser, and the broker listens on 61616. You may need to confirm that. You can check out for more information here