[[geode-data-using]]
== Using Data
:geode-name: {apache-geode-name}


One of the most important tasks during development is ensuring your Spring Boot application handles data correctly.
To verify the accuracy, integrity, and availability of your data, your application needs data with which to work.

For those of you already familiar with Spring Boot's support for
{spring-boot-docs-html}/howto.html#howto-initialize-a-database-using-spring-jdbc[SQL database initialization],
the approach when using {geode-name} should be easy to understand.

{geode-name} provides built-in support, similar in function to Spring Boot's SQL database initialization, by using:

* Gfsh's {apache-geode-docs}/tools_modules/gfsh/quick_ref_commands_by_area.html#topic_C7DB8A800D6244AE8FF3ADDCF139DCE4[import/export] data commands.
* {apache-geode-docs}/managing/cache_snapshots/chapter_overview.html[Snapshot service]
* {apache-geode-docs}/developing/storing_data_on_disk/chapter_overview.html[Persistence] with {apache-geode-docs}/managing/disk_storage/chapter_overview.html[disk storage]

For example, by enabling persistence with disk storage, you could
{apache-geode-docs}/managing/disk_storage/backup_restore_disk_store.html[backup and restore]
persistent `DiskStore` files from one cluster to another.

Alternatively, using {geode-name}'s Snapshot Service, you can export data contained in targeted `Regions` from one
cluster during shutdown and import the data into another cluster on startup. The Snapshot Service lets you filter data
while it is being imported and exported.

Finally, you can use {geode-name} shell (Gfsh) commands to
{spring-data-geode-docs-html}/tools_modules/gfsh/command-pages/export.html#topic_263B70069BFC4A7185F86B3272011734[export data]
and {apache-geode-docs}/tools_modules/gfsh/command-pages/import.html#topic_jw2_2ld_2l[import data].

TIP: Spring Data for {geode-name} (SDG) contains dedicated support for
{spring-data-geode-docs-html}/#bootstrap:region:persistence[persistence]
and the {spring-data-geode-docs-html}/#bootstrap:snapshot[Snapshot Service].

In all cases, the files generated by persistence, the Snapshot Service and Gfsh's `export` command are in a proprietary
binary format.

Furthermore, none of these approaches are as convenient as Spring Boot's database initialization automation. Therefore,
Spring Boot for {geode-name} (SBDG) offers support to import data from JSON into {geode-name} as PDX.

Unlike Spring Boot, SBDG offers support to export data as well. By default, data is imported and exported in JSON format.

NOTE: SBDG does not provide an equivalent to Spring Boot's `schema.sql` file. The best way to define the data structures
(the `Region` instances) that manage your data is with SDG's annotation-based configuration support for defining cache
`Region` instances from your application's {spring-data-geode-docs-html}/#bootstrap-annotation-config-regions[entity classes]
or indirectly from Spring and JSR-107 or JCache {spring-data-geode-docs-html}/#bootstrap-annotation-config-caching[caching annotations].

TIP: See SBDG's <<geode-configuration-declarative-annotations-productivity-regions,documentation>> on the same.

WARNING: While this feature works and many edge cases were thought through and tested thoroughly, there are still some
limitations that need to be ironed out. See https://github.com/spring-projects/spring-boot-data-geode/issues/82[issue-82]
and https://github.com/spring-projects/spring-boot-data-geode/issues/83[issue-83] for more details. The Spring team
strongly recommends that this feature be used only for development and testing purposes.

[[geode-data-using-import]]
=== Importing Data

You can import data into a `Region` by defining a JSON file that contain the JSON objects you wish to load. The JSON
file must follow a predefined naming convention and be placed in the root of your application classpath:

`data-<regionName>.json`

NOTE: `<regionName>` refers to the lowercase "name" of the `Region`, as defined by
{apache-geode-javadoc}/org/apache/geode/cache/Region.html#getName--[`Region.getName()`].

For example, if you have a `Region` named "Orders", you would create a JSON file called `data-orders.json` and place it
in the root of your application classpath (for example, in `src/test/resources`).

Create JSON files for each `Region` that is implicitly defined (for example, by using `@EnableEntityDefinedRegions`)
or explicitly defined (with `ClientRegionFactoryBean` in Java configuration) in your Spring Boot application
configuration that you want to load with data.

The JSON file that contains JSON data for the "Orders" `Region` might appear as follows:

.`data-orders.json`
====
[source,json]
----
[{
  "@type": "example.app.pos.model.PurchaseOrder",
  "id": 1,
  "lineItems": [
    {
      "@type": "example.app.pos.model.LineItem",
      "product": {
        "@type": "example.app.pos.model.Product",
        "name": "Apple iPad Pro",
        "price": 1499.00,
        "category": "SHOPPING"
      },
      "quantity": 1
    },
    {
      "@type": "example.app.pos.model.LineItem",
      "product": {
        "@type": "example.app.pos.model.Product",
        "name": "Apple iPhone 11 Pro Max",
        "price": 1249.00,
        "category": "SHOPPING"
      },
      "quantity": 2
    }
  ]
}, {
  "@type": "example.app.pos.model.PurchaseOrder",
  "id": 2,
  "lineItems": [
    {
      "@type": "example.app.pos.model.LineItem",
      "product": {
        "@type": "example.app.pos.model.Product",
        "name": "Starbucks Vente Carmel Macchiato",
        "price": 5.49,
        "category": "SHOPPING"
      },
      "quantity": 1
    }
  ]
}]
----
====

The application entity classes that matches the JSON data from the JSON file might look something like the following
listing:

.Point-of-Sale (POS) Application Domain Model Classes
====
[source,java]
----
@Region("Orders")
class PurchaseOrder {

	@Id
    Long id;

	List<LineItem> lineItems;

}

class LineItem {

	Product product;
	Integer quantity;

}

@Region("Products")
class Product {

	String name;
	Category category;
	BigDecimal price;

}
----
====

As the preceding listings show, the object model and corresponding JSON can be arbitrarily complex with a hierarchy of
objects that have complex types.

[[geode-data-using-import-metadata]]
==== JSON metadata

We want to draw your attention to a few other details contained in the object model and JSON shown
<<geode-data-using-import,earlier>>.

[[geode-data-using-import-metadata-attype]]
===== The `@type` metadata field

First, we declared a `@type` JSON metadata field. This field does not map to any specific field or property of
the application domain model class (such as `PurchaseOrder`). Rather, it tells the framework and {geode-name}'s JSON/PDX
converter the type of object the JSON data would map to if you were to request an object (by calling
`PdxInstance.getObject()`).

Consider the following example:

.Deserializing PDX as an Object
====
[source,java]
----
@Repository
class OrdersRepository {

    @Resource(name = "Orders")
    Region<Long, PurchaseOrder> orders;

    PurchaseOrder findBy(Long id) {

        Object value = this.orders.get(id);

        return value instanceof PurchaseOrder ? (PurchaseOrder) value
            : value instanceof PdxInstance ? ((PdxInstance) value).getObject()
            : null;
    }
}
----
====

Basically, the `@type` JSON metadata field informs the `PdxInstance.getObject()` method about the type of Java object
to which the JSON object maps. Otherwise, the `PdxInstance.getObject()` method would silently return a `PdxInstance`.

It is possible for {geode-name}'s PDX serialization framework to return a `PurchaseOrder` from `Region.get(key)` as well,
but it depends on the value of PDX's `read-serialized`, cache-level configuration setting, among other factors.

NOTE: When JSON is imported into a `Region` as PDX, the
{apache-geode-javadoc}/org/apache/geode/pdx/PdxInstance.html#getClassName--[`PdxInstance.getClassName()`]
does not refer to a valid Java class. It is
{apache-geode-javadoc}/org/apache/geode/pdx/JSONFormatter.html#JSON_CLASSNAME[`JSONFormatter.JSON_CLASSNAME`].
As a result, `Region` data access operations, such as `Region.get(key)`, return a `PdxInstance` and not a Java object.

TIP: You may need to proxy `Region` read data access operations (such as `Region.get(key)`) by setting the SBDG property
`spring.boot.data.gemfire.cache.region.advice.enabled` to `true`. When this property is set, `Region` instances are
proxied to wrap a `PdxInstance` in a `PdxInstanceWrapper` to appropriately handle the `PdxInstance.getObject()` call
in your application code.

[[geode-data-using-import-metadata-id]]
===== The `id` field and the `@identifier` metadata field

Top-level objects in your JSON must have an identifier, such as an `id` field. This identifier is used as the identity
and key of the object (or `PdxInstance`) when stored in the `Region` (for example, `Region.put(key, object)`).

You may have noticed that the JSON for the "Orders" `Region` shown earlier declared an `id` field as the identifier:

.PurchaseOrder identifier ("id")
====
[source,text]
----
[{
  "@type": "example.app.pos.model.PurchaseOrder",
  "id": 1,
  ...
----
====

This follows the same convention used in Spring Data. Typically, Spring Data mapping infrastructure looks for a POJO
field or property annotated with {spring-data-commons-javadoc}/org/springframework/data/annotation/Id.html[`@Id`]. If no
field or property is annotated with `@Id`, the framework falls back to searching for a field or property named `id`.

In Spring Data for {geode-name}, this `@Id`-annotated or `id`-named field or property is used as the identifier
and as the key for the object when storing it into a `Region`.

However, what happens when an object or entity does not have a surrogate ID defined? Perhaps the application domain
model class is appropriately using natural identifiers, which is quite common in practice.

Consider a `Book` class defined as follows:

.Book class
====
[source,java]
----
@Region("Books")
class Book {

	Author author;

	@Id
	ISBN isbn;

	LocalDate publishedDate;

	Sring title;

}
----
====

As declared in the `Book` class, the identifier for `Book` is its `ISBN`, since the `isbn` field was annotated with
Spring Data's `@Id` mapping annotation. However, we cannot know this by searching for an `@Id` annotation in JSON.

You might be tempted to argue that if the `@type` metadata field is set, we would know the class type and could load
the class definition to learn about the identifier. That is all fine until the class is not actually on the application
classpath in the first place. This is one of the reasons why SBDG's JSON support serializes JSON to {geode-name}'s PDX
format. There might not be a class definition, which would lead to a `NoClassDefFoundError` or `ClassNotFoundException`.

So, what then?

In this case, SBDG lets you declare the `@identifier` JSON metadata field to inform the framework what to use as
the identifier for the object.

Consider the following example:

.Using "@identifer"
====
[source,json]
----
{
  "@type": "example.app.books.model.Book",
  "@identifier": "isbn",
  "author": {
    "id": 1,
    "name": "Josh Long"
  },
  "isbn": "978-1-449-374640-8",
  "publishedDate": "2017-08-01",
  "title": "Cloud Native Java"
}
----
====

The `@identifier` JSON metadata field informs the framework that the `isbn` field is the identifier for a `Book`.

[[geode-data-using-import-conditional]]
==== Conditionally Importing Data

While the Spring team recommends that users should only use this feature when developing and testing their Spring Boot
applications with {geode-name}, you may still occasionally use this feature in production.

You might use this feature in production to preload a (REPLICATE) Region with reference data. Reference data is largely
static, infrequently changing, and non-transactional. Preloading reference data is particularly useful when you want to
warm the cache.

When you use this feature for development and testing purposes, you can put your `Region`-specific JSON files in
`src/test/resources`. This ensures that the files are not included in your application artifact (such as a JAR or WAR)
when built and deployed to production.

However, if you must use this feature to preload data in your production environment, you can still conditionally load
data from JSON. To do so, configure the `spring.boot.data.gemfire.cache.data.import.active-profiles` property set to
the Spring profiles that must be active for the import to take effect.

Consider the following example:

.Conditional Importing JSON
====
[source,properties]
----
# Spring Boot application.properties

spring.boot.data.gemfire.cache.data.import.active-profiles=DEV, QA
----
====

For import to have an effect in this example, you must specifically set the `spring.profiles.active` property to one of
the valid, `active-profiles` listed in the import property (such as `QA`). Only one needs to match.

NOTE: There are many ways to conditionally build application artifacts. You might prefer to handle this concern in your
Gradle or Maven build.

[[geode-data-using-export]]
=== Exporting Data

Certain data stored in your application's `Regions` may be sensitive or confidential, and keeping the data secure is of
the utmost concern and priority. Therefore, exporting data is *disabled* by default.

However, if you use this feature for development and testing purposes, enabling the export capability may be useful to
move data from one environment to another. For example, if your QA team finds a bug in the application that uses a
particular data set, they can export the data and pass it back to the development team to import in their local
development environment to help debug the issue.

To enable export, set the `spring.boot.data.gemfire.cache.data.export.enabled` property to `true`:

.Enable Export
====
[source,properties]
----
# Spring Boot application.properties

spring.boot.data.gemfire.cache.data.export.enabled=true
----
====

SBDG is careful to export data to JSON in a format that {geode-name} expects on import and includes things such as
`@type` metadata fields.

WARNING: The `@identifier` metadata field is not generated automatically. While it is possible for POJOs stored in a
`Region` to include an `@identifier` metadata field when exported to JSON, it is not possible when the `Region` value
is a `PdxInstance` that did not originate from JSON. In this case, you must manually ensure that the `PdxInstance`
includes an `@identifier` metadata field before it is exported to JSON if necessary (for example, `Book.isbn`). This is
only necessary if your entity classes do not declare an explicit identifier field, such as with the `@Id` mapping
annotation, or do not have an `id` field. This scenario can also occur when inter-operating with native clients
that model the application domain objects differently and then serialize the objects by using PDX, storing them in
Regions on the server that are then later consumed by your Java-based, Spring Boot application.

WARNING: You may need to set the `-Dgemfire.disableShutdownHook` JVM System property to `true` before your Spring
Boot application starts up when using export. Unfortunately, this Java runtime shutdown hook is registered and enabled
in {geode-name}  by default, which results in the cache and the Regions being closed before the SBDG Export
functionality can export the data, thereby resulting in a `CacheClosedException`. SBDG
{github-url}/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/DataImportExportAutoConfiguration.java#L173-L183[makes a best effort]
to disable the {geode-name} JVM shutdown hook when export is enabled, but it is at the mercy of the JVM `ClassLoader`,
since {geode-name}'s JVM shutdown hook
{apache-geode-src}/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalDistributedSystem.java#L2185-L2223[registration]
is declared in a `static` initializer.

[[geode-data-using-import-export-api-extensions]]
=== Import/Export API Extensions

The API in SBDG for import and export functionality is separated into the following concerns:

* Data Format
* Resource Resolving
* Resource Reading
* Resource Writing

By breaking each of these functions apart into separate concerns, a developer can customize each aspect of the import
and export functions.

For example, you could import XML from the filesystem and then export JSON to a REST-based Web Service. By default, SBDG
imports JSON from the classpath and exports JSON to the filesystem.

However, not all environments expose a filesystem, such as cloud environments like PCF. Therefore, giving users control
over each aspect of the import and export processes is essential for performing the functions in any environment.

[[geode-data-using-import-export-api-extensions-data-format]]
==== Data Format

The primary interface to import data into a `Region` is `CacheDataImporter`.

`CacheDataImporter` is a `@FunctionalInterface` that extends Spring's
{spring-framework-javadoc}/org/springframework/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
interface to trigger the import of data after the `Region` has been initialized.

The interface is defined as follows:

.`CacheDataImporter`
====
[source,java]
----
interface CacheDataImporter extends BeanPostProcessor {

	Region importInto(Region region);

}
----
====

You can code the `importInto(:Region)` method to handle any data format (JSON, XML, and others) you prefer. Register a
bean that implements the `CacheDataImporter` interface in the Spring container, and the importer does its job.

On the flip side, the primary interface to export data from a `Region` is the `CacheDataExporter`.

`CacheDataExporter` is a `@FunctionalInterface` that extends Spring's
{spring-framework-javadoc}/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.html[`DestructionAwareBeanPostProcessor`]
interface to trigger the export of data before the `Region` is destroyed.

The interface is defined as follows:

.`CacheDataExporter`
====
[source,java]
----
interface CacheDataExporter extends DestructionAwareBeanPostProcessor {

	Region exportFrom(Region region);
}
----
====

You can code the `exportFrom(:Region)` method to handle any data format (JSON, XML, and others) you prefer. Register a
bean implementing the `CacheDataExporter` interface in the Spring container, and the exporter does its job.

For convenience, when you want to implement both import and export functionality, SBDG provides the
`CacheDataImporterExporter` interface, which extends both `CacheDataImporter` and `CacheDataExporter`:

.`CacheDataImporterExporter`
====
[source,java]
----
interface CacheDataImporterExporter extends CacheDataExporter, CacheDataImporter { }
----
====

For added support, SBDG also provides the `AbstractCacheDataImporterExporter` abstract base class to simplify
the implementation of your importer/exporter.

[[geode-data-using-import-export-api-extensions-data-format-lifecycle-management]]
===== Lifecycle Management

Sometimes, it is necessary to precisely control when data is imported or exported.

This is especially true on import, since different `Region` instances may be collocated or tied together through a
cache callback, such as a `CacheListener`. In these cases, the other `Region` may need to exist before the import
on the dependent `Region` proceeds, particularly if the dependencies were loosely defined.

Controlling the import is also important when you use SBDG's `@EnableClusterAware` annotation to push configuration
metadata from the client to the cluster in order to define server-side `Region` instances that match the client-side
`Region` instances, especially client `Region` instances targeted for import. The matching `Region` instances on the
server side must exist before data is imported into client (`PROXY`) `Region` instances.

In all cases, SBDG provides the `LifecycleAwareCacheDataImporterExporter` class to wrap your `CacheDataImporterExporter`
implementation. This class implements Spring's
{spring-framework-javadoc}/https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html[`SmartLifecycle`]
interface.

By implementing the `SmartLifecycle` interface, you can control in which `phase` of the Spring container the import
occurs. SBDG also exposes two more properties to control the lifecycle:

.Lifecycle Management Properties
====
[source,properties]
----
# Spring Boot application.properties

spring.boot.data.gemfire.cache.data.import.lifecycle=[EAGER|LAZY]
spring.boot.data.gemfire.cache.data.import.phase=1000000
----
====

`EAGER` acts immediately, after the `Region` is initialized (the default behavior). `LAZY` delays the import until the
`start()` method is called, which is invoked according to the `phase`, thereby ordering the import relative to the other
lifecycle-aware components that are registered in the Spring container.

The following example shows how to make your `CacheDataImporterExporter` lifecycle-aware:

====
[source,java]
----
@Configuration
class MyApplicationConfiguration {

	@Bean
    CacheDataImporterExporter importerExporter() {
		return new LifecycleAwareCacheDataImporterExporter(new MyCacheDataImporterExporter());
    }
}
----
====

[[geode-data-using-import-export-api-extensions-resource-resolution]]
==== Resource Resolution

Resolving resources used for import and export results in the creation of a Spring
{spring-framework-javadoc}/https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/io/Resource.html[`Resource`]
handle.

Resource resolution is a vital step to qualifying a resource, especially if the resource requires special logic
or permissions to access it. In this case, specific `Resource` handles can be returned and used by the reader
and writer of the `Resource` as appropriate for import or export operation.

SBDG encapsulates the algorithm for resolving `Resources` in the `ResourceResolver`
(https://en.wikipedia.org/wiki/Strategy_pattern[Strategy]) interface:

.ResourceResolver
====
[source,java]
----
@FunctionalInterface
interface ResourceResolver {

	Optional<Resource> resolve(String location);

	default Resouce required(String location) {
		// ...
    }
}
----
====

Additionally, SBDG provides the `ImportResourceResolver` and `ExportResourceResolver` marker interfaces and the
`AbstractImportResourceResolver` and `AbstractExportResourceResolver` abstract base classes for implementing the
resource resolution logic used by both import and export operations.

If you wish to customize the resolution of `Resources` used for import or export, your `CacheDataImporterExporter`
implementation can extend the `ResourceCapableCacheDataImporterExporter` abstract base class, which provides the
aforementioned interfaces and base classes.

As stated earlier, SBDG resolves resources on import from the classpath and resources on export to the filesystem.

You can customize this behavior by providing an implementation of `ImportResourceResolver`, `ExportResourceResolver`,
or both interfaces and declare instances as beans in the Spring context:

.Import & Export ResourceResolver beans
====
[source,java]
----
@Configuration
class MyApplicationConfiguration {

	@Bean
    ImportResourceResolver importResourceResolver() {
		return new MyImportResourceResolver();
    }

    @Bean
    ExportResourceResolver exportResourceResolver() {
		return new MyExportResourceResolver();
    }
}
----
====

TIP: If you need to customize the resource resolution process for each location (or `Region`) on import or export,
you can use the https://en.wikipedia.org/wiki/Composite_pattern[Composite software design pattern].

[[geode-data-using-import-export-api-extensions-resource-resolution-default-customization]]
===== Customize Default Resource Resolution

If you are content with the provided defaults but want to target specific locations on the classpath or filesystem
used by the import or export, SBDG additionally provides the following properties:

.Import/Export Resource Location Properties
====
[source,properties]
----
# Spring Boot application.properties

spring.boot.data.gemfire.cache.data.import.resource.location=...
spring.boot.data.gemfire.cache.data.export.resource.location=...
----
====

The properties accept any valid resource string, as specified in the Spring
{spring-framework-docs}/core.html#resources-resourceloader[documentation] (see *Table 10. Resource strings*).

This means that, even though import defaults from the classpath, you can change the location from classpath
to filesystem, or even network (for example, https://) by changing the prefix (or protocol).

Import/export resource location properties can refer to other properties through property placeholders, but SBDG
further lets you use SpEL inside the property values.

Consider the following example:

.Using SpEL
====
[source,properties]
----
# Spring Boot application.properties

spring.boot.data.gemfire.cache.data.import.resource.location=\
  https://#{#env['user.name']}:#{someBean.lookupPassword(#env['user.name'])}@#{host}:#{port}/cache/#{#regionName}/data/import
----
====

In this case, the import resource location refers to a rather sophisticated resource string by using a complex SpEL
expression.

SBDG populates the SpEL `EvaluationContext` with three sources of information:

* Access to the Spring `BeanFactory`
* Access to the Spring `Environment`
* Access to the current `Region`

Simple Java System properties or environment variables can be accessed with the following expression:

====
[source,text]
----
#{propertyName}
----
====

You can access more complex property names (including properties that use dot notation, such as the `user.home`
Java System property), directly from the `Environment` by using map style syntax as follows:

====
[source,text]
----
#{#env['property.name']}
----
====

The `#env` variable is set in the SpEL `EvaluationContext` to the Spring `Environment`.

Because the SpEL `EvaluationContext` is evaluated with the Spring `ApplicationContext` as the root object, you also have
access to the beans declared and registered in the Spring container and can invoke methods on them, as shown earlier
with `someBean.lookupPassword(..)`. `someBean` must be the name of the bean as declared and registered in the Spring
container.

CAUTION: Be careful when accessing beans declared in the Spring container with SpEL, particularly when using `EAGER`
import, as it may force those beans to be eagerly (or even prematurely) initialized.

SBDG also sets the `#regionName` variable in the `EvaluationContext` to the name of the `Region`, as determined by
{apache-geode-javadoc}/https://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/Region.html#getName--[`Region.getName()`],
targeted for import and export.

This lets you not only change the location of the resource but also change the resource name (such as a filename).

Consider the following example:

.Using `#regionName`
====
[source,properties]
----
# Spring Boot application.properties

spring.boot.data.gemfire.cache.data.export.resource.location=\
    file://#{#env['user.home']}/gemfire/cache/data/custom-filename-for-#{#regionName}.json
----
====

NOTE: By default, the exported file is stored in the working directory (`System.getProperty("user.dir")`)
of the Spring Boot application process.

TIP: See the Spring Framework {spring-framework-docs}/core.html#expressions[documentation] for more information on SpEL.

[[geode-data-using-import-export-api-extensions-resource-reading-writing]]
==== Reading & Writing Resources

The Spring {spring-framework-javadoc}/org/springframework/core/io/Resource.html[`Resource`] handle
specifies tion of a resource, not how the resource is read or written. Even the Spring
{spring-framework-javadoc}/org/springframework/core/io/ResourceLoader.html[`ResourceLoader`],
which is an interface for loading `Resources`, does not specifically read or write any content to the `Resource`.

SBDG separates these concerns into two interfaces: `ResourceReader` and `ResourceWriter`, respectively.
The design follows the same pattern used by Java's `InputStream/OutputStream` and `Reader/Writer` classes
in the `java.io` package.

The `ResourceReader` interfaces is defined as:

.ResourceReader
====
[source,java]
----
@FunctionalInterface
interface ResourceReader {

    byte[] read(Resource resource);

}
----
====

The `ResourceWriter` interfaces is defined as:

.ResourceWriter
====
[source,java]
----
@FunctionalInterface
interface ResourceWriter {

    void write(Resource resource, byte[] data);

}
----
====

Both interfaces provide additional methods to compose readers and writers, much like Java's `Consumer` and `Function`
interfaces in the `java.util.function` package. If a particular reader or writer is used in a composition and is unable
to handle the given `Resource`, it should throw a `UnhandledResourceException` to let the next reader or writer in the
composition try to read from or write to the `Resource`.

The reader or writer are free to throw a `ResourceReadException` or `ResourceWriteException` to break the chain of
reader and writer invocations in the composition.

To override the default export/import reader and writer used by SBDG, you can implement the `ResourceReader`
or `ResourceWriter` interfaces as appropriate and declare instances of these classes as beans in the Spring container:

.Custom `ResourceReader` & `ResourceWriter` beans
====
[source,java]
----
@Configuration
class MyApplicationConfiguration {

	@Bean
    ResourceReader myResourceReader() {
		return new MyResourceReader()
            .thenReadFrom(new MyOtherResourceReader());
    }

    @Bean
    ResourceWriter myResourceWriter() {
		return new MyResourceWriter();
    }
}
----
====
