Introduction
For applications, I have implemented in the last couple of years, I quite often had the requirement to store additional lookup data within the resource folder of my Maven project. Interestingly, many solutions you will find on the internet will work well in an IDE like Eclipse, STS or IntelliJ, but not when you create a Spring Boot application. The main reason is that IDEs usually can access these resource files from the source or target folder. Though, some approaches don't work if the application is being packaged, for example when using mvn package from the spring-boot-maven-plugin.
To demonstrate a way to read files from the resource folder after packaged, I have created a small command line Spring Boot application that implements the following requirements.
We create a CountryService that returns a list of configured countries - country name and 2-letter ISO code. A country can be looked up by it's ISO code. The list of available countries will be stored as key-value pairs in a properties file. When the application starts up, we read the content of the properties file and store the result internally in a Map.
The code for this post is available in this GitHub repository.
Country properties file
The properties file I use as example stores four countries.
CA=Canada
DE=Germany
UK=United Kingdom
US=United States of AmericaCountry service
Then we create the service interface and implementation in the package com.progressive.code.resource.service
package com.progressive.code.resource.service;
import java.util.Map;
/**
* Created by abraun on 14/11/2017.
*/
public interface CountryService {
String getCountryByIsoCode(String isoCode);
Map<String,String> getAllCountries();
}CountryService implementation
Let's focus on the method readCountries() invoked by the constructor.
- First, we create an instance of a
Propertiesobject - Then we create a
ClassPathResource(from the Springcore.iopackage), using the location of the properties file within the resource folder - The
ClassPathResourceclass allows us to read the properties based on anInputStream - And finally, we use
forEachto iterate through the properties and store the ISO code as key and the country name as value within theHashMap
package com.progressive.code.resource.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Created by abraun on 14/11/2017.
*/
@Service
public class CountryServiceImpl implements CountryService {
private static final Logger LOGGER = LoggerFactory.getLogger(CountryServiceImpl.class);
private static final String COUNTRY_DATA = "data/countryCodes.properties";
private final Map<String, String> countries = new HashMap<>();
public CountryServiceImpl() {
readCountries();
LOGGER.info("Loaded {} countries", countries.keySet().size());
}
@Override
public String getCountryByIsoCode(String isoCode) {
return countries.get(isoCode);
}
@Override
public Map<string, string> getAllCountries() {
return countries;
}
/**
* This method reads the country codes properties file from the resource
* folder (ClassPath) and stores the result in a HashMap.
*/
private void readCountries() {
try {
//Instantiate a new Properties object
Properties props = new Properties();
//Create a ClassPathResource, based on the given location within the resource folder
ClassPathResource res = new ClassPathResource(COUNTRY_DATA);
//Load the properties using InputStream provided by the ClassPathResource
props.load(res.getInputStream());
//Map the properties to the HashMap
props.stringPropertyNames().forEach(
isoName -> countries.put(isoName, props.get(isoName).toString())
);
} catch (IOException e) {
//For this example just log the exception
LOGGER.error("Exception occurred while trying to read {}", COUNTRY_DATA, e);
}
}
}Create a JUnit Test
Let's create a JUnit test to ensure that everything works as expected.
package com.progressive.code.resource.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Created by abraun on 14/11/2017.
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class CountryServiceImplTest {
@Autowired
private CountryService countryService;
@Test
public void testGetCountryByIsoCode() {
String countryName = countryService.getCountryByIsoCode("CA");
assertEquals("Canada", countryName);
}
@Test
public void testGetAllCountries() {
Map countries = countryService.getAllCountries();
assertNotNull(countries);
assertEquals(4, countries.size());
assertEquals("Canada", countries.get("CA"));
assertEquals("Germany", countries.get("DE"));
}
}Get the Code on GitHhub The code for this post is available in this GitHub repository.