Lost and "unacked" messages with Spring Integration AMQP and RabbitMQ - java

I am trying to create 2 simple applications; one is posting messages to RabbitMQ channel and the other one is receiving it from the channel and print them out the console. Sender app starts and post 10 messages immediately.
What I see on the client side console is only about half of the messages printed.
I also see one of the messages always sits in the "Unacked" state when I checked the RabbitMQ web client.
When I read the documents, as far as I understand "amqp inbound/outbound gateway" is an easy way to implement this.
Can you please help me to understand why I am loosing some messages and one sits in the "Unacked" state?
Also, how should I change it to get all the messages on the other side?
Thank you in advance.
Here are the xml configuration and files on sender side:
integrationContext.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-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
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/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Configuration for Component Scan -->
<context:component-scan base-package="com.amqp.sample" />
<context:property-placeholder location="classpath*:rabbitmq.properties"/>
<int:gateway id="taskGateway" service-interface="com.amqp.sample.TaskGateway" default-request-channel="processChannel" />
<int-amqp:channel id="processChannel"
connection-factory="connectionFactory"
message-driven="true"
queue-name="ha.rabbit.channel" />
<!-- RabbitMQ Connection Factory -->
<rabbit:connection-factory id="connectionFactory"
addresses="${rabbitmq.addresses}"
username="${rabbitmq.username}"
password="${rabbitmq.password}" />
<rabbit:template id="amqpTemplate"
connection-factory="connectionFactory"
reply-timeout="-1" />
<rabbit:admin connection-factory="connectionFactory" />
<int-amqp:outbound-gateway request-channel="processChannel"
reply-channel="processChannel"
reply-timeout="-1" />
</beans>
TaskGateway.java
import org.springframework.messaging.Message;
import com.amqp.sample.model.Task;
public interface TaskGateway {
void processTaskRequest(Message<Task> message);
}
Task.java
import java.io.Serializable;
public class Task implements Serializable {
private static final long serialVersionUID = -2138235868650860555L;
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Task(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public String toString() {
return "Task [id=" + id + ", name=" + name + "]";
}
}
Application.Java
#PropertySources({
#PropertySource("classpath:application.properties"),
})
#EnableConfigurationProperties
#ComponentScan
#EnableAutoConfiguration
#ImportResource("classpath:integrationContext.xml")
public class Application extends SpringBootServletInitializer {
public static final Logger logger = LoggerFactory.getLogger(Application.class);
private static TaskGateway taskGateway;
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(Application.class, args);
taskGateway = context.getBean(TaskGateway.class);
for(int i=0; i<10; i++){
Message<Task> message = MessageBuilder.withPayload(getTask(i)).build();
taskGateway.processTaskRequest(message);
}
}
/**
* Creates a sample task returns.
*
* #return Task
*/
private static Task getTask(final int id) {
return new Task(id, "Task with ID:" + id);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
And, here are the files on receiver side:
integrationContext.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-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
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/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Configuration for Component Scan -->
<context:component-scan base-package="com.amqp.sample" />
<context:property-placeholder location="classpath*:rabbitmq.properties"/>
<!-- RabbitMQ Connection Factory -->
<rabbit:connection-factory id="connectionFactory"
addresses="${rabbitmq.addresses}"
username="${rabbitmq.username}"
password="${rabbitmq.password}" />
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>
<rabbit:admin connection-factory="connectionFactory" />
<int:channel id="inputChannel"/>
<int-amqp:inbound-gateway request-channel="inputChannel" reply-channel="inputChannel"
queue-names="ha.rabbit.channel"
connection-factory="connectionFactory"
amqp-template="amqpTemplate"/>
<int:service-activator input-channel="inputChannel" ref="taskProcessService" method="process" />
</beans>
ProcessService.java
import org.springframework.messaging.Message;
public interface ProcessService<T> {
/**
* Processes incoming message(s)
*
* #param message SI Message.
*/
void process(Message<T> message);
}
TaskProcessService
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
import com.amqp.sample.model.Task;
#Component("taskProcessService")
public class TaskProcessService implements ProcessService<Task> {
private final Logger logger = LoggerFactory.getLogger(TaskProcessService.class);
#Override
public void process(Message<Task> message) {
logger.info("Received Message : " + message.getPayload());
}
}
Application.java
#PropertySources({
#PropertySource("classpath:application.properties"),
})
#EnableConfigurationProperties
#ComponentScan
#EnableAutoConfiguration
#ImportResource("classpath:integrationContext.xml")
public class Application extends SpringBootServletInitializer {
public static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}

First of all, gateways are for request/reply scenarios; since your client expects no response and the service does not return one, you should be using channel adapters not gateways. Try that and get back if you are still having trouble.
EDIT
#SpringBootApplication
#IntegrationComponentScan
public class So40680673Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So40680673Application.class, args);
FooGate gate = context.getBean(FooGate.class);
for (int i = 0; i < 10; i++) {
System.out.println(gate.exchange("foo" + i));
}
context.close();
}
#MessagingGateway(defaultRequestChannel = "out.input")
public interface FooGate {
String exchange(String out);
}
#Bean
public IntegrationFlow out(AmqpTemplate amqpTemplate) {
return f -> f.handle(Amqp.outboundGateway(amqpTemplate).routingKey(queue().getName()));
}
#Bean
public IntegrationFlow in(ConnectionFactory connectionFactory) {
return IntegrationFlows.from(Amqp.inboundGateway(connectionFactory, queue().getName()))
.<String, String>transform(String::toUpperCase)
.get();
}
#Bean
public Queue queue() {
return new AnonymousQueue();
}
}

Related

Apache Ignite. How to create timer task (cron-based) without it execution on per node?

How to create timer task (cron-based) without it execution on each node i.e. one execution per one time point in timetable using Apache Ingite?
I have cluster that consists in 2 nodes and application (war) with timer-task. In non-cluster mode application works well. But it has inside timer-task (ie. start each 5 minutes), that works with shared resources.
I try to do it. But IngiteScheduler#scheduleLocal deploys and runs task on each node, if both application instance started (each application instance try to start same timer task).
I assume that ignite have mechanism for deploy task with id fot it...
Thanks.
Update 18.12.2018:
(thank #alamar for idea)
Below I show source and test for successful solutuion:
IgniteTimerTest.java:
package com.stackoverflow.question53780890.test;
import com.stackoverflow.question53780890.JobRunner;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteServices;
import org.apache.ignite.resources.SpringResource;
import org.apache.ignite.services.Service;
import org.apache.ignite.services.ServiceContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import java.io.Serializable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class IgniteTimerTest {
private static final ExecutorService ES = Executors.newFixedThreadPool(5);
#Test
public void test() throws Exception {
Future<ConfigurableApplicationContext> applicationContextFutureOne = ES.submit(() -> create(ConfigOne.class));
Future<ConfigurableApplicationContext> applicationContextFutureTwo = ES.submit(() -> create(ConfigTwo.class));
try (ConfigurableApplicationContext applicationContextOne = applicationContextFutureOne.get();
ConfigurableApplicationContext applicationContextTwo = applicationContextFutureTwo.get();) {
Ignite igniteOne = applicationContextOne.getBean(Ignite.class);
Ignite igniteTwo = applicationContextTwo.getBean(Ignite.class);
IgniteServices servicesOne = igniteOne.services();
IgniteServices servicesTwo = igniteTwo.services();
servicesOne.deployClusterSingleton(TestTimerRunner.class.getName(), new TestTimerRunner());
Thread.sleep(JobRunner.PERIOD * 3);
servicesTwo.deployClusterSingleton(TestTimerRunner.class.getName(), new TestTimerRunner());
Thread.sleep(JobRunner.PERIOD * 3);
applicationContextOne.close();
Thread.sleep(JobRunner.PERIOD * 3);
int countValue = JobRunner.getConterValue();
Assertions.assertTrue(9 <= countValue && countValue <= 11);
}
}
private ConfigurableApplicationContext create(Class mainClass) {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(mainClass);
return context;
}
class TestTimerRunner implements Serializable, Service {
#SpringResource(resourceClass = JobRunner.class)
private transient JobRunner jobRunner;
public TestTimerRunner() {
}
#Override
public void cancel(ServiceContext ctx) {
jobRunner.stop();
}
#Override
public void init(ServiceContext ctx) throws Exception {
}
#Override
public void execute(ServiceContext ctx) throws Exception {
jobRunner.start();
}
}
#Configuration
#ImportResource("classpath:common.xml")
public static class ConfigOne {
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer holder = new PropertySourcesPlaceholderConfigurer();
holder.setLocation(new ClassPathResource("one.properties"));
return holder;
}
}
#Configuration
#ImportResource("classpath:common.xml")
public static class ConfigTwo {
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer holder = new PropertySourcesPlaceholderConfigurer();
holder.setLocation(new ClassPathResource("two.properties"));
return holder;
}
}
}
JobRunner.java:
package com.stackoverflow.question53780890;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
public class JobRunner implements Runnable {
public static final long PERIOD = 5_000;
private static AtomicInteger counter = new AtomicInteger();
#Autowired
private TaskScheduler taskScheduler;
private ScheduledFuture<?> job;
public static int getConterValue() {
return counter.get();
}
public void start() {
job = taskScheduler.scheduleAtFixedRate(this, PERIOD);
}
public void stop() {
job.cancel(true);
}
#Override
public void run() {
counter.incrementAndGet();
}
}
common.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:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="5"/>
<bean id="jobRunner" class="com.stackoverflow.question53780890.JobRunner"/>
<bean id="ignite" class="org.apache.ignite.IgniteSpringBean">
<property name="configuration" ref="igniteConfiguration"/>
</bean>
<bean id="igniteConfiguration" class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="igniteInstanceName" value="${igniteInstanceName}"/>
<property name="discoverySpi">
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
<property name="ipFinder">
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder"
p:addresses-ref="igniteNodesAddresses"/>
</property>
<property name="joinTimeout" value="20000"/>
<property name="localPort" value="${igniteLocalPort}"/>
<property name="localPortRange" value="1"/>
</bean>
</property>
<property name="communicationSpi">
<bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi">
<property name="localPort" value="${communicationSpiPort}"/>
</bean>
</property>
</bean>
<bean id="igniteNodesAddresses" class="org.springframework.util.StringUtils"
factory-method="commaDelimitedListToStringArray">
<constructor-arg type="java.lang.String" value="${igniteNodes}"/>
</bean>
</beans>
one.properties:
igniteLocalPort=47501
communicationSpiPort=47101
igniteNodes=localhost:47501,localhost:47502
igniteInstanceName=one
two.properties:
igniteLocalPort=47502
communicationSpiPort=47102
igniteNodes=localhost:47501,localhost:47502
igniteInstanceName=two
I think you could deploy a singleton service which will do scheduleLocal in its execute() and de-schedule in its cancel() method. This way it will be failed over to next node and properly re-schedule your tasks if current node leaves.

Spring MVC transaction manager defined in root context doesn't open transactions in dao defined in child context

I have faced a real proglem and solved it, but couldn't figure out what has been happened.
I defined transactionManager and sessionFactory bean in root context and my dao class with #Transactional methods in dispatcher context. And that's all. When I was trying to use getCurrentSession() in dao, I was getting "could not obtain a current session".
But, as I can remember, dispatcher context is aware about root context and has access to all beans in root context.
Can somebody explain me, why do not transactions open before #Transactional method if transactionManager and sessionFactory were defined in root context and class with #Transactional in child context?
Database config class
#Configuration
#EnableTransactionManagement
public class DatabaseConfig {
#Bean
public LocalSessionFactoryBean sessionFactory() throws IOException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(getDatabaseDataSource());
sessionFactoryBean.setPackagesToScan("com.varguss.domain");
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL57Dialect");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.connection.useUnicode", "true");
properties.setProperty("hibernate.connection.characterEncoding", "utf8");
properties.setProperty("hibernate.connection.charSet", "utf8");
sessionFactoryBean.setHibernateProperties(properties);
return sessionFactoryBean;
}
#Bean
#Autowired
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
#Bean(name = "dataSource", destroyMethod = "close")
public BasicDataSource getDatabaseDataSource() throws IOException {
BasicDataSource databaseDataSource = new BasicDataSource();
Properties properties = new Properties();
ClassPathResource propertiesFileResource = new ClassPathResource("database.properties");
properties.load(propertiesFileResource.getInputStream());
databaseDataSource.setDriverClassName(properties.getProperty("driverClassName"));
databaseDataSource.setUrl(properties.getProperty("url"));
databaseDataSource.setUsername(properties.getProperty("username"));
databaseDataSource.setPassword(properties.getProperty("password"));
return databaseDataSource;
}
}
DAO class
#Repository
#Transactional
public class DbComputerPartDAO implements ComputerPartDAO {
private SessionFactory sessionFactory;
private Strategy strategy;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
strategy = StrategyFactory.getStrategy(StrategyType.ALL, sessionFactory);
}
#Override
#Transactional(readOnly = true)
public List<ComputerPart> allParts() {
return sessionFactory.getCurrentSession().createQuery("FROM ComputerPart part ORDER BY part.count DESC", ComputerPart.class).getResultList();
}
#Override
#Transactional(readOnly = true)
public ComputerPart part(Long id) {
return sessionFactory.getCurrentSession().find(ComputerPart.class, id);
}
#Override
public void save(String name, boolean isImportant, Long count) {
sessionFactory.getCurrentSession().saveOrUpdate(new ComputerPart(name, isImportant, count));
}
#Override
public void remove(Long id) {
ComputerPart computerPart = part(id);
if (computerPart != null)
sessionFactory.getCurrentSession().delete(computerPart);
}
#Override
#Transactional(readOnly = true)
public List<ComputerPart> byImportance(boolean isImportant) {
return sessionFactory.getCurrentSession().createQuery("FROM ComputerPart part WHERE part.isImportant ORDER BY part.count DESC", ComputerPart.class).getResultList();
}
#Override
public void updateImportance(Long id, boolean isImportant) {
ComputerPart computerPart = part(id);
if (computerPart != null)
computerPart.setImportant(isImportant);
}
#Override
public void updateName(Long id, String name) {
ComputerPart computerPart = part(id);
if (computerPart != null)
computerPart.setName(name);
}
#Override
public void updateCount(Long id, Long count) {
ComputerPart computerPart = part(id);
if (computerPart != null)
computerPart.setCount(count);
}
#Override
#Transactional(readOnly = true)
public List<ComputerPart> page(int pageNumber) {
return strategy.page(pageNumber);
}
#Override
#Transactional(readOnly = true)
public List<ComputerPart> parts() {
return strategy.parts();
}
#Override
#Transactional(readOnly = true)
public Integer lastPageNumber() {
return strategy.lastPageNumber();
}
#Override
#Transactional(readOnly = true)
public List<ComputerPart> search(String partOfName) {
return strategy.search(partOfName);
}
#Override
public void changeStrategy(StrategyType strategyType) {
this.strategy = StrategyFactory.getStrategy(strategyType, sessionFactory);
}
}
Root context
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:annotation-config/>
<bean class="com.varguss.config.DatabaseConfig"/>
</beans>
Child context
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources in the /resources/views/ directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/views/" p:suffix=".jsp" />
<context:component-scan base-package="com.varguss.dao" />
<context:component-scan base-package="com.varguss.controller" />
</beans:beans>
When using hierarchical application context (a parent and child) the child can see the beans from the parent. So it can detect the EntityManagerFactory and the PlatformTransactionManager.
However when using things like AOP that only applies to beans in the same application context as the AOP is defined in. So AOP defined in the parent context only applies to beans in the parent context, not to beans in the child contexts.
So in your case the #EnableTransactionManagement is in the parent context but in there there aren't any beans with #Transactional, those are in the child context. So either create an #Configuration which enables transactions there or use <tx:annotation-driven /> in your XML configuration.

Spring Cloud Gateway injection in Unit Tests is not Working

I have a Global Filter with an injected attribute.
public class AuthenticationGlobalFilter implements GlobalFilter, Ordered {
#Autowired
private Permission permission;
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long serviceId = 0;
try {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String fromKey = headers.getFirst("x-api-key");
String fromIp = headers.getFirst("x-forwarded-for");
String fromSubject = headers.getFirst("******");
URI rqUrl = request.getURI();
String path = rqUrl.getPath();
serviceId = Long.parseLong(path.split("/")[6]);
permission.check(permission.identity(fromKey, fromSubject, fromIp), serviceId);
return chain.filter(exchange);
} catch (PermissionException e) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
} catch (IllegalArgumentException e) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
}
return Mono.empty();
}
}
Injection works perfectly when I run the application
#EnableDiscoveryClient
#SpringBootApplication
#EnableConfigurationProperties(ApiGatewayProperties.class)
#Import({ PermitAllSecurityConfiguration.class, CustomAttributesConfiguration.class })
#ImportResource(value = { "classpath:datasource.xml", "classpath:api-gateway-unico-beans.xml" })
public class ApiGatewayUnicoApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayUnicoApplication.class, args);
}
}
With this configuration file:
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<cache:annotation-driven
cache-manager="cacheManager" />
<context:annotation-config />
<context:component-scan
base-package="*****.apigatewayunico" />
<bean
class="*****.apigatewayunico.filters.AuthenticationGlobalFilter" />
<bean class="*****.apigatewayunico.Persistence"
p:sql="SELECT rep.url FROM RestService rs, RestEndpoint rep WHERE rep.id = rs.endpoint_id AND rs.id = ? AND rs.available = true" />
<bean id="permission"
class=*****.permission.Permission" />
The problem is that when I make a unit test, the property is injected in the unit test by not in the filter being tested, giving me errors about not being able to locate a bean that can satisfy this dependency.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
#DirtiesContext
public class AuthenticationGlobalFilterIntegrationTests extends BaseWebClientTests {
#Autowired
private Permission permission;
#Before
public void setup() {
super.setup();
reset(permission);
permission.check(null, 123456L);
}
#Test
public void runSucessTest() {
expectLastCall();
replay(permission);
testClient.get().uri("/status/200").exchange().expectStatus().isEqualTo(HttpStatus.OK);
}
#Test
public void runPermissionExceptionTest() throws Exception {
expectLastCall().andThrow(new PermissionException(""));
replay(permission);
testClient.get().uri("/status/200").exchange().expectStatus().isEqualTo(HttpStatus.FORBIDDEN);
}
#EnableAutoConfiguration
#SpringBootConfiguration
#Import(DefaultTestConfig.class)
#ImportResource(value = "classpath:authentication-filter-test-beans.xml")
public static class TestConfig {
}
}
authentication-filter-test-beans.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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:api-gateway-unico-test-beans.xml" />
<bean id="authenticationFilter"
class="*****.apigatewayunico.filters.AuthenticationGlobalFilter">
</bean>
</beans>
api-gateway-unico-test-beans.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-lazy-init="false">
<bean id="mockSupport" class="org.easymock.EasyMockSupport" />
<bean id="pp" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="*****.permission.PermissionPersistence" />
<bean id="permission" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="*****.permission.Permission" />
<bean id="jdbc" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="org.springframework.jdbc.core.JdbcTemplate" />
<bean id="descoveryClienteMock" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="org.springframework.cloud.client.discovery.DiscoveryClient" />
<bean id="serviceInstanceMock" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="org.springframework.cloud.client.ServiceInstance" />
</beans>
Now when I try to add the unit test's configuration xml to the #ConextConfiguration annotation in the unit test, I get a test related to Reactor dependencies!
org.springframework.context.ApplicationContextException: Unable to
start reactive web server
Solved by annotating the dependency with #Lazy

Couldn't pass the #bean value to another class through Autowiring

I am new to spring. So I couldn't figure out what issue is there in the below code. I have two classes. I am defining a #Bean Channel and am autowiring that bean in the next class. I am using annotation for autowiring. When I try " System.out.println(externalChannel.toString());" am getting null values and the exception null pointer exception is thrown.
#Configuration
public class MessagingConfig
{
#Value("${service.name}")
private String queueName; // NOSONAR
#Value("${${user}")
private String schema;
#Value("${Owner}")
private String owner;
#Bean
public Channel externalChannel()
{
EventsProvider eventsProvider = new EventsProvider();
eventsProvider.setOwner(owner);
System.out.println("-------Cost And Events Channel-------"+eventsProvider.getEventsChannel());
return eventsProvider.getEventsChannel();
}
}
And Another class is
#Component
#LoggedService
#Monitored(useActualType = true)
public class MessagePublish {
#Autowired
private MessagingService messageService;
#Autowired
private Channel externalChannel;
public void publishTPSMessage(SourceTransaction sourceTransaction)
{
TPSEvent event = new TPSEvent(ContextHolder.getContextId(), new Date(), GuidUtils.generateGuid(),sourceTransaction);
Message<TPSEvent> message = new Message<TPSEvent>(event);
message.getHeader().setMessageType(TPSEvent.class.getName());
message.getHeader().setPayloadEncodingType(SystemCodecs.XML.name());
System.out.println(message.toString());
System.out.println(externalChannel.toString());
messageService.publish(externalChannel, message);
}
}
More Info
public Channel getEventsChannel() {
return Channel.builder()
.setName("necessarySources")
.setConnectionName("defaultConnection")
.setType(ChannelType.Topic)
.setConnectionData(AqSqlConnectionData.buildString(this.owner, "Safi"))
.setSubscriberNames(new String[]{"Safi_Autowire"})
.build();
}
Main Class
public class TPSHandler {
public static void main(String[] args) {
BatchExport batchExportBean=getBatchExportASJAXBElement();
System.out.println(" batchExportBean Trans Description : " + batchExportBean.getDueDate());
for(Pay pay :batchExportBean.getPay()) {
SourceTransaction sourceTransaction=mapPayBatchToSourceTransaction(pay,batchExportBean);
String sourceTraString = getSourceTransactionASXML(sourceTransaction);
System.out.println(" sourceTraString : \n" + sourceTraString);
MessagePublish messagePublish= new MessagePublish();
//SourceTransaction sourceTransaction= new SourceTransaction();
messagePublish.publishTPSMessage(sourceTransaction);
}
}
}
My Servlet.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:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<import resource="classpath:config-properties.xml" />
<import resource="classpath:datasource.xml" />
<import resource="classpath:transaction-manager.xml" />
<import resource="classpath:jmx.xml" />
<context:component-scan base-package="com.imosAdapter" />
<bean class="com.oasis.services.messaging.config.MessagingServicesConfig" />
<bean class="com.oasis.services.messaging.config.DefaultMessagingConnectorsConfig" />
<bean class="com.oasis.services.messaging.config.LoaderConfig" />
<context:annotation-config />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>/config/oasis-component.properties</value>
</property>
</bean>
</beans>
The exception thrown:
Exception in thread "main" java.lang.NullPointerException
at com.imosAdapter.tps.events.MessagePublish.publishTPSMessage(MessagePublish.java:46)
at com.imosAdapter.tps.TPSHandler.main(TPSHandler.java:64)
What is the issue over here? Can anybody help me with this? what exactly I am missing here?
You need to initialize a Spring context at the start of your application when you do not create a servlet / run as a Server.
See Using Spring in a standalone application

How can I run a method Asynchronously with spring?

The following code is suppose to work Asynchronously but instead it waits for the Async part to finish and then goes. How can I make the blah() method run Asynchronously?
spring.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:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<!-- Activates #Scheduled and #Async annotations for scheduling -->
<task:annotation-driven />
<bean id="test"
class="com.spring.test.Test">
</beans>
Test.java
#Path("/test")
public class Test
{
#GET
#Path("/test")
#Produces("text/plain")
public String tester()
{
return "Running...";
}
#GET
#Path("/triggerNew")
#Produces("text/plain")
public String triggerNew()
{
System.out.println("BEFORE " + new Date() + " BEFORE");
new Process().blah();
System.out.println("AFTER " + new Date() + " AFTER");
return "TRIGGERED";
}
}
Process.java
#Component
public class Process
{
#Async
public void blah()
{
try
{
Thread.currentThread().sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("NEW THREAD " + new Date() + " NEW THREAD");
}
}
#Async only works when you annotate Spring-managed beans, not arbitrary classes. You need to define Process as a Spring bean, and then inject it into your controller class, e.g.
<bean id="test" class="com.spring.test.Test">
<property name="process">
<bean class="com.spring.test.Process"/>
</property>
</bean>
public class Test {
private Process process;
public void setProcess(Process process) {
this.process = process;
}
...
public String triggerNew() {
process.blah();
}
}
Alternatively you can execute your task manually with TaskExecutor.
Just define executor in the context:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"/>
Than you can execute yor task:
taskExecutor.execute(new Process ());
But in this case your Process class must implement Runnable interface

Categories