Using Spring Boot with JavaFX – Using Spring Profiles

In the previous post, I showed how to get started with Spring Boot and JavaFX.

Continuing with the same example, we are going to create a new implementation of the CountryService interface that talks to the Open AQ Platform API so the list of countries will be a lot bigger than our HardcodedListCountryService we have now.

Using Retrofit

First, we add 2 dependencies in our pom.xml to be able to use Retrofit:

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>converter-jackson</artifactId>
  <version>2.3.0</version>
</dependency>

Using Retrofit, we can easily get information from the API and transform it using Jackson into Java Objects.

First, we create an interface with a method for each call to the API we want to do:

public interface OpenAqApi {
    @GET("countries")
    Call<OpenAqCountriesResponse> listCountries();
}

Next, we create objects that correspond to the returned JSON structures:

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public class OpenAqCountriesResponse {
    private List<OpenAqCountry> results;
}

and

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public class OpenAqCountry {
    private String code;
    private String name;
}

Note: We use @JsonIgnoreProperties(ignoreUnknown = true) to avoid having to add properties in Java for JSON fields we are not interested in anyway.

Finally, we create an OpenAqServiceImpl class that sets up Retrofit for our API:

@Component
public class OpenAqServiceImpl implements OpenAqService {

    private final OpenAqApi api;

    public OpenAqServiceImpl() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor).build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.openaq.org/v1/")
                .addConverterFactory(JacksonConverterFactory.create())
                .client(client)
                .build();

        api = retrofit.create(OpenAqApi.class);
    }

    @Override
    public Set<OpenAqCountry> listCountries() {
        try {
            OpenAqCountriesResponse response = api.listCountries().execute().body();
            if (response != null) {
                return new HashSet<>(response.getResults());
            } else {
                return Collections.emptySet();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Note: The current code also logs the HTTP requests and responses. For this to work, you need an additional dependency:

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>logging-interceptor</artifactId>
  <version>3.8.0</version>
</dependency>

If you don’t want/need this logging, you can remove the dependency.

With all this plumbing in place, we can create our actual CountryServiceImpl that is quite simple:

@Component
public class CountryServiceImpl implements CountryService {

    private final OpenAqService service;

    public CountryServiceImpl(OpenAqService service) {
        this.service = service;
    }

    @Override
    public Set<Country> getAllCountries() {
        return service.listCountries().stream()
                      .map(openAqCountry -> new Country(openAqCountry.getCode(), openAqCountry.getName()))
                      .collect(Collectors.toSet());
    }
}

Spring profiles

However, if we now start our JavaFX application, Spring will complain that there are 2 classes that implement CountryService and it has no clue which one it has to inject into our controller. To solve this, we will add 2 extra annotations to the HardcodedListCountryService:

@Component
@Profile("offline")
@Primary
public class HardcodedListCountryService implements CountryService {
    @Override
    public Set<Country> getAllCountries() {
        Set<Country> result = new HashSet<>();
        result.add(new Country("AU", "Australia"));
        result.add(new Country("BR", "Brazil"));
        result.add(new Country("BE", "Belgium"));
        return result;
    }
}
  • @Profile("offline") instructs Spring to only create an instance of this class when the “offline” profile is active.
  • @Primary instructs Spring to always give preference to this instance when autowiring

As a result, if we start the application without any argument, it will use the “online” version of the CountryService that uses the Open AQ API. When starting with

--spring.profiles.active=offline

as program arguments, the hardcoded list will be used without contacting the API online.

Summary

Using Spring profiles makes it really easy to switch your dependencies depending on how you want to run the application. There are many cases where this can be useful, ranging from the API does not exist yet, the API is currently down or maybe you want to have some “nicer” data for screenshots.

This know-how originated during the development of a PegusApps project.

Advertisements

One thought on “Using Spring Boot with JavaFX – Using Spring Profiles

  1. Pingback: Using Spring Boot with JavaFX | Wim Deblauwe's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s