1

I'm trying to develop my first java Spring Bboot app that calls Strava API and gets my activities for the given period of time. I've registered my app on Strava's website and got client_id and client secret. I've generated spring-swagger-codegen-api-client and awtowired the client to the app.

@Configuration
public class StravaIntegrationConfiguration {

    @Bean
    public ActivitiesApi stravaApi(){
        return new ActivitiesApi(apiClient());
    }

    @Bean
    public ApiClient apiClient(){
        return new ApiClient();
    }
}

Then I use this bean in AdapterClass

public class Adapter {
    @Autowired
    private static ActivitiesApi activitiesApi;
    
    public static void getActivities(Integer before, Integer after, Integer page, Integer perPage) {
        final List<SummaryActivity> loggedInAthleteActivities = activitiesApi.getLoggedInAthleteActivities(before, after, page, perPage);
        System.out.println("ВСЕГО АКТИВНОСТЕЙ"+ loggedInAthleteActivities.size());
    }
}
@SpringBootApplication
@Import(StravaIntegrationConfiguration.class)
public class App {

    public static void main(String[] args) throws SQLException {
        SpringApplication.run(App.class);
        Adapter.getActivities(1636130496, 1635529296, 1,  30);

    }
}

When I run this code I get NPE, because activitiesApi is null.
What is the problem? Please kindly advise. Does it concern authentication? Could you advise also any code sample on how to make Strava authentication in my app?

João Dias
  • 16,277
  • 6
  • 33
  • 45
Anna Bar
  • 31
  • 5

1 Answers1

1

It has nothing to do with Strava authentication. It is related to Spring context and Spring Beans and how to inject them. As already mentioned you can't autowire Spring-managed beans in static fields (it makes no sense actually). Having said that you need to fix that first:

@Component
public class Adapter {
    @Autowired
    private ActivitiesApi activitiesApi;
    
    public void getActivities(Integer before, Integer after, Integer page, Integer perPage) {
        final List<SummaryActivity> loggedInAthleteActivities = activitiesApi.getLoggedInAthleteActivities(before, after, page, perPage);
        System.out.println("ВСЕГО АКТИВНОСТЕЙ"+ loggedInAthleteActivities.size());
    }
}

Also, note that the method changed from a static one to an instance one and that the annotation @Component was added to the class. The reason is that a Spring-managed bean can only be injected into other Spring-managed beans.

Additionally, it seems to me that you are trying to do something after the Spring context has been initialized. One possible way to do this is creating a bean that implements the ApplicationListener interface:

@Component
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private Adapter adapter;

    @Override 
    public void onApplicationEvent(ContextRefreshedEvent event) {
        adapter.getActivities(1636130496, 1635529296, 1,  30);
    }
}

This means that you can and you should remove the line Adapter.getActivities(1636130496, 1635529296, 1, 30); from your main class:

@SpringBootApplication
@Import(StravaIntegrationConfiguration.class)
public class App {

    public static void main(String[] args) throws SQLException {
        SpringApplication.run(App.class);
    }
}

Finally, and as a side note, please consider using constructor injection instead of field injection. It has a couple of advantages over field injection: making the class easier to unit test, allowing the objects to be immutable, explicitly definition of which dependencies are mandatory, etc... (you can read more at https://reflectoring.io/constructor-injection/).

João Dias
  • 16,277
  • 6
  • 33
  • 45
  • Hi João! Thank you very much, the app started! Now I see 401 Unauthorized from Strava, but at least the problem with Spring context is resolved. Thanks!!! – Anna Bar Nov 07 '21 at 18:36
  • but in this case my app calls Adapter after context was created. What if I need to call it later and with different parameters? – Anna Bar Nov 15 '21 at 09:10
  • You can always `@Autowire` the `Adapter` Spring-managed Bean in whatever Spring-managed Bean you are interested in and then simply call the `getActivities()` method. – João Dias Nov 15 '21 at 10:25