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.

A convenient way to read resources in Java

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

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel