To call Strava API from SpringBoot application - java

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?

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/).

Related

Constructor Injection together with JDA and Spring Boot [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed last year.
I am struggling with initializing JDA as the "addEventListeners" requires me to input the EventListeners.. However, my EventListeners have "injected constructors" (if that's the correct word) that is grabbing my Dao's.
I would gladly use #Autowire in my EventListeners, but it keeps on giving me a NullPointer.
I think the issue is that JDA extends their EventListener, which basically loads outside of Spring Boot, even though I've added the #Service annotation on the Listener.
You can see my problem below at:
.addEventListeners(new JDAEventListener())
Obviously, I am not able to do new JDAEventListener()as it requires the WixSubscriptionDao. However, I am not able to understand how to initiate the JDAEventListener without WixSubscriptionDao, as I need the Dao for further data handling.
public static void main(String[] args) throws InterruptedException, LoginException {
JDA jda = JDABuilder.createDefault("XXXXXXXXXXXXXXXXXXXXXX")
.enableIntents(GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS)
.setChunkingFilter(ChunkingFilter.ALL)
.setMemberCachePolicy(MemberCachePolicy.ALL)
.addEventListeners(new JDAEventListener())
.build();
DISCORD_CONSTANT.jda = jda.awaitReady();
setupDefault();
}
#Service
public class JDAEventListener implements EventListener {
private final WixSubscriptionDao wixSubscriptionDao;
public JDAEventListener(WixSubscriptionDao wixSubscriptionDao) {
this.wixSubscriptionDao = wixSubscriptionDao;
}
#Override
public void onEvent(#NotNull GenericEvent genericEvent) {
if (genericEvent instanceof ReadyEvent) {
System.out.println("ReadyEvent done");
ArrayList<WixSubscription> wixSubscriptions = wixSubscriptionDao.findAll();
System.out.println(wixSubscriptions.size());
}
}
I would love to do this, but as wrote above, the #Autowired Dao is giving me a NullPointer, even though it's defined in the SpringConfig. (The DAO works perfectly when using the constructor method)
#Service
public class JDAEventListener implements EventListener {
#Autowired
private WixSubscriptionDao wixSubscriptionDao;
public JDAEventListener() {
}
#Override
public void onEvent(#NotNull GenericEvent genericEvent) {
if (genericEvent instanceof ReadyEvent) {
System.out.println("ReadyEvent done");
ArrayList<WixSubscription> wixSubscriptions = wixSubscriptionDao.findAll();
System.out.println(wixSubscriptions.size());
}
}
I suggest that you create class annotated with #Component which implements CommandLineRunner interface. This means that, the run method will be executed when the application starts. Also, you can inject other Spring beans into it, like for example JDAEventListener beans.
#Component
public class JDAInitializer implements CommandLineRunner {
private final JDAEventListener jdaEventListener;
// Constructor injection
public JDAInitializer(JDAEventListener jdaEventListener) {
this.jdaEventListener = jdaEventListener;
}
#Override
public void run(String... args) throws Exception {
JDA jda = JDABuilder.createDefault("XXXXXXXXXXXXXXXXXXXXXX")
.enableIntents(GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS)
.setChunkingFilter(ChunkingFilter.ALL)
.setMemberCachePolicy(MemberCachePolicy.ALL)
.addEventListeners(jdaEventListener)
.build();
DISCORD_CONSTANT.jda = jda.awaitReady();
setupDefault();
}
...
}

Injecting a single Spring bean instance into a static variable of a utility class

I have this web application built with Spring and Vaadin, in which I wanted to do this, for the sake of convenience:
Create a utility class that wraps a Spring service, and allows the use of its static methods throughout the application, without having to worry about injecting this service everywhere, like so:
String configurationValue = ConfigurationUtil.getString("some.property.key");
If you work with Vaadin, you might see how convenient this is, because the whole presentation layer is written in Java and you can't always inject Spring services into your Vaadin components as these Vaadin components are not always Spring components themselves.
So this is my utility class:
public final class ConfigurationUtil {
// this is the spring service:
private static ConfigurationService configurationService;
public static void setConfigurationService(final ConfigurationService configurationService) {
ConfigurationUtil.configurationService = configurationService;
}
public static String getString(final String key) {
return configurationService.getString(key);
}
}
This is my service:
#Service("configurationService")
public class ConfigurationServiceImpl implements ConfigurationService, BeanFactoryAware {
private final FrameworkService frameworkService;
#Autowired
public ConfigurationServiceImpl(final FrameworkService frameworkService) throws IOException, ConfigurationException {
// this is where I set this service bean to the utility class
ConfigurationUtil.setConfigurationService(this);
this.frameworkService = frameworkService;
}
public String getString(String key) {
// code that retrieves a configuration value from our configuration files
}
}
My question here is: I'm a bit worried about this causing a bottleneck to access the configuration service, as multiple threads will be calling it, from multiple user sessions. Would this be a problem? Please explain why. Also, feel free to point out other problems with this solution.
I suggest to create a bean that implements ApplicationContextAware like this:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext ac) {
context = ac;
}
public static String getString(final String key) {
ConfigurationService configurationService = context.getBean(ConfigurationService.class);
return configurationService.getString(key);
}
public static <T> T bean(Class<T> beanType) {
return context.getBean(beanType);
}
}
You can create a method like in the example to give static access to Spring Beans or what you requested to get a String from your ConfigurationService.
Btw. I use this a lot in Vaadin applications because I don't want to make every component a Spring Bean.

How could I take an instance of class in Spring Boot?

It is a class which instance is connected to the external service and it is listening constantly of it.
#Component
public class Service extends PollingBot {
#Value("${token}")
private String token;
#Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
}
}
public void sendMessageToUser(String message) {
try {
execute(sendMessage);
} catch (ApiException e) {
}
}
}
You could see that there is a method called sendMessageToUser which send message. It could not be static because execute method not allow static context. This method could not be separeted to other class. /
So, I have to call this method from other class. However I don't want to create additional instance of Service class otherwise I have two instances which are listen for updates, but I want it is sole class instance doing so.
I have tried to run a Application Context and run method from it, but it was not worked.
So, my question is very simple. How could I run this class non-static(!) method from other class?
By default all spring managed beans are singleton. You need to use #Autowired to inject the bean into other and then you can call the methods of that bean.
#Autowired
private Service service;
public void sendMessage(String message){
service.sendMessageToUser(message);
}
You can use #Autowired annotation to call a method of a bean class(component) in Spring. Also, as mentioned by default beans are singleton in spring so you don't need to worry about creating a single instance explicitly every time.
Try to use the below code in the calling class:
#Autowired
private Service service;
public void sendText() {
service.sendMessage(message);
}

Why is my Spring Boot #Autowired MyBatis static mapper null?

I have the following class, but Spring and MyBatis-Spring-Boot-Starter will not autowire my mapper.
When I run the request, I get the output from the println()
sourceMapper = null
Model
public class Source {
#Autowired
public static SourceMapper sourceMapper; #### Why isn't this set?
public static Source findOrCreate(String url) {
...
System.out.println("sourceMapper = " + sourceMapper);
source = sourceMapper.findByHost(host);
...
}
}
I followed the examples as closely as possible.
http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
The other #Autowired Mappers in the main #Controller class that handles requests work, even though they are private.
This is the Mapper class
package ...mapper;
#Mapper
public interface SourceMapper {
...
I ran into this issue again with a new model and mapper. I tried to follow Why is my Spring #Autowired field null? and the code sample, but it was still null! I tried #Configurable, #Service, #Component.
Model
#Configurable
public class Domain {
#Autowired
private static DomainMapper domainMapper;
public static void incrementCounter(String host) {
...
Domain d = getDomainMapper().find(host, thisMonth);
public static DomainMapper getDomainMapper() {
return domainMapper;
public static void setDomainMapper(DomainMapper domainMapper) {
Domain.domainMapper = domainMapper;
Mapper
#Mapper
public interface DomainMapper {
MyBatis 3.4.5, MyBatis Spring 1.3.1, MyBatis Spring Boot Autoconfigure 1.3.1, MyBatis Spring Boot Starter 1.3.1
I fixed it with
private static DomainMapper getDomainMapper() {
// https://stackoverflow.com/a/52997701/148844
if (domainMapper == null)
domainMapper = MyApplication.getApplicationContext().getBean(DomainMapper.class);
return domainMapper;
And
MyApplication
#Autowired // for AWS
private static ApplicationContext context;
// I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`.
// I don't believe it runs when deploying to Tomcat on AWS.
public static void main(String[] args) {
context = SpringApplication.run(MyApplication.class, args);
But I don't like it!
Spring will only try to inject a bean for you if another bean requires it.
Your class Source is just a normal class with bunch of static methods.
Hence it's not under creation control of Spring.
If you want to inject SourceMapper to your Source, you should mark Source with #Component or #Service so that the container will know it should create a bean of Source type for you and give you an instance of SourceMapper.
Moreover, the SourceMapper should be declared non-static, to prevent if a class access the variable before injected. And static field can be injected only if it use field setter injection.

How to #autowire some bean into JsonSerializer?

I am using lazy loading with hibernate in my web app.
I would like to load some objects from the database at the parsing stage of the server response
#Component
public class DesignSerializer extends JsonSerializer<Design> {
#Autowired
IDesignService designService; <-- is null
}
Which is totally understandable because DesignSerializer is being instantiated with the "new" operator for each object.
I am sure there is a way to inject my bean into that serializer when ever it is created, I just don't know how.
Can you guys help me or point me in the right direction.
Solution is SpringBeanAutowiringSupport if you are using Spring Framework 2.5+.
public class DesignSerializer extends JsonSerializer<Design> {
#Autowired
IDesignService designService;
}
public DesignSerializer(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
...
}
I Hope that help you
We had the same problem with JsonSerializer and Spring autowiring. The solution that worked for us was to make two constructors. One for Spring which sets the dependency as a static field, and another one that is used by the Jackson initialisation.
This works because the Spring dependency injection (autowiring) happens before Jackson initialises the serializer.
#Component
public class MyCustomSerializer extends JsonSerializer<String> {
private static IDesignService designService;
// Required by Jackson annotation to instantiate the serializer
public MyCustomSerializer() { }
#Autowired
public MyCustomSerializer(IDesignService designService) {
this.designService = designService;
}
#Override
public void serialize(String m, JsonGenerator gen, SerializerProvider s) {
gen.writeObject(MyCustomSerializer.designService.method(..));
}
}
I Solved the problem by creating a static field in a different bean and then #Autowire its setter method.
#Service("ToolBox")
#Transactional
public class ToolBox
{
static Logger logger = Logger.getLogger(ToolBox.class);
private static IService service;
#Autowired
public void setService(IService service)
{
ToolBox.service = service;
}
public static IService getService()
{
return ToolBox.service;
}}
like shown in this thread: Can you use #Autowired with static fields?

Categories