exclude CommandLineRunner execution in the test - spring

I've got main class that looks like this
#SuppressWarnings("checkstyle:hideutilityclassconstructor") // FIXME:doesn't work
#ComponentScan({"com.lapots.breed.judge"})
#SpringBootApplication
public class JudgeRuleEngineApplication {
public static void main(final String[] args) {
SpringApplication.run(JudgeRuleEngineApplication.class, args);
}
#Bean
public CommandLineRunner initCache(final InMemoryPlayerLevelCache cache) {
return args -> {
cache.put(1, 100);
cache.put(2, 1000);
cache.put(3, 10000);
cache.put(4, 100000);
};
}
}
On startup I initialize my cache with values.
But during test I want to prevent that - I want to exclude the commandlinerunner from the execution
My test looks like this
#WebFluxTest(excludeFilters =
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CommandLineRunner.class))
class InMemoryPlayerLevelCacheTestSpec extends Specification {
#Autowired
InMemoryPlayerLevelCache cache
def "should access cache"() {
when:
cache.put(10, 200)
then:
1 == cache.size()
200 == cache.get(10)
}
}
I tried to exclude it using excludeFilter but still my test fails because it has more elements than expect (should have 1 but it has 5 as 1 in test + 4 initialization).
What is wrong?

Related

weird issue with PostConstruct in parent class

I have such a simple example:
public class Base<T> {
private String value;
public Base(String value) {
this.value = value;
}
#PostConstruct
private void init() {
System.out.println(value);
}
}
public class StringTest extends Base<String> {
public StringTest(String value) {
super(value);
}
}
public class IntegerTest extends Base<Integer> {
public IntegerTest(String value) {
super(value);
}
}
#Configuration
public class Configuration {
#bean(name="stringtest1")
public Base<?> getStringTest1() {
return new StringTest("string test 1");
}
#bean(name="stringtest2")
public Base<?> getStringTest2() {
return new StringTest("string test 2");
}
#bean(name="integertest1")
public Base<?> getIntegerTest1() {
return new IntegerTest("integer test 1");
}
#bean(name="integertest2")
public Base<?> getIntegerTest2() {
return new IntegerTest("integer test 2");
}
}
The output is
string test 1
string test 2
PostConstruct is only invoked for beans stringtest1 and stringtest2 but not for integertest1 and integertest2.
If I change the configuration to below (change ? to the concrete type)
#bean(name="stringtest1")
public Base<String> getStringTest1() {
return new StringTest("string test 1");
}
#bean(name="stringtest2")
public Base<String> getStringTest2() {
return new StringTest("string test 2");
}
#bean(name="integertest1")
public Base<Integer> getIntegerTest1() {
return new IntegerTest("integer test 1");
}
#bean(name="integertest2")
public Base<Integer> getIntegerTest2() {
return new IntegerTest("integer test 2");
}
I will get the expected output:
string test 1
string test 2
integer test 1
integer test 2
Any idea why my previous code only have the PostConstruct invoked for the first 2 beans?

Spring Integration: how to unit test a poller advice

I'm trying to unit test an advice on the poller which blocks execution of the mongo channel adapter until a certain condition is met (=all messages from this batch are processed).
The flow looks as follow:
IntegrationFlows.from(MongoDb.reactiveInboundChannelAdapter(mongoDbFactory,
new Query().with(Sort.by(Sort.Direction.DESC, "modifiedDate")).limit(1))
.collectionName("metadata")
.entityClass(Metadata.class)
.expectSingleResult(true),
e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(pollingIntervalSeconds))
.advice(this.advices.waitUntilCompletedAdvice())))
.handle((p, h) -> {
this.advices.waitUntilCompletedAdvice().setWait(true);
return p;
})
.handle(doSomething())
.channel(Channels.DOCUMENT_HEADER.name())
.get();
And the following advice bean:
#Bean
public WaitUntilCompletedAdvice waitUntilCompletedAdvice() {
DynamicPeriodicTrigger trigger = new DynamicPeriodicTrigger(Duration.ofSeconds(1));
return new WaitUntilCompletedAdvice(trigger);
}
And the advice itself:
public class WaitUntilCompletedAdvice extends SimpleActiveIdleMessageSourceAdvice {
AtomicBoolean wait = new AtomicBoolean(false);
public WaitUntilCompletedAdvice(DynamicPeriodicTrigger trigger) {
super(trigger);
}
#Override
public boolean beforeReceive(MessageSource<?> source) {
if (getWait())
return false;
return true;
}
public boolean getWait() {
return wait.get();
}
public void setWait(boolean newWait) {
if (getWait() == newWait)
return;
while (true) {
if (wait.compareAndSet(!newWait, newWait)) {
return;
}
}
}
}
I'm using the following test for testing the flow:
#Test
public void testClaimPoollingAdapterFlow() throws Exception {
// given
ArgumentCaptor<Message<?>> captor = messageArgumentCaptor();
CountDownLatch receiveLatch = new CountDownLatch(1);
MessageHandler mockMessageHandler = mockMessageHandler(captor).handleNext(m -> receiveLatch.countDown());
this.mockIntegrationContext.substituteMessageHandlerFor("retrieveDocumentHeader", mockMessageHandler);
LocalDateTime modifiedDate = LocalDateTime.now();
ProcessingMetadata data = Metadata.builder()
.modifiedDate(modifiedDate)
.build();
assert !this.advices.waitUntilCompletedAdvice().getWait();
// when
itf.getInputChannel().send(new GenericMessage<>(Mono.just(data)));
// then
assertThat(receiveLatch.await(10, TimeUnit.SECONDS)).isTrue();
verify(mockMessageHandler).handleMessage(any());
assertThat(captor.getValue().getPayload()).isEqualTo(modifiedDate);
assert this.advices.waitUntilCompletedAdvice().getWait();
}
Which works fine but when I send another message to the input channel, it still processes the message without respecting the advice.
Is it intended behaviour? If so, how can I verify using unit test that the poller is really waiting for this advice?
itf.getInputChannel().send(new GenericMessage<>(Mono.just(data)));
That bypasses the poller and sends the message directly.
You can unit test the advice has been configured by calling beforeReceive() from your test
Or you can create a dummy test flow with the same advice
IntegationFlows.from(() -> "foo", e -> e.poller(...))
...
And verify that just one message is sent.
Example implementation:
#Test
public void testWaitingActivate() {
// given
this.advices.waitUntilCompletedAdvice().setWait(true);
// when
Message<ProcessingMetadata> receive = (Message<ProcessingMetadata>) testChannel.receive(3000);
// then
assertThat(receive).isNull();
}
#Test
public void testWaitingInactive() {
// given
this.advices.waitUntilCompletedAdvice().setWait(false);
// when
Message<ProcessingMetadata> receive = (Message<ProcessingMetadata>) testChannel.receive(3000);
// then
assertThat(receive).isNotNull();
}
#Configuration
#EnableIntegration
public static class Config {
#Autowired
private Advices advices;
#Bean
public PollableChannel testChannel() {
return new QueueChannel();
}
#Bean
public IntegrationFlow fakeFlow() {
this.advices.waitUntilCompletedAdvice().setWait(true);
return IntegrationFlows.from(() -> "foo", e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(1))
.advice(this.advices.waitUntilCompletedAdvice()))).channel("testChannel").get();
}
}

Spring Boot How to run multiple method with #Scheduled

I have a Spring Boot app where I want to have multiple methods to run at different times of the day. The first one runs, but no subsequent method runs. What do I need to do to fix this? Here is my code:
#EnableScheduling
#Configuration
//#ConditionalOnProperty(name = "spring.enable.scheduling")
#SpringBootApplication
#PropertySources({
#PropertySource(value = "prop.properties", ignoreResourceNotFound = true)
})
public class Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
public static MyClass class = new MyClass();
public static void main(String[] args) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("log4j2.properties");
PropertyConfigurator.configure(resourceAsStream);
SpringApplication.run(Application.class, args);
}
#Scheduled(cron = "${4am.cron.expression}", zone = "America/New_York") //0 0 6 * * ?
public void method1() {
something;
}
#Scheduled(cron = "${10am.cron.expression}", zone = "America/New_York") //0 0 6 * * ?
public void method2() {
something;
}
#Scheduled(cron = "${10am.cron.expression}", zone = "America/New_York") //0 0 6 * * ?
public void method3() {
something;
}
#Scheduled(cron = "${330pm.cron.expression}", zone = "America/New_York") //0 0 6 * * ?
public void method4() {
something;
}
#Scheduled(cron = "${430pm.cron.expression}", zone = "America/New_York") //0 0 6 * * ?
public void stopExecutor() {
MyClass class = new MyClass();
Executor executor = new Executor(class);
executor.stop();
}
You can try annonate method you are trying to run at given scheduled day / time using #Scheduled ( cron = "your cron job time ") on method.
E.g.
#Scheduled(cron = " specify cron job here ")
public void run job() {
// Code here
}
Hope this helps !

Cucumber Guice / Injector seems not to be thread-safe (Parallel execution / ExecutorService)

[long description warning]
I'm running some cucumber tests which have to be executed intercalated a defined server - for instance:
a.feature -> JBoss Server 1 | b.feature -> JBoss Serv. 2 | c.feature -> JB1 | etc.
For that, I created a hypothetical ExecutorService like this:
final ExecutorService executorService = Executors.newFixedThreadPool(2); //numberOfServers
for (Runnable task : tasks) {
executorService.execute(task);
}
executorService.shutdown();
try {
executorService.awaitTermination(1000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
//doX();
}
The way that I manage about how will be the server chosen as liable to execute is:
inside of my Runnable class created for the executorService, I pass as a parameter a instanceId to a TestNG (XmlTest class) as below:
#Override
public void run() {
setupTest().run();
}
private TestNG setupTest() {
TestNG testNG = new TestNG();
XmlSuite xmlSuite = new XmlSuite();
XmlTest xmlTest = new XmlTest(xmlSuite);
xmlTest.setName(//irrelevant);
xmlTest.addParameter("instanceId", String.valueOf(instanceId));
xmlTest.setXmlClasses(..........);
testNG.setXmlSuites(..........);
return testNG;
}
Then, I get this just fine in a class that extends TestNgCucumberAdaptor:
#BeforeTest
#Parameters({"instanceId"})
public void setInstanceId(#Optional("") String instanceId) {
if (!StringUtils.isEmpty(instanceId)) {
this.instanceId = Integer.valueOf(instanceId);
}
}
And inside a #BeforeClass I'm populating a Pojo with this instanceId and setting the Pojo in a threadLocal attribute of another class. So far, so good.
public class CurrentPojoContext {
private static final ThreadLocal<PojoContext> TEST_CONTEXT = new ThreadLocal<PojoContext>();
...
public static PojoContext getContext(){
TEST_CONTEXT.get();
}
Now the problem really starts - I'm using Guice (Cucumber guice as well) in a 3rd class, injecting this pojo object that contains the instanceId. The example follows:
public class Environment {
protected final PojoContext pojoContext;
#Inject
public Environment() {
this.pojoContext = CurrentPojoContext.getContext();
}
public void foo() {
print(pojoContext.instanceId); // output: 1
Another.doSomething(pojoContext);
}
class Another{
public String doSomething(PojoContext p){
print(p.instanceId); // output: 2
}
}
}
Here it is not every time like this the outputs (1 and 2) but from time to time, I realized that the execution of different threads is messing with the attribute pojoContext. I know that is a little confusing, but my guess is that the Guice Injector is not thread-safe for this scenario - it might be a long shot, but I'd appreciate if someone else takes a guess.
Regards
Well, just in order to provide a solution for someone else, my solution was the following:
Create a class that maintains a Map with an identifier (unique and thread-safe one) as the key and a Guice Injector as value;
Inside my instantiation of Guice injector, I created my own module
Guice.createInjector(Stage.PRODUCTION, MyOwnModules.SCENARIO, new RandomModule());
and for this module:
public class MyOwnModules {
public static final Module SCENARIO = new ScenarioModule(MyOwnCucumberScopes.SCENARIO);
}
the scope defined here provides the following:
public class MyOwnCucumberScopes {
public static final ScenarioScope SCENARIO = new ParallelScenarioScope();
}
To sum up, the thread-safe will be in the ParallelScenarioScope:
public class ParallelScenarioScope implements ScenarioScope {
private static final Logger LOGGER = Logger.getLogger(ParallelScenarioScope.class);
private final ThreadLocal<Map<Key<?>, Object>> threadLocalMap = new ThreadLocal<Map<Key<?>, Object>>();
#Override
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
return new Provider<T>() {
public T get() {
Map<Key<?>, Object> scopedObjects = getScopedObjectMap(key);
#SuppressWarnings("unchecked")
T current = (T) scopedObjects.get(key);
if (current == null && !scopedObjects.containsKey(key)) {
current = unscoped.get();
scopedObjects.put(key, current);
}
return current;
}
};
}
protected <T> Map<Key<?>, Object> getScopedObjectMap(Key<T> key) {
Map<Key<?>, Object> map = threadLocalMap.get();
if (map == null) {
throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block");
}
return map;
}
#Override
public void enterScope() {
checkState(threadLocalMap.get() == null, "A scoping block is already in progress");
threadLocalMap.set(new ConcurrentHashMap<Key<?>, Object>());
}
#Override
public void exitScope() {
checkState(threadLocalMap.get() != null, "No scoping block in progress");
threadLocalMap.remove();
}
private void checkState(boolean expression, String errorMessage) {
if (!expression) {
LOGGER.info("M=checkState, Will throw exception: " + errorMessage);
throw new IllegalStateException(errorMessage);
}
}
}
Now the gotcha is just to be careful regarding the #ScenarioScoped and the code will work as expected.

Schedule a task with Cron which allows dynamic update

I use sprint boot 1.3, spring 4.2
In this class
#Service
public class PaymentServiceImpl implements PaymentService {
....
#Transactional
#Override
public void processPayment() {
List<Payment> payments = paymentRepository.findDuePayment();
processCreditCardPayment(payments);
}
}
I would like to call processPayment every x moment.
This x moment is set in a database.
The user can modify it.
So i think i can't use anotation.
I started to this this
#EntityScan(basePackageClasses = {MyApp.class, Jsr310JpaConverters.class})
#SpringBootApplication
#EnableCaching
#EnableScheduling
public class MyApp {
#Autowired
private DefaultConfigService defaultConfigService;
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
#Bean
public TaskScheduler poolScheduler() {
SimpleAsyncTaskExecutor taskScheduler = new SimpleAsyncTaskExecutor();
DefaultConfigDto defaultConfigDto = defaultConfigService.getByFieldName("payment-cron-task");
String cronTabExpression = "0 0 4 * * ?";
if (defaultConfigDto != null && !defaultConfigDto.getFieldValue().isEmpty()) {
cronTabExpression = "0 0 4 * * ?";
}
appContext.getBean("scheduler");
taskScheduler.schedule(task, new CronTrigger(cronTabExpression));
return scheduler;
}
Maybe it's not the good way.
Any suggestion?
Don't know if to get my context if i need to create a property like
#Autowired
ConfigurableApplicationContext context;
and after in the main
public static void main(String[] args) {
context = SpringApplication.run(MyApp.class, args);
}
Looking at the question seems like you want to update the scheduler, without restart.
The code you have shared only ensures the config is picked from DB, but it will not refresh without application restart.
The following code will use the default scheduler available in the spring context and dynamically compute the next execution time based on the available cron setting in the DB:
Here is the sample code:
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
#SpringBootApplication
#EnableScheduling
public class Perses implements SchedulingConfigurer {
private static final Logger log = LoggerFactory.getLogger(Perses.class);
#Autowired
private DefaultConfigService defaultConfigService;
#Autowired
private PaymentService paymentService;
public static void main(String[] args) {
SpringApplication.run(Perses.class, args);
}
private String cronConfig() {
String cronTabExpression = "*/5 * * * * *";
if (defaultConfigDto != null && !defaultConfigDto.getFieldValue().isEmpty()) {
cronTabExpression = "0 0 4 * * ?";
}
return cronTabExpression;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
#Override
public void run() {
paymentService.processPayment();
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cron = cronConfig();
log.info(cron);
CronTrigger trigger = new CronTrigger(cron);
Date nextExec = trigger.nextExecutionTime(triggerContext);
return nextExec;
}
});
}
}
Just if someone still having this issue a better solution getting value from database whenever you want without many changes would be run cron every minute and get mod between current minute versus a configurated value delta from database, if this mod is equals to 0 means it has to run like if it is a mathematical multiple, so if you want it to run every 5 minutes for example delta should be 5.
A sample:
#Scheduled(cron = "0 */1 * * * *") //fire every minute
public void perform() {
//running
Integer delta = 5;//get this value from databse
Integer minutes = getField(Calendar.MINUTE)//calendar for java 7;
Boolean toRun = true;//you can also get this one from database to make it active or disabled
toRun = toRun && (minutes % delta == 0);
if (toRun && (!isRunning)) {
isRunning = true;
try {
//do your logic here
} catch (Exception e) { }
isRunning = false;
}
}
public Integer getField(int field) {
Calendar now = Calendar.getInstance();
if(field == Calendar.MONTH) {
return now.get(field)+ 1; // Note: zero based!
}else {
return now.get(field);
}
}
Hope this help :D

Resources