Autowiring in concrete classes of an abstract class than implements an interface - java

I am using Spring boot and I have the following Task model
public class Task {
private String name;
private TaskType type; // ManualTask, AutomatedTask
private boolean completed;
//....other fields
//getters and setters
}
A controller
#Controller
#RequestMapping("/api/task")
public class TaskController {
#Autowired
private TaskService taskService;
#GetMapping("/{taskId}/handle")
public String handle(Model model, #PathVariable("taskId") Long taskId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
try {
Task task = taskService.handle(taskId);
model.addAttribute("task", task);
} catch (Exception e) {
return "errorpage";
}
return "successpage";
}
}
I have an interface
public interface TaskService {
Task findById(Long taskId);
Task handleTask(Long taskId) throws ClassNotFoundException, InstantiationException, IllegalAccessException;
}
An abstract class implements the interface:
#Service
public abstract class TaskServiceImpl implements TaskService {
#Autowired
private TaskRepository taskRepository;
private static final String PATH_OF_CLASS = "com.task.service.impl";
protected abstract Task doTypeSpecificTask(Long taskId);
#Override
public Task findById(Long taskId) {
return taskRepository.findById(taskId).get();
}
#Override
public Task handleTask(Long taskId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Task task = findById(taskId);
TaskServiceImpl service = getHandlerService(task);
return service.doTypeSpecificTask(taskId);
}
private TaskServiceImpl getHandlerService(Task task) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String serviceClassName = PATH_OF_CLASS.concat(".").concat(task.getTaskType().getName()).concat("Service");
Class<?> serviceClass = Class.forName(serviceClassName);
if (!TaskServiceImpl.class.isAssignableFrom(serviceClass)) {
throw new RuntimeException("Service class " + serviceClassName + " did not implements " + TaskServiceImpl.class.getName());
}
Object serviceObject = serviceClass.newInstance();
TaskServiceImpl service = (TaskServiceImpl) serviceObject;
return service;
}
}
And concrete services that extend the abstract class
#Service
#Primary
public class ManualTaskService extends TaskServiceImpl {
#Autowired
private TaskRepository taskRepository;
#Autowired
private ManualTaskHandlerService manualTaskHandlerService;
#Override
protected Task doTypeSpecificTask(Long taskId) {
Task task = findById(taskId);
manualTaskHandlerService.handleManualTask(task);
task.setCompleted(true);
return taskRepository.save(task);
}
}
#Service
public class AutomatedTaskService extends TaskServiceImpl {
#Autowired
private TaskRepository taskRepository;
#Autowired
private AutomatedTaskHandlerService automatedTaskHandlerService;
#Override
protected Task doTypeSpecificTask(Long taskId) {
Task task = findById(taskId);
automatedTaskHandlerService.handleAutomatedTask(task);
task.setCompleted(true);
return taskRepository.save(task);
}
}
public interface TaskRepository extends JpaRepository<Task, Long> {
}
The ManualTaskService or AutomatedTaskService is selected dynamically based on the type of task on runtime.
Now, without the #Primary, I get the following error:
Field taskService in com.test.controller.TaskController required a single bean, but 2 were found:
- manualTaskService
- automatedTaskService
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
With #Primary set in ManualTaskService, doTypeSpecificTask in ManualTaskService works but in AutomatedTaskService it fails because of automatedTaskHandlerService.handleAutomatedTask(task). Also calls to taskRepository from AutomatedTaskService fail.
I've tried using #Qualifier as well as defining all #Autowired in the abstract class as protected but nothing works. What am I doing wrong?

You should have different names to each Qualifier:
#Autowired
#Qualifier("manualTaskService")
private TaskServiceImpl manualTaskService;
#Autowired
#Qualifier("automatedTaskService")
private TaskServiceImpl automatedTaskService;
Which is defined in services:
#Service("manualTaskService")
public class ManualTaskService extends TaskServiceImpl {
#Service("automatedTaskService")
public class AutomatedTaskService extends TaskServiceImpl {

I solved the issue by using the factory pattern as mentioned in this link (thanks to #user7294900 for providing the link)
I completely removed the abstract class TaskServiceImpl. Instead I created two new interfaces ManualTaskService and AutomatedTaskService both extending TaskService interface
public interface ManualTaskService extends TaskService {
}
public interface AutomatedTaskService extends TaskService {
}
Then I created a TaskServiceFactory
#Component
public class TaskServiceFactory {
#Autowired
private ManualTaskService manualTaskService;
#Autowired
private AutomatedTaskService automatedTaskService;
public TaskService getService(TaskType type) throws Exception {
switch (type) {
case MANUAL_TASK:
return manualTaskService;
case AUTOMATED_TASK:
return automatedTaskService;
default:
throw new Exception("Unrecognized task type");
}
}
}
Next I created implementations for both ManualTaskService and AutomatedTaskService
#Service
public class ManualTaskServiceImpl implements ManualTaskService {
#Autowired
private TaskRepository taskRepository;
#Autowired
private ManualTaskHandlerService manualTaskHandlerService;
#Override
public Task findById(Long taskId) {
return taskRepository.findById(taskId).get();
}
#Override
public Task handleTask(Long taskId) throws Exception {
Task task = findById(taskId);
manualTaskHandlerService.handleManualTask(task);
task.setCompleted(true);
return taskRepository.save(task);
}
}
#Service
public class AutomatedTaskServiceImpl implements AutomatedTaskService {
#Autowired
private TaskRepository taskRepository;
#Autowired
private AutomatedTaskHandlerService automatedTaskHandlerService;
#Override
public Task findById(Long taskId) {
return taskRepository.findById(taskId).get();
}
#Override
public Task handleTask(Long taskId) throws Exception {
Task task = findById(taskId);
automatedTaskHandlerService.handleAutomatedTask(task);
task.setCompleted(true);
return taskRepository.save(task);
}
}
Finally I updated the controller to get the task type from the user and then use the TaskServiceFactory to get the correct service instance based on the type
#Controller
#RequestMapping("/api/task")
public class TaskController {
#Autowired
private TaskServiceFactory taskServiceFactory;
#PostMapping("/{taskId}/handle")
public String handle(Model model, #PathVariable("taskId") Long taskId, HttpServletRequest request) throws Exception {
try {
TaskType type = TaskType.valueOf(request.getParameter("type"));
Task task = taskServiceFactory.getService(type).handleTask(taskId, request);
model.addAttribute("task", task);
} catch (Exception e) {
return "errorpage";
}
return "successpage";
}
}

Related

Google Guice with Junit and Mockito

how can i inject EntityManager(jpa) with Mockito?
I wanna bind Mockito.spy(UserService.class) to guice injector.
but UserService.class has EntityManager for query execution.
When installing 'JunitServiceModule' in guice injector, EntityManager is not found.
See below for error details
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Error in custom provider, java.lang.NullPointerException
while locating com.google.inject.persist.jpa.JpaPersistService
while locating javax.persistence.EntityManager
My code is below.
(The test code just made the error situation similar.)
(actually 'EntityManager' is located in UserRepository... It's just example!)
(#Transactional is belong to guice)
public class UserServiceTest {
#Inject
private UserService userService;
#Before
public void setUp() {
Injector injector = new TestBuilder().init();
injector.initMembers(this);
Mockito.doReturn(10).when(userService).getEntityCount(UserEntity.class);
}
#Test
public void test() {
assertEquals(10, userService.getEntityCount(UserEntity.class));
}
}
public class TestBuilder {
public TestBuilder() {
}
public Injector init() {
Injector injector = Guice.createInjector(
new TestDBInjectModule("test"),
new JunitServiceModule()
);
}
}
public class TestDBInjectModule extends AbstractModule {
private String unitName;
public TestDBInjectModule(String unitName) {
this.unitName = unitName;
}
#Override
protected void configure() {
install(new JpaPersistModule(unitName));
bind(JpaInitializer.class).asEagerSingleton();
}
#Singleton
private static class JpaInitializer {
#Inject
public JpaInitializer(final PersistService persistService) {
persistService.start();
}
}
}
public class JunitServiceModule extends AbstractModule {
#Override
protected void configure() {
bind(UserService.class).toInstance(Mockito.spy(UserService.class));
}
}
public class UserService {
#Inject
private EntityManager entityManager;
public UserService {} // <-- throw NullPointerException!!! since EntityManager
#Transactional
public void addUser(User user) {
return entityManager.persist(user);
}
public Number getCount() {
return entityManager.createQuery("select count(*) from user", Number.class).getSingleResult();
}
}

Autowired object populated using ThreadLocalTargetSource does not populate for every class

My Spring Boot application implements the TenantStore example for storing data in ThreadLocalTargetSource detailed in this link
#Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
The working example allows for the TenantStore object to be set and injected by the Spring Framework. My version of the TenantFilter class described in that article sets the properties of the TenantStore object whenever a Servlet request is made
#Autowired
private TenantStore tenantStore;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
String token = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
if (token != null) {
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (oAuth2AccessToken.getAdditionalInformation() != null) {
String tenantName = (String) oAuth2AccessToken.getAdditionalInformation().get("tenant");
storeTenantInThread(tenantName);
}
}
}
chain.doFilter(request, response);
} catch (ResourceNotFoundException e) {
log.error(e.getMessage());
} finally {
clearTenant();
}
}
private void storeTenantInThread(String tenantName) {
tenantStore.setName(tenantName);
}
private void clearTenant() {
tenantStore.clear();
}
I then have a number of services where TenantStore is autowired and in each of these services the TenantStore contains the information that was populated in the doFilter() method. Except for one class. For some reason the properties of the TenantStore in this class are still null. The name of the class affected is MyCacheService and the architecture is as follows:
#RestController
#RequestMapping("/here")
public class MyController {
#Autowired
private MyService myService
#GetMapping
public ResponseEntity myGetMethod(#RequestParam("text") String text) {
myService.myMethod(text);
return new ResponseEntity(Http.OK);
}
}
#Service
public class MyService {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyOtherService myOtherService;
public void myMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
myOtherService.myOtherMethod(text);
}
}
#Service
public class MyOtherService {
#Autowired
private TenantStore tenantStore;
#Autowired
private Map<String, MyComponent> myComponents;
public void myOtherMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
MyComponent useThisComponent = myComponents.get("componentName");
useThisComponent.myComponentMethod(text);
}
}
#Component("componentName")
public class MyComponent {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyCacheService myCacheService;
public void myComponentMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
entityAliasCacheService.myCacheMethod(String text);
}
}
#Service
public class MyCacheService {
#Autowired
private TenantStore tenantStore;
public void myCacheMethod(String text) {
System.out.println(tenantStore.getName()); //DOES NOT WORK - tenantStore object is not null but the name property is
}
}
From what I can guess, for some reason the TenantStore in MyCacheService is being populated in a different thread, though I've no idea why.
I noticed similar behaviour. I fixed the issue by adding a bean dependancy
#Service
#DependsOn("proxiedThreadLocalTargetSource") // asks Spring to first load proxy bean
public class MyCacheService {
where proxiedThreadLocalTargetSource bean is defined like in the OP's example -
#Primary
#Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
So, by adding the dependancy, Spring knows that it should load MyCacheService bean after the proxiedThreadLocalTargetSource. Without this dependancy, I noticed that TenantStore got injected instead of the proxy bean.
Getting instance of TenantStore from org.springframework.context.ApplicationContext
First implement ApplicationContextAware like as below
#Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext context() {
return context;
}
}
And your MyCacheService Will be like this:
public class MyCacheService {
public void myCacheMethod(String text) {
TenantStore tenantStore = ApplicationContextUtil.context().getBean(TenantStore.class);
System.out.println(tenantStore.getName());
}
}

Autowired annotated bean gets null in Listener class

I get null logservice in after method.
I used #Component, #ComponentScan, #Service and #Configuration, but none worked.
Here is my Listener class
#Component
public class LogOrder {
private static LogService logService;
#Autowired
public void setLogService(LogService logService) {
LogOrder.logService = logService;
}
#PostUpdate
private void after(Order order) {
logService.log("Logged");
}
}
Here is my Entity class
#EntityListeners(LogOrder.class)
#Entity
public class Order{
}
Here is LogService interface and its implementation
public interface LogService {
void send("");
}
#Service(value = "logService")
public class LogServiceImpl implements LogService {
private final SomeOtherService someOtherService;
#Autowired
public LogServiceImpl(SomeOtherService someOtherService) {
this.someOtherService = someOtherService;
}
public void send(String someText) {
SomeTemplate someTemplate = someOtherService.someTemplate();
someTemplate.convertAndSend(someText);
}
}
What can I do to get LogService not null.
You are trying to autowire a static field.
private static LogService logService;
Remove static from the attribute.

Spring Bean Factory Using Class Name

I have an interface/implementation like so:
public interface Processor {
void processMessage(Message m);
}
#Component
public class FooAProcessor implements Processor {
private FooA fooA;
public FooAProcessor(FooA fooA) {
this.fooA = fooA;
}
#Override
public void processMessage(Message m) {
//do stuff
}
}
#Component
public class FooBProcessor implements Processor {
private FooA fooA;
public FooBProcessor(FooA fooA) {
this.fooA = fooA;
}
#Override
public void processMessage(Message m) {
//do stuff
}
}
The FooA bean is simple, like this:
#Component
public class FooA {
//stuff
}
And the message class:
public class Message {
private Class clazz;
}
I am pulling messages off a queue. I need to provide a concrete Processor to handle the different types of messages appropriately. Here's the message receiver:
public class MessageReceiver {
public void handleMessage(Message m) {
Processor processor = //get concrete implementation from Message clazz
processor.processMessage(m);
}
}
How exactly can I use the class name/object to define a concrete implementation of Processor?
My first thought was to develop some sort of a factory that takes in a class and provides the concrete implementation. Something like this:
#Component
public class ProcessorFactory {
private FooAProcessor fooAProcessor;
private FooBProcessor fooBProcessor;
public ProcessorFactory(FooAProcessor fooAProcessor, FooBProcessor fooBProcessor) {
this.fooAProcessor = fooAProcessor;
this.fooBProcessor = fooBProcessor;
}
public Processor getFactory(Class clazz) {
if(clazz.isAssignableFrom(FooAProcessor.class)) {
return fooAProcessor;
}
}
}
Or to use the application context like this:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getBean(clazz);
Is this the best way to go about this problem? Is there a better practice?
You can inject ApplicationContext into your factory and get beans from there:
#Component
public class Factory {
#Autowired ApplicationContext applicationContext;
public Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
}
Or you can put your processors into map and get them from it:
#Component
public class ProcessorFactory {
private final Processor fooAProcessor;
private final Processor fooBProcessor;
private final Map<Class<T extends Processor>, Processor> beanMap;
public ProcessorFactory (Processor fooAProcessor, Processor fooBProcessor) {
this.fooAProcessor = fooAProcessor;
this.fooBProcessor = fooBProcessor;
this.beanMap = new HashMap(2);
}
#PostConstruct
public void init() {
beanMap.put(FooAProcessor.class, fooAProcessor);
beanMap.put(FooBProcessor.class, fooBProcessor);
}
public Processor getProcessor(Class<T extends Processor> clazz) {
return beanMap.get(clazz);
}
}
I recommend to not rely on class when working with spring context but use beanNames instead.

Rollback All DB Commits in Nested Transactions in Spring

I have the following service layout of nested transactions:
#Component
public class Main implements RPCInterface {
#Autowired
private ServiceA serviceA;
#Autowired
private ServiceB serviceB;
#Autowired
private ServiceC serviceC;
#Override
#Transactional (value="txManager", propagation=Propagation.REQUIRED, rollbackFor={ExceptionOne.class, ExceptionTwo.class, ExceptionThree.class})
public void outerMethod() throws ExceptionO {
try {
serviceA.methodA();
serviceB.methodB();
serviceC.methodC();
} catch (ExceptionOne e) {
throw new ExceptionO(e.getMessage, e);
} catch (ExceptionTwo e) {
throw new ExceptionO(e.getMessage, e);
} catch (ExceptionThree e) {
throw new ExceptionO(e.getMessage, e);
}
}
}
#Service
public class ServiceA implements SA {
#Autowired
private ServiceA1 serviceA1;
#Override
public void methodA() {
serviceA1.methodA1();
}
}
#Service
public class ServiceA1 implements SA1 {
#Autowired
private ServiceDBTable1 serviceDBTable1;
#Autowired
private ServiceA1A serviceA1A;
#Transactional
#Override
public void methodA1() {
serviceDBTable4.callToMapper4();
serviceA1A.methodA1A();
}
}
#Service
#Transactional (value="txManager", propagation=Propagation.REQUIRED)
public class ServiceA1A implements SA1A {
#Autowired
private ServiceDBTable2 serviceDBTable2;
#Override
public void methodA1A() {
serviceDBTable1.callToMapper1();
}
}
#Service
public class ServiceB implements SB {
#Autowired
private ServiceDBTable3 serviceDBTable3;
#Override
#Transactional (value="txManager", propagation=Propagation.REQUIRED)
public void methodB() {
serviceDBTable3.callToMapper3();
}
}
#Service
public class ServiceC implements SC {
#Override
public void methodC() throws ExceptionThree {
// code that throws ExceptionThree
}
}
I need to make all the DB calls within ServiceA and ServiceB nested calls to rollback when ServiceC#methodC() throws an exception (or any of them for that matter that throws an exception -- ServiceA or ServiceB).
I tried to make Main#outerMethod transactional with REQUIRED propagation, but it seems like the database commits are not being rolled back. I have even specified the specific classes with rollbackFor but the commits persist. Does anyone know how to fix this issue?
What I did to make it work was to migrate ServiceB.methodB() and ServiceC.methodC() calls to ServiceA.methodA(), and make methodA() #Transactional while throwing all my exceptions from methodA() and rollback based on those three exceptions (my logic actually allowed me to do that):
#Component
public class Main implements RPCInterface {
#Autowired
private ServiceA serviceA;
#Override
public void outerMethod() throws ExceptionO {
try {
serviceA.methodA();
} catch (ExceptionOne e) {
throw new ExceptionO(e.getMessage, e);
} catch (ExceptionTwo e) {
throw new ExceptionO(e.getMessage, e);
} catch (ExceptionThree e) {
throw new ExceptionO(e.getMessage, e);
}
}
}
#Service
public class ServiceA implements SA {
#Autowired
private ServiceA1 serviceA1;
#Autowired
private ServiceB serviceB;
#Autowired
private ServiceC serviceC;
#Override
#Transactional (value="txManager", propagation=Propagation.REQUIRED, rollbackFor={ExceptionOne.class, ExceptionTwo.class, ExceptionThree.class})
public void methodA() throw ExceptionOne, ExceptionTwo, ExceptionThree {
serviceA1.methodA1();
serviceB.methodB();
serviceC.methodC();
}
}
#Service
public class ServiceA1 implements SA1 {
#Autowired
private ServiceDBTable1 serviceDBTable1;
#Autowired
private ServiceA1A serviceA1A;
#Transactional
#Override
public void methodA1() {
serviceDBTable4.callToMapper4();
serviceA1A.methodA1A();
}
}
#Service
#Transactional (value="txManager", propagation=Propagation.REQUIRED)
public class ServiceA1A implements SA1A {
#Autowired
private ServiceDBTable2 serviceDBTable2;
#Override
public void methodA1A() {
serviceDBTable1.callToMapper1();
}
}
#Service
public class ServiceB implements SB {
#Autowired
private ServiceDBTable3 serviceDBTable3;
#Override
#Transactional (value="txManager", propagation=Propagation.REQUIRED)
public void methodB() {
serviceDBTable3.callToMapper3();
}
}
#Service
public class ServiceC implements SC {
#Override
public void methodC() throws ExceptionThree {
// code that throws ExceptionThree
}
}
Since there is no code presented it is hard to know for sure.
However, transactions only work when methods are public. Private methods are not proxied and hence transaction support for them is not there.
Read through Declarative Transations - Spring Docs for more details.
Please post code if you still are struggling for getting better help.

Categories

Resources