[[geode-data-using]]
== Using Data
:geode-name: Apache Geode


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

For those 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_ allows you to filter
data while its being imported and exported.

Finally, {geode-name} Shell (_Gfsh_) commands can be used 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. Data is imported and exported in JSON format, by default.

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

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

WARNING: While this feature has utility 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 only be used for development and testing purposes.

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

You can import data into a `Region` by defining a JSON file containing the JSON object(s) you wish to load. The JSON
file must follow the naming convention below 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_", then you would create a JSON file called `data-orders.json`
and place it in the root of your application classpath (e.g. in `src/test/resources`).

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

The JSON file containing JSON data for _Orders_ 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 matching the JSON data might look something like:

.Point-of-Sale (POS) Application 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 seen above, the object model and corresponding JSON can be arbitrarily complex with a hierarchy of objects
having complex types.

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

You will notice a few other details contained in the object model and JSON shown above.

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

First, we declared an `@type` JSON metadata field.  This field does not map to any specific field or property of
the application domain model class (e.g. `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 (i.e. by calling
`PdxInstance.getObject()`).

For 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
the JSON object will map to. 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 (e.g. `Region.get(key)`) by setting the SBDG property
`spring.boot.data.gemfire.cache.region.advice.enabled` to `true`. When this property is set, `Regions` are proxied to
wrap a `PdxInstance` in a `PdxInstanceWrapper` in order to appropriately handle the `PdxInstance.getObject()` call in
your application code.

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

The top-level objects in your JSON must have an identifier, such as an "id" field.  This identifier is used as the
object's (or `PdxInstance`'s) identity and "key" when stored in the `Region` (e.g. `Region.put(key, object)`).

You will have noticed the the JSON for the _Orders_ above 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`, then the framework falls back to searching for a field or property named "id".

In Spring Data for {geode-name} (SDG), 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 and simply 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 above, 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 allows you to declare the `@identifier` JSON metadata field to inform the framework
what to use as the identifier for the object.

For 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"
}
----

Here, 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}, a user may occasionally use this feature in production.

Users 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 in caching
use cases, where you want to "warm" the cache.

When using this feature for development and testing purposes, you can simply put your `Region` specific JSON files in
`src/test/resources`. This ensures the files will not be included in your application artifact (e.g. JAR, WAR) when
deployed to production.

However, if you must use this feature to preload data in your production environment, then 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 profile(s) that must be active for the import to take effect.

For example:

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

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

In order for import to have an effect in this example, you must specifically set the `spring.profiles.active`
property to 1 of the valid, "_active-profiles_" listed in the import property (e.g. `QA`). Only 1 needs to match.

NOTE: There are many ways to conditionally build application artifacts. Some users might prefer to handle this concern
in their Gradle or Maven builds.

[[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 are using this feature for development and testing purposes then enabling the _export_ capability may be
useful to move data from 1 environment to another. For example, if your QA team finds a bug in the application using a
particular data set, then 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 the `PdxInstance` includes
an `@identifier` metadata field before it is exported to JSON if necessary (e.g. `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, then serialize the objects using PDX storing them in Regions on the server that are then
later consumed by your Spring Boot application.

WARNING: It may be necessary 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 _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} 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/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, it affords a developer the ability to customize each
aspect of the Import/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 the filesystem, such as cloud environments like PCF. Therefore, giving users
control over each aspect of import/export process 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 the `CacheDataImporter`.

`CacheDataImporter` is a `@FunctionalInterface` extending 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:

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

	Region importInto(Region region);

}
----

The `importInto(..)` method can be coded to handle any data format (JSON, XML, etc) you prefer. Simply register a bean
implementing the `CacheDataImporter` interface in the Spring container and the importer will do its job.

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

`CacheDataExporter` is a `@FunctionalInterface` extending 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:

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

	Region exportFrom(Region region);

}
----

The `exportFrom(..)` method can be coded to handle any data format (JSON, XML, etc) you prefer. Simply register a bean
implementing the `CacheDataExporter` interface in the Spring container and the exporter will do 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 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 control precisely when data is imported or exported.

This is especially true on import since different `Regions` maybe collocated or tied together via a cache callback like
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.

Another case when controlling the import is important is when you are using SBDG's `@EnableClusterAware` annotation to
push configuration metadata from the client to the cluster in order to define server-side `Regions` matching the
client-side `Regions`, especially client `Regions` targeted for import.  The matching `Regions` on the server-side must
exist before data is imported into client (`PROXY`) `Regions`.

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, it allows you to control which `phase` of the Spring container
the import occurs. As such SBDG 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 other
"lifecycle-aware" components registered in the Spring container.

To make your `CacheDataImporterExporter` "lifecycle-aware" simply do:

[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 qualify 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 is 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 along with
the `AbstractImportResourceResolver` and `AbstractExportResourceResolver` abstract base classes for implementing
resource resolution logic used by both import and export operations, for your convenience.

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

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

It is easy to customize this behavior simply by providing an implementation of either or both the
`ImportResourceResolver` and `ExportResourceResolver` 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 per location (or `Region`) on import or export, then you
could 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, then 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 even though the import defaults from the classpath, it is simple to change the location from classpath
to filesystem, or even network (e.g. https://) simply by changing the _prefix_ (or _protocol_).

Of course, import/export resource location properties can refer to other properties via property placeholders, but SBDG
further allows users to use SpEL inside the property values.

For 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
----

The import resource location in this case refers to a rather sophisticated resource string using a complex SpEL
expression.

Out-of-the-box, SBDG populates the SpEL `EvaluationContext` with 3 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 expression:

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

For more complex property names (e.g. properties using dot notation, such as the `user.home` Java System property),
users can access these properties directly from the `Environment` 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 context and can invoke methods on them, as shown above with
`someBean.lookupPassword(..)`.  "_someBean_" must be the name of the bean as declared/registered in the Spring context.

WARNING: Be careful when accessing beans declared in the Spring context 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/export.

This allows you to not only change the location of the resource but also change the resource name (e.g. filename).

For 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 (i.e. `System.getProperty("user.dir")`) of the
Spring Boot application process.

TIP: See the Spring {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
the location of a resource, not how to read or write it. 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`.

As such, 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 interfaces are basically defined as:

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

    byte[] read(Resource resource);

}
----

And...

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

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

}
----

Both of interfaces provide additional methods to _compose_ readers and writers, much like Java's own `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`, then it should throw a `UnhandledResourceException` to allow the next
reader or writer in the composition to try and read from or write to the `Resource`.

Of course, 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 out-of-the-box, simply implement
the `ResourceReader` and/or `ResourceWriter` interfaces as appropriate and declare instances of these classes as beans
in the Spring context:

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

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

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