java.lang.NullPointerException error on accessing page linked to controller class - spring

I am running a spring mvc application with classes annotated as #controller,#service,#component and for the view layer thymeleaf is used. However on navigating to the path localhost:8080/owners no owner data is displayed.Looks like even though data is getting saved in owner object but ownerservicemap is null for no reason.
Below is the error
Owner data is loaded
2019-06-26 12:39:47.237 INFO 5776 --- [ restartedMain]
.ConditionEvaluationDeltaLoggingListener : Condition evaluation
unchanged
2019-06-26 12:39:50.475 INFO 5776 --- [nio-8080-exec-1]
o.a.c.c.C.[Tomcat-1].[localhost].[/] : Initializing Spring
DispatcherServlet 'dispatcherServlet'
2019-06-26 12:39:50.475 INFO 5776 --- [nio-8080-exec-1]
o.s.web.servlet.DispatcherServlet : Initializing Servlet
'dispatcherServlet'
2019-06-26 12:39:50.483 INFO 5776 --- [nio-8080-exec-1]
o.s.web.servlet.DispatcherServlet : Completed initialization in
8 ms
2019-06-26 12:39:50.507 ERROR 5776 --- [nio-8080-exec-1]
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for
servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is
java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.example.Project.controllers.OwnerController.listOwners(OwnerController.java:33)
~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
~[na:1.8.0_211]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
~[na:1.8.0_211]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_211]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
DataLoader class to load the data
#Component public class DataLoader implements CommandLineRunner{
private final OwnerService ownerService;
public DataLoader()
{
ownerService=new OwnerServiceMap();
}
#Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
Owner owner1=new Owner();
owner1.setId(1L);
owner1.setFirstName("ally");
owner1.setLastName("nilson");
Owner sa1=ownerService.save(owner1);
}}
Below is the ownercontroller class
#RequestMapping("/owners") #Controller public class OwnerController {
private OwnerService ownerservice;
#Autowired
public OwnerController(OwnerService ownerservice)
{
this.ownerservice=ownerservice;
}
#RequestMapping({"","/","/index","/index.html"})
public String listOwners(Model model)
{
model.addAttribute("owner",ownerservice.findAll());// System.out.println(ownerservice.findById(1L).getLastName());
return "owner/index";
}}
OwnerService interface
public interface OwnerService extends CrudService<Owner, Long>{
Owner findByLastName(String lastname);}
AbstractmapService class
public abstract class AbstractMapService<T,ID> {
protected Map<ID,T> map=new HashMap<>();
Set<T> findAll()
{
return new HashSet<>(map.values());
}
T findById(ID id)
{
return map.get(id);
}
T save(ID id,T object)
{
map.put(id, object);
return object;
}
void deleteById(ID id)
{
map.remove(id);
}
void delete(T object)
{
map.entrySet().removeIf(entry->entry.getValue().equals(object));
}}
OwnerServiceMap class
#Service public class OwnerServiceMap extends AbstractMapService<Owner,Long>implements OwnerService{
#Override
public Set<Owner> findAll() {
// TODO Auto-generated method stub
return super.findAll();
}
#Override
public Owner findById(Long id) {
// TODO Auto-generated method stub
return super.findById(id);
}
#Override
public Owner save(Owner object) {
// TODO Auto-generated method stub
return super.save(object.getId(),object);
}
#Override
public void delete(Owner object) {
// TODO Auto-generated method stub
super.delete(object);
}
#Override
public void deleteById(Long id) {
// TODO Auto-generated method stub
super.deleteById(id);
}
#Override
public Owner findByLastName(String lastname) {
// TODO Auto-generated method stub
return null;
}
}

#Service
public class OwnerServiceMap extends AbstractMapService<Owner,Long> implements OwnerService{ ... // }
To be autowired, you must register with the bean.
EDIT1
You did to save another service's Map.
#Component public class DataLoader implements CommandLineRunner{
private final OwnerService ownerService;
public DataLoader()
{
ownerService=new OwnerServiceMap();
}
// ...
And here,
#RequestMapping("/owners") #Controller public class OwnerController {
private OwnerService ownerservice;
#Autowired
public OwnerController(OwnerService ownerservice)
{
this.ownerservice=ownerservice;
}
check this please.
EDIT2
If you register Object to bean, container has that by singleton object.
then, Using #Autowired get singleton object from container.
NOTE : it's different with GOF's singleton.
To summarize, what you use with the new keyword like ownerService=new OwnerServiceMap(); was to create a new instance, not to use the bean instance that you assigned to the Container. So, using the different instances, the above problem occurs.

Related

Spring-Boot: Can not Create Custom Security Expression

I am using Spring boot 2.4.1 and am following the instructions from link to create a Custom Security Expression. Unfortunately, I can't createSecurityExpressionRoot method and my annotation also doesn't work.
I get an error when calling into api
[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression 'hasAccessToCollection('Administrator')'] with root cause
org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method hasAccessToCollection(java.lang.String) cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
My Service
#PreAuthorize("hasAccessToCollection('Administrator')")
public Map getCustomPermission() {
Map<String, String> response = new HashMap<String, String>() {{
put("message", "Successful");
}};
return response;
}
My CustomMethodSecurityExpressionRoot
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public IcodeMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
public boolean hasAccessToCollection(String permission) {
return true;
}
public boolean hasAccessToCollection(String permission, String attribute) {
return true;
}
...
}
My CustomMethodSecurityExpressionHandler
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private AuthenticationTrustResolver trustResolver =
new AuthenticationTrustResolverImpl();
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root =
new CustomMethodSecurityExpressionRoot(authentication);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
Add to your MethodSecurityConfig this:
#Override
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
var expresionAdvice= new ExpressionBasedPreInvocationAdvice();
expresionAdvice.setExpressionHandler(getExpressionHandler());
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expresionAdvice));
decisionVoters.add(new AuthenticatedVoter()); //It is necessary to add this one when we override the default AccessDecisionManager
/*Block N°3 Add the customized RoleVoter Bean if you have one
decisionVoters.add(roleVoter);
*/
return new AffirmativeBased(decisionVoters);
}
https://medium.com/#islamboulila/how-to-create-a-custom-security-expression-method-in-spring-security-e5b6353f062f

Getting NULL POINTER With Dependency Injection in QuartzJobBean extended class

Iam using Quartz scheduler to run a Job, Here i am getting Null pointer Exception in the
QuartzJobBean extended class at Autowired Object i.e at DailyEmailsJob {testService.ts();}
please find the code below
This is the scheduler Class, Here i am calling DailyEmailsJob class
#Component
public interface EmailSchedulers {
void dailyEmailTrigger();
#Service
#Transactional
#Slf4j
public class Impl implements EmailSchedulers{
#Override
public void dailyEmailTrigger() {
JobDetail job = JobBuilder.newJob(DailyEmailsJob.class)
.withIdentity("DailyEmail", "group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("DailyEmailTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 3 * * ?")) //At 03:00:00am every day
.build();
//schedule it
Scheduler scheduler;
try {
scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
log.info("erorrrrrrrrrrrrrrrrrr");
}
}
}
QuartzJobBean EXTENDED CLASS, Here at testService.ts(); i am getting Null pointer as shown in Error.
#Slf4j
#Component
public class DailyEmailsJob extends QuartzJobBean {
#Autowired
private TestService testService;
#Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
log.info("**************** DAILY SCHEDULER STARTED*****************");
testService.ts(); // Getting null pointer
log.info("**************** DAILY SCHEDULER ENDEND*****************");
}
}
This is the service class I am injecting at DailyEmailsJob class
#Component
public interface TestService {
void ts();
#Slf4j
#Service
public class Impl implements TestService{
#Override
public void ts() {
log.info("SERVICE WORKED");
}
}
}
This is the Error:
2018-09-23 03:00:00.040 INFO 6020 --- [eduler_Worker-1] DailyEmailsJob : **************** DAILY SCHEDULER STARTED*****************
2018-09-23 03:00:00.041 ERROR 6020 --- [eduler_Worker-1] org.quartz.core.JobRunShell : Job group1.DailyEmail threw an unhandled Exception:
java.lang.NullPointerException: null
at DailyEmailsJob.executeInternal(DailyEmailsJob.java:42) ~[classes/:na]
at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75) ~[spring-context-support-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.0.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.0.jar:na]
2018-09-23 03:00:00.042 ERROR 6020 --- [eduler_Worker-1] org.quartz.core.ErrorLogger : Job (group1.DailyEmail threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.0.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.0.jar:na]
Caused by: java.lang.NullPointerException: null
at DailyEmailsJob.executeInternal(DailyEmailsJob.java:42) ~[classes/:na]
at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75) ~[spring-context-support-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.0.jar:na]
... 1 common frames omitted
Add the following line to the start of the executeInternal method in DailyEmailsJob class:
#Override
public void executeInternal(final JobExecutionContext context) throws JobExecutionException {
// Adding this autowires everything as needed
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
...
}

exception handler method in #ControllerAdvice are not get called

I'm test my controller using junit5. In test method, EntityNotFoundException is throwed but exception handler is not called.
I have tried declaring ExceptionHandlerExceptionResolver bean with order 1. But it didn't works.
Exception handler which handles EntityNotFoundException:
#ControllerAdvice
#EnableWebMvc
public class AppWideExceptionHandler {
#ExceptionHandler(EntityNotFoundException.class)
public #ResponseBody
String handleEntityNotFoundException(EntityNotFoundException e) {
return "test";
}
...
}
The AppWideExceptionHandler is in youshu.exception package which will be scanned because of #ComponentScan({"youshu.controller", "youshu.service","youshu.exception"}) annotated on WebConfig class.
The processRefundApplication controller method calls RefundService.get(String orderID) which may throw EntityNotFoundException:
#Controller
#RequestMapping("/Order")
public class OrderController {
#AsSeller
#RequestMapping(value = "/{orderID}/RefundApplication",
method = RequestMethod.PATCH,
params = "isApproved",
produces = "application/json")
#Transactional(rollbackFor = RuntimeException.class)
public #ResponseBody
Map processRefundApplication(#SessionAttribute("user") User user,
#PathVariable("orderID") String orderID,
#RequestParam("isApproved") boolean isApproved) {
...
}
debug information:
...
17:58:34.575 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 5312115 to pool.
17:58:34.576 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Resolving exception from handler [public java.util.Map youshu.controller.OrderController.processRefundApplication(youshu.entity.User,java.lang.String,boolean)]: youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
17:58:34.576 [main] DEBUG org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver - Resolving exception from handler [public java.util.Map youshu.controller.OrderController.processRefundApplication(youshu.entity.User,java.lang.String,boolean)]: youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
17:58:34.576 [main] DEBUG org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Resolving exception from handler [public java.util.Map youshu.controller.OrderController.processRefundApplication(youshu.entity.User,java.lang.String,boolean)]: youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
17:58:34.577 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Could not complete request
youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
at youshu.service.RefundService.get(RefundService.java:23) ~[classes/:?]
...
test class:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {WebConfig.class, RootConfig.class, DataConfig.class})
#WebAppConfiguration
class OrderControllerTest {
#Autowired
OrderController controller;
#Autowired
UserService userService;
#Autowired
OrderService orderService;
private User customer;
private User seller;
private HashMap<String, Object> sessionAttrs;
private ResultMatcher success = jsonPath("$.code")
.value("0");
private MockMvc mockMvc;
#BeforeEach
void init() {
if (customer == null) {
customer = new User();
customer.setID(18222);
customer.setName("shijiliyq");
customer.setPassword("...");
customer.setPaymentPassword("...");
}
if (seller == null) {
seller = new User();
seller.setID(27895);
}
if (sessionAttrs == null) {
sessionAttrs = new HashMap<>();
sessionAttrs.put("user", customer);
}
if (mockMvc == null)
mockMvc = standaloneSetup(controller).build();
}
#Test
void processRefundApplication() throws Exception{
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
String path = String.format("/Order/%s%d0001/RefundApplication"
, simpleDateFormat.format(new Date()), customer.getID());
HashMap<String,Object> sessionAttributes=new HashMap<>();
sessionAttributes.put("user",seller);
mockMvc.perform(patch(path)
.characterEncoding("UTF-8")
.param("isApproved","true")
.sessionAttrs(sessionAttributes))
.andDo(print())
.andExpect(success);
}
...
}
You need to point your mockMvc instance to your controller advice class:
#Autowired
AppWideExceptionHandler exceptionHandler;
...
mockMvc = standaloneSetup(controller).setControllerAdvice(exceptionHandler).build();

ApplicationEventPublisher is not being autowired into Component

I have a Spring Boot app and #Component class which looks like:
#Component
public class CustomEvent {
#Autowired
ApplicationEventPublisher publisher;
#PreRemove
public void onItemDelete(Object entity) {
System.out.println(" =======PUBLISH====== " + entity);
publisher.publishEvent(new EntityDeleteEvent<>(entity));
}
}
When it goes to run above method the first line is printed with proper entity but the publisher.publishEvent line throws a NullPointerException. I suppose it that the ApplicationEventPublisher is not being #Autowired but couldn't find why. Other #Components which are in the app are found by #ComponentScanner.
Of course in my entity this CustomEvent is registered:
#Entity
#EntityListeners(
CustomEvent.class
)
#Data
#AllArgsConstructor
public class Item
The exact error which is thrown looks like:
2017-10-26 16:46:06.190 ERROR 10176 --- [io-8091-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.inventory.events.CustomEvent.onItemDelete(CustomEvent.java:19)
Do you have any suggestion why publisher is null?
The initialisation of ApplicationEventPublisher doesn't happen OR will remain null, if you have created the CustomeEvent without the help of Bean (like CustomEvent event = new CustomEvent().
Instead, declare the CustomEvent as bean in your configuration (Spring) and get the CustomEvent using application context.
If CustomEvent is in the Spring's package scan, then I don't know.
But, there is an additional solution.
Create a class to instantiate spring managed class, but by ApplicationContext.
1 - Create the class below:
public class AppContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
context = appContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
public static <T> T getBean(Class<T> classe) {
return context.getBean(classe);
}
}
2 - Instance class as below:
public class CustomEvent {
private ApplicationEventPublisher publisher;
#PreRemove
public void onItemDelete(Object entity) {
System.out.println(" =======PUBLISH====== " + entity);
getApplicationEventPublisher().publishEvent(new EntityDeleteEvent<>(entity));
}
private ApplicationEventPublisher getApplicationEventPublisher() {
return AppContextUtil.getBean(ApplicationEventPublisher.class);
}
}

Spring #Transactional propagation is not working

I have a very simple code comprising of Service -> RequestProcessor -> DAO having 2-3 classes (interface, abstract, concrete) in each layer.
Service layer:-
public interface Service {
public void saveOrUpdate(Object entity, String operationName);
}
}
public abstract class AbstractService implements Service{
public abstract ReqProcessor getRP();
#Override
public void saveOrUpdate(Object entity, String operationName) {
ReqProcessor hiberTestRP = getRP();
hiberTestRP.saveOrUpdate(entity, operationName);
}
}
#Component
public class ServiceImpl extends AbstractService {
#Autowired
public ReqProcessor hibertestRPImpl;
#Override
public HiberTestRP getRP() {
return hibertestRPImpl;
}
}
ReqProcessor layer:-
public interface ReqProcessor {
public void saveOrUpdate(Object entity, String operationName);
public void saveObject();
}
}
public abstract class AbstractReqProcessor implements ReqProcessor {
#Override
public void saveOrUpdate(Object entity, String operationName) {
saveObject();
}
}
#Component
public class ReqProcessorImpl extends AbstractReqProcessor {
#Autowired
public CustomHibernateDao customWSDaoImpl;
#Override
#Transactional(value="transactionManagerWS", propagation=Propagation.REQUIRED)
public void saveObject() {
// object created //
customWSDaoImpl.saveOrUpdate(object); // exception is thrown at this line
}
}
DAO layer:-
public interface CustomHibernateDao {
public void saveOrUpdate(Object entity, String operationName);
}
#Repository
#Transactional(value="transactionManagerWS", propagation=Propagation.MANDATORY)
public class CustomWSDaoImpl implements CustomHibernateDao {
#Autowired
public SessionFactory sessionFactoryWS;
protected Session getCurrentSession() {
return sessionFactoryWS.getCurrentSession();
}
#Override
public void saveOrUpdate(Object entity, String operationName) {
Session session = getCurrentSession();
session.saveOrUpdate(entity);
}
}
I get the following exception at the commented line :
Exception in thread "main" org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:359)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy37.saveOrUpdate(Unknown Source)
The code works absolutely fine when the abstract classes are removed, with only interfaces and their implementing classes remaining. But with the above setup, the transaction is not being propagated from ReqProcessor layer to the DAO layer. Please help. (Dont mind the 'public' accessors everywhere, it's just for testing)
I have also searched on SO and other forums but couldnt find a solution.
As #m-deinum has mentioned, Spring uses proxies to add "transactional" functionality, and this feature does not work when you call method annotated with #Transactional from another method of the class.
You have two ways to fix the problem:
In AbstractReqProcessor autowire ApplicationContext and then use it to get a bean of CustomHibernateDao type. On this retrieved object you can call saveObject - then the transactional magic happens.
The more preferred way is to annotate method saveOrUpdate of class AbstractService with #Transactional annotation too - then it will work again.
But I think you know the cause of the problem now and you can find another - more suitable for you - way.

Resources