Factory Method return Spring service - java

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();
}
}
}

Related

Custom Spring validator not working properly

I'm trying to make artificial CONSTRAINT violation by Spring instead of throwing exception from DB (an expert sad DB-produced errors have high performance cost):
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
#Component
public class AccountValidator implements org.springframework.validation.Validator {
#Autowired
private Validator validator;
private final AccountService accountService;
public AccountValidator(#Qualifier("accountServiceAlias")AccountService accountService) {
this.accountService = accountService;
}
#Override
public boolean supports(Class<?> clazz) {
return AccountRequestDTO.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Set<ConstraintViolation<Object>> validates = validator.validate(target);
for (ConstraintViolation<Object> constraintViolation : validates) {
String propertyPath = constraintViolation.getPropertyPath().toString();
String message = constraintViolation.getMessage();
errors.rejectValue(propertyPath, "", message);
}
AccountRequestDTO account = (AccountRequestDTO) target;
if(accountService.getPhone(account.getPhone()) != null){
errors.rejectValue("phone", "", "Validator in action! This number is already in use.");
}
}
}
However, second part of validate() method never works for reasons I cant understand and always pass a call from controller to be handled in try-catch block throwing exception from DB:
public void saveAccount(AccountRequestDTO accountRequestDTO) throws Exception {
LocalDate birthday = LocalDate.parse(accountRequestDTO.getBirthday());
if (LocalDate.from(birthday).until(LocalDate.now(), ChronoUnit.YEARS) < 18) {
throw new RegistrationException("You must be 18+ to register");
}
Account account = new Account(accountRequestDTO.getName(), accountRequestDTO.getSurname(),
accountRequestDTO.getPhone(), birthday, BCrypt.hashpw
(accountRequestDTO.getPassword(), BCrypt.gensalt(4)));
account.addRole(Role.CLIENT);
try {
accountRepository.save(account);
}
catch (RuntimeException exc) {
throw new PersistenceException("Database exception: this number is already in use.");
}
}
Here's a controller method:
#PostMapping("/confirm")
public String signIn(#ModelAttribute("account") #Valid AccountRequestDTO accountRequestDTO,
BindingResult result, Model model) {
accountValidator.validate(accountRequestDTO, result);
if(result.hasErrors()) {
return "/auth/register";
}
try {
accountService.saveAccount(accountRequestDTO);
}
catch (Exception exc) {
model.addAttribute("message", exc.getMessage());
return "/auth/register";
}
return "/auth/login";
}
At service:
#Transactional(readOnly = true)
public String getPhone(String phone){
return accountRepository.getPhone(phone);
}
JpaRepository query:
#Query("SELECT phone FROM Account accounts WHERE phone=:check")
String getPhone(String check);
Tests are green:
#BeforeAll
static void prepare() {
search = new String("0000000000");
}
#BeforeEach
void set_up() {
account = new Account
("Admin", "Adminov", "0000000000", LocalDate.of(2001, 01, 01), "superadmin");
accountRepository.save(account);
}
#Test
void check_if_phone_presents() {
assertThat(accountRepository.getPhone(search).equals(account.getPhone())).isTrue();
}
#Test
void check_if_phone_not_presents() {
String newPhone = "9999999999";
assertThat(accountRepository.getPhone(newPhone)).isNull();
}
#AfterEach
void tear_down() {
accountRepository.deleteAll();
account = null;
}
#AfterAll
static void clear() {
search = null;
}
You need to register your validator.
After we've defined the validator, we need to map it to a specific
event which is generated after the request is accepted.
This can be done in three ways:
Add Component annotation with name “beforeCreateAccountValidator“.
Spring Boot will recognize prefix beforeCreate which determines the
event we want to catch, and it will also recognize WebsiteUser class
from Component name.
#Component("beforeCreateAccountValidator")
public class AccountValidator implements Validator {
...
}
Create Bean in Application Context with #Bean annotation:
#Bean
public AccountValidator beforeCreateAccountValidator () {
return new AccountValidator ();
}
Manual registration:
#SpringBootApplication
public class SpringDataRestApplication implements RepositoryRestConfigurer {
public static void main(String[] args) {
SpringApplication.run(SpringDataRestApplication.class, args);
}
#Override
public void configureValidatingRepositoryEventListener(
ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", new AccountValidator ());
}
}

Why my Mono should be subscribed while another case it runs correctly

on this code, orderRepository.save(orders) is not working until i subscribe it. But i have called subscribe from my RestController.
#Service
public class CompleteOrderCommandImpl implements CompleteOrderCommand {
#Autowired
private OrderRepository orderRepository;
#Override
public Mono<Boolean> execute(CancelOrderCommandRequest request) {
return orderRepository.findById(request.getOrderId())
.flatMap(this::changeToCancel)
.map(order -> true);
}
private Mono<Orders> changeToCancel(Orders orders) {
orders.setStatus(StatusEnum.COMPLETED.toString());
return orderRepository.save(orders);
}
}
i have no idea why this happens while another case like this. restaurantRepository.save(restaurant) run without subscribe. because it is (subscribe) called in RestController.
#Service
public class ApprovePendingRestaurantCommandImpl implements ApprovePendingRestaurantCommand {
#Autowired
private RestaurantRepository restaurantRepository;
#Override
public Mono<RestaurantResponse> execute(ApprovePendingRestaurantCommandRequest request) {
return restaurantRepository.findById(request.getRestaurantId())
.flatMap(this::changeRestaurantStatus)
.map(this::constructResponse);
}
private Mono<Restaurant> changeRestaurantStatus(Restaurant restaurant) {
restaurant.setStatus(true);
return restaurantRepository.save(restaurant);
}
private RestaurantResponse constructResponse(Restaurant restaurant) {
RestaurantResponse response = new RestaurantResponse();
BeanUtils.copyProperties(restaurant, response);
return response;
}
}
this is my controller
#PostMapping("/api/merchant/orders/{orderId}/complete")
public Mono<Response<Boolean>> completeOrder(Authentication authentication, #PathVariable String orderId) {
return commandExecutor.execute(CompleteOrderCommand.class, constructCancelOrderCommandRequest(authentication, orderId))
.map(ResponseHelper::ok)
.subscribeOn(Schedulers.elastic());
}
#PostMapping("/api/admin/restaurant/{restaurantId}")
public Mono<Response<RestaurantResponse>> approvePendingRestaurant(#PathVariable String restaurantId) {
return commandExecutor.execute(ApprovePendingRestaurantCommandImpl.class, constructApprovePendingRestaurantCommandRequest(restaurantId))
.map(ResponseHelper::ok)
.subscribeOn(Schedulers.elastic());
}

Calling all implementation of an interface within one call

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.

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;
}

"must implement the inherited abstract method"

I had in my class AbstractJpaDao method
#Override
public EntityManager getEntityManager() {
return em;
}
now it isn't in use and I wanted to delete it, but i get error:
The type JpaAclIdentityDao must implement the inherited abstract method IJpaDao.getEntityManager() in class JpaAclIdentityDao.
is that getter necessary? if not how to remove it
my code:
public abstract class AbstractJpaDao implements IJpaDao {
protected final IApplicationConfig config;
protected final EntityManager em;
private final SingletonEventBus eventBus;
public AbstractJpaDao(EntityManager entityManager, IApplicationConfig config, SingletonEventBus eventBus) {
checkArgument(entityManager != null);
checkArgument(config != null);
checkArgument(eventBus != null);
this.em = entityManager;
this.config = config;
this.eventBus = eventBus;
}
protected void saveEntity(IEntity entity) {
boolean isNew = entity.getId() == 0;
em.getTransaction().begin();
try {
em.persist(entity);
em.getTransaction().commit();
if (isNew) {
eventBus.post(new EntityCreatedEvent(entity));
}
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
}
}
#Repository
public class JpaAclIdentityDao extends AbstractJpaDao implements IAclIdentityDao {
public static final String GROUP_NAME_PATTERN = "GROUP_%d";
private static final String GROUP_TEMP_NAME = "TEMP_GROUP_NAME";
#Inject
public JpaAclIdentityDao(EntityManager entityManager, IApplicationConfig config, SingletonEventBus eventBus) {
super(entityManager, config, eventBus);
}
#Override
public AclIdentity findById(Object id) throws EntityNotFoundException {
return em.find(AclIdentity.class, id);
}
#Override
public List<AclIdentity> findAll() {
return findAllByType(AclIdentity.class);
}
#Override
public void delete(AclIdentity entity) {
// TODO Auto-generated method stub
}
#Override
public void save(AclIdentity entity) {
saveEntity(entity);
}
#Override
public AclIdentity createNew(String sid, boolean principal) {
AclIdentity identity = new AclIdentity(sid, principal);
save(identity);
return identity;
}
#Override
public AclIdentity createNew(User entity) {
return createNew(entity.getEmail(), true);
}
#Override
public AclIdentity createNew(Group entity) {
AclIdentity identity = createNew(GROUP_TEMP_NAME, false);
identity.setSid(String.format(GROUP_NAME_PATTERN, identity.getId()));
save(identity);
return identity;
}
}
Yes, you have to implement all methodes which are defined in the implemented interface. The only possible solutions i can think of, is to implement the method and leave it empty, or don't implement the interface.
Or, as ben75 said, just remove the method in the declaration of your interface "IJpaDao" if you don't need it (anywhere).
The method getEntityManager is defined in IJpaDao (or one super interface) that's why you need to provide an implementation of it in your class.
If it is not use at all (i.e. even by some reflection mechanism inside some frameworks you are using), then you can remove it from IJpaDao and you won't be forced to implement it.
If you don't want to use it, then throw an UnsupportedOperationException:
public class JpaAclIdentityDao extends AbstractJpaDao ... { // Or AbstractJpaDao...
// Some Code...
public EntityManager getEntityManager() {
throw new UnsupportedOperationException();
// return null; (This is not needed, due to the exception thrown above. Thanks dedek!)
}
// More Code...
}
Due to OO programing, if a concrete class inherits a class/interface with an abstract method, you must define that method, or make your class abstract and pass it down the line, like you did with AbstractJpaDao.
I am guessing that the Interface IJpaDao contains a getEntityManager abstract method.

Categories