Include guide and example of Spring Boot auto-configuration for Apache Geode & Pivotal GemFire.

This commit is contained in:
John Blum
2019-04-22 00:46:55 -07:00
parent ce2cdc3660
commit 687ba1e54c
16 changed files with 1039 additions and 2 deletions

View File

@@ -26,8 +26,9 @@ asciidoctor {
'version-snapshot': snapshotBuild,
'version-milestone': milestoneBuild,
'version-release': releaseBuild,
'download-url' : "${githubBaseUrl}/archive/${githubTag}.zip",
'github-url': githubUrl,
'github-samples-url': "${githubUrl}/spring-geode-samples",
'download-url' : "${githubBaseUrl}/archive/${githubTag}.zip",
'spring-version' : versions['org.springframework:spring-core'],
'spring-boot-version' : "${springBootVersion}",
'spring-boot-data-geode-version' : project.version,
@@ -37,7 +38,8 @@ asciidoctor {
'spring-session-data-gemfire-version' : "${springSessionDataGeodeVersion}",
'spring-session-data-geode-version' : "${springSessionDataGeodeVersion}",
'docs-src-dir' : rootProject.projectDir.path + '/spring-geode-docs/src/main/java',
'examples-dir' : rootProject.projectDir.path + '/spring-geode-examples/'
'examples-dir' : rootProject.projectDir.path + '/spring-geode-examples/',
'samples-dir' : rootProject.projectDir.path + '/spring-geode-samples/'
}
javadoc {

View File

@@ -0,0 +1,767 @@
[[geode-samples-boot-configuration]]
= Spring Boot Auto-configuration for Apache Geode & Pivotal GemFire
This guide walks you through building a simple Customer Service, Spring Boot application using Apache Geode to manage
Customer interactions. It is assumed that you are already familiar with Spring Boot and Apache Geode. By the end of
this lesson, users should have a better understanding of the _auto-configuration_ support provided by
Spring Boot Data Geode (SBDG).
Let's begin.
NOTE: This guide builds on the https://www.youtube.com/watch?v=OvY5wzCtOV0[_Simplifying Apache Geode with Spring Data_]
talk presented by John Blum during the 2017 SpringOne Platform conference in San Francisco, CA. While this example
as well as the example presented during the talk both use Spring Boot, only this example is using Spring Boot
for Apache Geode (SBDG). Therefore, this guide and its example is an improvement over the example in the presentation.
[[geode-samples-boot-configuration-app-domain-classes]]
== Application Domain Classes
==== `Customer` class
Like any sensible application development project, we begin by modeling the data our application needs to manage,
i.e. a `Customer`. For this example, the `Customer` class is implemented as follows:
link:{samples-dir}/boot/configuration/src/main/java/example/app/crm/model/Customer.java[]
The `Customer` class uses https://projectlombok.org/[Project Lombok] to simplify the implementation so that we can focus
on the important details. Lombok is useful for testing or prototyping purposes. However, using Lombok is not required
and in most production applications, I would not recommend using it.
Additionally, the `Customer` class is annotated with Spring Data Geode's (SDG) `@Region` annotation. `@Region`
is a mapping annotation declaring the Apache Geode cache `Region` in which Customer data will be persisted.
Finally, the `org.springframework.data.annotation.Id` annotation was used to designate the `Customer.id` field as
the identifier for `Customer` objects. The identifier is the Key of the Entry in the Apache Geode cache `Region`.
A `Region` is a distributed version of `java.util.Map`.
NOTE: If the `@Region` annotation is not explicitly declared, then SDG uses the simple name of the class, which in this
case would just be "Customer", to identify the `Region`. However, there is another reason we explicitly annotated the
`Customer` class with `@Region`, which we will cover below.
==== `CustomerRepository` interface
Next, we create a _Data Access Object_ (DAO), or Spring Data _Repository_ to persist `Customers` to Apache Geode:
link:{samples-dir}/boot/configuration/src/main/java/example/app/crm/repo/CustomerRepository.java[]
`CustomerRepository` is a Spring Data `CrudRepository`. `CrudRepository` provides basic CRUD (Create, Read, Update,
Delete) data access operations along with simple queries for `Customer` objects stored in Apache Geode.
Spring Data Geode is responsible for creating a proxy for your application-defined _Repository_ interfaces in order to
implement any query methods you may have explicitly defined on the interface in addition to the data access operations
provided by the `CrudRepository` interface extension.
In addition to the base `CrudRepository` operations, `CustomerRepository` has additionally defined a
`findByNameLike(:String):Customer` query method.
NOTE: Though it is beyond the scope of this document, Spring Data's _Repository_ infrastructure is capable of generating
data store specific queries (e.g. Apache Geode OQL) for _Repository_ interface query method declarations just by
introspecting the method signature. The query methods must conform to specific conventions. Alternatively, users
may use `@Query` to annotate query methods and specify the raw query instead (i.e. OQL for Apache Geode,
SQL for JDBC, and so on).
==== Customer Service Application (main class)
Now that we have created the basic domain classes of our Customer Service application, we need a main application class
to drive the interactions with `Customers`.
The end result looks like this:
link:{samples-dir}/boot/configuration/src/main/java/example/app/crm/CustomerServiceApplication.java[]
The `CustomerServiceApplication` class is annotated with `@SpringBootApplication`. Therefore, this main class is
a proper Spring Boot application equipped with all the features of Spring Boot (e.g. _auto-configuration_).
Additionally, we use Spring Boot's `SpringApplicationBuilder` in the `main` method to configure and bootstrap
the Customer Service application.
Then, we declare a Spring Boot `ApplicationRunner` bean, which is invoked by Spring Boot after the Spring container
(i.e. `ApplicationContext`) has been properly initialized and started. Our `ApplicationRunner` defines the Customer
interactions performed by our Customer Service application.
Specifically, we create a new `Customer` object ("Jon Doe"), save him to the Apache Geode "Customers" cache Region,
and then query for "Jon Doe" using an OQL query with the predicate: `name LIKE '%Doe'`.
NOTE: `%` is the wildcard for OQL text searches.
[[geode-samples-boot-configuration-autoconfig]]
== Auto-configuration for Apache Geode, Take One
"_With great power comes great responsibility._" - Uncle Ben
While it is not apparent (yet), there is a lot of intrinsic power provided by Spring Boot Data Geode (SBDG)
in this example.
==== Cache instance
First, in order to put anything into Apache Geode, you need a cache instance. A cache instance is also required to
create the Regions which ultimately will store the application's data (state). Again, a `Region` is just a Key/Value
data structure, like a `java.util.Map`, mapping a Key to an Object. A `Region` is actually much more than a simple
`Map` since it is distributed. However, since `Region` implements `java.util.Map`, it can be treated as such.
NOTE: A complete discussion of a `Region` and it concepts are beyond the scope of this document. You may learn more
by reading the Apache Geode User Guide on {apache-geode-docs}/developing/region_options/chapter_overview.html[Regions].
SBDG is opinionated and assumes most developer applications will be client applications in Apache Geode's
{apache-geode-docs}/topologies_and_comm/cs_configuration/chapter_overview.html[client/server topology]. As a result,
SBDG will auto-configure a `ClientCache` instance by default.
We can make this more apparent by disabling the _auto-configuration_ of the `ClientCache` instance provided by SBDG:
.Disabling ClientCache auto-configuration
[source,java]
----
@SpringBootApplication(exclude = ClientCacheAutoConfiguration.class)
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
public class CustomerServiceApplication {
...
}
----
Note the `exclude` on the `ClientCacheAutoConfiguration` class.
With the correct log level set, you will see an error message similar to:
.Error resulting from no ClientCache instance
[source,txt]
----
16:20:47.543 [main] DEBUG o.s.b.d.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'example.app.crm.repo.CustomerRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1509) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:819) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
...
16:20:47.548 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method runner in example.app.crm.CustomerServiceApplication required a bean of type 'example.app.crm.repo.CustomerRepository' that could not be found.
----
Essentially, the `CustomerRepository` could not be injected into our `CustomerServiceApplication` class,
`ApplicationRunner` bean method because the `CustomerRepository`, which depends on the "Customers" Region,
could not be created. The `CustomerRepository` could not be created because the "Customers" Region
could not be created. The "Customers" Region could not be created because there was no cache instance available
(e.g. `ClientCache`).
The `ClientCache` auto-configuration is equivalent to the following:
.Equivalent ClientCache configuration
[source,java]
----
@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
public class CustomerServiceApplication {
...
}
----
That is, we would need to explicitly declare the `@ClientCacheApplication` annotation if we were not using SBDG.
==== Repository instance
We are also using the Spring Data (Geode) _Repository_ infrastructure in the Customer Service application.
This should be evident from our definition of the application-specific `CustomerRepository` interface.
If we disable the auto-configuration of the Spring Data Repository infrastructure:
. Disabling Spring Data Repositories
[source,java]
----
@SpringBootApplication(exclude = RepositoriesAutoConfiguration.class)
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
public class CustomerServiceApplication {
...
}
----
We would run into a similar error:
.Error resulting from no proxied CustomerRepository instance
[source,txt]
----
17:31:21.231 [main] DEBUG o.s.b.d.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'example.app.crm.repo.CustomerRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1509) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:819) ~[spring-beans-5.0.13.RELEASE.jar:5.0.13.RELEASE]
...
17:31:21.235 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method runner in example.app.crm.CustomerServiceApplication required a bean of type 'example.app.crm.repo.CustomerRepository' that could not be found.
----
The Spring Data _Repository auto-configuration_ even takes care of locating our application Repository interface
definitions for us.
Without auto-configuration, you would need to:
.Equivalent Spring Data Repositories configuration
[source,java]
----
@SpringBootApplication
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
public class CustomerServiceApplication {
...
}
----
That is, you would need to explicitly declare the `@EnableGemfireRepositories` annotation and set the `basePackages`
attribute, or the equivalent, type-safe `basePackageClasses` attribute, if you were not using SBDG.
==== Entity-defined Regions
The only explicit declaration of configuration in our Customer Service application is with the
`@EnableEntityDefinedRegions` annotation. As was alluded to above, there was another reason to explicitly declare
the `@Region` annotation on our `Customer` class.
We could, for all intents and purposes, explicitly define the client-local, "Customers" Regions as so:
.JavaConfig Bean Defintion for the Customers Region
[source,java]
----
@Bean("Customers")
public ClientRegionFactoryBean<Long, Customer> customersRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Long, Customer> customersRegion = new ClientRegionFactoryBean<>();
customersRegion.setCache(gemfireCache);
customersRegion.setClose(false);
customersRegion.setShortcut(ClientRegionShortcut.LOCAL);
return customersRegion;
}
----
Or, even define the "Customers" Region using XML:
.XML Bean Definition for the Customers Region
[source,xml]
----
<gfe:client-region id="Customers" shortcut="LOCAL"/>
----
But, it is very convenient to scan and then define Regions (whether client or server/peer Regions) based on
your application entity classes themselves (e.g. `Customer`):
.Annotation-based config for the Customers Region
[source,java]
----
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
----
The `basePackageClasses` attribute is an alternative to `basePackages`, and is a more ideal, type-safe way to target
the packages (and subpackages) containing the entity classes that your application will persist to Apache Geode. You
need only choose 1 class in the top-level package where you want the scan to begin. Spring Data Geode uses this class
to determine the package to start the scan. 'basePackageClasses` accepts an array of `Class` types so that you can
specify multiple independent top-level packages. The annotation also includes the ability to filter types.
However, the `@EnableEntityDefinedRegions` annotation only works when the entity class (e.g. `Customer`) is explicitly
annotated with the `@Region` annotation (e.g. `@Region("Customers")`), otherwise it ignores the class.
You will also notice that the data policy type (i.e. `clientRegionShort`, or simply `shortcut`) is set to `LOCAL`
in our example. Why?
Well, initially we wanted to get up and running as quickly as possible, without a lot of ceremony and fuss. By using a
`LOCAL` client Region initially, we are not required to start a server for the client to be able to store data.
However, while `LOCAL` client Regions can be useful for some purposes (e.g. local processing/querying), it is
more common for a client to persist data in a cluster of servers, and for that data to be share by multiple clients,
especially as the application is scaled out to meet demand.
[[geode-samples-boot-configuration-clientserver]]
== Switching to Client/Server
We continue with our example by switching from local to a client/server architecture.
If you are rapidly prototyping your application and want to lift off the ground quickly, then it is useful to start
locally and gradually migrate to a client/server topology.
To switch to the client/server architecture, all you need to do is remove the `clientRegionShortcut` attribute:
.Client/Server Topology Region Configuration
[source,java]
----
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
----
The default value for the `clientRegionShortcut` attribute is `ClientRegionShortcut.PROXY`. This means no data
is kept locally. All data is sent from the client to 1 or more servers in a cluster.
However, if we try to run the application, it will fail:
.NoAvailableServersException
[source,txt]
----
Caused by: org.apache.geode.cache.client.NoAvailableServersException
at org.apache.geode.cache.client.internal.pooling.ConnectionManagerImpl.borrowConnection(ConnectionManagerImpl.java:234) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:136) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:115) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.PoolImpl.execute(PoolImpl.java:763) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.QueryOp.execute(QueryOp.java:58) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.ServerProxy.query(ServerProxy.java:70) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.query.internal.DefaultQuery.executeOnServer(DefaultQuery.java:456) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:338) ~[geode-core-1.2.1.jar:?]
at org.springframework.data.gemfire.GemfireTemplate.find(GemfireTemplate.java:311) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
at org.springframework.data.gemfire.repository.support.SimpleGemfireRepository.count(SimpleGemfireRepository.java:129) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
...
at example.app.crm.CustomerServiceApplication.lambda$runner$0(CustomerServiceApplication.java:59) ~[classes/:?]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:783) ~[spring-boot-2.0.9.RELEASE.jar:2.0.9.RELEASE]
----
The client is expecting there to be a cluster of servers to communicate with and to store/access data.
There are several ways in which to to start a cluster of GemFire/Geode servers. For example, you may use Spring
to configure and bootstrap the cluster, which is demonstrated <<geode-cluster-configuration-bootstrapping,here>>.
For this sample, we are going to use the tools provided with Apache Geode or Pivotal GemFire, e.g. _Gfsh_
(GemFire/Geode Shell) for reasons that will become apparent later.
NOTE: You need to https://geode.apache.org/releases/[download] and {apache-geode-docs}/prereq_and_install.html[install]
a full distribution of Apache Geode to make use of the provided tools. After installation, you will need to set
the GEODE (or GEMFIRE) environment variable to the location of your installation. Additionally, add $GEODE/bin
to your system $PATH.
Once Apache Geode is successfully installed, you can open a command prompt (terminal) and do:
.Running Gfsh
[source,txt]
----
$ echo $GEMFIRE
/Users/jblum/pivdev/apache-geode-1.2.1
jblum-mbpro-2:lab jblum$ gfsh
_________________________ __
/ _____/ ______/ ______/ /____/ /
/ / __/ /___ /_____ / _____ /
/ /__/ / ____/ _____/ / / / /
/______/_/ /______/_/ /_/ 1.2.1
Monitor and Manage Apache Geode
gfsh>
----
You are set to go.
For convenience, this sample provides a _Gfsh_ shell script to start the cluster:
link:{samples-dir}/boot/configuration/src/main/resources/geode/bin/start-simple-cluster.gfsh[]
Specifically, we are starting 1 Locator and 1 Server, all running with the default ports.
Then you can execute the Gfsh shell script using:
.Run the start-simple-cluster.gfsh
[source,txt]
----
gfsh>run --file=/Users/jblum/pivdev/spring-boot-data-geode/samples/boot/configuration/src/main/resources/geode/bin/start-simple-cluster.gfsh
1. Executing - start locator --name=LocatorOne --log-level=config
Starting a Geode Locator in /Users/jblum/pivdev/lab/LocatorOne...
....
Locator in /Users/jblum/pivdev/lab/LocatorOne on 10.99.199.24[10334] as LocatorOne is currently online.
Process ID: 68425
Uptime: 2 seconds
Geode Version: 1.2.1
Java Version: 1.8.0_192
Log File: /Users/jblum/pivdev/lab/LocatorOne/LocatorOne.log
JVM Arguments: -Dgemfire.log-level=config -Dgemfire.enable-cluster-configuration=true -Dgemfire.load-cluster-configuration-from-dir=false -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/jblum/pivdev/apache-geode-1.2.1/lib/geode-core-1.2.1.jar:/Users/jblum/pivdev/apache-geode-1.2.1/lib/geode-dependencies.jar
Successfully connected to: JMX Manager [host=10.99.199.24, port=1099]
Cluster configuration service is up and running.
2. Executing - start server --name=ServerOne --log-level=config
Starting a Geode Server in /Users/jblum/pivdev/lab/ServerOne...
.....
Server in /Users/jblum/pivdev/lab/ServerOne on 10.99.199.24[40404] as ServerOne is currently online.
Process ID: 68434
Uptime: 2 seconds
Geode Version: 1.2.1
Java Version: 1.8.0_192
Log File: /Users/jblum/pivdev/lab/ServerOne/ServerOne.log
JVM Arguments: -Dgemfire.default.locators=10.99.199.24[10334] -Dgemfire.use-cluster-configuration=true -Dgemfire.start-dev-rest-api=false -Dgemfire.log-level=config -XX:OnOutOfMemoryError=kill -KILL %p -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/jblum/pivdev/apache-geode-1.2.1/lib/geode-core-1.2.1.jar:/Users/jblum/pivdev/apache-geode-1.2.1/lib/geode-dependencies.jar
----
NOTE: You will need to change the path to spring-boot-data-geode/samples/boot/configuration directory in the
`run --file=...` _Gfsh_ command above based on where you cloned the `spring-boot-data-geode` project on your computer.
Now, our simple cluster with an Apache Geode Locator and (Cache) Server is running. We can verify by
listing and describing members:
.List and Describe Members
[source,txt]
----
gfsh>list members
Name | Id
---------- | ---------------------------------------------------
LocatorOne | 10.99.199.24(LocatorOne:68425:locator)<ec><v0>:1024
ServerOne | 10.99.199.24(ServerOne:68434)<v1>:1025
gfsh>describe member --name=ServerOne
Name : ServerOne
Id : 10.99.199.24(ServerOne:68434)<v1>:1025
Host : 10.99.199.24
Regions :
PID : 68434
Groups :
Used Heap : 27M
Max Heap : 3641M
Working Dir : /Users/jblum/pivdev/lab/ServerOne
Log file : /Users/jblum/pivdev/lab/ServerOne/ServerOne.log
Locators : 10.99.199.24[10334]
Cache Server Information
Server Bind : null
Server Port : 40404
Running : true
Client Connections : 0
----
What happens if we try to run our application now?
It will fail:
.RegionNotFoundException
[source,txt]
----
17:42:16.873 [main] ERROR o.s.b.SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to execute ApplicationRunner
...
at example.app.crm.CustomerServiceApplication.main(CustomerServiceApplication.java:51) [classes/:?]
Caused by: org.springframework.dao.DataAccessResourceFailureException: remote server on 10.99.199.24(SpringBasedCacheClientApplication:68473:loner):51142:f9f4573d:SpringBasedCacheClientApplication: While performing a remote query; nested exception is org.apache.geode.cache.client.ServerOperationException: remote server on 10.99.199.24(SpringBasedCacheClientApplication:68473:loner):51142:f9f4573d:SpringBasedCacheClientApplication: While performing a remote query
at org.springframework.data.gemfire.GemfireCacheUtils.convertGemfireAccessException(GemfireCacheUtils.java:230) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
at org.springframework.data.gemfire.GemfireAccessor.convertGemFireAccessException(GemfireAccessor.java:91) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
at org.springframework.data.gemfire.GemfireTemplate.find(GemfireTemplate.java:329) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
at org.springframework.data.gemfire.repository.support.SimpleGemfireRepository.count(SimpleGemfireRepository.java:129) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
...
at example.app.crm.CustomerServiceApplication.lambda$runner$0(CustomerServiceApplication.java:59) ~[classes/:?]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:783) ~[spring-boot-2.0.9.RELEASE.jar:2.0.9.RELEASE]
... 3 more
Caused by: org.apache.geode.cache.client.ServerOperationException: remote server on 10.99.199.24(SpringBasedCacheClientApplication:68473:loner):51142:f9f4573d:SpringBasedCacheClientApplication: While performing a remote query
at org.apache.geode.cache.client.internal.AbstractOp.processChunkedResponse(AbstractOp.java:352) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.QueryOp$QueryOpImpl.processResponse(QueryOp.java:170) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.AbstractOp.processResponse(AbstractOp.java:230) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.AbstractOp.attempt(AbstractOp.java:394) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.AbstractOp.attemptReadResponse(AbstractOp.java:203) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.ConnectionImpl.execute(ConnectionImpl.java:275) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.pooling.PooledConnection.execute(PooledConnection.java:332) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.OpExecutorImpl.executeWithPossibleReAuthentication(OpExecutorImpl.java:900) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:158) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:115) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.PoolImpl.execute(PoolImpl.java:763) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.QueryOp.execute(QueryOp.java:58) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.ServerProxy.query(ServerProxy.java:70) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.query.internal.DefaultQuery.executeOnServer(DefaultQuery.java:456) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:338) ~[geode-core-1.2.1.jar:?]
at org.springframework.data.gemfire.GemfireTemplate.find(GemfireTemplate.java:311) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
at org.springframework.data.gemfire.repository.support.SimpleGemfireRepository.count(SimpleGemfireRepository.java:129) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
...
at example.app.crm.CustomerServiceApplication.lambda$runner$0(CustomerServiceApplication.java:59) ~[classes/:?]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:783) ~[spring-boot-2.0.9.RELEASE.jar:2.0.9.RELEASE]
... 3 more
Caused by: org.apache.geode.cache.query.RegionNotFoundException: Region not found: /Customers
at org.apache.geode.cache.query.internal.DefaultQuery.checkQueryOnPR(DefaultQuery.java:599) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:348) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:319) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.BaseCommandQuery.processQueryUsingParams(BaseCommandQuery.java:121) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.BaseCommandQuery.processQuery(BaseCommandQuery.java:65) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.command.Query.cmdExecute(Query.java:91) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.BaseCommand.execute(BaseCommand.java:165) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.ServerConnection.doNormalMsg(ServerConnection.java:791) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.ServerConnection.doOneMessage(ServerConnection.java:922) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.ServerConnection.run(ServerConnection.java:1180) ~[geode-core-1.2.1.jar:?]
...
----
The application fails to run because we (deliberately) did not create a corresponding, server-side, "Customers" Region.
In order for a client to send data via a client `PROXY` Region (a Region with no local state) to a server in a cluster,
at least one server in the cluster must have a matching Region by name (i.e. "Customers").
Indeed, we have no Regions in the cluster:
.List Regions
[source,txt]
----
gfsh>list regions
No Regions Found
----
Of course, you could have created the matching server-side, "Customers" Region using _Gfsh_:
[source,txt]
----
gfsh>create region --name=Customers --type=PARTITION
----
But, what if you have hundreds of domain objects, which is not unreasonable in a practical enterprise application?
While it is not a "convention" in Spring Boot for Apache Geode, Spring Data for Apache Geode (SDG) comes to our rescue.
We simply only need to enable cluster configuration from the client:
.Enable Cluster Configuration from the Client
[source,java]
----
@SpringBootApplication
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
@EnableClusterConfiguration(useHttp = true)
public class CustomerServiceApplication {
...
}
----
That is, we annotate our Customer Service application class with SDG's `@EnableClusterConfiguration` annotation.
Additionally, we have set the `useHttp` attribute to `true`. This sends the configuration meta-data from the client
to the cluster via GemFire/Geode's Management REST API.
This is useful when your GemFire/Geode cluster may be running behind a firewall, such as on public cloud infrastructure.
However, there are other benefits to using HTTP as well. As stated, the client send configuation meta-data to
GemFire/Geode's Management REST interface, which is a facade for the server-side Cluster Configuration Service. If
another member (e.g. server) is added to the cluster as a peer, then this member will get the same configuration. If
the entire cluster goes down, it will have the same configuration when it restarts.
SDG is careful not to stomp on existing Regions since those Regions might have data in them. Declaring the
`@EnableClusterConfiguration` annotation is a useful development-time utility, but it is recommended to explicitly
define and declare your Regions in production environments, either using _Gfsh_ or Spring confg.
Now, we can run our application again, and this time, it works!
.Client/Server Run Successful
[source,java]
----
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.9.RELEASE)
Saving Customer [Customer(name=Jon Doe)]
Querying for Customer [SELECT * FROM /Customers WHERE name LIKE '%Doe']
Customer was [Customer(name=Jon Doe)]
Process finished with exit code 0
----
In the cluster (server-side), we will also see that the "Customers" Region was created successfully:
.List & Describe Regions
[source,txt]
----
gfsh>list regions
List of regions
---------------
Customers
gfsh>describe region --name=/Customers
..........................................................
Name : Customers
Data Policy : partition
Hosting Members : ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 1
| data-policy | PARTITION
----
We see that the "Customers" Region has a size of 1. We can even query the "Customers" Region:
.Query for all Customers
[source,java]
----
gfsh>query --query="SELECT customer.name FROM /Customers customer"
Result : true
Limit : 100
Rows : 1
Result
-------
Jon Doe
----
[[geode-samples-boot-configuration-clientserver-autoconfig]]
== Auto-configuration for Apache Geode, Take Two
What may not be apparent in this example up to this point is how the data got from the client to the server. Certainly,
our client did send `Jon Doe` to the server, but our `Customer` class is not `java.io.Serializable`.
Any object that is sent over a network, between two Java processes, or streamed to/from disk, must be serializable.
Additionally, when we started the cluster, we also did not include any application domain classes on the classpath
of any member in the cluster. As further evidence, we an adjust our query slightly:
.Invalid Query
[source,txt]
----
gfsh>query --query="SELECT * FROM /Customers"
Message : Could not create an instance of a class example.app.crm.model.Customer
Result : false
----
If we tried to perform a `get`, we would hit a similar error:
.Region.get(key)
[source,txt]
----
gfsh>get --region=/Customers --key=1 --key-class=java.lang.Long
Message : Could not create an instance of a class example.app.crm.model.Customer
Result : false
----
So, how was the data sent, then?
Well, Apache Geode and Pivotal GemFire provide 2 proprietary serialization formats in addition to _Java Serialization_:
{apache-geode-docs}/developing/data_serialization/gemfire_data_serialization.html[Data Serialization]
and {apache-geode-docs}/developing/data_serialization/gemfire_pdx_serialization.html[PDX], or _Portable Data Exchange_.
While _Data Serialization_ is more efficient, PDX is more flexible (i.e. "portable"). PDX enables data to be queried
in serialized form and is the format used to support both Java and native clients (C++, C#). Therefore, PDX is
auto-configured by Spring Boot Data Geode (SBDG), by default.
This is convenient since you may not want to implement `java.io.Serializable` for all your application domain model
types that you store in Apache Geode. In other cases, you may not have control over the types referred to by your
application domain model types, such as when using a 3rd party library.
So, SBDG auto-configures PDX and uses Spring Data Geode's `MappingPdxSerializer` as the `PdxSerializer` to de/serialize
all application domain types.
If we disable PDX auto-configuration, we can see the effects of trying to serialize a non-serializable type, `Customer`.
First, let's destroy the server-side "Customers" Region:
.Destroy "Customers" Region
[source,txt]
----
gfsh>destroy region --name=/Customers
"/Customers" destroyed successfully.
gfsh>list regions
No Regions Found
----
Then, we disable PDX _auto-configuration_:
.Disable PDX Auto-configuration
[source,java]
----
@SpringBootApplication(exclude = PdxSerializationAutoConfiguration.class)
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
@EnableClusterConfiguration(useHttp = true)
public class CustomerServiceApplication {
...
}
----
When we re-run the application, we get the error we would expect:
.NotSerializableException
[source,txt]
----
Caused by: java.io.NotSerializableException: example.app.crm.model.Customer
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) ~[?:1.8.0_192]
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) ~[?:1.8.0_192]
at org.apache.geode.internal.InternalDataSerializer.writeSerializableObject(InternalDataSerializer.java:2248) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.InternalDataSerializer.basicWriteObject(InternalDataSerializer.java:2123) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.DataSerializer.writeObject(DataSerializer.java:2936) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.util.BlobHelper.serializeTo(BlobHelper.java:66) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.Message.serializeAndAddPart(Message.java:396) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.Message.addObjPart(Message.java:340) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.tier.sockets.Message.addObjPart(Message.java:319) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.PutOp$PutOpImpl.<init>(PutOp.java:281) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.PutOp.execute(PutOp.java:66) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.cache.client.internal.ServerRegionProxy.put(ServerRegionProxy.java:162) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegion.serverPut(LocalRegion.java:3006) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegion.cacheWriteBeforePut(LocalRegion.java:3115) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.ProxyRegionMap.basicPut(ProxyRegionMap.java:222) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5628) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegionDataView.putEntry(LocalRegionDataView.java:151) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegion.basicPut(LocalRegion.java:5057) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegion.validatedPut(LocalRegion.java:1595) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.LocalRegion.put(LocalRegion.java:1582) ~[geode-core-1.2.1.jar:?]
at org.apache.geode.internal.cache.AbstractRegion.put(AbstractRegion.java:325) ~[geode-core-1.2.1.jar:?]
at org.springframework.data.gemfire.GemfireTemplate.put(GemfireTemplate.java:193) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
at org.springframework.data.gemfire.repository.support.SimpleGemfireRepository.save(SimpleGemfireRepository.java:86) ~[spring-data-geode-2.0.14.RELEASE.jar:2.0.14.RELEASE]
...
at example.app.crm.CustomerServiceApplication.lambda$runner$0(CustomerServiceApplication.java:70) ~[spring-samples-boot-configuration-1.0.0.RELEASE.jar]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:783) ~[spring-boot-2.0.9.RELEASE.jar:2.0.9.RELEASE]
...
----
Our "Customers" Region is recreated, but is empty:
.Empty "Customers" Region
[source,txt]
----
gfsh>list regions
List of regions
---------------
Customers
gfsh>describe region --name=/Customers
..........................................................
Name : Customers
Data Policy : partition
Hosting Members : ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 0
| data-policy | PARTITION
----
So, SBDG can take care of all your serialization needs without you having to configure serialization or implement
`java.io.Serializable` on all your application domain types, including types your application domain types refer to.
The PDX _auto-configuration_ provided by SBDG is equivalent to:
.Equivalent PDX Configuration
[source,java]
----
@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
@EnableClusterConfiguration(useHttp = true)
@EnablePdx
public class CustomerServiceApplication {
...
}
----
`@EnablePdx` is responsible for configuring PDX serialization and registering SDG's `MappingPdxSerializer`.
[[geode-samples-boot-configuration-clientserver-secure]]
== Securing the Client & Server
[[geode-samples-boot-configuration-conclusion]]
== Conclusion
Hopefully this guide has now given you a better understanding of what the _auto-configuration_ support provided by
Spring Boot for Apache Geode/Pivotal GemFire is giving you when developing Apache Geode or Pivotal GemFire applications
with Spring.

View File

@@ -217,4 +217,5 @@ include::data-serialization.adoc[]
include::session.adoc[]
include::security.adoc[]
include::actuator.adoc[]
include::samples.adoc[]
include::appendix.adoc[]

View File

@@ -0,0 +1,19 @@
[[geode-samples]]
== Samples
This section contains working examples demonstrating how to use Spring Boot for Apache Geode and Pivotal GemFire (SBDG)
effectively.
Some examples focus on specific Use Cases (e.g. [(HTTP) Session state] caching) while other examples demonstrate how
SBDG works under-the-hood to give users a better understanding of what is actually happening and how to debug problems
with their Apache Geode / Pivotal GemFire, Spring Boot applications.
.Example Apache Geode Applications using Spring Boot
|===
| Guide | Description | Source
| link:guides/boot-configuration.html[Spring Boot Auto-configuration for Apache Geode/Pivotal GemFire]
| Explains what auto-configuration is provided by SBDG out-of-the-box and what the auto-configuration is doing.
| {github-samples-url}/boot/configuration[Boot Auto-configuration]
|===

View File

@@ -0,0 +1,15 @@
apply plugin: 'io.spring.convention.spring-sample-boot'
description = "Spring Geode Guides demonstrating the use of Spring Boot Auto-configuration for Apache Geode."
dependencies {
compile project(":spring-geode-starter")
compile "org.springframework.data:spring-data-geode-test"
compile "org.assertj:assertj-core"
compile "org.projectlombok:lombok"
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package example.app.crm;
import static org.assertj.core.api.Assertions.assertThat;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import example.app.crm.model.Customer;
import example.app.crm.repo.CustomerRepository;
/**
* Spring Boot application implementing a Customer Service.
*
* @author John Blum
* @see org.springframework.boot.ApplicationRunner
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.builder.SpringApplicationBuilder
* @see org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions
* @since 1.0.0
*/
// tag::class[]
@SpringBootApplication
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.LOCAL)
public class CustomerServiceApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(CustomerServiceApplication.class)
.web(WebApplicationType.NONE)
.build()
.run(args);
}
@Bean
ApplicationRunner runner(CustomerRepository customerRepository) {
return args -> {
assertThat(customerRepository.count()).isEqualTo(0);
Customer jonDoe = Customer.newCustomer(1L, "Jon Doe");
System.err.printf("Saving Customer [%s]%n", jonDoe);
jonDoe = customerRepository.save(jonDoe);
assertThat(jonDoe).isNotNull();
assertThat(jonDoe.getId()).isEqualTo(1L);
assertThat(jonDoe.getName()).isEqualTo("Jon Doe");
assertThat(customerRepository.count()).isEqualTo(1);
System.err.println("Querying for Customer [SELECT * FROM /Customers WHERE name LIKE '%Doe']");
Customer queriedJonDoe = customerRepository.findByNameLike("%Doe");
assertThat(queriedJonDoe).isEqualTo(jonDoe);
System.err.printf("Customer was [%s]%n", queriedJonDoe);
};
}
}
// end::class[]

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package example.app.crm.model;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
/**
* An Abstract Data Type (ADT) modeling a {@literal Customer}.
*
* @author John Blum
* @see org.springframework.data.annotation.Id
* @see org.springframework.data.gemfire.mapping.annotation.Region
* @since 1.0.0
*/
// tag::class[]
@Region("Customers")
@EqualsAndHashCode
@ToString(of = "name")
@RequiredArgsConstructor(staticName = "newCustomer")
public class Customer {
@Id @NonNull @Getter
private Long id;
@NonNull @Getter
private String name;
}
// end::class[]

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package example.app.crm.repo;
import org.springframework.data.repository.CrudRepository;
import example.app.crm.model.Customer;
/**
* Data Access Object (DAO) used to perform basic CRUD and simple query operations on {@link Customer} objects
* store in Apache Geode
*
* @author John Blum
* @see java.lang.Long
* @see org.springframework.data.repository.CrudRepository
* @see example.app.crm.model.Customer
* @since 1.0.0
*/
// tag::class[]
public interface CustomerRepository extends CrudRepository<Customer, Long> {
Customer findByNameLike(String name);
}
// end::class[]

View File

@@ -0,0 +1 @@
spring.data.gemfire.cache.log-level=error

View File

@@ -0,0 +1,4 @@
# Gfsh shell script to start a simple GemFire/Geode cluster
start locator --name=LocatorOne --log-level=config
start server --name=ServerOne --log-level=config

View File

@@ -0,0 +1,3 @@
#!/bin/bash
gfsh -e "run --file=/Users/jblum/pivdev/spring-boot-data-geode/spring-geode-samples/boot/configuration/src/main/resources/geode/bin/start-simple-cluster.gfsh"

View File

@@ -0,0 +1,4 @@
# Gfsh shell script to stop the cluster
stop server --name=ServerOne
stop locator --name=LocatorOne

View File

@@ -0,0 +1,3 @@
#!/bin/bash
gfsh -e "run --file=/Users/jblum/pivdev/spring-boot-data-geode/spring-geode-samples/boot/configuration/src/main/resources/geode/bin/stop-cluster.gfsh"

View File

@@ -0,0 +1,4 @@
# java.util.logging (JUL) configuration
org.apache=ERROR
org.springframework=ERROR

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{1.} - %msg%n"/>
</Console>
<File name="File" fileName="build/logs/spring-test.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{1.} - %msg%n"/>
</File>
<Null name="nop"/>
</Appenders>
<Loggers>
<Logger name="org.apache" level="error"/>
<Logger name="org.springframework" level="error"/>
<Root level="error">
<AppenderRef ref="Console"/>
<!--AppenderRef ref="File" /-->
</Root>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="ch.qos.logback" level="${logback.log.level:-ERROR}"/>
<logger name="org.apache" level="${logback.log.level:-ERROR}"/>
<logger name="org.springframework" level="${logback.log.level:-ERROR}"/>
<root level="${logback.log.level:-ERROR}">
<appender-ref ref="console"/>
</root>
</configuration>