I have an interface and two implementations of that interface.
Now on the interface I am adding '#Component' annotation. One of the implementation has a '#primary' annotation which is only getting called up.
I want to call both the implementations when I call the interface's method from the autowired interface bean.
#Component
public interface CustomerPersister {
AbuserDetails setAbuserDetails(AbuserDetails customer);
}
#Primary
#Component
public class CustomerRedisPersisterImpl implements CustomerPersister{
#Autowired
private CustomerManager customerManager;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
if(customerManager.setAbuserDetails
(customer,ATSNamespaces.ABUSERDETAILS)){
return customer;
}else{
return new AbuserDetails();
}
}
#Component
public class MongoDbRepositoryImpl implements CustomerPersister{
#Autowired
MongoTemplate mongoTemplate;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
Query query = new Query(Criteria.where("login").is(customer.getLogin()));
System.out.println("query is:"+query);
Update update = new Update();
update.set("isReturnAbuser", customer.getIsReturnAbuser());
update.set("reasonReturnAbuser", customer.getReasonReturnAbuser());
update.set("isCODThrottled", customer.getIsCODThrottled());
update.set("reasonRTOAbuser", customer.getReasonRTOAbuser());
update.set("isFakeEmail", customer.getIsFakeEmail());
update.set("reasonFakeEmail", customer.getReasonFakeEmail());
update.set("amount",customer.getAmount());
WriteResult result = mongoTemplate.upsert(query, update, AbuserDetails.class);
System.out.println("This is the class that got returned:"+result.getClass());
System.out.println("New design result:"+result);
if(result!=null){
if(result.getN() != 0)
return customer;
else
return null;
}else
return null;
}
someOtherClass
#Autowired
private CustomerPersister customerPersister;
#Override
#RequestMapping(value = "/abuser/details/set", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public AbuserDetails setAbuserDetails(#RequestBody AbuserDetails customer){
return customerPersister.setAbuserDetails(customer);
}
You can tell Spring to autowire all implementations of an interface as a List and then call the method an all implementations.
class SomeClass {
List<CustomerPersister> customerPersisters;
#Autowired
SomeClass(List<CustomerPersister> customerPersisters) {
this.customerPersisters = customerPersisters;
}
public void setAbuserDetails(#RequestBody AbuserDetails customer) {
for (CustomerPersister customerPersister: customerPersisters) {
customerPersister.setAbuserDetails(customer);
}
}
}
Of course this will not allow you to return the result of customerPersister.setAbuserDetails(), because you can't return a single value from multiple persister calls. You either have to write some code in SomeClass that will determine which object should be returned or you could return a list of the results from all persisters. Or you have to redesign your interface to match the requirements.
Related
I have the following REST API controller class.
The endpoint retrieves a paged list of customers.
#RestController
public class CustomerController {
#Autowired
private CustomerRepository customerRepository;
public void setCustomerRepositoryMock(CustomerRepository mockedCustomerRepository) {
this.customerRepository = mockedCustomerRepository;
}
#GetMapping(value="/customers", produces = "application/json")
public ResponseEntity<Page<Customer>> customersList(
#RequestParam(value="pageNumber") int pageNumber,
#RequestParam(value="pageSize") int pageSize){
Pageable customersPageable = PageRequest.of(pageNumber, pageSize);
Page<Customer> customersList = customerRepository.findAll(customersPageable);
return new ResponseEntity<Page<Customer>>(customersList, HttpStatus.OK);
}
}
Now I want to create a mocked unit test for that method.
This is what I have.
public class CustomerControllerTest {
private CustomerRepository mockedCustomerRepository;
private CustomerController customerController;
private Customer customer;
private Page<Customer> customersList;
Pageable customersPageable;
#BeforeEach
void setup() {
customer = new Customer();
customer.setName("Pete");
customer.setAge(35);
customer.setEmail("pete#test.com");
List<Customer> customersListTest = new ArrayList<Customer>();
customersListTest.add(customer);
customersList = new PageImpl<>(customersListTest);
mockedCustomerRepository = mock(CustomerRepository.class);
customerController = new CustomerController();
customerController.setCustomerRepositoryMock(mockedCustomerRepository);
}
#Test
void testListCustomers() {
when(mockedCustomerRepository.findAll(customersPageable)).thenReturn(customersList);
ResponseEntity<Page<Customer>> respPageCustomers = customerController.customersList(0, 3);
assertTrue(respPageCustomers.getBody() != null);
}
}
The problem is that when the following line is executed (in the API method), CustomerList is null.
Page<Customer> customersList = customerRepository.findAll(customersPageable);
But it should have content, because the content was added in the setup method of the test class and then in the following line of the test method.
when(mockedCustomerRepository.findAll(customersPageable)).thenReturn(customersList);
Replace
when(mockedCustomerRepository.findAll(customersPageable)).thenReturn(customersList);
with
when(mockedCustomerRepository.findAll(any(Pageable.class))).thenReturn(customersList);
What you currently have - is that mocked repository will return result only when it receives exact customersPageable (which is null). Using any() will return expected result if any object of mentioned class will be passed as parameter
I'm working on updating some legacy code for a jboss application and I've run into a bit of a jam. I have two ejb beans, PersonBean and ClientBean. ClientBean is responsible for handling client specific services. ClientBean gets injected with an instance of PersonBean which it uses as a delegate to pass along requests to server side services. The problem I'm running into is that both beans also implement a LoginService interface which also needs to get injected. What I want is for an instance of PersonBean to get injected into ClientBean as the loginService but I'm ending up with ClientBean being injected into itself.
What do I need to do to correctly define this ejb mapping?
ClientServiceProviderBean:
#Stateless(name = "ClientServiceProvider")
#Local({ ClientServiceProvider.class, LoginService.class })
public class ClientServiceProviderBean implements ClientServiceProvider, LoginService
{
#EJB(name = "personService")
protected PersonService personService;
#EJB(name = "loginService")
protected LoginService loginService;
#Override
public LoginDTO getLoggedInUser()
{
LoginDTO loginDTO = loginService.getLoggedInUser();
return loginDTO;
}
#Override
public Long activateSession(String applicationName, String ipAddress)
{
return personService.activateSession(applicationName, ipAddress);
}
}
PersonServiceBean:
#Stateless(name = "PersonService")
#Local({ PersonService.class })
#Remote({ RemotePersonService.class })
public class PersonServiceBean implements PersonService, RemotePersonService, LoginService
{
#Override
#RolesAllowed({ "authenticated" })
public Long activateSession(String applicationName, String ipAddress)
{
Person p = getCallerAsPerson(entityManager, context.getCallerPrincipal());
SessionActivity sessionActivity = new SessionActivity(p.getId(), applicationName, true, ipAddress);
sessionActivity = save(entityManager, sessionActivity);
return sessionActivity.getId();
}
#Override
#PermitAll
public LoginDTO getLoggedInUser()
{
Principal p = context.getCallerPrincipal();
if (p != null && !"unauthenticated".equals(p.getName()))
{
try
{
Person person = getCallerAsPerson(entityManager, p);
if (person != null)
{
return createLoginDTO(person);
}
}
catch (javax.persistence.NoResultException e)
{
}
}
return null;
}
}
If you want to inject PersonServiceBean as a LoginService in ClientServiceProviderBean you should add the LoginService.class in the #Local anotation of PersonServiceBean and remove it from ClientServiceProviderBean.
I want a factory class that return a service that I can use to do some validations. I implemented this class
public class EventUpdateValidatorFactory {
public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
if (SECOND_APPROVAL.equals(eventStatus)) {
return new EventSecondApprovalValidator();
} else if (APPROVED.equals(eventStatus)) {
return new EventApprovedValidator();
} else if (ACCOUNTING_HQ.equals(eventStatus)) {
return new EventAccountingHqValidator();
}
throw new IllegalArgumentException("Unknown status");
}
}
The interface EventUpdateValidatorStrategy is this
public interface EventUpdateValidatorStrategy {
default <T extends EventUpdateValidatorStrategy> void validate(User user, EventMasterData masterData, Event event, List<EventExternalSystemExpenseSave> expenses,
List<EventExternalSystemSpeakerSave> speakers, long eventId) {
this.validateMasterData(masterData, event);
this.validateSpeakers(speakers, eventId);
this.validateExpenses(expenses, eventId);
this.doUpdate(user, masterData, expenses, speakers, eventId);
}
void validateMasterData(EventMasterData masterData, Event event);
void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId);
void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId);
void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId);
}
The EventSecondApprovalValidator is this
#Service
#Transactional
public class EventSecondApprovalValidator implements EventUpdateValidatorStrategy {
#Autowired
private EventService eventService;
#Autowired
private ContextDateService contextDateService;
#Autowired
private EventExpenseService eventExpenseService;
#Autowired
private EventExternalSystemDAO eventExternalSystemDAO;
#Override
public void validateMasterData(LocalEventMasterData masterData, Event event) {
// some logic
}
#Override
public void validateExpenses(List<EventExternalSystemExpenseSave> expenses, long eventId) {
// some logic
}
#Override
public void validateSpeakers(List<EventExternalSystemSpeakerSave> speakers, long eventId) {
// some logic
}
#Override
public void doUpdate(User user, EventMasterData masterData, List<EventExternalSystemExpenseSave> expenses, List<EventExternalSystemSpeakerSave> speakers, long eventId) {
ofNullable(expenses).ifPresent(expensesToSave -> expensesToSave.forEach(expense -> this.eventExternalSystemDAO.updateExpense(user, expense)));
this.eventExternalSystemDAO.updateEvent(user, masterData, eventId);
}
}
The other EventApprovedValidator and EventAccountingHqValidator implementations are similar.
From main code I do this call
final EventUpdateValidatorStrategy validator = EventUpdateValidatorFactory.getValidator(event.getStatus());
validator.validate(user, eventSave.getMasterData(), event, eventSave.getExpenses(), eventSave.getSpeakers(), eventID);
and the result is that when I enter inside a EventSecondApprovalValidator all the autowired services are null and, obviously, I receive a NPE the first time that I use one of that service.
How I correctly use the factory to return the service that I need based on EEventStatus?
In EventUpdateValidatorFactory.getValidator(EEventStatus) method, you need to return the EventSecondApprovalValidator bean from context, instead of creating a new instance using new keyword.
The class EventSecondApprovalValidator is #Service annotated (and assuming there is only one of this type), an instance of this type will be added to ApplicationContext by Spring with all dependencies injected. So, just fetch it from context and use it.
One quick way to do this is as follows:
public EventUpdateValidatorStrategy getValidator(ApplicationContext context,
EEventStatus eventStatus) {
if (SECOND_APPROVAL.equals(eventStatus)) {
return context.getBean(EventSecondApprovalValidator.class);
} else if (APPROVED.equals(eventStatus)) {
return context.getBean(EventApprovedValidator.class);
} else if (ACCOUNTING_HQ.equals(eventStatus)) {
return context.getBean(EventAccountingHqValidator.class);
}
throw new IllegalArgumentException("Unknown status");
}
You can also #Autowire all validators in EventUpdateValidatorFactory and return the #Autowired instances. This will keep the getValidator method's signature same, but you'll have to make EventUpdateValidatorFactory a #Component-esque class.
#Component
public class EventUpdateValidatorFactory {
#Autowired
EventSecondApprovalValidator a;
#Autowired
EventApprovedValidator b;
#Autowired
EventAccountingHqValidator c;
public EventUpdateValidatorStrategy getValidator(EEventStatus eventStatus) {
if (SECOND_APPROVAL.equals(eventStatus)) {
return a;
} else if (APPROVED.equals(eventStatus)) {
return b;
} else if (ACCOUNTING_HQ.equals(eventStatus)) {
return c;
}
throw new IllegalArgumentException("Unknown status");
}
Creating an object manually you are not letting Spring perform autowiring. Consider managing your services by Spring as well.
#Component
public class MyServiceAdapter implements MyService {
#Autowired
private MyServiceOne myServiceOne;
#Autowired
private MyServiceTwo myServiceTwo;
#Autowired
private MyServiceThree myServiceThree;
#Autowired
private MyServiceDefault myServiceDefault;
public boolean checkStatus(String service) {
service = service.toLowerCase();
if (service.equals("one")) {
return myServiceOne.checkStatus();
} else if (service.equals("two")) {
return myServiceTwo.checkStatus();
} else if (service.equals("three")) {
return myServiceThree.checkStatus();
} else {
return myServiceDefault.checkStatus();
}
}
}
Let's say I have a controller which handles requests such as www.xyz.com/api/<someParam>. This is my controller, and my Service:
#Controller
public class MyController {
#Autowired MyService service;
#RequestMapping(value = "/api/{someParam}", method = RequestMethod.GET)
public String processRequest(
#PathVariable("someParam") String someParam) {
return service.processRequest(someParam);
}
}
#Service
public class MyService {
#Autowired APICaller apiCaller;
public String processRequest(someParam){
SomeObj obj = apiCaller.callApi();
// do something with obj
// return response;
}
}
Based on the param passed in the URL, I need to call some API, do some processing to the API response, and return it. All these APIs have different processing.
Let's say the APICaller interface is like this:
#Service
public interface APICaller {
public SomeObj callAPI();
}
#Service
public class ABC implements APICaller {
#Override
public SomeObj callAPI() {
// calls some REST api, does some processing to response and returns SomeObj
}
}
#Service
public class XYZ implements APICaller {
#Override
public SomeObj callAPI() {
// calls some SOAP api, does some processing to response and returns SomeObj
}
}
So if the param in the url is 'abc', I need to call ABCImpl. And if it is 'xyz', then I need to call XYZImpl. What should I do in the MyService class to instantiate the proper implementation? I might have multiple implementations based on the param, not just these two.
Thanks.
Define a named Map of beans in your configuration class.
#SpringBootApplication
public class Application {
#Bean
ABC abc() {
return new ABC();
}
#Bean
XYZ xyz() {
return new XYZ();
}
#Bean(name="apis")
Map<String, APICaller> apis() {
Map<String, APICaller> map = new HashMap<>();
map.put("xmz", xyz());
map.put("abc", abc());
return map;
}
}
Then inject it as follows:
#Service
public class MyService {
#Resource(name="apis")
Map<String, APICaller> apis;
public String processRequest(String param){
apis.get(param).callApi();
// do required null checks before calling callApi()
// do something with obj
// return response;
}
}
Update
As per you comment if you still wanted the dependencies of APICaller be autowired, this is how to to do it with #Bean
#Bean
ABC abc(DependancyBean1 bean2) {
return new ABC(bean1);
}
#Bean
XYZ xyz(DependancyBean2 bean2) {
return new XYZ(bean2);
}
I am working with Spring MVC application.
I want to create new event:
fill data at page > controller handle this info > saving new event to DB
I have already created DAO layer and repository layer. But at some places, it behaves very strangely. I have created Java configuration for all repositories in the same way.
Here is Repositories Configuration snippet:
#Configuration
#ComponentScan({ "net.lelyak.edu.repository", "net.lelyak.edu.service" })
#Import({ DatabaseDAOConfiguration.class })
public class ApplicationConfiguration {
#Autowired
private UserDAO userDAO;
#Autowired
private EventDAO eventDAO;
#Autowired
private TicketDAO ticketDAO;
#Autowired
private AuditoriumDAO auditoriumDAO;
#Bean
public AuditoriumRepository auditoriumRepository() {
AuditoriumRepository auditoriumRepository = new AuditoriumRepository();
auditoriumRepository.setDao(auditoriumDAO);
return auditoriumRepository;
}
#Bean
public EventRepository eventRepository() {
EventRepository eventRepository = new EventRepository();
eventRepository.setDao(eventDAO);
return eventRepository;
}
I have faced strange behavior at the controller level.
Here is controller code snippet:
#Controller
#RequestMapping("events")
public class EventsController {
#Autowired
private AuditoriumRepository auditoriumRepository;
#Autowired
private EventRepository eventRepository;
#RequestMapping(path = "add", method = RequestMethod.POST)
public String addEvent(#RequestParam Map<String, String> allRequestParams) {
Event newEvent = new Event();
// get auditorium id from request
String auditoryIdString = allRequestParams.get("auditorium");
Long auditoryId = Long.parseLong(auditoryIdString);
Auditorium auditorium = auditoriumRepository.getById(auditoryId);
newEvent.setAuditorium(auditorium);
AuditoriumRepository is auto-wired fine.
Here is snippet from debug view:
but EventRepository isn't:
Configuration is the same for both. One repository is auto-wired fine, second fails. I am newly at Spring. I can't get a clue why does this happen?
Here is snippet of code from EventRepository:
public class EventRepository extends BaseRepository<Event, EventDAO> {
#Autowired
private AuditoriumRepository auditoriumRepository;
#Override
public int put(Event entity) {
auditoriumRepository.put(entity.getAuditorium());
return super.put(entity);
}
AuditoriumRepository code snippet:
public class AuditoriumRepository extends BaseRepository<Auditorium, AuditoriumDAO> {
#Override
public Auditorium preSave(Auditorium entity) {
return entity;
}
For saving new event to DB I have to use exactly EventRepository. It fails, of course, with following stack trace:
java.lang.NullPointerException
at net.lelyak.edu.repository.BaseRepository.put(BaseRepository.java:23)
at net.lelyak.edu.repository.EventRepository.put(EventRepository.java:20)
at net.lelyak.edu.repository.EventRepository$$FastClassBySpringCGLIB$$5de8d2a5.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
I am using spring 4.2.4.RELEASE on Windows 10.
UPDATE:
BaseRepository code snippet:
public abstract class BaseRepository<T extends BaseEntity, E extends BaseDAO<T>> {
private E dao;
public E getDao() {
return dao;
}
public void setDao(E dao) {
this.dao = dao;
}
public int put(T entity) {
return dao.save(preSave(entity));
}
I can't understand why with the same configuration and repository structure. One instance is auto-wired by Spring fine, but the second one fails. How to find the root of this problem? And some solution.
UPDATE 2:
I have tried recommended solution, and added next setter to EventRepository:
public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository) {
System.out.println("EventRepository.setAuditoriumRepository");
this.auditoriumRepository = auditoriumRepository;
}
As log message show this setter is executed. But keep failing for the same reason.
How to solve this issue?
i saw your code and the problem is not related to Spring and autowiring process. The problem is in your save method in BaseDAO class:
#Override
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return null;
}
if you change the metod to return newly inserted/updated entity id it will work fine.
try to hardcode it just for test :
#Override
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return 1;
}
and the problem is raised when you return null from save method then you are doing assignment in the EventsController class which is:
int eventId = eventRepository.put(newEvent); //this is null and throws NullPointerException because you are trying to assign null to primitive variable.
Try to inject the dependencies yourself in the Java #Configuration class. Update your EventRepository so that it has a setter method for the AuditoriumRepository dependency as follows
public class EventRepository extends BaseRepository<Event, EventDAO> {
private AuditoriumRepository auditoriumRepository;
#Override
public int put(Event entity) {
auditoriumRepository.put(entity.getAuditorium());
return super.put(entity);
}
public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository){
this.auditoriumRepository=auditoriumRepository;
}
}
In your ApplicationConfiguration update as follow:
#Bean
public EventRepository eventRepository() {
EventRepository eventRepository = new EventRepository();
eventRepository.setDao(eventDAO);
eventRepository.setAuditoriumRepository(auditoriumRepository());
return eventRepository;
}
The null pointer exception is because auditoriumRepository is not set in the eventRepository bean.
So while creating the EventRepository bean in ApplicationConfiguration.java, we need to set auditoriumRepository as well.
For this I have updated ApplicationConfiguration as below:
public EventRepository eventRepository() {
EventRepository eventRepository = new EventRepository();
eventRepository.setDao(eventDAO);
eventRepository.setAuditoriumRepository(auditoriumRepository());
return eventRepository;
}
and added setter for auditoriumRepository in the EventRepository class
public AuditoriumRepository getAuditoriumRepository() {
return auditoriumRepository;
}
public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository) {
this.auditoriumRepository = auditoriumRepository;
}
This will ensure that the NPE you are seeing will be resolved.
But subsequently we will still get the NPE at a different point now and i.e., at BaseDAO class. The save method is returning a null object and that is causing this NPE.
To resolve this, I have updated the save method of BaseDAO to return a dummy value of 1. You may want to return an appropriate value as per you application needs.
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return 1;
}
With these changes, we will able to save the event and proceed to the next screen.