Why Is My Java Method Reading an Old File Version Resource Properties File
Reading resources in Java is an former topic, and information technology looks similar there is nothing more to say. Only even in 2020 dealing with resources in Java still harder that information technology should be. Together, we will brand a turn of existing solutions and discover a new user-friendly 1.
Here at Adelean, we work with Elasticsearch and we are used to see a lot of JSONs: requests, responses, mappings, settings, etc. In 90% of cases, functionality of software we make involves manipulation of JSON. And it's pretty common for united states of america to have JSON files as resources of Java code.
One day, subsequently writing code to load and parse JSON for 100500th time I understood one affair: reading content of resources in Java is harder than it should be!
Merely take a look. Let's assume we have a resource com/adelean/junit/jupiter/resource.txt
containing text:
The quick brown fob jumps over the lazy dog.
This is one of existing ways to load its content:
String resourcePath = "com/adelean/junit/jupiter/resources.txt" ; ClassLoader classLoader = getClass(). getClassLoader (); StringBuilder textBuilder = new StringBuilder(); try (InputStream resourceAsStream = classLoader. getResourceAsStream (resourcePath); InputStreamReader streamReader = new InputStreamReader(resourceAsStream); BufferedReader bufferedReader = new BufferedReader(streamReader)) { int c = 0; while ((c = bufferedReader. read ()) != -i) { textBuilder. append (( char ) c); } } grab (IOException ioException) { // handle exception } String resourceContent = textBuilder. toString ();
Looks scary, doesn't information technology? Demand to open up and shut multiple streams/readers, handle IOException, all only to get text content.
This Stackoverflow topic (https://stackoverflow.com/questions/15749192/how-do-i-load-a-file-from-resource-folder), and many others, explicate how to load resources. You lot can give it a look, or ameliorate, skip information technology and keep to read this article.
Luckily for us, in that location are swell libraries like google Guava, that significantly simplify the task. Here is the same performance made with Guava:
URL url = Resources. getResource ( "com/adelean/junit/jupiter/resource.txt" ); endeavor { String resourceContent = Resources. toString (url, StandardCharsets. UTF_8 ); } catch (IOException ioException) { // handle exception }
Even if it's simpler, information technology is still likewise complicated for a task that probably must exist done with one line. And we still have to handle exceptions.
I always found it surprising why we must handle exceptions when manipulating Coffee resource. While exception handling is justified for the files, considering file can be absent, or doesn't have right permissions, the situation is different for resources.
Resource lay on the classpath of Java application, not on the filesystem. You almost never volition have IOException, unless y'all made a mistake in your lawmaking. Loading of resource is closer to class import than reading a file from the filesystem. You don't surround your imports with try-take hold of, do y'all?
That's where @InjectResources library (https://github.com/hosuaby/inject-resources) comes to rescue. Information technology offers a fluid Coffee DSL for loading and parsing of resources without boilerplate lawmaking. It likewise provides extensions for Spring, and testing frameworks (JUnit4/v). With @InjectResources, resource reading can become equally unproblematic as adding notation on the class field:
@TextResource ( "com/adelean/junit/jupiter/resource.txt" ) private Cord text;
Let's take a look how it works!
The core of @InjectResources (https://hosuaby.github.io/inject-resources/0.1.0/asciidoc/#inject-resources-cadre) is a convenient and fluid Java DSL that takes abroad all average code like opening/closing of input streams and handling of I/O exceptions. Beginning, you volition need to add together inject-resources-cadre
to your project.
With Gradle:
repositories { jcenter() } dependencies { compile grouping: 'com.adelean' , name: 'inject-resources-core' , version: '0.1.0' }
Or with Maven:
<repositories> <repository> <snapshots> <enabled>false</enabled> </snapshots> <id>cardinal</id> <proper name>bintray</name> <url>https://jcenter.bintray.com</url> </repository> </repositories> <dependencies> <dependency> <groupId>com.adelean</groupId> <artifactId>inject-resources-core</artifactId> <version>0.1.0</version> </dependency> </dependencies>
The entry point of DSL is method InjectResources.resources
. It's recommended to import that method statically:
import static com.adelean.inject.resources.core.InjectResources.resource;
After we can use fluent syntax to go content of whatever resource:
var text = resources() . withPath ( "/com/adelean/junit/jupiter" , "resource.txt" ) . text ();
This syntax also offers various methods to open resources as binary InputStream or text Reader.
var schema = JavaPropsSchema . emptySchema () . withoutPathSeparator (); var reader = new JavaPropsMapper() . readerFor (DbConnection. form ) . with (schema); DbConnection dbConnection = resource() . withPath ( "/com/adelean/junit/jupiter" , "db.properties" ) . asInputStream () . parseChecked (reader::readValue);
Resource besides can be read line by line in functional way:
var header = new AtomicReference<String>(); var lines = new ArrayList<String>(); resources() . onClassLoaderOf (getClass()) . withPath ( "/com/adelean/junit/jupiter" , "cities.csv" ) . asLines () . onFirstLine (header::set) . forEachLine (lines::add);
Some method names finish with a discussion "Checked". Those methods tin accept lambdas that may throw catched exceptions. They are very convenient to avert exception handling while using parsers that may throw exceptions:
Instead of this:
var dbConnection = resource() . withPath ( "db.properties" ) . asInputStream () . parse (inputStream -> { try { return reader. readValue (inputStream); } catch (IOException parsingException) { throw new RuntimeException(parsingException); } });
We can write this:
var dbConnection = resource() . withPath ( "db.properties" ) . asInputStream () . parseChecked (reader::readValue);
inject-resources-core is a neat alternative to low level libraries like Apache Commons or Google Guava. If y'all are interested, check the user guide for more information and examples: https://hosuaby.github.io/inject-resources/0.1.0/asciidoc/#inject-resources-core
But if you are using Spring, you fifty-fifty tin can get your resources parsed and injected direct into your components. Adjacent section will discuss integration of @InjectResources with Spring.
Spring has its ain mechanism for resource loading. First, nosotros need to get reference to resource by ane of three proposed methods. Using ResourceLoader
:
@Autowired individual ResourceLoader resourceLoader; // Later... var resource = resourceLoader . getResource ( "classpath:com/adelean/junit/jupiter/resource.txt" );
Using ResourceUtils
:
var file = ResourceUtils . getFile ( "classpath:com/adelean/junit/jupiter/resource.txt" );
Or using @Value
annotation:
@Value ( "classpath:com/adelean/junit/jupiter/resource.txt" ) private Resource resource;
After nosotros got a reference to resource, we can read its content:
var textContent = Files. readString (resources. getFile (). toPath ());
Module inject-resource-bound (https://hosuaby.github.io/inject-resource/0.1.0/asciidoc/#inject-resources-jump) makes information technology possible to get content of resource with a single notation. Information technology also knows how to handle resources in diverse formats: binary, text, coffee properties, JSON and YAML.
First, add together dependency to your project
With Gradle:
compile grouping: 'com.adelean' , proper noun: 'inject-resources-leap' , version: '0.1.0'
Or with Maven:
<dependency> <groupId>com.adelean</groupId> <artifactId>inject-resources-spring</artifactId> <version>0.1.0</version> </dependency>
and then enable resource injection in your Leap project:
@Configuration @EnableResourceInjection public class MyConfig { }
After content of resources tin be injected in components:
@Component public course BeanWithTextResource { @TextResource ( "/com/adelean/junit/jupiter/resource.txt" ) private String text; }
Until that moment, nosotros only saw how to punch with text resources, but @InjectResources tin do much more. We can load and parse automatically coffee properties
@Component public class BeanWithPropertiesResource { @PropertiesResource ( "/com/adelean/junit/jupiter/db.backdrop" ) private Properties dbProperties; }
and JSON documents
@Component public class BeanWithJsonResource { @JsonResource ( "/com/adelean/junit/jupiter/sponge-bob.json" ) private Map<String, Object> jsonAsMap; }
and YAML documents
@Component public course BeanWithYamlResource { @YamlResource ( "/com/adelean/junit/jupiter/sponge-bob.yaml" ) private Person spongeBob; }
Annotations JsonResource
and YamlResource
will automatically catechumen resources content to the right types. Merely in order to work, they need parser. JsonResource
uses ObjectMapper
from Jackson
or Gson
object as parser. Ensure that you lot have one in application context:
@Configuration public course JacksonConfig { @Bean public ObjectMapper defaultObjectMapper () { return new ObjectMapper(); } }
YamlResource
is using Snakeyaml, so bean of type Yaml
must be present in context:
@Configuration public class SnakeyamlConfig { @Edible bean public Yaml defaultYaml () { return new Yaml(); } }
This was an introduction into @InjectResource. This library is very simple just powerful. Don't hesitate to accept a wait into documentation for more info and examples: https://hosuaby.github.io/inject-resources/0.1.0/asciidoc/#inject-resources-spring
In the post-obit article "A convenient way to read a resource file in Java unit tests" we will see how to read resources in JUnit tests.
kohlerblevensight.blogspot.com
Source: https://www.adelean.com/en/blog/20200819_lire_et_parser_resources_java_facilement/
0 Response to "Why Is My Java Method Reading an Old File Version Resource Properties File"
Post a Comment