I am having trouble getting messages from a locally run ActiveMQ. I can produce them onto the queue and my PC also is registered as producer. However, another Spring App on the machine should be configured as a listener. So far it is not working. ActiveMQ is listening on the default ports.
My JMS config for the sender:
package at.dkepr.queueservice;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.jms.Queue;
#Configuration
public class JmsConfig {
#Bean
public Queue queue(){
return new ActiveMQQueue("indexing-queue");
}
}
And this is the consumer:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import at.dkepr.entity.UserSearchEntity;
#Component
#EnableJms
public class JmsConsumer {
private final Logger logger = LoggerFactory.getLogger(JmsConsumer.class);
#JmsListener(destination = "indexing-queue", containerFactory = "jmsListenerContainerFactory")
public void receive(UserSearchEntity user){
logger.info(user.getEmail());
}
}
In the application.propertiers I have added the necessary properties:
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
Also the UserSearchEntity implements Serializable.
To the best of my knowledge for this setup I should not even need a config for the consumer. Never the less, I added one.
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
#Configuration
#EnableJms
public class ConsumerConfig {
#Value("${spring.activemq.broker-url}")
private String brokerUrl;
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(brokerUrl);
return activeMQConnectionFactory;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(activeMQConnectionFactory());
factory.setConcurrency("1-3");
return factory;
}
}
I am not getting any error logs. Unfortunately, it is simply doing nothing.
This is a screenshot from the ActiveMQ web console with the enqueued messages:
My consuming application was running when I took this screenshot, but the broker clearly does not recognize it since the "Number of Consumers" is 0.
Edit:
I just tried adding the Listener to the same Spring Application where the Producer is. Surprinsingly, the Listener connected fine. It seems like the problem lies in the different Spring Applications. However, i used the same application.properties for both Spring Apps. The Config File is the same too.
To everyone having the same problem:
For me it was a simple problem with folder structure. For some reason the Application.java for the consumer service was in a subfolder. After i moved the Application.java one folder up, the connection to the ActiveMQ worked.
Related
I am trying to read a message from different properties based on the profile. For example I have placed 3 properties files in github:
test-app-dev.properties
test-app-prod.properties
test-app-stage.properties
Now my config server has below details in application.properties
spring.cloud.config.server.git.uri=https://github.com/asudheer09/local-config-server-configs.git
server.port=8888
spring.cloud.config.server.git.default-label=main
spring.application.name=config-server
server.servlet.context-path=/config-service
When I am trying to access the http://localhost:8888/config-service/test-app-prod.properties I can see the properties file on browser and similarly others also i can see.
The below are my config client details:
In bootstrap.properties:
spring.profiles.active=dev
spring.cloud.config.uri=http://localhost:8888/config-service
management.security.enabled=false
spring.application.name=test-app
java file :
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RefreshScope
public class SpringProfilesExampleApplication {
#Autowired
public void setEnv(Environment e) {
System.out.println(e.getActiveProfiles().toString());
System.out.println(e.getProperty("message"));
}
public static void main(String[] args) {
SpringApplication.run(SpringProfilesExampleApplication.class, args);
}
}
#RefreshScope
#RestController
class MessageRestController {
#Value("${message:Config Server is not working. Please check...}")
private String msg;
#GetMapping("/msg")
public String getMsg() {
return this.msg;
}
}
When i run my config client i am getting the message value as null, but i am not able to see any error message. can any one help me on this ?
Try making the following changes
In git you must have different urls for each enviroment (dev, prod, ..).
spring:
cloud:
config:
server:
git:
repos:
dev:
pattern: test-app/dev
uri: https://github.com/asudheer09/local-config-server-configs/test-app-dev.properties
prod:
pattern: test-app/prod
uri: https://github.com/asudheer09/local-config-server-configs/test-app-prod.properties
With these configurations your config server will serve clients using the following logic
If client application has name test-app and profile dev then it will load the properties from https://github.com/asudheer09/local-config-server-configs/test-app-dev.properties
If client application has name test-app and profile prod then it will load the properties from https://github.com/asudheer09/local-config-server-configs/test-app-prod.properties
It does not make any sense to try and make System.out.println directly on config server when it starts up. You don't need the application properties of config server but those properties that will be served on clients.
Check more info here
spring cloud config documentation
I have a SpringBoot application. I have a dedicated server, where I shall read data by the HTTP GET requests. I configured the http-outbound-config.xml file for Spring Integration module.
When I run the following code, everything is fine:
http-outbound-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http https://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<int:channel id="requestChannel"/>
<int:channel id="replyChannel">
<int:queue capacity='10'/>
</int:channel>
<int-http:outbound-gateway id="outboundGateway"
request-channel="requestChannel"
url="http://server/API.jsp?id=1"
http-method="GET"
expected-response-type="java.lang.String"
charset="UTF-8"
reply-channel="replyChannel"/>
</beans>
Main Application Class:
package test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
import ru.eco.products.waste.egr.Integration;
#SpringBootApplication
#ImportResource("/META-INF/spring/integration/http-outbound-config.xml")
public class Application {
public static void main(String[] args) {
Integration integration = new Integration();
integration.start();
SpringApplication.run(WasteWebClientApplication.class,
args
);
}
}
Integration class:
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
#Configuration
public class Integration {
public void start() {
ClassPathXmlApplicationContext
context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/integration/http-outbound-config.xml");
context.start();
MessageChannel requestChannel = context.getBean("requestChannel", MessageChannel.class);
PollableChannel replyChannel = context.getBean("replyChannel", PollableChannel.class);
Message<?> message = MessageBuilder.withPayload("").build();
requestChannel.send(message);
Message<?> receivedMsg = replyChannel.receive();
System.out.println("RESULT IS : " + receivedMsg.getPayload());
}
}
BUT, when I try to Autowire MessageChannel and PollableChannel, I receive a null pointer exception.
Integration class(not working example):
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
#Configuration
public class Integration {
#Autowired
#Qualifier("requestChannel")
MessageChannel requestChannel;
#Autowired
#Qualifier("replyChannel")
PollableChannel replyChannel;
public void start() {
Message<?> message = MessageBuilder.withPayload("").build();
requestChannel.send(message);
Message<?> receivedMsg = replyChannel.receive();
System.out.println("RESULT IS : " + receivedMsg.getPayload());
}
}
Question 1: Why Autowiring is not working?
Question 2: What is the best way to get data from dedicated server and save it into DB? Such config is ok? I will create a model class for the response and after will save it into DB via JPA.
Question 3: I need reading from server works in Async mode. How can I implement it in Spring Boot? So the main idea here is that I will receive a POST method from the UI, and will launch the integration with web-service. After integration will be finished, I need to notify user.
Question 4: Maybe Camel will be the best solution here?
Stack: Java 11, Spring Boot, thymeleaf + bootstrap.
Thank you for the answer in advance.
Since you do new Integration();, you definitely not going to have dependency injection since inversion of control container is not involved. Although it is fully not clear why do you need that new ClassPathXmlApplicationContext("classpath:META-INF/spring/integration/http-outbound-config.xml") if you already do Spring Boot and that proper #ImportResource.
The best way as you already do with the #ImportResource for that Spring Integration XML config. Then you need to get access to the ApplicationContext of the SpringApplication.run() and getBean(Integration.class) to call your start() method. However you fully need to forget about new Integratio(). Spring is going to manage a bean for that Integration and then dependency injection is going to work.
Async mode can be achieved with an ExecutorChannel in Spring Integration. So, when you send a message to this channel, the logic is going to be processed on a different thread. However it is not clear why you state such a requirement since you still going to block via that replyChannel.receive()... Although this should be addressed in a separate SO thread.
Camel VS Spring Integration question is out of StackOverflow policies.
Thymeleaf and Bootstrap are misleading in the context of this question.
Please, consider to learn how to ask properly here on SO: https://stackoverflow.com/help/how-to-ask
Also read some docs about dependency injection and inversion of control: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
I have a spring boot application I'm building, and at the start, I need to check some system files and prepare some database pools using the information the app finds there. Normally, I'd include this in the main method of the #SpringBootApplication annotated class, however, when I deploy my app as a WAR file to an external Tomcat server, that main class doesn't seem to run. I've checked around at what you're supposed to have in that main class, and my main application class now looks like this:
package com.companyname.projectname;
import com.companyname.projectname.database.DatabasePoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class WebApplication extends SpringBootServletInitializer {
private static final Logger logger = LoggerFactory.getLogger(WebApplication.class);
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(WebApplication.class, args);
DatabasePoolManager dpm = applicationContext.getBean(DatabasePoolManager.class);
dpm.setUpPools();
logger.error("\n\nIS ANYBODY OUT THERE?\n\n");
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
logger.error("\n\nIS ANYBODY OUT THERE? (But in the configure method)\n\n");
return builder.sources(WebApplication.class);
}
}
This is different than my original setup because of the extends and override of configure.
So far, this still runs fine with my Intellij IDE, but once moved and deployed to the tomcat server, none of the log messages appear. The app still works, but is clearly missing some setup that grants it's functionality (connections to databases). How would I go about running some setup code on the application start, when I deploy this app as a WAR file?
Thanks again to M. Deinum in the comments above, to run once on startup, I used this new class shown below:
package com.companyname.projectname;
import com.companyname.projectname.database.DatabasePoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
#Component
public class AppStartupRunner implements ApplicationRunner {
#Autowired
ApplicationContext applicationContext;
private static final Logger logger = LoggerFactory.getLogger(AppStartupRunner.class);
#Override
public void run(ApplicationArguments args) throws Exception {
DatabasePoolManager dpm = applicationContext.getBean(DatabasePoolManager.class);
dpm.setUpPools();
}
}
In a Spring Boot application I'm trying to setup multiple database connections. I've started building the primary datasource, but I'm getting the following error on the mySqlEntityManagerFactory method.
Could not autowire. no beans of EntityManagerFactoryBuilder
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
#Transactional
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "digital.sheppard.dao",
entityManagerFactoryRef = "entityManager",
transactionManagerRef = "transactionManager")
public class PrimaryDBConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "primary.datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#PersistenceContext(unitName = "primary")
#Primary
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(mysqlDataSource()).persistenceUnit("primary").properties(jpaProperties())
.packages("digital.sheppard.model").build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernte.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return props;
}
}
How would I autowire the EntityManagerFactoryBuilder?
I'm trying to follow the code on this blog https://raymondhlee.wordpress.com/2015/10/31/configuring-multiple-jpa-entity-managers-in-spring-boot/
Here's the main application class if it's helpful
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For me the reason was a wrong import, so I advise everybody to look at the often overlooked import section of your configuration class. There is a Hibernate class named the same as the springframework one. Of course you should choose the spring one...
I think you should remove this code
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
The exception is due to public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
which expects a bean of EntityManagerFactoryBuilder.
I checked the reference link, I am not sure if that code will work.
Typically, one creates an instance of LocalContainerEntityManagerFactoryBean and initializes it as per need. In your case you can do
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mysqlDataSource());
em.setPersistenceUnitName("primary");
em.setPackagesToScan(new String[] { "digital.sheppard.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
The persistence unit name should be same as defined in persistence.xml, though the file is now optional when using Spring JPA.
For a non spring version check out https://stackoverflow.com/a/26814642/776548
Also
since you are initializing EntityManagerFactory by yourself, we will have to exclude DataSourceAutoConfiguration.class.
#Primary is only required if you want multiple datasources. If you have only one, consider removing the annotation, and add it when you need to have multiple data sources
Change parameter name builder to entityManagerFactoryBuilder to inject bean present in JpaBaseConfiguration.class
Have you tried to remove your exclusion of 'DataSourceAutoConfiguration' ?
Using '#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})' prevent a lot of beans from beeing created.
If you got a problem when using a datasource and adding this is your solution, maybe it's not the right one.
Know that spring boot detect the presence of certain classes in the classpath.
If you're using maven, it's reading all classes from all dependencies.
So consider let this DataSourceAutoConfiguration.class running;
cheers
It could be, notice that just could be, your main class is not at the top of the "class tree". Spring needs to scan all classes that are a child (according to package convention) starting from the main class.
Maybe you would read https://www.baeldung.com/spring-component-scanning
If your classes aren't been read by spring scan, they will never be into spring context.
Couple of possibilities :
You need to add the #EnableJpaRepositories(basePackages = {"your.pkg.here"}) to the Application . This tells Spring Data to look for your repository classes under the specified package.
For me , removing #Primary on multiple Data sources, which was added by mistake fixed the issue
it was caused by your ide software,set up these options
I'm currently using Oracle AQ and would like to replace it by a persisted ActiveMQ.
My current setup using Oracle AQ is:
DB server: Oracle DB with a queue Q1
App server 1: Has a producer and multiple listeners on Q1
App server 2: Has a producer and multiple listeners on Q1
The following flow is currently followed:
App server 1:
Incoming message via a webservice
Start DB transaction
Save message in DB with id
Post id and other information of the message on queue Q1
commit transaction
App server 2:
Same setup, horizontal scaled
Requirements
When implementing ActiveMQ I want the data to the DB and the post on the queue in the same transaction. So that if one does a rollback, the other will do it as well.
Because I need to be able to produce messages on the queue with both app-servers at the same time, I need to run the ActiveMQ broker on the DB-server, and not on the app-servers. Otherwise they will act as a 'master slave'.
I an article I read, they explain how you can share transaction resources.
But this is done assuming you put the ActiveMQ broker on the same server as where the transaction is started.
Is there any way, except using JTA to accomplish this?
I'm using Java with:
Spring 2.5.6
Hibernate 3.3
TransactionManager: org.springframework.orm.hibernate3.HibernateTransactionManager
DataSource: oracle.jdbc.pool.OracleDataSource
I didn't test this. But isn't this what you're looking for?
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.JmsException;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
public class SomeServiceImpl implements SomeService {
#Autowired
private Queue someQueue;
private JmsTemplate jmsTemplate;
private SomeRepository repository;
public SomeServiceImpl() {}
public SomeServiceImpl(ConnectionFactory activeMQConnectionFactory, SomeRepository repository) {
this.jmsTemplate = new JmsTemplate(activeMQConnectionFactory);
this.repository = repository;
}
#Transactional(rollbackFor = {JmsException.class, RepositoryException.class})
public void sendMessage(final SomeObject object) {
repository.save(object);
jmsTemplate.send(
new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(object.getSpecialId());
}
});
}
}
This way if there is a failure while saving the object or an issue with sending the message to the queue (JMSException), the local transaction will be rolled back and the object won't be persisted. I don't think you will be needing a distributed transaction.