I have a model:
package com.example.asyncmethod;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class User {
private String name;
private String blog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBlog() {
return blog;
}
public void setBlog(String blog) {
this.blog = blog;
}
#Override
public String toString() {
return "User [name=" + name + ", blog=" + blog + "]";
}
}
a service call like below
package com.example.asyncmethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
#Service
public class GitHubLookupService {
private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);
private final RestTemplate restTemplate;
public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
#Async
public CompletableFuture<User> findUser(Integer user) throws InterruptedException {
logger.info("Looking up " + user);
String url = String.format("https://api.github.com/users/%s", user);
User results = restTemplate.getForObject(url, User.class);
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
Async runner class:
package com.example.asyncmethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
#Component
public class AppRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);
private final GitHubLookupService gitHubLookupService;
public AppRunner(GitHubLookupService gitHubLookupService) {
this.gitHubLookupService = gitHubLookupService;
}
#Override
public void run(String... args) throws Exception {
// Start the clock
long start = System.currentTimeMillis();
// in real use case before the for loop I will have a database call to get the number of records and based on the size I need to call that many number of times.
for(int i=0; i <= 10; i++){
CompletableFuture<User> page1 = gitHubLookupService.findUser(1);
}
// Wait until they are all done
CompletableFuture.allOf(page1).join();
// Print results, including elapsed time
logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
logger.info("--> " + page1.get());
}
}
So in the above for loop, I might call the findUser as per the number of records returned by DB.
Also in the future I might need to add few more async calls like findOrders, findInventory, findAccess etc which will call another services over http.
Considering the async call may succeed for one record and fail for another record, how can I approach here to call the async in the best possible way?
If you want to realize complete advantage of Async calls, I will suggest to avoid using CompletableFuture.get() or CompletableFuture.join().
Using this calls blocks your main thread till the time all the tasks (as part of CompletableFuture.allOf() ) are completed.
Instead you can use various functions that are provided to run a Lamba function on completion of all the futures (Also allows to handle failure in any of the Future).
Please refer the Java Docs for more details and check which method is more convenient in your implementation:
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
Related
I wrote a spring-boot application that recieves an object named Calc from the user, which contains two parameters, and returns an answer that consists of a complex calculation (the calculation itself is not relevant to the question). Because the system may be busy, each object is entered into the queue, and there is a scheduler that passes by order on the queue, and preforms the calculation.
My problem is how to return the result of the item's calculation to the correct request.
I've included the code I wrote:
controller:
#RestController
public class CalcController {
#Autowired
private CalculateService calculateService;
#RequestMapping("/")
public int calculate(#RequestBody Calc calc) {
return calculateService.calculate(calc);
}
}
Calc Object:
#Data
public class Calc {
private int paramA;
private int paramB;
}
CalculateService:
#Service
public class CalculateService {
private BlockingQueue<Calc> calcQueue;
#PostConstruct
private void init() {
calcQueue = new LinkedBlockingDeque<>();
}
public int calculate(Calc calc) {
calcQueue.add(calc);
// TODO: Return calculation result.
return 0;
}
#Scheduled(fixedRate = 2000)
public void calculateQueue() throws InterruptedException {
while (!calcQueue.isEmpty()) {
Calc calc = calcQueue.take();
int result = Calculator.calculate(calc);
// TODO: Return calculation result to the right request.
}
}
}
Thanks
You can use an ExecutorService which essentially maintains an internal queue to dispatch your work requests to a number of threads.
class Service {
// use 4 threads; optimal is the amount of processor cores available
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(4);
public int calculate(int input) {
Future<Integer> future = EXECUTOR.submit(() -> Calculator.calculate(input));
return future.get(); // this actually thrown an Exception you have to catch
}
}
The submit will call Calculator.calculate() when the executor has a thread available, and future.get() will extract the actual result of that call.
Note that this code does block until the result has been calculated, only the calculation itself will be parallel. If you do want to return immediately and provide the result later, that's a different story, but it doesn't really fit the REST controller concept.
You can also make this code simpler by using CompletableFuture
class Service {
public int calculate(int input) {
return CompletableFuture.supplyAsync(() -> Calculator.calculate(input)).get();
}
}
You can use Spring's #Async ability
Create a Thread pool
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
#EnableAsync
#Configuration
public class ThreadConfig {
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
Update calculateService, you do not need to store objects in Queue, it will be handled by Spring's async utility
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
#Service
public class CalculateService {
#Async("threadPoolTaskExecutor")
public CompletableFuture<Integer> calculate(Calc calc) {
int result = Calculator.calculate(calc);
return CompletableFuture.completedFuture(result);
}
}
Update Controller method to
import com.example.service.CalculateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
#RestController
public class CalcController {
#Autowired
private CalculateService calculateService;
#RequestMapping("/")
public int calculate(#RequestBody Calc calc) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> resultFut = calculateService.calculate(calc);
// some other calls/logic
return resultFut.get();
}
}
If you want to store sessions per request, refer this SO post
Is there anyway we can share data between different extensions in JUNIT 5 using store
Example
public class Extension1{
beforeAllCallback(){
getStore(GLOBAL).put(projectId,"112");
}
}
public class Extension2{
beforeTestExecutionCallback(){
System.out.println("projectId="+getStore(GLOBAL).get(projectId));
}
}
Yes, two extensions can share state via the Store as follows.
Note, however, that you may wish to store the shared state in the root context Store if you want the state to be accessible across test classes.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
#ExtendWith({ Extension1.class, Extension2.class })
public class Tests {
#Test
void test() {
// executing this results in the following being printed to SYS_OUT.
// PROJECT_ID=112
}
}
class Extension1 implements BeforeAllCallback {
public static final String PROJECT_ID = Extension1.class.getName() + ".PROJECT_ID";
#Override
public void beforeAll(ExtensionContext context) throws Exception {
context.getStore(Namespace.GLOBAL).put(PROJECT_ID, "112");
}
}
class Extension2 implements BeforeTestExecutionCallback {
#Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
System.out.println("PROJECT_ID=" + context.getStore(Namespace.GLOBAL).get(Extension1.PROJECT_ID));
}
}
Axon message receives but event handler not call.
I am trying to implement the event sourcing in both the side with tow different queue.
My First Queue is test and the Second one is testdemo
I have two separate application running on the same server.
User Management
Wallet Management
I have implemented the event sourcing from User Management to wallet management. and it is working fine.
Now I am trying to implement the wallet management to UserManagement, Means that When I will publish the event from the wallet management ( Producer )
and ( Consume ) the user management application. So the event is received but event handler is not called.
Following is my application code. Please help me to figure out what I will be missing.
My Axon Configuration Class
package com.peaas.ngapblueprintdemo.config;
import org.axonframework.amqp.eventhandling.DefaultAMQPMessageConverter;
import org.axonframework.amqp.eventhandling.spring.SpringAMQPMessageSource;
import org.axonframework.serialization.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.rabbitmq.client.Channel;
#Configuration
public class AxonConfiguration {
private final static Logger logger = LoggerFactory.getLogger(AxonConfiguration.class);
#Value("${axon.amqp.exchange}")
private String exchange;
#Bean
public Exchange exchange() {
logger.info(exchange + " AMQP Exchange Registering ");
return ExchangeBuilder.fanoutExchange(exchange).build();
}
#Bean
public Queue queue() {
return QueueBuilder.durable(exchange).build();
}
#Bean
public Binding binding() {
return BindingBuilder.bind(queue()).to(exchange()).with("*").noargs();
}
#Autowired
public void configure(AmqpAdmin amqpAdmin) {
amqpAdmin.declareExchange(exchange());
amqpAdmin.declareQueue(queue());
amqpAdmin.declareBinding(binding());
}
#Bean
public SpringAMQPMessageSource testdemo(Serializer serializer) {
System.out.println("--- On Message Call ---");
return new SpringAMQPMessageSource(new DefaultAMQPMessageConverter(serializer)) {
#RabbitListener(queues = "testdemo")
#Override
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println(message.getMessageProperties());
System.out.println("channel == "+channel);
super.onMessage(message, channel);
}
};
}
}
WalletCreatedEvent Class
package com.peaas.ngapblueprintdemo.events;
public class WalletCreatedEvent {
private Long id;
private String walletId;
private Double amount;
private Long userId;
public WalletCreatedEvent(Long id, String walletId, Double amount, Long userId) {
super();
System.out.println("--- call ---");
this.id = id;
this.walletId = walletId;
this.amount = amount;
this.userId = userId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getWalletId() {
return walletId;
}
public void setWalletId(String walletId) {
this.walletId = walletId;
}
#Override
public String toString() {
return "WalletCreatedEvent [id=" + id + ", walletId=" + walletId + ", amount=" + amount + ", userId=" + userId
+ "]";
}
}
EventHandler Class
package com.peaas.ngapblueprintdemo.eventHandlers;
import org.axonframework.eventhandling.EventHandler;
import org.springframework.stereotype.Component;
import com.peaas.ngapblueprintdemo.events.WalletCreatedEvent;
#Component
public class UserEventHandler {
#EventHandler
public void onCreateWalletEvent(WalletCreatedEvent event) {
System.out.println("--- Wallet Created Successfully ---");
System.out.println(event);
}
}
Following is my application.yml file properties
axon:
amqp:
exchange: test
eventhandling:
processors:
amqpEvents:
source: testdemo
Following is my log data that showing event is received.
MessageProperties [headers={axon-message-id=fa60968c-6905-46b5-8afe-6da853a4c51a, axon-message-aggregate-seq=0, axon-metadata-correlationId=589ef284-176f-49b8-aae0-0ad1588fa735, axon-message-aggregate-type=WalletAggregate, axon-message-revision=null, axon-message-timestamp=2018-08-06T11:09:26.345Z, axon-message-type=com.peaas.ngapblueprintdemo.events.WalletCreatedEvent, axon-metadata-traceId=589ef284-176f-49b8-aae0-0ad1588fa735, axon-message-aggregate-id=9524f7df-44fb-477f-83b8-d176583a126e}, contentLength=0, receivedDeliveryMode=PERSISTENT, redelivered=false, receivedExchange=testdemo, receivedRoutingKey=com.peaas.ngapblueprintdemo.events, deliveryTag=1, consumerTag=amq.ctag-fGm3jQcP_JIoTGf4ZMhAIg, consumerQueue=testdemo]
channel == Cached Rabbit Channel: AMQChannel(amqp://guest#127.0.0.1:5672/,1), conn: Proxy#3dcd657d Shared Rabbit Connection: SimpleConnection#19b12fd2 [delegate=amqp://guest#127.0.0.1:5672/, localPort= 52963]
You have most of the right configuration in place, but you are forgetting to tie your SpringAMQPMessageSource to an Event Processor under which your event handling component is placed.
See the reference guide for a correct example on how to reach this.
Here is a direct snippet from that reference guide to configure the message source to an event processor:
#Autowired
public void configure(EventHandlingConfiguration ehConfig, SpringAmqpMessageSource myMessageSource) {
ehConfig.registerSubscribingEventProcessor("myProcessor", c -> myMessageSource);
}
Edit
I think I see which part you where missing.
You did correctly wire the queue as a subscribable message source to an Event Processor. This follows from you application.yml, which ties the testdemo message source to the amqpEvents Event Processor. Thus sorry for my earlier assumption on that part.
The reasoning why you don't receive your events in the UserEventHandler, is because that event handler isn't tied to the amqpEvents Event Processor.
To solve that you should add the #ProcessingGroup("amqpEvents") annotation to the UserEventHandler component.
I want to mock a private method which has been called inside another method.
Following is the sample code, I have written.
Java code:
package org.mockprivatemethods;
public class AccountDeposit {
// Instantiation of AccountDetails using some DI
AccountDetails accountDetails;
public long deposit(long accountNum, long amountDeposited){
long amount = 0;
try{
amount = accountDetails.getAmount(accountNum);
updateAccount(accountNum, amountDeposited);
amount= amount + amountDeposited;
} catch(Exception e){
// log exception
}
return amount;
}
private void updateAccount(long accountNum, long amountDeposited) throws Exception {
// some database operation to update amount in the account
}
// for testing private methods
private int sum(int num1, int num2){
return num1+num2;
}
}
class AccountDetails{
public long getAmount(long accountNum) throws Exception{
// some database operation returning amount in the account
long amount = 10000L;
return amount;
}
}
Testclass:
package org.mockprivatemethods;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
#RunWith(PowerMockRunner.class)
public class TestAccountDeposit {
#InjectMocks
AccountDeposit accountDeposit;
#Mock
AccountDetails accountDetailsMocks;
#Test
public void testDposit() throws Exception{
long amount = 200;
when(accountDetailsMocks.getAmount(Mockito.anyLong()))
.thenReturn(amount);
// need to mock private method updateAccount
// just like we tested the private method "sum()" using Whitebox
// How to mock this private method updateAccount
long totalAmount = accountDeposit.deposit(12345678L, 50);
assertTrue("Amount in Account(200+50): "+totalAmount , 250==totalAmount);
}
#Test
public void testSum(){
try {
int amount = Whitebox.invokeMethod(accountDeposit, "sum", 20, 30);
assertTrue("Sum of (20,30): "+amount, 50==amount);
} catch (Exception e) {
Assert.fail("testSum() failed with following error: "+e);
}
}
}
We can test the private methods using Whitebox.invokeMethod(). My question is: Is there any to way mock the private method using Whitebox. Can we write something similar to below code to mock the updateAccount() as I do not want any database operation to be performed while testing the Deposit() method?strong text
when(accountDetailsMocks.getAmount(Mockito.anyLong()))
.thenReturn(amount);
Any help is appreciated! Thanks!
I have an actor class EmployeeActor, inside that actor, some other actor is fired using payrollRunActor.tell(). I need to write a JUnit test for EmployeeActor.java, but I don't want to fire payrollRunActor.tell(), means I want to mock it.
Is there a way to do it? I tried a lot, but real payrollRunActor is getting fired.
Here is the actual code of my EmployeeActor class.
package com.test.periodic.actors;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.test.avs.domain.boundedcontext.Employee;
import com.test.avs.domain.boundedcontext.PayrollRun;
import com.test.entity.BusinessDTO;
import com.test.periodic.actors.aggregrators.EmployeeAggregator;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.routing.RoundRobinPool;
public class EmployeeActor extends AbstractActor {
private static final Logger logger = LoggerFactory.getLogger(EmployeeActor.class);
private boolean rollup;
public static Props props() {
return Props.create(EmployeeActorTest.class);
}
private List<PayrollRun> payrollRuns;
private String instanceId;
private String employeeAggregatorId;
private Employee employee;
private ActorRef organizationAggregatorActor;
private List<BusinessDTO> businessDTOs;
final ActorSystem payrollRunSystem = ActorSystem.create("payrollRun");
ActorRef employeeAggregator;
public EmployeeActor(ActorRef organizationAggregatorActor, List<PayrollRun> payrollRuns,
Employee employee, List<BusinessDTO> businessDTOs, boolean rollup) {
this.payrollRuns = payrollRuns;
this.employee = employee;
this.organizationAggregatorActor = organizationAggregatorActor;
this.businessDTOs = businessDTOs;
this.rollup = rollup;
}
#Override
public void preStart() throws Exception {
instanceId = RandomStringUtils.randomAlphanumeric(6);
employeeAggregatorId = "employeeAggregator-" + instanceId;
employeeAggregator = getContext().system().actorOf(
Props.create(EmployeeAggregator.class, organizationAggregatorActor, employee),
employeeAggregatorId);
super.preStart();
}
#Override
public Receive createReceive() {
return receiveBuilder().match(Employee.class, employee -> {
if (rollup) {
logger.info("Rollingup business entities.");
employeeAggregator.tell(employee, getSelf());
} else {
ActorRef payrollRunActor = payrollRunSystem.actorOf(new RoundRobinPool(payrollRuns.size())
.props(Props.create(PayrollRunActor.class, employeeAggregator, employee, businessDTOs)));
for (PayrollRun payrollRun : payrollRuns) {
**payrollRunActor.tell(payrollRun, getSelf());**
}
}
}).match(PayrollRun.class, maxPaydatePayrollRun -> {
ActorRef payrollRunActor = payrollRunSystem
.actorOf(Props.create(PayrollRunActor.class, employeeAggregator, employee, businessDTOs));
**payrollRunActor.tell(maxPaydatePayrollRun, getSelf());**
}).build();
}
}
First of all you would have to mock the static method call which is invoked during the creation of class under test. Then make it return a spied object and mock the method you want to avoid calling:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ActorSystem.class)
public void TestClass{
#Test
public void test(){
// Arrange
PowerMockito.mockStatic(ActorSystem.class);
ActorSystem actorSystemMock = Mockito.mock(ActorSystem.class);
Actor actorSpy = Mockito.spy(new Actor());
Mockito.when(ActorSystem.create("payrollRun")).thenReturn(actorSystemSpy);
Mockito.when(actorSystemMock.actorOf(any(RoundRobinPool.class)))
.thenReturn(actorSpy);
Mockito.doNothing().when(actorSpy)
.tell(Mockito.any(PayrollRun.class), Mockito.any(Self.class));
EmployeeActor employeeActor = new EmployeeActor();
// Act and assert...
employeeActor.createReceive();
}
}
Remember that all other methods of actorSystemSpy will be called will real implementation. If you want to mock all of them then use Mockito.mock instead of spy.