Call service method from non controller class - java

In HomeController i am doing the following
#Controller
public class HomeController {
#Autowired
private EUserService userDao;
#RequestMapping(value = "/")
public String setupForm(Map<String, Object> map) {
User user=(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
EUser currentUser = userDao.findUserByName(user.getUsername());
System.out.println(currentUser.getUserName());
}
}
It works fine and shows me the output properly. Now If I do the same thing in a non controller type class like following
public class Utility {
#Autowired
private EUserService userDao;
public void getLoggedUser() {
User user = (User) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
EUser currentUser = (EUser) userService.findUserByName(user
.getUsername());
System.out.println(currentUser.getUserName());
}
}
it gives me the following NullPointerException
SEVERE: Servlet.service() for servlet [spring] in context with path [/Ebajar] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException
How to fix this??

The problem is not that you are calling this not from controller. The problem is that your are calling this from class that is not managed by Spring, so the userDao is not injected here.
It think that "right" solution is to turn your utility to Spring bean, e.g. marking it as #Service and call it via Spring. Alternatively you can retrieve it programmatically using ApplicationContext.getBean() (see here for details)

you should declare/annotate your Utility class as a spring bean so that let other bean get injected. in this case, it is EUserService
try adding #Component to your Utility class. I assumed your package is involved by spring annotated bean scanning settings.

Only Spring managed bean can autowire an instance. Read here
You annotate Utility with #Component.

Related

Create a bean from PathVariable in Spring

In my controller, i get a Pathvariable called "realm".
I want to use this variable in service layer. I don't want to set realm in every method.
This is my controller:
#RestController
#RequestMapping("/user-managment/{realm}/")
#PreAuthorize("#someBean.test(#requestHelper.getPathVariableByName('realm'))")
public class KeycloackUserController {
#Autowired
private KeyCloakAdminAPIService keyCloakAdminAPIService;
As you can see, i get a "realm" in request. I tried to initialize a new bean with that variable to use it in service but it didn't work.
This is my service:
#Service
public class KeyCloakAdminAPIService {
#Autowired
#Value("${someBean.test}")
private String realm;
I get an error:
Error creating bean with name 'keycloackUserController': Unsatisfied dependency expressed through field 'keyCloakAdminAPIService';
Do you have any advice for this problem?

How to initialize bean method defined in Main class into test context

I have created a bean method in the main class:
#SpringBootApplication
#EnableScheduling
public class SpringApplication{
#Bean
Public String getCronValue(ServiceImpl service){
return service.getConfig().get("cron duration");
}
}
using this bean in a scheduled task:
#Component
public Class MySch{
#Scheduled(cron="#{getCronValue}")
public void schedulerMethod(){
//Do something
}
}
Now the problem is when I try to run JUnit tests #Bean GetCronValue is not initialized in test context and #Scheduled annotation throws an exception:
Update:-
It throws an exception:-
BeanCreationException: Error creating bean with name
'SchedulerMethod' : Initialization of bean failed; nested
exception is ' org. springframework.beans.
factory.Beanexpressio exception: Expression parsing
failed; nested exception is org. springframework.
expression.spel.SpelEvaluationException: EL1021E: A
problem occurred whilst attempting to access the
property ' getCronValue' : Error creating bean with name
'getCronValue' : Unsetisfied dependency expressed
through method 'getCronValue' parameter 0; nested
exception is org. springframework. beans. factory.
NoSuchBeanDefinitionException: No qualifying bean of
type 'com.pkg.service.ServiceImpl' available: expected at
least 1 bean which qualifies as a autowire candidate.
Dependemcy annotations: {}'
My Controller test class looks like:-
#Transactional
public class ControllerTest{
#MockBean
private Service service;
.
.
// test cases
}
How to resolve this issue.
I assume that you're using #SpringBootTest annotation.
When you test a Controller you may want narrow the tests to only the web layer by using #WebMvcTest. Any other dependencies required by the controller will be then mocked using #MockBean.
When #WebMvcTest is used Spring Boot instantiates only the web layer rather than the whole context. In an application with multiple controllers, you can even ask for only one to be instantiated for example.
#WebMvcTest(controllers =Controller.class)
public class ControllerTest{
#MockBean
private Service service;
#Autowired
private MockMvc mockMvc;
// test cases
}
I noticed that you have the #Transactional annotation in your example. This can indicate that you maybe giving too match responsibilities to your controller and may consider passing Database access related logic to a service/repostory/DAO
See https://spring.io/guides/gs/testing-web/

In Spring Web, how do I get the full URL of a #RestController?

Lets say I inject an ApplicationContext into a bean. I can then discover all the beans annotated with RestController. How would I discover the base URL associated with said beans?
Example:
#Component
public class ServiceEnumerator {
#Autowired
private ApplicationContext context;
#PostConstruct
private void postConstruct() {
final Map<String, Object> beansToExpose = context.getBeansWithAnnotation(RestController.class);
}
}
As per standard, Base URL (Context root) needs to setup in configuration file.
Not sure if below will help you....Try to inject HttpServletRequest in controller then from controller, pass it to required service
#Context
private HttpServletRequest httpRequest;
LOGGER.info("URL "+httpRequest.getRequestURI());
will give /services/v1/hello if your URL is http://localhost:8080/services/v1/hello
This could be possible by retrieving bean definition from bean factory and parsing its metadata. for instance:
retrieve beanFactory from your application Context
getting your bean definition from bean factory
retrieving its metadata
fetching attributes
metadata.getAnnotationAttributes(
"org.springframework.web.bind.annotation.RestController")

two conversionService bean created

I am getting an error while trying to up my application
Description:
Field conversionService in com.profectus.dashboard.service.impl.DashBoardSettingsServiceimpl required a single bean, but 2 were found:
- mvcConversionService: defined by method 'mvcConversionService' in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]
- defaultConversionService: defined by method 'defaultConversionService' in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]
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
Why 2 beans are created and how to keep only one converter, I just want only spring core converter who can convert entity to pojo or pojo to entity.
I am stuck because of this issue, any lead would be helpful.
Service class code:-
import org.springframework.core.convert.ConversionService;
//other imports
#Service
public class DashBoardSettingsServiceimpl implements DashBoardSettingsService {
#Autowired
private DashBoardSettingJpaRepository dashBoardSettingRepo;
#Autowired
private ConversionService conversionService;
#Override
public DashBoardSettingResponse save(UserInfo userInfo, DashBoardSettingRequest request) {
//other coded
DashBoardSettigEntity entity = conversionService.convert(request.getDashBoardSetting(),
DashBoardSettigEntity.class);
DashBoardSettigEntity entityRetrieve = dashBoardSettingRepo.save(entity);
DashBoardSetting setting = conversionService.convert(entityRetrieve, DashBoardSetting.class);
DashBoardSettingResponse response = new DashBoardSettingResponse();
response.addDashBoardSetting(setting);
return response;
}
}
Autowire type DefaultConversionService instead of ConversionService
Seems like it has something to do with spring data rest having its own ConversionService instance.
Can you try this:
#Autowired #Qualifier("mvcConversionService") ConversionService conversionService;
It worked for me.
Don't forget to add your converters to a WebMvcConfigurer implementation.

Spring autoscanning of Components failed

I want to schedule a method in my spring application, i've found documentation here http://spring.io/guides/gs/scheduling-tasks/, well it works, but i dont understand why i can't autowire a bean in class annotated with component annotation.
#Service
public void MyService { ... }
#Controller
public void RestController {
#Autowired
private void MyService service;
}
Everything works here, the field "service" is properly set, no warning/exceptions in console
#Component
public void Scheduler {
#Autowired
private void MyService service;
}
There im getting an exception and application wont start. The class Scheduler is in same package as RestController and MyService but im getting exception
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Scheduler': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private some.package.MyService some.package.Scheduler.service; nested exception is java.lang.IllegalArgumentException: Can not set some.package.MyService field some.package.Scheduler.service to $Proxy176
Actually Spring tries to create Proxy from your MyService (e.g. because of #Transactional annotation on method). The proxy is not MyService instance and can't be autowired.
You can try to introduce MyService interface and define MyServiceImpl class implementing the interface. Then you can use MyService interface with #Autowired
Because spring is not able to find bean to inject at
#Autowired
private void MyService service;
You don't have to declare a bean in your context file if you
Annotate the class with: #Component or #Service or #Controller or
#Repository
you have given #service on method that's wrong
You need to create bean of class MyService from xml or make class
MyService and use #Component as you have done with Scheduler class

Categories

Resources