javaspringspring-bootspring-mvcnullpointerexception

Null exception @Service in a spring @RestController using constructor injection


This is my third personal spring-boot project, I am using 99% of the config I used before, so have no clue why I am getting an error this time around.

The project starts up fine, but once I make a request on the unique @RestController I have, it immediately throws a null pointer exception. This endpoint receives an alpha-2 country code + city name + the desired IP address. If the IP address is not provided, it takes the client IP address which is used as param for the @Service I am getting the null exception from.

After that, the controller should do many other actions before sending the expected output to the client but it never reaches that point since the null exception is thrown.

This is the request I am using:

http://localhost:8080/apiv1/get-distance?country=af&city=Qalat&ip=150.135.184.38

Which outputs:

There was an unexpected error (type=Internal Server Error, status=500).
Cannot invoke "me.givo.distancebetweenus.getdistance.services.CurrentLocationService.getCurrentLocation(String)" because "this.currentLocationService" is null
java.lang.NullPointerException: Cannot invoke "me.givo.distancebetweenus.getdistance.services.CurrentLocationService.getCurrentLocation(String)" because "this.currentLocationService" is null
    at me.givo.distancebetweenus.getdistance.controllers.DistanceController.getDistance(DistanceController.java:53)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:833)

This is my controller:

package me.givo.distancebetweenus.getdistance.controllers;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;

import me.givo.distancebetweenus.getdistance.dtos.CityDto;
import me.givo.distancebetweenus.getdistance.dtos.CountryDto;
import me.givo.distancebetweenus.getdistance.entities.CityEntity;
import me.givo.distancebetweenus.getdistance.entities.CountryEntity;
import me.givo.distancebetweenus.getdistance.models.ApiCurrentLocationResponse;
import me.givo.distancebetweenus.getdistance.models.Coordinate;
import me.givo.distancebetweenus.getdistance.models.DistanceResponse;
import me.givo.distancebetweenus.getdistance.repositories.ICitiesRepository;
import me.givo.distancebetweenus.getdistance.repositories.ICountriesRepository;
import me.givo.distancebetweenus.getdistance.services.CurrentLocationService;
import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RequestMapping("/apiv1")
@Validated
@RestController
public class DistanceController {

    private final CurrentLocationService currentLocationService;
    private final ICitiesRepository citiesRepository;
    private final ICountriesRepository countriesRepository;
    private final ModelMapper modelMapper;

    public DistanceController(CurrentLocationService currentLocationService, ICitiesRepository citiesRepository,
                              ICountriesRepository countryRepository, ModelMapper modelMapper) {
        this.currentLocationService = currentLocationService;
        this.citiesRepository = citiesRepository;
        this.countriesRepository = countryRepository;
        this.modelMapper = modelMapper;
    }

    @GetMapping("/get-distance")
    @ResponseBody
    private ResponseEntity<Object> getDistance(
            @RequestParam(required = true, name = "country") @Min(2) @Max(2) String country,
            @RequestParam(required = true, name = "city") String city,
            @RequestParam(required = false, name = "ip") @Min(7) @Max(15) String ip,
            HttpServletRequest request) {

        ApiCurrentLocationResponse currentLocation;
        if (ip == null || ip.isEmpty()) {
            String requestIP = request.getRemoteAddr();
            System.out.println(requestIP);
            try {
                currentLocation = currentLocationService.getCurrentLocation(requestIP);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        } else {
            try {
                currentLocation = currentLocationService.getCurrentLocation(ip);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        CountryEntity countryEntity = countriesRepository.getById(country);
        CityEntity cityEntity = citiesRepository.getByNameAndCode(city, country);

        CountryDto countryDto = convertToCountryDto(countryEntity);
        CityDto cityDto = convertToCityDto(cityEntity);

        Coordinate currentLocationCoordinate = new Coordinate(currentLocation);
        Coordinate destinationCoordinate = new Coordinate(cityDto);

        Double distance = currentLocationCoordinate.distanceBetweenUsKm(destinationCoordinate);

        DistanceResponse response = new DistanceResponse(countryDto, cityDto, distance);

        return ResponseEntity.status(HttpStatus.OK).body(response);
    }

    private CityDto convertToCityDto(CityEntity cityEntity) {
        return modelMapper.map(cityEntity, CityDto.class);
    }

    private CountryDto convertToCountryDto(CountryEntity countryEntity) {
        return modelMapper.map(countryEntity, CountryDto.class);
    }
}

This is the "null" @Service:

package me.givo.distancebetweenus.getdistance.services;

import me.givo.distancebetweenus.getdistance.models.ApiCurrentLocationResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class CurrentLocationService {

    private final WebClient.Builder webClient;

    private final String key = "fakekey347834834892923fake";

    public CurrentLocationService(WebClient.Builder webClient) {
        this.webClient = webClient;
    }

    public ApiCurrentLocationResponse getCurrentLocation(String requestIP) {

        String url = "https://api.fakeapi.app/json/";
        return webClient.baseUrl(url).build().get()
                .uri(uriBuilder -> uriBuilder
                        .path(requestIP)
                        .queryParam("apikey", key)
                        .build())
                .retrieve()
                .bodyToMono(ApiCurrentLocationResponse.class)
                .block();
    }
}

Edit

shivam's comment gave me the idea of tagging my injections with @Autowired so I did it and I am not getting the null pointer exception in the @Service anymore, as it now happens in the ICountriesRepository which is just the next line of code in my @RestController. These are the changes I made:

public class DistanceController {

    private final CurrentLocationService currentLocationService;
    private final ICitiesRepository citiesRepository;
    private final ICountriesRepository countriesRepository;
    private final ModelMapper modelMapper;

    @Autowired
    public DistanceController(CurrentLocationService currentLocationService, ICitiesRepository citiesRepository,
                              ICountriesRepository countriesRepository, ModelMapper modelMapper) {
        this.currentLocationService = currentLocationService;
        this.citiesRepository = citiesRepository;
        this.countriesRepository = countriesRepository;
        this.modelMapper = modelMapper;
    }

This is the "new" error log:

There was an unexpected error (type=Internal Server Error, status=500).
Cannot invoke "me.givo.distancebetweenus.getdistance.repositories.ICountriesRepository.getById(Object)" because "this.countriesRepository" is null
java.lang.NullPointerException: Cannot invoke "me.givo.distancebetweenus.getdistance.repositories.ICountriesRepository.getById(Object)" because "this.countriesRepository" is null
    at me.givo.distancebetweenus.getdistance.controllers.DistanceController.getDistance(DistanceController.java:72)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:833)

There is the code of the ICountriesRepository:

package me.givo.distancebetweenus.getdistance.repositories;


import me.givo.distancebetweenus.getdistance.entities.CountryEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;


@Repository
public interface ICountriesRepository extends JpaRepository<CountryEntity, String> {

}

Also, here is the code of a @Configuration I made for the ModelMapper:

package me.givo.distancebetweenus.getdistance.config;

import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {

    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }

}

Solution

  • I found that the only @GetMapping method my controller has was private. I just made it public and all run as expected already.