Better solution to call method from interface in every service - java

I would like to create service which searching and returns objects from repositories, so:
I created interface which has method:
public interface ShapeServicesInterface {
List<ShapeEntity> getAll();
String getName();
}
and few services which implements that interface:
#Service
#RequiredArgsConstructor
public class CircleEntityService implements ShapeServicesInterface {
private final CircleEntityRepository circleEntityRepository;
#Override
public List<ShapeEntity> getAll() {
return new ArrayList<>(circleEntityRepository.findAll());
}
#Override
public String getName() {
return "circle";
}
}
and second one:
#Service
#RequiredArgsConstructor
public class SquareEntityService implements ShapeServicesInterface {
private final SquareEntityRepository squareEntityRepository;
#Override
public List<ShapeEntity> getAll() {
return new ArrayList<>(squareEntityRepository.findAll());
}
#Override
public String getName() {
return "square";
}
}
and next in other service I would like to call that method for getting all entites from that repositories (entites extend abstract class ShapeEntity) - found solution like that:
#Service
#RequiredArgsConstructor
public class TestService {
private final ShapeServiceFacade facade;
private final ExecutorService executorService;
public List<ShapeEntity> getAll() throws ExecutionException, InterruptedException {
List<ShapeEntity> allShapes = new ArrayList<>();
List<Future<List<ShapeEntity>>> futures = new ArrayList<>();
for (ShapeServicesInterface shapeDownloader : facade.getServices()) {
futures.add(executorService.submit(new ShapeTask(shapeDownloader)));
}
for (Future<List<ShapeEntity>> future : futures) {
allShapes.addAll(future.get());
}
return allShapes;
}
ShapeTask is:
#RequiredArgsConstructor
private static class ShapeTask implements Callable<List<ShapeEntity>> {
private final ShapeServicesInterface servicesInterface;
#Override
public List<ShapeEntity> call() {
return servicesInterface.getAll();
}
}
Facade is:
#Service
public class ShapeServiceFacade {
private final Map<String, ShapeServicesInterface> shapeServices;
public ShapeServiceFacade(Set<ShapeServicesInterface> allServices) {
this.shapeServices = allServices.stream()
.collect(Collectors.toMap(ShapeServicesInterface::getName,Function.identity()));
}
public List<ShapeServicesInterface> getServices() {
return new ArrayList<>(shapeServices.values());
}
}
but it is a little complicated. Is there a easier way to call that methods? I would like to add more methods so I will have to implement another task and another method in service, and in interface. I care about searching in every repostiory.

Maybe the ShapeServiceFacade can be omitted, if you are using spring boot, like that
#Service
#RequiredArgsConstructor
public class TestService {
#Autowired
private final List<ShapeServicesInterface> serviceList;
private final ExecutorService executorService;
public List<ShapeEntity> getAll() throws ExecutionException, InterruptedException {
List<ShapeEntity> allShapes = new ArrayList<>();
List<Future<List<ShapeEntity>>> futures = new ArrayList<>();
for (ShapeServicesInterface shapeDownloader : serviceList) {
futures.add(executorService.submit(new ShapeTask(shapeDownloader)));
}
for (Future<List<ShapeEntity>> future : futures) {
allShapes.addAll(future.get());
}
return allShapes;
}

Related

Issue in making Consumer Listener aware of seeking from beginning

I am not able to understand design of SeekAware interfaces.
I am trying to implement dynamic listeners to the new topic created from spring-kafka rest api.As of now only consumes consumer records from the time the listener started on topic but also wanted to modify offsets on start of this listener so that if want to seekToBeginning, my listener whenever invoked will read from start of topic.
the bits and pieces of important code are as follows
#Component
public class CustomKafkaListenerRegistrar implements InitializingBean {
#Autowired
private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;
#Autowired
private KafkaListenerContainerFactory kafkaListenerContainerFactory;
public void registerCustomKafkaListener(String name, CustomKafkaListenerProperty customKafkaListenerProperty,
boolean startImmediately) {
String listenerClass = String.join(".", CustomKafkaListenerRegistrar.class.getPackage().getName(),
customKafkaListenerProperty.getListenerClass());
CustomMessageListener customMessageListener =
(CustomMessageListener) beanFactory.getBean(Class.forName(listenerClass));
kafkaListenerEndpointRegistry.registerListenerContainer(
customMessageListener.createKafkaListenerEndpoint(name, customKafkaListenerProperty.getTopic()),
kafkaListenerContainerFactory, startImmediately);
}
public abstract class CustomMessageListener {
private static int NUMBER_OF_LISTENERS = 0;
#Autowired
private KafkaProperties kafkaProperties;
public abstract KafkaListenerEndpoint createKafkaListenerEndpoint(String name, String topic);
protected MethodKafkaListenerEndpoint<String, String> createDefaultMethodKafkaListenerEndpoint(String name,
String topic) {
MethodKafkaListenerEndpoint<String, String> kafkaListenerEndpoint = new MethodKafkaListenerEndpoint<>();
kafkaListenerEndpoint.setId(getConsumerId(name));
kafkaListenerEndpoint.setGroupId(kafkaProperties.getConsumer().getGroupId());
kafkaListenerEndpoint.setAutoStartup(true);
kafkaListenerEndpoint.setTopics(topic);
kafkaListenerEndpoint.setMessageHandlerMethodFactory(new DefaultMessageHandlerMethodFactory());
return kafkaListenerEndpoint;
}
private String getConsumerId(String name) {
if (isBlank(name)) {
return CustomMessageListener.class.getCanonicalName() + "#" + NUMBER_OF_LISTENERS++;
} else {
return name;
}
}
private boolean isBlank(String string) {
return Optional.ofNullable(string)
.map(String::isBlank)
.orElse(true);
}
}
#Component
public class MyCustomMessageListener extends CustomMessageListener {
#Override
#SneakyThrows
public KafkaListenerEndpoint createKafkaListenerEndpoint(String name, String topic) {
MethodKafkaListenerEndpoint<String, String> kafkaListenerEndpoint =
createDefaultMethodKafkaListenerEndpoint(name, topic);
kafkaListenerEndpoint.setBean(new MyMessageListener());
kafkaListenerEndpoint.setMethod(MyMessageListener.class.getMethod("onMessage", ConsumerRecord.class));
return kafkaListenerEndpoint;
}
#Slf4j
private static class MyMessageListener implements MessageListener<String, String> {
#Override
public void onMessage(ConsumerRecord<String, String> record) {
log.info("My message listener got a new record: " + record);
log.info("message is: "+record.toString());
CompletableFuture.runAsync(this::sleep)
.join();
log.info("My message listener done processing record: " + record);
}
#SneakyThrows
private void sleep() {
Thread.sleep(5000);
}
}
}
As far as I understood, MyCustomMessageListener will implement the interface ConsumerSeekAware which in turn will provide method seekToBeginning will get invoked on creation and trigering of listener.
Kindly help.
Note: I am using MessageListener<String, String> having method onMessage which as per my knowledge is equivalent to working of #KafkaListener.Kindly correct me if I am wrong.
If you implement MessageListener there is no need for all the KafkaListenerEndpoint stuff; just create a listener container and add your listener to it.
#SpringBootApplication
public class So72546425Application {
public static void main(String[] args) {
SpringApplication.run(So72546425Application.class, args);
}
#Bean
ApplicationRunner runner1(KafkaTemplate<String, String> template) {
return args -> {
template.send("so72546425", "foo");
};
}
#Bean
ApplicationRunner runner2(ContainerCreator creator) {
return args -> {
System.out.println("Hit enter to create a container");
System.in.read();
MessageListenerContainer container = creator.createContainerForTopic("so72546425");
container.start();
};
}
}
#Component
class ContainerCreator {
private final ConcurrentKafkaListenerContainerFactory<String, String> factory;
ContainerCreator(ConcurrentKafkaListenerContainerFactory<String, String> factory) {
this.factory = factory;
}
ConcurrentMessageListenerContainer<String,String> createContainerForTopic(String topic) {
ConcurrentMessageListenerContainer<String, String> container = factory.createContainer(topic);
container.getContainerProperties().setMessageListener(new MyListener());
container.getContainerProperties().setGroupId("group.for." + topic);
return container;
}
}
class MyListener extends AbstractConsumerSeekAware implements MessageListener<String, String> {
#Override
public void onMessage(ConsumerRecord<String, String> data) {
System.out.println(data);
}
#Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
System.out.println("Seeking to beginning");
callback.seekToBeginning(assignments.keySet());
}
}

Why can't mock static method in Spring condition

this is my code.
class ACondition extends SpringBootConditoin {
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (Config.isA()) {
return new ConditionOutcome(true, "ok");
} else {
return new ConditionOutcome(false, "error");
}
}
}
class BCondition extends SpringBootConditoin {
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (Config.isA()) {
return new ConditionOutcome(false, "error");
} else {
return new ConditionOutcome(true, "ok");
}
}
}
#Service
#Conditional(ACondition.class)
class APolicy implements Policy {
...
}
#Service
#Conditional(BCondition.class)
class BPolicy implements Policy {
...
}
class PolicyManager {
#Autowired
#Getter
List<Policy> policyList;
...
}
the default value of Config.isA() is true.
I want to make Config.isA() to return false. so I use Mockito.mockstatic.
#Autowired
PolicyManager manager;
#Test
public void get_B_policy() {
try(MockedStatic<Config> mocked = Mockito.mockStatic(Config.class) {
mocked.when(() -> Config.isA()).thenReturn(false);
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // this is not right
}
}
Why can't mock the online method?
by the way. If I test the BCondition class, the Config.isA() can be mocked. I can enter the branch which I want. It does not work only in conditional annotation.
Spring Context is already loaded by the time it is reaching the Test Case. Hence, Manager already has selected APolicy.
If you could move the static mock config before spring context loads, then it should match your expectations.
mocked.when(() -> Config.isA()).thenReturn(false);
One way of doing it is initialising the static Mock like below -
Junit4
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {PolicyManager.class, APolicy.class, BPolicy.class})
public class ConditionTest
{
#Autowired
PolicyManager manager;
static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);
#BeforeClass
public static void setup()
{
mocked.when(() -> Config.isA()).thenReturn(false);
}
#AfterClass
public static void clear()
{
mocked.close();
}
#Test
public void get_B_policy() {
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}
}
Junit5
Please use Jupiter's annotations.
#Autowired
PolicyManager manager;
static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);
#BeforeAll
public static void setup() {
mocked.when(() -> Config.isA()).thenReturn(true);
}
#AfterAll
public static void clear() {
mocked.close();
}
#Test
public void get_B_policy() {
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}

Spring ApplicationContext.getBean returns wrong class

I've been beating my head over this and I just can't figure out what's wrong.
I have a Spring app which uses ApplicationContext.getBean() to retrieve 2 similar classes. I'm getting the wrong instance class from the bean lookup.
Here's ApplicationContext class:
public class DomainRegistryCab {
private static ApplicationContext applicationContext;
private static ApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext( CaBridgeDomainServiceConfig.class );
}
public static CertificateProductApplicationService certificateProductAppService() {
var service = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
applicationContext().getAutowireCapableBeanFactory(),
CertificateProductApplicationService.class,
CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE);
// var service = applicationContext().getBean(
// CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE,
// CertificateProductApplicationService.class);
// var service = applicationContext().getBean(CertificateProductApplicationService.class);
validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
return service;
}
public static CertificateProgramApplicationService certificateProgramAppService() {
var service = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
applicationContext().getAutowireCapableBeanFactory(),
CertificateProgramApplicationService.class,
CaBridgeDomainServiceConfig.CERTIFICATE_PROGRAM_APP_SERVICE);
// var service = applicationContext().getBean(
// CaBridgeDomainServiceConfig.CERTIFICATE_PROGRAM_APP_SERVICE,
// CertificateProgramApplicationService.class);
// service = applicationContext().getBean(CertificateProgramApplicationService.class);
validateDataSourceIs(DataSource.ProgramDataStore, service.dataSource());
return service;
}
Here is CaBridgeDomainServiceConfig:
#Configuration
#ComponentScan(basePackageClasses = { HibernateConfigurationMarker.class })
public class CaBridgeDomainServiceConfig {
public static final String CERTIFICATE_PRODUCT_APP_SERVICE = "certificateProductAppService";
public static final String CERTIFICATE_PROGRAM_APP_SERVICE = "certificateProgramAppService";
#Bean(name= CERTIFICATE_PRODUCT_APP_SERVICE)
public CertificateProductApplicationService certificateProductAppService() {
return new CertificateProductApplicationServiceCabImpl();
}
#Bean(name= CERTIFICATE_PROGRAM_APP_SERVICE)
public CertificateProgramApplicationService certificateProgramAppService() {
return new CertificateProgramApplicationServiceCabImpl();
}
}
public interface CertificateProductApplicationService extends CertificateApplicationService {
}
public interface CertificateProductApplicationService extends CertificateApplicationService {
}
public interface CertificateApplicationService {
}
Using the above classes if I call DomainRegistryCab.certificateProductAppService() I get an instance of CertificateProgramApplicationService not CertificateProductApplicationService.
I get similar results if I use this method:
public static CertificateProductApplicationService certificateProductAppService() {
var service = applicationContext().getBean(
CaBridgeDomainServiceConfig.CERTIFICATE_PRODUCT_APP_SERVICE,
CertificateProductApplicationService.class);
validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
return service;
}
I've also tried having the #Bean methods return the implementation classes and the ApplicationContext().getBean() to request the implementation classes instead of the interfaces:
public class DomainRegistryCab {
private static ApplicationContext applicationContext;
private static ApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext( CaBridgeDomainServiceConfig.class );
}
public static CertificateProductApplicationService certificateProductAppService() {
var service = applicationContext().getBean(CertificateProductApplicationServiceCabImpl.class);
validateDataSourceIs(DataSource.ProductDataStore, service.dataSource());
return service;
}
public static CertificateProgramApplicationService certificateProgramAppService() {
var service = applicationContext().getBean(CertificateProgramApplicationServiceCabImpl.class);
validateDataSourceIs(DataSource.ProgramDataStore, service.dataSource());
return service;
}
public static ApplicationContext applicationContext() {
if (applicationContext == null)
applicationContext = createApplicationContext();
return applicationContext;
}
}
#ComponentScan(basePackageClasses = { HibernateConfigurationMarker.class })
public class CaBridgeDomainServiceConfig {
#Bean(name= CERTIFICATE_PRODUCT_APP_SERVICE)
public CertificateProductApplicationServiceCabImpl certificateProductAppService() {
return new CertificateProductApplicationServiceCabImpl();
}
#Bean(name= CERTIFICATE_PROGRAM_APP_SERVICE)
public CertificateProgramApplicationServiceCabImpl certificateProgramAppService() {
return new CertificateProgramApplicationServiceCabImpl();
}
}
This code results in spring not finding the implementation classes at all:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cmb.cabridge.application.cert.CertificateProductApplicationServiceCabImpl' available
I was eventually able to get things to work using the applicationContext().getBean("beanName", CertificateProductApplicationService.class). The problem was deeper in my code in the CertificateProductApplicationServiceCabImpl class which used and returned the wrong datasource.

Making Generic Class Using Lambda Expression

I have the below Service class which implement retry mechanism :
#RequiredArgsConstructor(onConstructor_ = {#Autowired})
#Service
public class RetryService {
private final RetryTemplate vcRetry;
private final Order1dao order1;
private final Order2dao order2;
public void saveOrder1(OrderRecord1 record) {
vcRetry.execute(
context -> {
order1.save(record);
});
}
public OrderRecord2 UpdateOrder2(OrderRecord2 record) {
return vcRetry.execute(
context -> {
return order2.Update(record);
});
}
public void saveOrder2(OrderRecord2 record) {
vcRetry.execute(
context -> {
order2.save(record);
});
}
There can be many daoBean which may uses the same RetryService class so i need to write the separate function for the individual bean.
Main Class:
public class OrderProcessor {
private final RetryService retryService;
public void SaveOrder(Order1 x, Order2 y,....) {
retryService.saveOrder1(x);
retryService.saveOrder2(y);
retryService.Update(y);
}
}
How can I make my RetryService class generic ?
You can create a GenericDao and use it in RetryService as below,
class GenericDao<T>{
//dao operations
}
class RetryService<T1 , T2> {
private final RetryTemplate vcRetry;
private final GenericDao<T1> order1;
private final GenericDao<T2> order2;
public void saveOrder1(T1 record) {
vcRetry.execute(
context -> {
order1.save(record);
});
}
public T2 UpdateOrder2(T2 record) {
return vcRetry.execute(
context -> {
return order2.Update(record);
});
}
public void saveOrder2(T2 record) {
vcRetry.execute(
context -> {
order2.save(record);
});
}
}
I hope it helps!

Spring #Async method inside a Service

I have this service bean with a sync method calling the internal async method:
#Service
public class MyService {
public worker() {
asyncJob();
}
#Async
void asyncJob() {
...
}
}
The trouble is that the asyncJob is not really called in async way.
I found that this doesn't work because an internal call skips the AOP proxy.
So I try to self-refer the bean:
#Service
public class MyService {
MyService mySelf;
#Autowired
ApplicationContext cnt;
#PostConstruct
public void init() {
mySelf=(MyService)cnt.getBean("myService");
}
public void worker() {
mySelf.asyncJob();
}
#Async
void asyncJob() {
...
}
}
It fails. Again no async call.
So I tried to divide it in two beans:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
void asyncJob() {
...
}
}
Fails again.
The only working way is to call it from a Controller Bean:
#Controller
public class MyController {
#Autowired
MyAsyncService myAsyncService;
#RequestMapping("/test")
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() {
...
}
}
But in this case it is a service job. Why I cannot call it from a service?
Found a really nice way to solve this (with java8) in the case where you have a lot of various things you want to both sync and async. Instead of creating a separate XXXAsync service for each 'synchronous' service, create a generic async service wrapper:
#Service
public class AsyncService {
#Async
public void run(final Runnable runnable) {
runnable.run();
}
}
and then use it as such:
#Service
public class MyService {
#Autowired
private AsyncService asyncService;
public void refreshAsync() {
asyncService.run(this::refresh);
}
public void refresh() {
// my business logic
}
public void refreshWithParamsAsync(String param1, Integer param2) {
asyncService.run(() -> this.refreshWithParams(param1, param2));
}
public void refreshWithParams(String param1, Integer param2) {
// my business logic with parameters
}
}
I solved the third method (divide it in two beans) changing the async method's access modifier to public:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() { // switched to public
...
}
}
In my case, it was easier to remove the #Async annotation and use the taskExecutor directly to submit my task:
Before
#Async("taskExecutor")
private Future<U> executerEnAsync(
final T pInput) {
final U resultat = this.appelerBS(pInput);
return new AsyncResult<U>(resultat);
}
After
#Autowired
private AsyncTaskExecutor taskExecutor;
private Future<U> executerEnAsync(
final T pInput) {
final Future<U> future = taskExecutor.submit(new Callable<U>() {
#Override
public U call() {
final U resultat = appelerBS(pInput);
return resultat;
}
});
return future;
}

Categories