#539 - Add spring-data-geode-examples module.
This commit is contained in:
committed by
Mark Paluch
parent
08dce4f0f3
commit
fa0021cffb
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,3 +7,7 @@ target/
|
||||
#IntelliJ Stuff
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
#Geode Stuff
|
||||
*.log
|
||||
*.dat
|
||||
|
||||
11
README.md
11
README.md
@@ -6,6 +6,17 @@ This repository contains example projects for the different Spring Data modules
|
||||
|
||||
We have separate folders for the samples of individual modules:
|
||||
|
||||
## Spring Data for Apache Geode
|
||||
|
||||
* `events` - In this example the test will make use of event handlers and async event queue to handle events.
|
||||
* `expiration-eviction` - In these examples the server is configured to delete entries after a certain idle period or after a Time-To-Live period (expiration0 or remove data from memory when certain thresholds are reached (eviction).
|
||||
* `function-invocation` - In this example the server will have 3 functions registered. The client will invoke each of the functions.
|
||||
* `queries` - In this example a client will query the data in various ways using OQl, continuous queries, and Apache Lucene indexes.
|
||||
* `security` - In this example the servers and clients are set up with security (username/password) authentication using Geode Security and Apache Shiro.
|
||||
* `storage` - In this example the server is configured to store data off of hte JVM heap using the `@EnableOffHeap` annotation and to compress region data using SnappyCompressor`.
|
||||
* `transactions` - In this example the client will perform operations within a transaction. First, it will do a successful transaction where entries are saved to the server, and then a failed transaction where all changes are reverted.
|
||||
* `wan` - In these example two servers are deployed. One server populates itself with data, and the other server gets populated with that data via WAN replication.
|
||||
|
||||
## Spring Data JPA
|
||||
|
||||
* `eclipselink` - Sample project to show how to use Spring Data JPA with Spring Boot and [Eclipselink](https://www.eclipse.org/eclipselink/).
|
||||
|
||||
22
geode/README.md
Executable file
22
geode/README.md
Executable file
@@ -0,0 +1,22 @@
|
||||
Spring Data For GemFire and Apache Geode Examples
|
||||
=========================================================
|
||||
|
||||
This project provides a number of examples to get you started using Spring Data for Apache Geode or Pivotal GemFire. These examples are designed to work with [Spring Data for Pivotal GemFire](http://projects.spring.io/spring-data-gemfire) 2.0.9-RELEASE or higher and are organized into the following sub projects:
|
||||
|
||||
It is important to note that all examples will follow the prescribed Maven directory structure.
|
||||
|
||||
Examples:
|
||||
|
||||
* **events** - In this example the test will make use of event handlers and async event queue to handle events.
|
||||
* **expiration-eviction** - In these examples the server is configured to delete entries after a certain idle period or after a Time-To-Live period (expiration0 or remove data from memory when certain thresholds are reached (eviction).
|
||||
* **function-invocation** - In this example the server will have 3 functions registered. The client will invoke each of the functions.
|
||||
* **queries** - In this example a client will query the data in various ways using OQl, continuous queries, and Apache Lucene indexes.
|
||||
* **security** - In this example the servers and clients are set up with security (username/password) authentication using Geode Security and Apache Shiro.
|
||||
* **storage** - In this example the server is configured to store data off of hte JVM heap using the `@EnableOffHeap` annotation and to compress region data using SnappyCompressor`.
|
||||
* **transactions** - In this example the client will perform operations within a transaction. First, it will do a successful transaction where entries are saved to the server, and then a failed transaction where all changes are reverted.
|
||||
* **wan** - In these example two servers are deployed. One server populates itself with data, and the other server gets populated with that data via WAN replication.
|
||||
|
||||
# Running The Examples
|
||||
|
||||
Each example has at least one test file located in the test directory. The examples are driven by the tests, so simply run the test either through your IDE or via the commandline.
|
||||
The logging level of the examples is set to "error", so there will be no output. To see output, simply find the `logback.xml` file located in src/test/resources and set the loglevel to "info".
|
||||
5
geode/events/README.md
Executable file
5
geode/events/README.md
Executable file
@@ -0,0 +1,5 @@
|
||||
# Events Example
|
||||
|
||||
In this example you will how to handle events using Async event queue, Cache Listeners, Cache Loaders, and Cache Writers.
|
||||
|
||||
NOTE: Inorder to see output, you must change the loglevel from "error" to "info" in the `logback.xml` file located under src/test/resources.
|
||||
17
geode/events/pom.xml
Executable file
17
geode/events/pom.xml
Executable file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>example.springdata.geode</groupId>
|
||||
<artifactId>spring-data-geode-examples</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>events</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An address used in the examples.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class Address implements Serializable {
|
||||
private String street;
|
||||
private String city;
|
||||
private String country;
|
||||
|
||||
public Address(String street, String city, String country) {
|
||||
this.street = street;
|
||||
this.city = city;
|
||||
this.country = country;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A customer used for Lucene examples.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region(name = "Customers")
|
||||
public class Customer implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private EmailAddress emailAddress;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private List<Address> addresses;
|
||||
|
||||
public Customer(Long id, EmailAddress emailAddress, String firstName, String lastName, Address... addresses) {
|
||||
this.id = id;
|
||||
this.emailAddress = emailAddress;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.addresses = Arrays.asList(addresses);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.apache.geode.cache.CacheWriterException;
|
||||
import org.apache.geode.cache.EntryEvent;
|
||||
import org.apache.geode.cache.util.CacheWriterAdapter;
|
||||
import org.apache.geode.internal.cache.EntryEventImpl;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CustomerCacheWriter extends CacheWriterAdapter<Long, Customer> {
|
||||
|
||||
@Override
|
||||
public void beforeCreate(EntryEvent<Long, Customer> event) throws CacheWriterException {
|
||||
EntryEventImpl e = (EntryEventImpl) event;
|
||||
super.beforeCreate(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class EmailAddress implements Serializable {
|
||||
private String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
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 java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = EventServerConfig.class)
|
||||
public class EventServer {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(EventServer.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.build()
|
||||
.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationRunner runner(CustomerRepository customerRepository, OrderRepository orderRepository,
|
||||
ProductRepository productRepository, OrderProductSummaryRepository orderProductSummaryRepository, @Qualifier("Products") Region<Long, Product> products) {
|
||||
return args -> {
|
||||
createCustomerData(customerRepository);
|
||||
|
||||
createProducts(productRepository);
|
||||
|
||||
createOrders(productRepository, orderRepository);
|
||||
|
||||
logger.info("Completed creating orders ");
|
||||
|
||||
final List<OrderProductSummary> allForProductID = orderProductSummaryRepository.findAllForProductID(3L);
|
||||
allForProductID.forEach(orderProductSummary -> logger.info("orderProductSummary = " + orderProductSummary));
|
||||
};
|
||||
}
|
||||
|
||||
private void createOrders(ProductRepository productRepository, OrderRepository orderRepository) {
|
||||
Random random = new Random(System.nanoTime());
|
||||
Address address = new Address("it", "doesn't", "matter");
|
||||
LongStream.rangeClosed(1, 10).forEach((orderId) ->
|
||||
LongStream.rangeClosed(1, 300).forEach((customerId) -> {
|
||||
Order order = new Order(orderId, customerId, address);
|
||||
IntStream.rangeClosed(0, random.nextInt(3) + 1).forEach((lineItemCount) -> {
|
||||
int quantity = random.nextInt(3) + 1;
|
||||
long productId = random.nextInt(3) + 1;
|
||||
order.add(new LineItem(productRepository.findById(productId).get(), quantity));
|
||||
});
|
||||
orderRepository.save(order);
|
||||
}));
|
||||
}
|
||||
|
||||
private void createProducts(ProductRepository productRepository) {
|
||||
productRepository.save(new Product(1L, "Apple iPod", new BigDecimal("99.99"),
|
||||
"An Apple portable music player"));
|
||||
productRepository.save(new Product(2L, "Apple iPad", new BigDecimal("499.99"),
|
||||
"An Apple tablet device"));
|
||||
Product macbook = new Product(3L, "Apple macBook", new BigDecimal("899.99"),
|
||||
"An Apple notebook computer");
|
||||
macbook.addAttribute("warranty", "included");
|
||||
productRepository.save(macbook);
|
||||
}
|
||||
|
||||
private void createCustomerData(CustomerRepository customerRepository) {
|
||||
LongStream.rangeClosed(0, 300)
|
||||
.parallel()
|
||||
.forEach(customerId ->
|
||||
customerRepository.save(new Customer(customerId, new EmailAddress(customerId + "@2.com"), "John" + customerId, "Smith" + customerId)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheListener;
|
||||
import org.apache.geode.cache.CacheLoader;
|
||||
import org.apache.geode.cache.CacheWriter;
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.asyncqueue.AsyncEventListener;
|
||||
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
import org.springframework.data.gemfire.wan.AsyncEventQueueFactoryBean;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@CacheServerApplication(logLevel = "error")
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
public class EventServerConfig {
|
||||
|
||||
@Bean
|
||||
AsyncEventListener orderAsyncEventListener(@Qualifier("OrderProductSummary") Region<Long, OrderProductSummary> orderProductSummary) {
|
||||
return new OrderAsyncQueueListener(orderProductSummary);
|
||||
}
|
||||
|
||||
@Bean
|
||||
AsyncEventQueueFactoryBean orderAsyncEventQueue(GemFireCache gemFireCache, AsyncEventListener orderAsyncEventListener) {
|
||||
final AsyncEventQueueFactoryBean asyncEventQueueFactoryBean = new AsyncEventQueueFactoryBean((Cache) gemFireCache);
|
||||
asyncEventQueueFactoryBean.setBatchTimeInterval(1000);
|
||||
asyncEventQueueFactoryBean.setBatchSize(5);
|
||||
asyncEventQueueFactoryBean.setAsyncEventListener(orderAsyncEventListener);
|
||||
return asyncEventQueueFactoryBean;
|
||||
}
|
||||
|
||||
@Bean(name = "OrderProductSummary")
|
||||
PartitionedRegionFactoryBean<Long, Order> createOrderProductSummaryRegion(GemFireCache gemFireCache) {
|
||||
final PartitionedRegionFactoryBean<Long, Order> partitionedRegionFactoryBean = new PartitionedRegionFactoryBean<>();
|
||||
partitionedRegionFactoryBean.setCache(gemFireCache);
|
||||
partitionedRegionFactoryBean.setRegionName("OrderProductSummary");
|
||||
partitionedRegionFactoryBean.setDataPolicy(DataPolicy.PARTITION);
|
||||
return partitionedRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Orders")
|
||||
PartitionedRegionFactoryBean<Long, Order> createOrderRegion(GemFireCache gemFireCache, AsyncEventQueue orderAsyncEventQueue) {
|
||||
final PartitionedRegionFactoryBean<Long, Order> partitionedRegionFactoryBean = new PartitionedRegionFactoryBean<>();
|
||||
partitionedRegionFactoryBean.setCache(gemFireCache);
|
||||
partitionedRegionFactoryBean.setRegionName("Orders");
|
||||
partitionedRegionFactoryBean.setDataPolicy(DataPolicy.PARTITION);
|
||||
partitionedRegionFactoryBean.setAsyncEventQueues(new AsyncEventQueue[]{orderAsyncEventQueue});
|
||||
return partitionedRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Products")
|
||||
ReplicatedRegionFactoryBean<Long, Product> createProductRegion(GemFireCache gemFireCache, CacheListener<Long, Product> loggingCacheListener,
|
||||
CacheLoader<Long, Product> productCacheLoader) {
|
||||
final ReplicatedRegionFactoryBean<Long, Product> replicatedRegionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
replicatedRegionFactoryBean.setCache(gemFireCache);
|
||||
replicatedRegionFactoryBean.setRegionName("Products");
|
||||
replicatedRegionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
replicatedRegionFactoryBean.setCacheLoader(productCacheLoader);
|
||||
replicatedRegionFactoryBean.setCacheListeners(new CacheListener[]{loggingCacheListener});
|
||||
return replicatedRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Customers")
|
||||
ReplicatedRegionFactoryBean<Long, Customer> createCustomerRegion(GemFireCache gemFireCache,
|
||||
CacheWriter<Long, Customer> customerCacheWriter,
|
||||
CacheListener<Long, Customer> loggingCacheListener) {
|
||||
final ReplicatedRegionFactoryBean<Long, Customer> replicatedRegionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
replicatedRegionFactoryBean.setCache(gemFireCache);
|
||||
replicatedRegionFactoryBean.setRegionName("Customers");
|
||||
replicatedRegionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
replicatedRegionFactoryBean.setCacheListeners(new CacheListener[]{loggingCacheListener});
|
||||
replicatedRegionFactoryBean.setCacheWriter(customerCacheWriter);
|
||||
return replicatedRegionFactoryBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* A LineItem used in the examples
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class LineItem implements Serializable {
|
||||
|
||||
private Product product;
|
||||
private Integer amount;
|
||||
|
||||
public LineItem(Product product, Integer amount) {
|
||||
this.product = product;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public BigDecimal calcTotal() {
|
||||
return product.getPrice().multiply(BigDecimal.valueOf(amount));
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return product.getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.geode.cache.EntryEvent;
|
||||
import org.apache.geode.cache.util.CacheListenerAdapter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class LoggingCacheListener<K, V> extends CacheListenerAdapter<K, V> {
|
||||
|
||||
private Log logger = LogFactory.getLog(LoggingCacheListener.class);
|
||||
|
||||
@Override
|
||||
public void afterCreate(EntryEvent<K, V> event) {
|
||||
logger.info("In region [" + event.getRegion().getName() + "] created key [" + event.getKey() + "] value [" + event.getNewValue() + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDestroy(EntryEvent<K, V> event) {
|
||||
logger.info("In region [" + event.getRegion().getName() + "] destroyed key [" + event.getKey() + "] ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterUpdate(EntryEvent<K, V> event) {
|
||||
logger.info("In region [" + event.getRegion().getName() + "] updated key [" + event.getNewValue() + "] [oldValue [" + event.getOldValue() + "]] new value [" + event.getNewValue() + "]");
|
||||
}
|
||||
}
|
||||
72
geode/events/src/main/java/example/springdata/geode/server/events/Order.java
Executable file
72
geode/events/src/main/java/example/springdata/geode/server/events/Order.java
Executable file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Orders object used in the examples
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region("Orders")
|
||||
public class Order implements Serializable {
|
||||
@Id
|
||||
private Long id;
|
||||
private Long customerId;
|
||||
private Address billingAddress;
|
||||
private Address shippingAddress;
|
||||
private List<LineItem> lineItems = new ArrayList<>();
|
||||
|
||||
public Order(Long orderId, Long customerId, Address address) {
|
||||
this.id = orderId;
|
||||
this.customerId = customerId;
|
||||
this.billingAddress = address;
|
||||
this.shippingAddress = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total of the [Order].
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BigDecimal calcTotal() {
|
||||
if (lineItems.size() == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
} else {
|
||||
return lineItems.stream().map(LineItem::calcTotal).reduce(BigDecimal::add).get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given [LineItem] to the [Order].
|
||||
*
|
||||
* @param lineItem
|
||||
*/
|
||||
public void add(LineItem lineItem) {
|
||||
lineItems.add(lineItem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.asyncqueue.AsyncEvent;
|
||||
import org.apache.geode.cache.asyncqueue.AsyncEventListener;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class OrderAsyncQueueListener implements AsyncEventListener {
|
||||
|
||||
private Region<Long, OrderProductSummary> summaryRegion;
|
||||
|
||||
public OrderAsyncQueueListener(Region<Long, OrderProductSummary> summaryRegion) {
|
||||
this.summaryRegion = summaryRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processEvents(List<AsyncEvent> list) {
|
||||
HashMap<Long, OrderProductSummary> summaryMap = new HashMap<>();
|
||||
list.forEach(asyncEvent -> {
|
||||
final Order order = (Order) asyncEvent.getDeserializedValue();
|
||||
if (order != null) {
|
||||
order.getLineItems().forEach(lineItem -> {
|
||||
OrderProductSummary orderProductSummary = summaryMap.get(lineItem.getProductId());
|
||||
if (orderProductSummary == null) {
|
||||
orderProductSummary = new OrderProductSummary(lineItem.getProductId(), new BigDecimal("0.00"));
|
||||
}
|
||||
orderProductSummary.setSummaryAmount(orderProductSummary.getSummaryAmount().add(lineItem.calcTotal()));
|
||||
summaryMap.put(lineItem.getProductId(), orderProductSummary);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
summaryMap.forEach((orderProductSummaryKey, orderProductSummary) -> {
|
||||
final OrderProductSummary productSummary = summaryRegion.get(orderProductSummaryKey);
|
||||
if (productSummary != null) {
|
||||
final BigDecimal newSummaryAmount = productSummary.getSummaryAmount().add(orderProductSummary.getSummaryAmount());
|
||||
summaryRegion.put(orderProductSummaryKey, new OrderProductSummary(orderProductSummaryKey, newSummaryAmount));
|
||||
} else {
|
||||
summaryRegion.put(orderProductSummaryKey, orderProductSummary);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* OrderProductSummary is an object used in the examples to show a summary of all Orders on a per product basis, on a per
|
||||
* timeframe shard (every 10s, or every 1hr)
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region("OrderProductSummary")
|
||||
public class OrderProductSummary implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long summaryKey;
|
||||
|
||||
private BigDecimal summaryAmount;
|
||||
|
||||
public OrderProductSummary(Long summaryKey, BigDecimal summaryAmount) {
|
||||
this.summaryKey = summaryKey;
|
||||
this.summaryAmount = summaryAmount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
import org.springframework.data.gemfire.repository.Query;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Hint;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Region("OrderProductSummary")
|
||||
public interface OrderProductSummaryRepository extends CrudRepository<OrderProductSummary, Long> {
|
||||
@Hint("emailAddressIndex")
|
||||
@Query("select orderSummary.value from /OrderProductSummary.entrySet orderSummary where orderSummary.key = $1")
|
||||
List<OrderProductSummary> findAllForProductID(long l);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface OrderRepository extends CrudRepository<Order, Long> {
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A product used in the examples.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region("Products")
|
||||
public class Product implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
private BigDecimal price;
|
||||
private String description;
|
||||
|
||||
@Transient
|
||||
private Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
@PersistenceConstructor
|
||||
public Product(Long id, String name, BigDecimal price, String description) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attribute with the given name to the given value.
|
||||
*
|
||||
* @param name must not be null or empty.
|
||||
* @param value
|
||||
*/
|
||||
public void addAttribute(String name, String value) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import com.github.javafaker.Faker;
|
||||
import org.apache.geode.cache.CacheLoader;
|
||||
import org.apache.geode.cache.CacheLoaderException;
|
||||
import org.apache.geode.cache.LoaderHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Component
|
||||
public class ProductCacheLoader implements CacheLoader<Long, Product> {
|
||||
private Faker faker = new Faker();
|
||||
|
||||
@Override
|
||||
public Product load(LoaderHelper loaderHelper) throws CacheLoaderException {
|
||||
return new Product((long) loaderHelper.getKey(), randomStringName(), randomPrice(), "");
|
||||
}
|
||||
|
||||
private BigDecimal randomPrice() {
|
||||
return new BigDecimal(faker.commerce().price());
|
||||
}
|
||||
|
||||
private String randomStringName() {
|
||||
return faker.commerce().productName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface ProductRepository extends CrudRepository<Product, Long> {
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.events;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = EventServer.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class EventServerTests {
|
||||
|
||||
@Autowired
|
||||
Cache cache;
|
||||
|
||||
@Autowired
|
||||
private ProductRepository productRepository;
|
||||
|
||||
@Autowired
|
||||
private OrderProductSummaryRepository orderProductSummaryRepository;
|
||||
|
||||
@Test
|
||||
public void asyncEventQueueEasConfiguredCorrectly() {
|
||||
assertThat(this.orderProductSummaryRepository.count()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void productCacheLoaderWorks() {
|
||||
long size = productRepository.count();
|
||||
assertThat(this.productRepository.findById(777L)).isNotNull();
|
||||
assertThat(productRepository.count()).isEqualTo(size + 1);
|
||||
productRepository.deleteById(777L);
|
||||
}
|
||||
}
|
||||
11
geode/events/src/test/resources/logback.xml
Normal file
11
geode/events/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="error">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
|
||||
</configuration>
|
||||
12
geode/expiration-eviction/README.md
Executable file
12
geode/expiration-eviction/README.md
Executable file
@@ -0,0 +1,12 @@
|
||||
# Expiration and Eviction Example
|
||||
|
||||
In this example we will show you ways to automatically remove data from your region. There are two ways to do this; Expiration and Eviction.
|
||||
|
||||
1. Expiration removes data after it has existed for a certain amount of time or after it has been unused for a certain amount of time. There are multiple ways to configure Expiration that will be shown in this example.
|
||||
1. Configure an eviction policy on the region using the `@EnableExpiration` and `@ExpirationPolicy` annotations.
|
||||
2. Configure a custom eviction policy to extending `CustomExpiry`.
|
||||
3. Entity defined expiration using the `@IdleTimeoutExpiration` and `@TimeToLiveExpiration` annotations.
|
||||
|
||||
Entity defined expiration has its own test class because it has a lower priority and is trumped by the expiration policy defined on the `@EnableExpiration` annotation. The polices defined on the `@EnableExpiration` annotation can be found in ExpirationPolicyConfig.
|
||||
|
||||
NOTE: Inorder to see output, you must change the loglevel from "error" to "info" in the `logback.xml` file located under src/test/resources.
|
||||
23
geode/expiration-eviction/pom.xml
Executable file
23
geode/expiration-eviction/pom.xml
Executable file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>example.springdata.geode</groupId>
|
||||
<artifactId>spring-data-geode-examples</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>expiration-eviction</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jayway.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An address used in the examples.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class Address implements Serializable {
|
||||
private String street;
|
||||
private String city;
|
||||
private String country;
|
||||
|
||||
public Address(String street, String city, String country) {
|
||||
this.street = street;
|
||||
this.city = city;
|
||||
this.country = country;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import org.apache.geode.cache.CustomExpiry;
|
||||
import org.apache.geode.cache.ExpirationAction;
|
||||
import org.apache.geode.cache.ExpirationAttributes;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
|
||||
public class CustomCustomerExpiry implements CustomExpiry<Long, Product> {
|
||||
|
||||
private int timeout;
|
||||
|
||||
public CustomCustomerExpiry(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpirationAttributes getExpiry(Region.Entry<Long, Product> entry) {
|
||||
if (entry.getKey() % 3 == 0) {
|
||||
return new ExpirationAttributes(timeout, ExpirationAction.DESTROY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A customer used for Lucene examples.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region(name = "Customers")
|
||||
public class Customer implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private EmailAddress emailAddress;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private List<Address> addresses;
|
||||
|
||||
public Customer(Long id, EmailAddress emailAddress, String firstName, String lastName, Address... addresses) {
|
||||
this.id = id;
|
||||
this.emailAddress = emailAddress;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.addresses = Arrays.asList(addresses);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
@Region("Customers")
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class EmailAddress implements Serializable {
|
||||
private String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = ExpirationEvictionServerConfig.class)
|
||||
public class ExpirationEvictionServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(ExpirationEvictionServer.class).web(WebApplicationType.NONE).build().run(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import com.github.javafaker.Faker;
|
||||
import org.apache.geode.cache.CustomExpiry;
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Scope;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableEviction;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableExpiration;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableLocator;
|
||||
import org.springframework.data.gemfire.eviction.EvictionActionType;
|
||||
import org.springframework.data.gemfire.eviction.EvictionPolicyType;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
|
||||
@Configuration
|
||||
@CacheServerApplication(logLevel = "error")
|
||||
@EnableLocator
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
@EnableExpiration
|
||||
@Import(ExpirationPolicyConfig.class)
|
||||
@EnableEviction(policies = @EnableEviction.EvictionPolicy(regionNames = "Orders",
|
||||
maximum = 10,
|
||||
action = EvictionActionType.LOCAL_DESTROY,
|
||||
type = EvictionPolicyType.ENTRY_COUNT))
|
||||
public class ExpirationEvictionServerConfig {
|
||||
|
||||
@Bean
|
||||
public Faker createDataFaker() {
|
||||
return new Faker();
|
||||
}
|
||||
|
||||
@Bean("IDLE")
|
||||
CustomExpiry<Long, Product> createIdleExpiration() {
|
||||
return new CustomCustomerExpiry(2);
|
||||
}
|
||||
|
||||
@Bean("TTL")
|
||||
CustomExpiry<Long, Product> createTtlExpiration() {
|
||||
return new CustomCustomerExpiry(4);
|
||||
}
|
||||
|
||||
@Bean("Products")
|
||||
public ReplicatedRegionFactoryBean<Long, Product> createProductRegion(GemFireCache gemFireCache,
|
||||
@Qualifier("IDLE") CustomExpiry<Long, Product> idleExpiry,
|
||||
@Qualifier("TTL") CustomExpiry<Long, Product> ttlExpiry) {
|
||||
final ReplicatedRegionFactoryBean<Long, Product> regionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
regionFactoryBean.setCache(gemFireCache);
|
||||
regionFactoryBean.setScope(Scope.DISTRIBUTED_ACK);
|
||||
regionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
regionFactoryBean.setName("Products");
|
||||
regionFactoryBean.setCustomEntryIdleTimeout(idleExpiry);
|
||||
regionFactoryBean.setCustomEntryTimeToLive(ttlExpiry);
|
||||
return regionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Customers")
|
||||
public ReplicatedRegionFactoryBean<Long, Customer> createCustomerRegion(GemFireCache gemFireCache) {
|
||||
final ReplicatedRegionFactoryBean<Long, Customer> regionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
regionFactoryBean.setCache(gemFireCache);
|
||||
regionFactoryBean.setScope(Scope.DISTRIBUTED_ACK);
|
||||
regionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
regionFactoryBean.setName("Customers");
|
||||
return regionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Orders")
|
||||
public ReplicatedRegionFactoryBean<Long, Order> createOrderRegion(GemFireCache gemFireCache) {
|
||||
final ReplicatedRegionFactoryBean<Long, Order> regionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
regionFactoryBean.setCache(gemFireCache);
|
||||
regionFactoryBean.setScope(Scope.DISTRIBUTED_ACK);
|
||||
regionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
regionFactoryBean.setName("Orders");
|
||||
return regionFactoryBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableExpiration;
|
||||
import org.springframework.data.gemfire.expiration.ExpirationActionType;
|
||||
|
||||
@Profile("!default")
|
||||
@Configuration
|
||||
@EnableExpiration(policies = {
|
||||
@EnableExpiration.ExpirationPolicy(timeout = 4, action = ExpirationActionType.DESTROY,
|
||||
regionNames = {"Customers"}, types = {EnableExpiration.ExpirationType.TIME_TO_LIVE}),
|
||||
@EnableExpiration.ExpirationPolicy(timeout = 2, action = ExpirationActionType.DESTROY,
|
||||
regionNames = {"Customers"}, types = {EnableExpiration.ExpirationType.IDLE_TIMEOUT})})
|
||||
public class ExpirationPolicyConfig {
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.expiration.IdleTimeoutExpiration;
|
||||
import org.springframework.data.gemfire.expiration.TimeToLiveExpiration;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Orders object used in the examples
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@IdleTimeoutExpiration(action = "DESTROY", timeout = "2")
|
||||
@TimeToLiveExpiration(action = "DESTROY", timeout = "4")
|
||||
@Region("Orders")
|
||||
public class Order implements Serializable {
|
||||
@Id
|
||||
private Long id;
|
||||
private Long total;
|
||||
private Address billingAddress;
|
||||
private Address shippingAddress;
|
||||
|
||||
public Order(Long orderId, Long total, Address address) {
|
||||
this.id = orderId;
|
||||
this.total = total;
|
||||
this.billingAddress = address;
|
||||
this.shippingAddress = address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
@Region("Orders")
|
||||
public interface OrderRepository extends CrudRepository<Order, Long> {
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* A product used in the examples.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region("Products")
|
||||
public class Product implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
private BigDecimal price;
|
||||
private String description;
|
||||
|
||||
@PersistenceConstructor
|
||||
public Product(Long id, String name, BigDecimal price, String description) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface ProductRepository extends CrudRepository<Product, Long> {
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import com.github.javafaker.Faker;
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = ExpirationEvictionServer.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class EntityDefinedExpirationTests {
|
||||
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
@Autowired
|
||||
Faker faker;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Test
|
||||
public void entityDefinedExpirationIsConfiguredCorrectly() {
|
||||
orderRepository.save(new Order(1L, 50L, new Address(faker.address().streetAddress(), faker.address().city(), faker.address().country())));
|
||||
|
||||
assertThat(orderRepository.count()).isEqualTo(1);
|
||||
|
||||
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss:SSS");
|
||||
logger.info("Starting TTL wait period: " + simpleDateFormat.format(new Date()));
|
||||
//Due to the constant "getting" of the entry, the idle expiry timeout will not be met and the time-to-live
|
||||
// will be used.
|
||||
Awaitility.await()
|
||||
.pollInterval(1, TimeUnit.SECONDS)
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> !orderRepository.findById(1L).isPresent());
|
||||
|
||||
assertThat(orderRepository.count()).isEqualTo(0);
|
||||
|
||||
logger.info("Ending TTL wait period: " + simpleDateFormat.format(new Date()));
|
||||
|
||||
orderRepository.save(new Order(1L, 50L, new Address(faker.address().streetAddress(), faker.address().city(), faker.address().country())));
|
||||
|
||||
assertThat(orderRepository.count()).isEqualTo(1);
|
||||
|
||||
logger.info("Starting Idle wait period: " + simpleDateFormat.format(new Date()));
|
||||
|
||||
//Due to the delay in "getting" the entry, the idle timeout of 2s should delete the entry.
|
||||
Awaitility.await()
|
||||
.pollDelay(2, TimeUnit.SECONDS)
|
||||
.pollInterval(100, TimeUnit.MILLISECONDS)
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> !orderRepository.findById(1L).isPresent());
|
||||
|
||||
assertThat(orderRepository.count()).isEqualTo(0);
|
||||
|
||||
logger.info("Ending Idle wait period: " + simpleDateFormat.format(new Date()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.server.expiration.eviction;
|
||||
|
||||
import com.github.javafaker.Faker;
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = ExpirationEvictionServer.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
@ActiveProfiles("expiration_policy")
|
||||
public class ExpirationEvictionServerTests {
|
||||
|
||||
@Autowired
|
||||
private CustomerRepository customerRepository;
|
||||
|
||||
@Autowired
|
||||
private ProductRepository productRepository;
|
||||
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
@Autowired
|
||||
Faker faker;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Test
|
||||
public void cacheDefinedExpirationIsConfiguredCorrectly() {
|
||||
customerRepository.save(new Customer(1L, new EmailAddress(faker.internet().emailAddress()),
|
||||
faker.name().firstName(), faker.name().lastName(),
|
||||
new Address(faker.address().streetAddress(), faker.address().city(), faker.address().country())));
|
||||
|
||||
assertThat(customerRepository.count()).isEqualTo(1);
|
||||
|
||||
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss:SSS");
|
||||
logger.info("Starting TTL wait period: " + simpleDateFormat.format(new Date()));
|
||||
//Due to the constant "getting" of the entry, the idle expiry timeout will not be met and the time-to-live
|
||||
// will be used.
|
||||
Awaitility.await()
|
||||
.pollInterval(1, TimeUnit.SECONDS)
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> !customerRepository.findById(1L).isPresent());
|
||||
|
||||
assertThat(customerRepository.count()).isEqualTo(0);
|
||||
|
||||
logger.info("Ending TTL wait period: " + simpleDateFormat.format(new Date()));
|
||||
|
||||
customerRepository.save(new Customer(1L, new EmailAddress(faker.internet().emailAddress()),
|
||||
faker.name().firstName(), faker.name().lastName(),
|
||||
new Address(faker.address().streetAddress(), faker.address().city(), faker.address().country())));
|
||||
|
||||
assertThat(customerRepository.count()).isEqualTo(1);
|
||||
|
||||
logger.info("Starting Idle wait period: " + simpleDateFormat.format(new Date()));
|
||||
|
||||
//Due to the delay in "getting" the entry, the idle timeout of 2s should delete the entry.
|
||||
Awaitility.await()
|
||||
.pollDelay(2, TimeUnit.SECONDS)
|
||||
.pollInterval(100, TimeUnit.MILLISECONDS)
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> !customerRepository.findById(1L).isPresent());
|
||||
|
||||
assertThat(customerRepository.count()).isEqualTo(0);
|
||||
|
||||
logger.info("Ending Idle wait period: " + simpleDateFormat.format(new Date()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customExpirationIsConfiguredCorrectly() {
|
||||
productRepository.save(new Product(3L, "MacBook Pro", BigDecimal.valueOf(20), "A cool computing device"));
|
||||
|
||||
assertThat(productRepository.count()).isEqualTo(1);
|
||||
|
||||
|
||||
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss:SSS");
|
||||
logger.info("Starting TTL wait period: " + simpleDateFormat.format(new Date()));
|
||||
//Due to the constant "getting" of the entry, the idle expiry timeout will not be met and the time-to-live
|
||||
// will be used.
|
||||
Awaitility.await()
|
||||
.pollInterval(1, TimeUnit.SECONDS)
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> !productRepository.findById(3L).isPresent());
|
||||
|
||||
assertThat(productRepository.count()).isEqualTo(0);
|
||||
|
||||
logger.info("Ending TTL wait period: " + simpleDateFormat.format(new Date()));
|
||||
|
||||
productRepository.save(new Product(3L, "MacBook Pro", BigDecimal.valueOf(20), "A cool computing device"));
|
||||
|
||||
assertThat(productRepository.count()).isEqualTo(1);
|
||||
|
||||
logger.info("Starting Idle wait period: " + simpleDateFormat.format(new Date()));
|
||||
|
||||
//Due to the delay in "getting" the entry, the idle timeout of 2s should delete the entry.
|
||||
Awaitility.await()
|
||||
.pollDelay(2, TimeUnit.SECONDS)
|
||||
.pollInterval(100, TimeUnit.MILLISECONDS)
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> !productRepository.findById(3L).isPresent());
|
||||
|
||||
assertThat(productRepository.count()).isEqualTo(0);
|
||||
|
||||
logger.info("Ending Idle wait period: " + simpleDateFormat.format(new Date()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictionIsConfiguredCorrectly() {
|
||||
|
||||
final int evictionThreshold = 10;
|
||||
for (long i = 0; i < evictionThreshold + 1; i++) {
|
||||
orderRepository.save(new Order(i, i, new Address(faker.address().streetName(), faker.address().city(), faker.address().country())));
|
||||
}
|
||||
|
||||
assertThat(orderRepository.count()).isEqualTo(evictionThreshold);
|
||||
orderRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
11
geode/expiration-eviction/src/test/resources/logback.xml
Normal file
11
geode/expiration-eviction/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="error">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
|
||||
</configuration>
|
||||
37
geode/function-invocation/README.md
Executable file
37
geode/function-invocation/README.md
Executable file
@@ -0,0 +1,37 @@
|
||||
# Function Invocation Example
|
||||
|
||||
In this example a [Pivotal GemFire](https://pivotal.io/pivotal-gemfire) / [Apache Geode](http://geode.apache.org/) client will invoke remote functions registered on the server.
|
||||
|
||||
To run the example simply run the tests located under function-invocation/src/test in your IDE.
|
||||
|
||||
The client is configured to connect to the deployed/started server on `localhost` port `40404`.
|
||||
|
||||
## Running the example
|
||||
|
||||
The example is broken up into multiple steps:
|
||||
1. Insert (Put) three Customer entries into the `Customers` region using the repositories `save` method.
|
||||
2. Insert (Put) three Product entries into the `Products` region using the repositories `save` method.
|
||||
3. Insert (Put) 100 Order entries into the `Orders` region using the repositories `save` method.
|
||||
|
||||
Your output from the test `functionsExecuteCorrectly` should look similar to the following:
|
||||
|
||||
Inserting 3 entries for keys: 1, 2, 3
|
||||
[FORK] - [info 2019/09/06 09:24:49.274 PDT <ServerConnection on port 49925 Thread 0> tid=0x47] In region [Customers] created key [1] value [Customer(id=1, emailAddress=EmailAddress(value=2@2.com), firstName=John, lastName=Smith)]
|
||||
[FORK] -
|
||||
[FORK] - [info 2019/09/06 09:24:49.278 PDT <ServerConnection on port 49925 Thread 0> tid=0x47] In region [Customers] created key [2] value [Customer(id=2, emailAddress=EmailAddress(value=3@3.com), firstName=Frank, lastName=Lamport)]
|
||||
[FORK] -
|
||||
[FORK] - [info 2019/09/06 09:24:49.279 PDT <ServerConnection on port 49925 Thread 0> tid=0x47] In region [Customers] created key [3] value [Customer(id=3, emailAddress=EmailAddress(value=5@5.com), firstName=Jude, lastName=Simmons)]
|
||||
[FORK] -
|
||||
All customers for emailAddresses:3@3.com,2@2.com using function invocation:
|
||||
[Customer(id=2, emailAddress=EmailAddress(value=3@3.com), firstName=Frank, lastName=Lamport), Customer(id=1, emailAddress=EmailAddress(value=2@2.com), firstName=John, lastName=Smith)]
|
||||
Running function to sum up all product prices:
|
||||
1499.97
|
||||
Running function to sum up all order lineItems prices for order 1:
|
||||
2399.96
|
||||
For order:
|
||||
Order(id=1, customerId=3, billingAddress=Address(street=it, city=doesn't, country=matter), shippingAddress=Address(street=it, city=doesn't, country=matter))
|
||||
LineItems:[Purchased 1 of Product Apple macBook at 899.99 for total of 899.99, Purchased 3 of Product Apple iPad at 499.99 for total of 1499.97]
|
||||
|
||||
NOTE: Inorder to see output, you must change the loglevel from "error" to "info" in the `logback.xml` file located under src/test/resources.
|
||||
|
||||
NOTE: Number of products purchased and total cost may vary from the above run as they are determined randomly.
|
||||
18
geode/function-invocation/pom.xml
Executable file
18
geode/function-invocation/pom.xml
Executable file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>example.springdata.geode</groupId>
|
||||
<artifactId>spring-data-geode-examples</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>function-invocation</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An address used in the examples.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class Address implements Serializable {
|
||||
private String street;
|
||||
private String city;
|
||||
private String country;
|
||||
|
||||
public Address(String street, String city, String country) {
|
||||
this.street = street;
|
||||
this.city = city;
|
||||
this.country = country;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A customer used for Lucene examples.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region(name = "Customers")
|
||||
public class Customer implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private EmailAddress emailAddress;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private List<Address> addresses;
|
||||
|
||||
public Customer(Long id, EmailAddress emailAddress, String firstName, String lastName, Address... addresses) {
|
||||
this.id = id;
|
||||
this.emailAddress = emailAddress;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.addresses = Arrays.asList(addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given [Address] to the [Customer].
|
||||
*/
|
||||
public void add(Address address) {
|
||||
this.addresses.add(address);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class EmailAddress implements Serializable {
|
||||
private String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* A LineItem used in the examples
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class LineItem implements Serializable {
|
||||
|
||||
private Product product;
|
||||
private Integer amount;
|
||||
|
||||
public LineItem(Product product, Integer amount) {
|
||||
this.product = product;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public BigDecimal calcTotal() {
|
||||
return product.getPrice().multiply(BigDecimal.valueOf(amount));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Orders object used in the examples
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region("Orders")
|
||||
public class Order implements Serializable {
|
||||
@Id
|
||||
private Long id;
|
||||
private Long customerId;
|
||||
private Address billingAddress;
|
||||
private Address shippingAddress;
|
||||
private List<LineItem> lineItems = new ArrayList<>();
|
||||
|
||||
public Order(Long orderId, Long customerId, Address address) {
|
||||
this.id = orderId;
|
||||
this.customerId = customerId;
|
||||
this.billingAddress = address;
|
||||
this.shippingAddress = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total of the [Order].
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
public BigDecimal calcTotal() {
|
||||
if (lineItems.size() == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
} else {
|
||||
return lineItems.stream().map(LineItem::calcTotal).reduce(BigDecimal::add).get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given [LineItem] to the [Order].
|
||||
*
|
||||
* @param lineItem
|
||||
*/
|
||||
public void add(LineItem lineItem) {
|
||||
lineItems.add(lineItem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A product used in the examples.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region("Products")
|
||||
public class Product implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
private BigDecimal price;
|
||||
private String description;
|
||||
|
||||
@Transient
|
||||
private Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
@PersistenceConstructor
|
||||
public Product(Long id, String name, BigDecimal price, String description) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attribute with the given name to the given value.
|
||||
*
|
||||
* @param name must not be null or empty.
|
||||
* @param value
|
||||
*/
|
||||
public void addAttribute(String name, String value) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import example.springdata.geode.client.function.Customer;
|
||||
import org.springframework.data.gemfire.function.annotation.FunctionId;
|
||||
import org.springframework.data.gemfire.function.annotation.OnRegion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@OnRegion(region = "Customers")
|
||||
public interface CustomerFunctionExecutions {
|
||||
|
||||
@FunctionId("listConsumersForEmailAddressesFnc")
|
||||
List<List<Customer>> listAllCustomersForEmailAddress(String... emailAddresses);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import example.springdata.geode.client.function.Customer;
|
||||
import org.springframework.data.gemfire.mapping.annotation.ClientRegion;
|
||||
import org.springframework.data.gemfire.repository.Query;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Hint;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Limit;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Trace;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ClientRegion("Customers")
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
|
||||
@Trace
|
||||
@Limit(100)
|
||||
@Hint("emailAddressIndex")
|
||||
@Query("select * from /Customers customer where customer.emailAddress.value = $1")
|
||||
List<Customer> findByEmailAddressUsingIndex(String emailAddress);
|
||||
|
||||
@Trace
|
||||
@Limit(100)
|
||||
@Query("select * from /Customers customer where customer.firstName = $1")
|
||||
List<Customer> findByFirstNameUsingIndex(String firstName);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import example.springdata.geode.client.function.Customer;
|
||||
import example.springdata.geode.client.function.Order;
|
||||
import example.springdata.geode.client.function.Product;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
|
||||
import org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
import org.springframework.data.gemfire.transaction.config.EnableGemfireCacheTransactions;
|
||||
|
||||
/**
|
||||
* Spring JavaConfig configuration class to setup a Spring container and infrastructure components.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
@EnableGemfireFunctionExecutions(basePackageClasses = CustomerFunctionExecutions.class)
|
||||
@ClientCacheApplication(name = "FunctionInvocationClient", logLevel = "error", pingInterval = 5000L, readTimeout = 15000, retryAttempts = 1)
|
||||
@EnableGemfireCacheTransactions
|
||||
public class FunctionInvocationClientApplicationConfig {
|
||||
|
||||
@Bean("Customers")
|
||||
protected ClientRegionFactoryBean<Long, Customer> configureProxyClientCustomerRegion(GemFireCache gemFireCache) {
|
||||
ClientRegionFactoryBean<Long, Customer> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
|
||||
clientRegionFactoryBean.setCache(gemFireCache);
|
||||
clientRegionFactoryBean.setName("Customers");
|
||||
clientRegionFactoryBean.setShortcut(ClientRegionShortcut.PROXY);
|
||||
return clientRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Products")
|
||||
protected ClientRegionFactoryBean<Long, Product> configureProxyClientProductRegion(GemFireCache gemFireCache) {
|
||||
ClientRegionFactoryBean<Long, Product> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
|
||||
clientRegionFactoryBean.setCache(gemFireCache);
|
||||
clientRegionFactoryBean.setName("Products");
|
||||
clientRegionFactoryBean.setShortcut(ClientRegionShortcut.PROXY);
|
||||
return clientRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Orders")
|
||||
protected ClientRegionFactoryBean<Long, Order> configureProxyClientOrderRegion(GemFireCache gemFireCache) {
|
||||
ClientRegionFactoryBean<Long, Order> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
|
||||
clientRegionFactoryBean.setCache(gemFireCache);
|
||||
clientRegionFactoryBean.setName("Orders");
|
||||
clientRegionFactoryBean.setShortcut(ClientRegionShortcut.PROXY);
|
||||
return clientRegionFactoryBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import org.springframework.data.gemfire.function.annotation.FunctionId;
|
||||
import org.springframework.data.gemfire.function.annotation.OnRegion;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@OnRegion(region = "Orders")
|
||||
public interface OrderFunctionExecutions {
|
||||
|
||||
@FunctionId("sumPricesForAllProductsForOrderFnc")
|
||||
List<BigDecimal> sumPricesForAllProductsForOrder(Long orderId);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import example.springdata.geode.client.function.Order;
|
||||
import org.springframework.data.gemfire.mapping.annotation.ClientRegion;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
@ClientRegion("Orders")
|
||||
public interface OrderRepository extends CrudRepository<Order, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import org.springframework.data.gemfire.function.annotation.FunctionId;
|
||||
import org.springframework.data.gemfire.function.annotation.OnRegion;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@OnRegion(region = "Products")
|
||||
public interface ProductFunctionExecutions {
|
||||
|
||||
@FunctionId("sumPricesForAllProductsFnc")
|
||||
List<BigDecimal> sumPricesForAllProducts();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import example.springdata.geode.client.function.Product;
|
||||
import org.springframework.data.gemfire.mapping.annotation.ClientRegion;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
@ClientRegion("Products")
|
||||
public interface ProductRepository extends CrudRepository<Product, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.server;
|
||||
|
||||
import example.springdata.geode.client.function.Customer;
|
||||
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
|
||||
import org.springframework.data.gemfire.function.annotation.RegionData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class CustomerFunctions {
|
||||
|
||||
@GemfireFunction(id = "listConsumersForEmailAddressesFnc", HA = true, optimizeForWrite = true, batchSize = 3, hasResult = true)
|
||||
public List<Customer> listAllCustomersForEmailAddress(@RegionData Map<Long, Customer> customerData,
|
||||
String... emailAddresses) {
|
||||
List<String> emailAddressesAsList = Arrays.asList(emailAddresses);
|
||||
List<Customer> collect = customerData.values().parallelStream()
|
||||
.filter((customer) -> emailAddressesAsList.contains(customer.getEmailAddress().getValue()))
|
||||
.collect(Collectors.toList());
|
||||
return collect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.server;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = FunctionServerApplicationConfig.class)
|
||||
public class FunctionServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(FunctionServer.class).web(WebApplicationType.NONE).build().run(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.server;
|
||||
|
||||
import example.springdata.geode.client.function.Customer;
|
||||
import example.springdata.geode.client.function.Order;
|
||||
import example.springdata.geode.client.function.Product;
|
||||
import example.springdata.geode.client.function.client.CustomerRepository;
|
||||
import org.apache.geode.cache.DataPolicy;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Scope;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableIndexing;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableLocator;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableManager;
|
||||
import org.springframework.data.gemfire.function.config.EnableGemfireFunctions;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = CustomerFunctions.class)
|
||||
@EnableGemfireFunctions
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
@EnableLocator
|
||||
@EnableIndexing
|
||||
@EnableManager
|
||||
@CacheServerApplication(port = 0, logLevel = "error")
|
||||
public class FunctionServerApplicationConfig {
|
||||
|
||||
@Bean("Customers")
|
||||
ReplicatedRegionFactoryBean<Long, Customer> createCustomerRegion(GemFireCache gemfireCache) {
|
||||
ReplicatedRegionFactoryBean<Long, Customer> replicatedRegionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
replicatedRegionFactoryBean.setCache(gemfireCache);
|
||||
replicatedRegionFactoryBean.setRegionName("Customers");
|
||||
replicatedRegionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
replicatedRegionFactoryBean.setScope(Scope.DISTRIBUTED_ACK);
|
||||
return replicatedRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Orders")
|
||||
ReplicatedRegionFactoryBean<Long, Order> createOrderRegion(GemFireCache gemfireCache) {
|
||||
ReplicatedRegionFactoryBean<Long, Order> replicatedRegionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
replicatedRegionFactoryBean.setCache(gemfireCache);
|
||||
replicatedRegionFactoryBean.setRegionName("Orders");
|
||||
replicatedRegionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
return replicatedRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("Products")
|
||||
ReplicatedRegionFactoryBean<Long, Product> createProductRegion(GemFireCache gemfireCache) {
|
||||
ReplicatedRegionFactoryBean<Long, Product> replicatedRegionFactoryBean = new ReplicatedRegionFactoryBean<>();
|
||||
replicatedRegionFactoryBean.setCache(gemfireCache);
|
||||
replicatedRegionFactoryBean.setRegionName("Products");
|
||||
replicatedRegionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
|
||||
return replicatedRegionFactoryBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.server;
|
||||
|
||||
import example.springdata.geode.client.function.Order;
|
||||
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
|
||||
import org.springframework.data.gemfire.function.annotation.RegionData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class OrderFunctions {
|
||||
|
||||
@GemfireFunction(id = "sumPricesForAllProductsForOrderFnc", HA = true, optimizeForWrite = false, hasResult = true)
|
||||
public BigDecimal sumPricesForAllProductsForOrderFnc(Long orderId, @RegionData Map<Long, Order> orderData) {
|
||||
return orderData.get(orderId).calcTotal();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.server;
|
||||
|
||||
import example.springdata.geode.client.function.Product;
|
||||
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
|
||||
import org.springframework.data.gemfire.function.annotation.RegionData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ProductFunctions {
|
||||
|
||||
@GemfireFunction(id = "sumPricesForAllProductsFnc", HA = true, optimizeForWrite = false, hasResult = true)
|
||||
public BigDecimal sumPricesForAllProductsFnc(@RegionData Map<Long, Product> productData) {
|
||||
return productData.values().stream().map(Product::getPrice).reduce(BigDecimal::add).get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.function.client;
|
||||
|
||||
import example.springdata.geode.client.function.Address;
|
||||
import example.springdata.geode.client.function.Customer;
|
||||
import example.springdata.geode.client.function.EmailAddress;
|
||||
import example.springdata.geode.client.function.LineItem;
|
||||
import example.springdata.geode.client.function.Order;
|
||||
import example.springdata.geode.client.function.Product;
|
||||
import example.springdata.geode.client.function.server.FunctionServer;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = FunctionInvocationClientApplicationConfig.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class FunctionInvocationClientTests extends ForkingClientServerIntegrationTestsSupport {
|
||||
|
||||
@Autowired
|
||||
private CustomerRepository customerRepository;
|
||||
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
@Autowired
|
||||
private ProductRepository productRepository;
|
||||
|
||||
@Autowired
|
||||
private CustomerFunctionExecutions customerFunctionExecutions;
|
||||
|
||||
@Autowired
|
||||
private OrderFunctionExecutions orderFunctionExecutions;
|
||||
|
||||
@Autowired
|
||||
private ProductFunctionExecutions productFunctionExecutions;
|
||||
|
||||
@Resource(name = "Customers")
|
||||
private Region<Long, Customer> customers;
|
||||
|
||||
@Resource(name = "Orders")
|
||||
private Region<Long, Order> orders;
|
||||
|
||||
@Resource(name = "Products")
|
||||
private Region<Long, Product> products;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws IOException {
|
||||
startGemFireServer(FunctionServer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void functionsExecuteCorrectly() {
|
||||
createCustomerData();
|
||||
|
||||
List<Customer> cust = customerFunctionExecutions.listAllCustomersForEmailAddress("2@2.com", "3@3.com").get(0);
|
||||
assertThat(cust.size()).isEqualTo(2);
|
||||
logger.info("All customers for emailAddresses:3@3.com,2@2.com using function invocation: \n\t " + cust);
|
||||
|
||||
createProducts();
|
||||
BigDecimal sum = productFunctionExecutions.sumPricesForAllProducts().get(0);
|
||||
assertThat(sum).isEqualTo(BigDecimal.valueOf(1499.97));
|
||||
logger.info("Running function to sum up all product prices: \n\t" + sum);
|
||||
|
||||
createOrders();
|
||||
|
||||
sum = orderFunctionExecutions.sumPricesForAllProductsForOrder(1L).get(0);
|
||||
assertThat(sum).isGreaterThanOrEqualTo(BigDecimal.valueOf(99.99));
|
||||
logger.info("Running function to sum up all order lineItems prices for order 1: \n\t" + sum);
|
||||
Order order = orderRepository.findById(1L).get();
|
||||
logger.info("For order: \n\t " + order);
|
||||
}
|
||||
|
||||
public void createCustomerData() {
|
||||
|
||||
logger.info("Inserting 3 entries for keys: 1, 2, 3");
|
||||
customerRepository.save(new Customer(1L, new EmailAddress("2@2.com"), "John", "Smith"));
|
||||
customerRepository.save(new Customer(2L, new EmailAddress("3@3.com"), "Frank", "Lamport"));
|
||||
customerRepository.save(new Customer(3L, new EmailAddress("5@5.com"), "Jude", "Simmons"));
|
||||
assertThat(customers.keySetOnServer().size()).isEqualTo(3);
|
||||
}
|
||||
|
||||
public void createProducts() {
|
||||
productRepository.save(new Product(1L, "Apple iPod", new BigDecimal("99.99"),
|
||||
"An Apple portable music player"));
|
||||
productRepository.save(new Product(2L, "Apple iPad", new BigDecimal("499.99"),
|
||||
"An Apple tablet device"));
|
||||
Product macbook = new Product(3L, "Apple macBook", new BigDecimal("899.99"),
|
||||
"An Apple notebook computer");
|
||||
macbook.addAttribute("warranty", "included");
|
||||
productRepository.save(macbook);
|
||||
assertThat(products.keySetOnServer().size()).isEqualTo(3);
|
||||
}
|
||||
|
||||
public void createOrders() {
|
||||
Random random = new Random();
|
||||
Address address = new Address("it", "doesn't", "matter");
|
||||
LongStream.rangeClosed(1, 100).forEach((orderId) ->
|
||||
LongStream.rangeClosed(1, 3).forEach((customerId) -> {
|
||||
Order order = new Order(orderId, customerId, address);
|
||||
IntStream.rangeClosed(0, random.nextInt(3) + 1).forEach((lineItemCount) -> {
|
||||
int quantity = random.nextInt(3) + 1;
|
||||
long productId = random.nextInt(3) + 1;
|
||||
order.add(new LineItem(productRepository.findById(productId).get(), quantity));
|
||||
});
|
||||
orderRepository.save(order);
|
||||
}));
|
||||
assertThat(orders.keySetOnServer().size()).isEqualTo(100);
|
||||
}
|
||||
}
|
||||
11
geode/function-invocation/src/test/resources/logback.xml
Normal file
11
geode/function-invocation/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="error">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
|
||||
</configuration>
|
||||
128
geode/pom.xml
Executable file
128
geode/pom.xml
Executable file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>example.springdata.geode</groupId>
|
||||
<artifactId>spring-data-geode-examples</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<url>https://github.com/spring-projects/spring-geode-examples</url>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>events</module>
|
||||
<module>expiration-eviction</module>
|
||||
<module>function-invocation</module>
|
||||
<module>queries</module>
|
||||
<module>security</module>
|
||||
<module>storage</module>
|
||||
<module>transactions</module>
|
||||
<module>wan</module>
|
||||
</modules>
|
||||
|
||||
<organization>
|
||||
<name>SpringSource</name>
|
||||
<url>http://springsource.org</url>
|
||||
</organization>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.2.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<log4j.version>2.12.1</log4j.version>
|
||||
<slf4j.log4j.version>1.7.28</slf4j.log4j.version>
|
||||
<spring.boot.version>2.2.4.BUILD-SNAPSHOT</spring.boot.version>
|
||||
<spring.test.data.geode.version>0.0.12.RELEASE</spring.test.data.geode.version>
|
||||
<spring.data.releasetrain.version>Moore-RELEASE</spring.data.releasetrain.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-releasetrain</artifactId>
|
||||
<version>${spring.data.releasetrain.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.12</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.javafaker</groupId>
|
||||
<artifactId>javafaker</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-geode</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-geode-test</artifactId>
|
||||
<version>${spring.test.data.geode.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.log4j.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
8
geode/queries/README.md
Executable file
8
geode/queries/README.md
Executable file
@@ -0,0 +1,8 @@
|
||||
# Queries Example
|
||||
|
||||
In this example we will demonstrate different kinds of queries. The kinds of queries we will demonstrate are listed below.
|
||||
1. OQL Queries - OQL is similar to SQl and can be used to query regions like SQl queries tables in a relational database.
|
||||
2. Continuous Queries - Continuous queries are special OQl queries that run continuously and are updated when th returned result set changes (like then more data is added/removed on the region)
|
||||
3. Lucene Queries - Lucene Queries allow you ot query data that has been indexed with Apache Geode's Apache Lucene support.
|
||||
|
||||
NOTE: Inorder to see output, you must change the loglevel from "error" to "info" in the `logback.xml` file located under src/test/resources.
|
||||
24
geode/queries/pom.xml
Executable file
24
geode/queries/pom.xml
Executable file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>example.springdata.geode</groupId>
|
||||
<artifactId>spring-data-geode-examples</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>example.springdata.geode.client</groupId>
|
||||
<artifactId>queries</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>3.1.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.LuceneIndexed;
|
||||
import org.springframework.data.gemfire.mapping.annotation.PartitionRegion;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A customer used for Lucene examples.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
|
||||
@Data
|
||||
@PartitionRegion(name = "Customers")
|
||||
public class Customer implements Serializable {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private EmailAddress emailAddress;
|
||||
|
||||
private String firstName;
|
||||
|
||||
@LuceneIndexed(name = "lastName_lucene")
|
||||
private String lastName;
|
||||
|
||||
public Customer(long id, EmailAddress emailAddress, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.emailAddress = emailAddress;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class EmailAddress implements Serializable {
|
||||
private String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries.client;
|
||||
|
||||
import example.springdata.geode.client.queries.Customer;
|
||||
import org.springframework.data.gemfire.mapping.annotation.ClientRegion;
|
||||
import org.springframework.data.gemfire.repository.Query;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Hint;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Limit;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Trace;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ClientRegion(name = "Customers")
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
@Trace
|
||||
@Limit(100)
|
||||
@Hint("emailAddressIndex")
|
||||
@Query("select * from /Customers customer where customer.emailAddress.value = $1")
|
||||
List<Customer> findByEmailAddressUsingIndex(String emailAddress);
|
||||
|
||||
@Trace
|
||||
@Limit(100)
|
||||
@Query("select * from /Customers customer where customer.firstName = $1")
|
||||
List<Customer> findByFirstNameUsingIndex(String firstName);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries.client;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.data.gemfire.GemfireTemplate;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableClusterDefinedRegions;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableContinuousQueries;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
import org.springframework.data.gemfire.search.lucene.LuceneTemplate;
|
||||
|
||||
@Configuration
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
@ClientCacheApplication(name = "CQClientCache", logLevel = "error", pingInterval = 5000L, readTimeout = 15000, subscriptionEnabled = true, readyForEvents = true)
|
||||
@EnableContinuousQueries
|
||||
@EnableClusterDefinedRegions(clientRegionShortcut = ClientRegionShortcut.PROXY)
|
||||
public class QueryClientConfig {
|
||||
|
||||
@Bean("customerTemplate")
|
||||
@DependsOn("Customers")
|
||||
protected GemfireTemplate configureCustomerTemplate(GemFireCache gemfireCache) {
|
||||
return new GemfireTemplate(gemfireCache.getRegion("Customers"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
LuceneTemplate createCustomerLuceneTemplate() {
|
||||
return new LuceneTemplate("lastName_lucene", "/Customers");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries.server;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = QueryServerConfig.class)
|
||||
public class QueryServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(QueryServer.class).web(WebApplicationType.NONE).build().run(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries.server;
|
||||
|
||||
import example.springdata.geode.client.queries.Customer;
|
||||
import example.springdata.geode.client.queries.client.CustomerRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableIndexing;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
import org.springframework.data.gemfire.search.lucene.LuceneTemplate;
|
||||
|
||||
@Configuration
|
||||
@CacheServerApplication(logLevel = "error")
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
@EnableEntityDefinedRegions(basePackageClasses = Customer.class)
|
||||
@EnableIndexing
|
||||
public class QueryServerConfig {
|
||||
|
||||
@Bean
|
||||
@DependsOn("lastName_lucene")
|
||||
LuceneTemplate createCustomerLuceneTemplate() {
|
||||
return new LuceneTemplate("lastName_lucene", "/Customers");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.queries;
|
||||
|
||||
import example.springdata.geode.client.queries.client.CustomerRepository;
|
||||
import example.springdata.geode.client.queries.client.QueryClientConfig;
|
||||
import example.springdata.geode.client.queries.server.QueryServer;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.lucene.LuceneResultStruct;
|
||||
import org.apache.geode.cache.query.CqEvent;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.After;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.gemfire.GemfireTemplate;
|
||||
import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer;
|
||||
import org.springframework.data.gemfire.listener.annotation.ContinuousQuery;
|
||||
import org.springframework.data.gemfire.search.lucene.LuceneTemplate;
|
||||
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = QueryClientConfig.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class QueryTests extends ForkingClientServerIntegrationTestsSupport {
|
||||
|
||||
@Autowired
|
||||
private ContinuousQueryListenerContainer container;
|
||||
|
||||
@Autowired
|
||||
private CustomerRepository customerRepository;
|
||||
|
||||
@Autowired
|
||||
private GemfireTemplate customerTemplate;
|
||||
|
||||
@Resource(name = "Customers")
|
||||
private Region<Long, Customer> customers;
|
||||
|
||||
@Autowired
|
||||
private LuceneTemplate luceneTemplate;
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws IOException {
|
||||
startGemFireServer(QueryServer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void luceneIsConfiguredCorrectly() {
|
||||
|
||||
customerRepository.save(new Customer(1L, new EmailAddress("name@internet.com"), "Stephanie", "Demarco"));
|
||||
customerRepository.save(new Customer(2L, new EmailAddress("cool_Guy57@mail.com"), "Patrick", "Dunham"));
|
||||
customerRepository.save(new Customer(3L, new EmailAddress("scientist@mail.com"), "Jasmine", "Oliander"));
|
||||
customerRepository.save(new Customer(4L, new EmailAddress("catlover42@mail.com"), "Erica", "Shu"));
|
||||
customerRepository.save(new Customer(5L, new EmailAddress("zolander@mail.com"), "Tom", "Darude"));
|
||||
|
||||
List<LuceneResultStruct<Long, Customer>> lastName = luceneTemplate.query("D*", "lastName", 10);
|
||||
|
||||
assertThat(lastName.size()).isEqualTo(3);
|
||||
logger.info("Customers with last names beginning with 'D':");
|
||||
lastName.forEach(result -> logger.info(result.getValue().toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oqlQueriesConfiguredCorrectly() {
|
||||
|
||||
logger.info("Inserting 3 entries for keys: 1, 2, 3");
|
||||
Customer john = new Customer(1L, new EmailAddress("2@2.com"), "John", "Smith");
|
||||
Customer frank = new Customer(2L, new EmailAddress("3@3.com"), "Frank", "Lamport");
|
||||
Customer jude = new Customer(3L, new EmailAddress("5@5.com"), "Jude", "Simmons");
|
||||
customerRepository.save(john);
|
||||
customerRepository.save(frank);
|
||||
customerRepository.save(jude);
|
||||
assertThat(customers.keySetOnServer().size()).isEqualTo(3);
|
||||
|
||||
Customer customer = customerRepository.findById(2L).get();
|
||||
assertThat(customer).isEqualTo(frank);
|
||||
logger.info("Find customer with key=2 using GemFireRepository: " + customer);
|
||||
List customerList = customerTemplate.find("select * from /Customers where id=$1", 2L).asList();
|
||||
assertThat(customerList.size()).isEqualTo(1);
|
||||
assertThat(customerList.contains(frank)).isTrue();
|
||||
logger.info("Find customer with key=2 using GemFireTemplate: " + customerList);
|
||||
|
||||
customer = new Customer(1L, new EmailAddress("3@3.com"), "Jude", "Smith");
|
||||
customerRepository.save(customer);
|
||||
assertThat(customers.keySetOnServer().size()).isEqualTo(3);
|
||||
|
||||
customerList = customerRepository.findByEmailAddressUsingIndex("3@3.com");
|
||||
assertThat(customerList.size()).isEqualTo(2);
|
||||
assertThat(customerList.contains(frank)).isTrue();
|
||||
assertThat(customerList.contains(customer)).isTrue();
|
||||
logger.info("Find customers with emailAddress=3@3.com: " + customerList);
|
||||
|
||||
customerList = customerRepository.findByFirstNameUsingIndex("Frank");
|
||||
assertThat(customerList.get(0)).isEqualTo(frank);
|
||||
logger.info("Find customers with firstName=Frank: " + customerList);
|
||||
customerList = customerRepository.findByFirstNameUsingIndex("Jude");
|
||||
assertThat(customerList.size()).isEqualTo(2);
|
||||
assertThat(customerList.contains(jude)).isTrue();
|
||||
assertThat(customerList.contains(customer)).isTrue();
|
||||
logger.info("Find customers with firstName=Jude: " + customerList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void continuousQueryWorkingCorrectly() {
|
||||
assertThat(this.customers).isEmpty();
|
||||
logger.info("Inserting 3 entries for keys: 1, 2, 3");
|
||||
customerRepository.save(new Customer(1L, new EmailAddress("2@2.com"), "John", "Smith"));
|
||||
customerRepository.save(new Customer(2L, new EmailAddress("3@3.com"), "Frank", "Lamport"));
|
||||
customerRepository.save(new Customer(3L, new EmailAddress("5@5.com"), "Jude", "Simmons"));
|
||||
assertThat(customers.keySetOnServer().size()).isEqualTo(3);
|
||||
|
||||
Awaitility.await().atMost(30, TimeUnit.SECONDS).until(() -> this.counter.get() == 3);
|
||||
}
|
||||
|
||||
@ContinuousQuery(name = "CustomerCQ", query = "SELECT * FROM /Customers")
|
||||
public void handleEvent(CqEvent event) {
|
||||
logger.info("Received message for CQ 'CustomerCQ'" + event);
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
customerRepository.deleteAll(customerRepository.findAll());
|
||||
container.getQueryService().closeCqs();
|
||||
}
|
||||
}
|
||||
11
geode/queries/src/test/resources/logback.xml
Normal file
11
geode/queries/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="error">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
|
||||
</configuration>
|
||||
15
geode/security/README.md
Executable file
15
geode/security/README.md
Executable file
@@ -0,0 +1,15 @@
|
||||
# Security Example
|
||||
|
||||
In this example a [Pivotal GemFire](https://pivotal.io/pivotal-gemfire) / [Apache Geode](http://geode.apache.org/) client and server will set up with security (username/password) authentication using Geode Security and Apache Shiro.
|
||||
|
||||
To run the example simply run the tests located under security/src/test in your IDE. There are two test files; one uses Geode Security and the other uses Apache Shiro. Both versions should behave the same.
|
||||
|
||||
The client is configured to connect to the server on `localhost` port `40404`. There is no need to start the server separately, as that is taken care of by the test.
|
||||
|
||||
## Running the example
|
||||
|
||||
The example is broken up into multiple steps:
|
||||
1. Insert (Put) three Customer entries into the `Customers` region using the repositories `save` method.
|
||||
2. Print the customers on the server.
|
||||
|
||||
NOTE: Inorder to see output, you must change the loglevel from "error" to "info" in the `logback.xml` file located under src/test/resources.
|
||||
42
geode/security/pom.xml
Executable file
42
geode/security/pom.xml
Executable file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>example.springdata.geode</groupId>
|
||||
<artifactId>spring-data-geode-examples</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>security</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<version>5.2.3.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
<version>2.2.4.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codeprimate</groupId>
|
||||
<artifactId>cp-elements</artifactId>
|
||||
<version>1.0.0.M5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.persistence</groupId>
|
||||
<artifactId>javax.persistence-api</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security;
|
||||
|
||||
/**
|
||||
* The [Constants] class contains properties and other constants used in the Apache Geode Integrated Security
|
||||
* framework.
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String SECURITY_PASSWORD_PROPERTY = "security-password";
|
||||
public static final String SECURITY_USERNAME_PROPERTY = "security-username";
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.gemfire.mapping.annotation.Region;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A customer used for Lucene examples.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
@Region(name = "Customers")
|
||||
public class Customer implements Serializable {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
private EmailAddress emailAddress;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
|
||||
public Customer(Long id, EmailAddress emailAddress, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.emailAddress = emailAddress;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Value object to represent email addresses.
|
||||
*
|
||||
* @author Udo Kohlmeyer
|
||||
* @author Patrick Johnson
|
||||
*/
|
||||
@Data
|
||||
public class EmailAddress implements Serializable {
|
||||
private String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
122
geode/security/src/main/java/example/springdata/geode/client/security/Role.java
Executable file
122
geode/security/src/main/java/example/springdata/geode/client/security/Role.java
Executable file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.cp.elements.lang.Identifiable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* The [Role] class is an Abstract Data Type (ADT) modeling a role of a user (e.g. Admin).
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Serializable
|
||||
* @see Comparable
|
||||
* @see Iterable
|
||||
* @see ResourcePermission
|
||||
* @see org.cp.elements.lang.Identifiable
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
public class Role implements Comparable<Role>, Identifiable<String>, Iterable<ResourcePermission>, Serializable {
|
||||
|
||||
private String name;
|
||||
private HashSet<ResourcePermission> permissions = new HashSet<>();
|
||||
|
||||
public Role(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
throw new UnsupportedOperationException("Operation Not Supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Role other) {
|
||||
return name.compareTo(other.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this [Role] has been assigned (granted) the given [permission][ResourcePermission].
|
||||
*
|
||||
* @param permission [ResourcePermission] to evaluate.
|
||||
* @return a boolean value indicating whether this [Role] has been assigned (granted)
|
||||
* the given [permission][ResourcePermission].
|
||||
* @see ResourcePermission
|
||||
*/
|
||||
public boolean hasPermission(ResourcePermission permission) {
|
||||
return this.permissions.contains(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Iterator<ResourcePermission> iterator() {
|
||||
return this.permissions.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds (assigns/grants) all given [persmissions][ResourcePermission] to this [Role].
|
||||
*
|
||||
* @param permissions [ResourcePermission]s to assign/grant to this [Role].
|
||||
* @return this [Role].
|
||||
* @see ResourcePermission
|
||||
*/
|
||||
public Role withPermissions(ResourcePermission... permissions) {
|
||||
this.permissions.addAll((Arrays.asList(permissions)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds (assigns/grants) all given [persmissions][ResourcePermission] to this [Role].
|
||||
*
|
||||
* @param permissions [ResourcePermission]s to assign/grant to this [Role].
|
||||
* @return this [Role].
|
||||
* @see ResourcePermission
|
||||
*/
|
||||
public Role withPersmissions(Iterable<ResourcePermission> permissions) {
|
||||
this.permissions.addAll((Collection<? extends ResourcePermission>) permissions);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of [Role] initialized with the given name.
|
||||
*
|
||||
* @param name [String] indicating the name of the new [Role].
|
||||
* @return a new [Role] initialized with the given name.
|
||||
* @see Role
|
||||
*/
|
||||
public static Role newRole(String name) {
|
||||
return new Role(name);
|
||||
}
|
||||
}
|
||||
144
geode/security/src/main/java/example/springdata/geode/client/security/User.java
Executable file
144
geode/security/src/main/java/example/springdata/geode/client/security/User.java
Executable file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.cp.elements.lang.Identifiable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class User implements Comparable<User>, Cloneable, Principal, Serializable, Iterable<Role>, Identifiable<String> {
|
||||
|
||||
private String name;
|
||||
private List<Role> roles;
|
||||
|
||||
private String credentials = null;
|
||||
|
||||
public User(String name, List<Role> roles) {
|
||||
this.name = name;
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public User(String name) {
|
||||
this(name, new ArrayList<Role>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
throw new UnsupportedOperationException("Operation Not Supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Role> iterator() {
|
||||
return roles.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Object clone() {
|
||||
return newUser(name).withCredentials(credentials).withRoles(roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(User other) {
|
||||
return this.getName().compareTo(other.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this [User] has been granted (assigned) the given [permission][ResourcePermission].
|
||||
*
|
||||
* @param permission [ResourcePermission] to evalute.
|
||||
* @return a boolean value indicating whether this [User] has been granted (assigned)
|
||||
* the given [ResourcePermission].
|
||||
* @see ResourcePermission
|
||||
*/
|
||||
public boolean hasPermission(ResourcePermission permission) {
|
||||
for (Role role : roles) {
|
||||
if (role.hasPermission(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this [User] has the specified [Role].
|
||||
*
|
||||
* @param role [Role] to evaluate.
|
||||
* @return a boolean value indicating whether this [User] has the specified [Role].
|
||||
* @see Role
|
||||
*/
|
||||
public boolean hasRole(Role role) {
|
||||
return roles.contains(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the array of [Roles][Role] granting (resource) permissions to this [User].
|
||||
*
|
||||
* @param roles array of [Roles][Role] granting (resource) permissions to this [User].
|
||||
* @return this [User].
|
||||
* @see Role
|
||||
*/
|
||||
public User withRoles(Role... roles) {
|
||||
this.roles.addAll(Arrays.asList(roles));
|
||||
return this;
|
||||
}
|
||||
|
||||
public User withRoles(Collection<Role> roles) {
|
||||
this.roles.addAll(roles);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this [User's][User] credentials (e.g. password) to the given value.
|
||||
*
|
||||
* @param credentials [String] containing this [User's][User] credentials (e.g. password).
|
||||
* @return this [User].
|
||||
* @see User
|
||||
*/
|
||||
public User withCredentials(String credentials) {
|
||||
this.credentials = credentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static User newUser(String name) {
|
||||
return new User(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.client;
|
||||
|
||||
import example.springdata.geode.client.security.Customer;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
List<Customer> findAll();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.client;
|
||||
|
||||
import example.springdata.geode.client.security.Customer;
|
||||
import org.apache.geode.cache.client.ClientRegionShortcut;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
|
||||
@Configuration
|
||||
@EnableSecurity
|
||||
@EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)
|
||||
@ClientCacheApplication(name = "SecurityClient", logLevel = "error", pingInterval = 5000L, readTimeout = 15000, retryAttempts = 1)
|
||||
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY)
|
||||
public class SecurityEnabledClientConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package example.springdata.geode.client.security.server;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
|
||||
|
||||
@Configuration
|
||||
@EnableSecurity(shiroIniResourcePath = "shiro.ini")
|
||||
@Profile("shiro-ini-configuration")
|
||||
public class ApacheShiroIniConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import example.springdata.geode.client.security.User;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The [CachingSecurityRepository] class caches Security Configuration Meta-Data and is meant to be extended
|
||||
* by classes that are data store specified (e.g. JDBC/RDBMS, LDAP, etc).
|
||||
*
|
||||
* @author John Blum
|
||||
* @see User
|
||||
* @see SecurityRepository
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class CachingSecurityRepository implements SecurityRepository {
|
||||
private Map<String, User> users = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Iterable<User> findAll() {
|
||||
return users.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(User user) {
|
||||
if (user != null) {
|
||||
return users.remove(user.getName()) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User save(User user) {
|
||||
User putUser = users.put(user.getName(), user);
|
||||
return putUser != null ? putUser : user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package example.springdata.geode.client.security.server;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableSecurity(securityManagerClassName = "example.springdata.geode.client.security.server.SecurityManagerProxy")
|
||||
@Profile({"default", "geode-security-manager-proxy-configuration"})
|
||||
public class GeodeIntegratedSecurityProxyConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import example.springdata.geode.client.security.Role;
|
||||
import example.springdata.geode.client.security.User;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public class JdbcSecurityRepository extends CachingSecurityRepository implements InitializingBean {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private static final String ROLES_QUERY = "SELECT name FROM geode_security.roles";
|
||||
private static final String ROLE_PERMISSIONS_QUERY = ""
|
||||
+ " SELECT rolePerms.resource, rolePerms.operation, rolePerms.region_name, rolePerms.key_name"
|
||||
+ " FROM geode_security.roles_permissions rolePerms"
|
||||
+ " INNER JOIN geode_security.roles roles ON roles.id = rolePerms.role_id "
|
||||
+ " WHERE roles.name = ?";
|
||||
private static final String USERS_QUERY = "SELECT name, credentials FROM geode_security.users";
|
||||
private static final String USER_ROLES_QUERY = ""
|
||||
+ " SELECT roles.name"
|
||||
+ " FROM geode_security.roles roles"
|
||||
+ " INNER JOIN geode_security.users_roles userRoles ON roles.id = userRoles.role_id"
|
||||
+ " INNER JOIN geode_security.users users ON userRoles.user_id = users.id"
|
||||
+ " WHERE users.name = ?";
|
||||
|
||||
public JdbcSecurityRepository(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
List<Role> roles = this.jdbcTemplate.query(ROLES_QUERY, (resultSet, i) -> Role.newRole(resultSet.getString(1)));
|
||||
HashMap<String, Role> roleMapping = new HashMap<>(roles.size());
|
||||
|
||||
roles.forEach((role) -> {
|
||||
this.jdbcTemplate.query(ROLE_PERMISSIONS_QUERY, Collections.singleton(role.getName()).toArray(),
|
||||
(RowMapper<Object>) (resultSet, i) -> role.withPermissions(newResourcePermission(
|
||||
resultSet.getString(1), resultSet.getString(2),
|
||||
resultSet.getString(3), resultSet.getString(4))));
|
||||
|
||||
roleMapping.put(role.getName(), role);
|
||||
});
|
||||
|
||||
List<User> users = this.jdbcTemplate.query(USERS_QUERY, (resultSet, i) ->
|
||||
createUser(resultSet.getString(1)).withCredentials(resultSet.getString(2)));
|
||||
|
||||
users.forEach((role) -> {
|
||||
this.jdbcTemplate.query(USER_ROLES_QUERY, Collections.singleton(role.getName()).toArray(),
|
||||
(RowMapper<Object>) (resultSet, i) -> role.withRoles(roleMapping.get(resultSet.getString(1))));
|
||||
|
||||
save(role);
|
||||
});
|
||||
|
||||
logger.debug("Users {}", users);
|
||||
}
|
||||
|
||||
protected ResourcePermission newResourcePermission(String resource, String operation, String region, String key) {
|
||||
return new ResourcePermission(resource, operation, region, key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = SecurityEnabledServerConfiguration.class)
|
||||
public class SecurityEnabledServer {
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(SecurityEnabledServer.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.build()
|
||||
.run(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import example.springdata.geode.client.security.Customer;
|
||||
import org.apache.geode.cache.RegionShortcut;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableIndexing;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableLocator;
|
||||
import org.springframework.data.gemfire.config.annotation.EnableManager;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
@EnableLocator
|
||||
@EnableIndexing
|
||||
@EnableManager
|
||||
@Import({ApacheShiroIniConfiguration.class, GeodeIntegratedSecurityProxyConfiguration.class})
|
||||
@CacheServerApplication(port = 0, logLevel = "error")
|
||||
@EnableEntityDefinedRegions(basePackageClasses = Customer.class, serverRegionShortcut = RegionShortcut.REPLICATE)
|
||||
public class SecurityEnabledServerConfiguration {
|
||||
|
||||
@Bean
|
||||
DataSource hsqlDataSource() {
|
||||
return new EmbeddedDatabaseBuilder()
|
||||
.setName("geode_security")
|
||||
.setScriptEncoding("UTF-8")
|
||||
.setType(EmbeddedDatabaseType.HSQL)
|
||||
.addScript("sql/geode-security-schema-ddl.sql")
|
||||
.addScript("sql/define-roles-table-ddl.sql")
|
||||
.addScript("sql/define-roles-permissions-table-ddl.sql")
|
||||
.addScript("sql/define-users-table-ddl.sql")
|
||||
.addScript("sql/define-users-roles-table-ddl.sql")
|
||||
.addScript("sql/insert-roles-dml.sql")
|
||||
.addScript("sql/insert-roles-permissions-dml.sql")
|
||||
.addScript("sql/insert-users-dml.sql")
|
||||
.addScript("sql/insert-users-roles-dml.sql")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import org.apache.geode.security.AuthenticationFailedException;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.cp.elements.lang.Assert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.gemfire.support.LazyWiringDeclarableSupport;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The {@link SecurityManagerProxy} class is a Proxy delegating to an underlying Apache Geode
|
||||
* {@link org.apache.geode.security.SecurityManager} implementation, that maybe a Spring managed bean
|
||||
* in a Spring context that may have been configured and auto-wired the Spring container, or possibly
|
||||
* other managed environment (Cloud or Java EE Server, etc).
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.security.SecurityManager
|
||||
* @see org.springframework.data.gemfire.support.LazyWiringDeclarableSupport
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SecurityManagerProxy extends LazyWiringDeclarableSupport
|
||||
implements org.apache.geode.security.SecurityManager {
|
||||
|
||||
private org.apache.geode.security.SecurityManager securityManager;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the {@link SecurityManagerProxy}, whick will delegate all Apache Geode
|
||||
* security operations to a Spring managed {@link org.apache.geode.security.SecurityManager} bean.
|
||||
*/
|
||||
public SecurityManagerProxy() {
|
||||
// TODO remove init() call when GEODE-2083 (https://issues.apache.org/jira/browse/GEODE-2083) is resolved!
|
||||
// NOTE the init(:Properties) call in the constructor is less than ideal since...
|
||||
// 1) it allows the *this* reference to escape, and...
|
||||
// 2) it is Geode's responsibility to identify Geode Declarable objects and invoke their init(:Properties) method
|
||||
// However, the init(:Properties) method invocation in the constructor is necessary to enable this Proxy to be
|
||||
// identified and auto-wired in a Spring context.
|
||||
init(new Properties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the Apache Geode {@link org.apache.geode.security.SecurityManager} instance
|
||||
* delegated to by this {@link SecurityManagerProxy}.
|
||||
*
|
||||
* @return a reference to the underlying, Apache Geode {@link org.apache.geode.security.SecurityManager}
|
||||
* instance delegated to by this {@link SecurityManagerProxy}.
|
||||
* @throws IllegalStateException if the configured Apache Geode {@link org.apache.geode.security.SecurityManager}
|
||||
* was not properly initialized.
|
||||
* @see org.apache.geode.security.SecurityManager
|
||||
*/
|
||||
protected org.apache.geode.security.SecurityManager getSecurityManager() {
|
||||
Assert.state(this.securityManager != null, "SecurityManager was not properly initialized");
|
||||
return this.securityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the Apache Geode {@link org.apache.geode.security.SecurityManager} instance
|
||||
* delegated to by this {@link SecurityManagerProxy}.
|
||||
*
|
||||
* @param securityManager reference to the underlying, Apache Geode {@link org.apache.geode.security.SecurityManager}
|
||||
* instance delegated to by this {@link SecurityManagerProxy}.
|
||||
* @throws IllegalArgumentException if the Apache Geode {@link org.apache.geode.security.SecurityManager} reference
|
||||
* is {@literal null}.
|
||||
* @see org.apache.geode.security.SecurityManager
|
||||
*/
|
||||
@Autowired
|
||||
public void setSecurityManager(org.apache.geode.security.SecurityManager securityManager) {
|
||||
Assert.notNull(securityManager, "SecurityManager must not be null");
|
||||
this.securityManager = securityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Object authenticate(Properties properties) throws AuthenticationFailedException {
|
||||
return getSecurityManager().authenticate(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean authorize(Object principal, ResourcePermission permission) {
|
||||
return getSecurityManager().authorize(principal, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
getSecurityManager().close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import example.springdata.geode.client.security.Constants;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The [SecurityManagerSupport] class is an Apache Geode [SecurityManager] interface adapter providing
|
||||
* default implementations of the [SecurityManager] interface operations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Principal
|
||||
* @see ResourcePermission
|
||||
* @see org.apache.geode.security.SecurityManager
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class SecurityManagerSupport implements org.apache.geode.security.SecurityManager {
|
||||
|
||||
protected Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
/* (non-Javadoc)*/
|
||||
protected String getName(Object principal) {
|
||||
if (principal instanceof Principal) {
|
||||
return ((Principal) principal).getName();
|
||||
} else {
|
||||
return principal.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)*/
|
||||
protected String getPassword(Properties securityProperties) {
|
||||
return getPropertyValue(securityProperties, Constants.SECURITY_PASSWORD_PROPERTY);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)*/
|
||||
protected String getUsername(Properties securityProperties) {
|
||||
return getPropertyValue(securityProperties, Constants.SECURITY_USERNAME_PROPERTY);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)*/
|
||||
protected String getPropertyValue(Properties properties, String propertyName) {
|
||||
return properties.getProperty(propertyName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)*/
|
||||
protected void logDebug(String message, Object... args) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(message, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void init(Properties securityProperties) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Security Properties [{}]", securityProperties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Object authenticate(Properties securityProperties) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean authorize(Object principal, ResourcePermission permission) {
|
||||
return principal != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Closing SecurityManager [{}]", this.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import example.springdata.geode.client.security.Role;
|
||||
import example.springdata.geode.client.security.User;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The [SecurityRepository] interface is a contract for Data Access Objects (DAO) implementing this interface
|
||||
* to perform CRUD and query operations on [User] information, pertinent to the security of the system.
|
||||
*
|
||||
* @author John Blum
|
||||
* @author Udo Kohlmeyer
|
||||
* @see Role
|
||||
* @see User
|
||||
* @see org.springframework.stereotype.Repository
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface SecurityRepository {
|
||||
/**
|
||||
* Finds all [Users][User] of the system.
|
||||
*
|
||||
* @return an [Iterable] of [Users][User] of the system.
|
||||
* @see User
|
||||
* @see Iterable
|
||||
*/
|
||||
Iterable<User> findAll();
|
||||
|
||||
/**
|
||||
* Deletes the given [User] from the system.
|
||||
*
|
||||
* @param user [User] to delete.
|
||||
* @return a boolean value indicating if the [User] was deleted successfully.
|
||||
* @see User
|
||||
*/
|
||||
boolean delete(User user);
|
||||
|
||||
/**
|
||||
* Records (persist) the information (state) of the [User].
|
||||
*
|
||||
* @param user [User] to store.
|
||||
* @return the [User].
|
||||
* @see User
|
||||
*/
|
||||
User save(User user);
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default int count() {
|
||||
int count = 0;
|
||||
Iterable<User> users = findAll();
|
||||
for (User user : users) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default User createUser(String username, Role... roles) {
|
||||
return save(User.newUser(username).withRoles(roles));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default boolean delete(String username) {
|
||||
User user = findBy(username);
|
||||
if (user != null) {
|
||||
return delete(user);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default boolean deleteAll(String... username) {
|
||||
return deleteAll(findAll(username));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default boolean deleteAll(User... users) {
|
||||
return deleteAll(Arrays.asList(users));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default boolean deleteAll(Iterable<User> users) {
|
||||
for (User user : users) {
|
||||
if (!delete(user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default boolean deleteAll() {
|
||||
return deleteAll(findAll());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default boolean exists(String username) {
|
||||
return findBy(username) != null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default Iterable<User> findAll(String... username) {
|
||||
return findAll(Arrays.asList(username));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default Iterable<User> findAll(Iterable<String> username) {
|
||||
List<User> all = new ArrayList<>();
|
||||
username.forEach(u -> {
|
||||
if (u != null) {
|
||||
all.add(findBy(u));
|
||||
}
|
||||
});
|
||||
return all;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default User findBy(String username) {
|
||||
for (User user : findAll()) {
|
||||
if (user.getName().equals(username)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default Iterable<User> saveAll(User... users) {
|
||||
return saveAll(Arrays.asList(users));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
default Iterable<User> saveAll(Iterable<User> users) {
|
||||
List<User> saved = new ArrayList<>();
|
||||
users.forEach(user -> saved.add(save(user)));
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2020 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.springdata.geode.client.security.server;
|
||||
|
||||
import example.springdata.geode.client.security.Role;
|
||||
import example.springdata.geode.client.security.User;
|
||||
import org.apache.geode.security.AuthenticationFailedException;
|
||||
import org.apache.geode.security.ResourcePermission;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The [SimpleSecurityManager] class is an example Apache Geode [SecurityManager] provider implementation
|
||||
* used to secure Apache Geode.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see SecurityManagerSupport
|
||||
* @see Role
|
||||
* @see User
|
||||
* @see SecurityRepository
|
||||
* @see ResourcePermission
|
||||
* @see org.apache.geode.security.SecurityManager
|
||||
* @see org.springframework.stereotype.Service
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Service
|
||||
public class SimpleSecurityManager extends SecurityManagerSupport {
|
||||
|
||||
protected SecurityRepository securityRepository;
|
||||
|
||||
public SimpleSecurityManager(SecurityRepository securityRepository) {
|
||||
this.securityRepository = securityRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public Object authenticate(Properties securityProperties) {
|
||||
String username = getUsername(securityProperties);
|
||||
String password = getPassword(securityProperties);
|
||||
|
||||
logDebug("User with name [{}] is attempting to login with password [{}]", username, password);
|
||||
|
||||
User user = securityRepository.findBy(username);
|
||||
|
||||
if (isNotAuthentic(user, password)) {
|
||||
throw new AuthenticationFailedException(String.format("Failed to authenticate user [%s]", username));
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isAuthentic(User user, String credentials) {
|
||||
return user != null && user.getCredentials().equals(credentials);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isNotAuthentic(User user, String credentials) {
|
||||
return !isAuthentic(user, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean authorize(Object principal, ResourcePermission permission) {
|
||||
logDebug("Principal [{}] is requesting access to a Resource {} with the required Permission [{}]",
|
||||
principal, permission.getResource(), permission);
|
||||
|
||||
return isAuthorized(principal, permission);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isAuthorized(Object principal, ResourcePermission permission) {
|
||||
User user = resolveUser(principal);
|
||||
|
||||
return user != null && isAuthorized(user, permission);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected User resolveUser(Object principal) {
|
||||
if (principal instanceof User) {
|
||||
return (User) principal;
|
||||
} else {
|
||||
return securityRepository.findBy(getName(principal));
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isAuthorized(User user, ResourcePermission requiredPermission) {
|
||||
if (!user.hasPermission(requiredPermission)) {
|
||||
for (Role role : user.getRoles()) {
|
||||
for (ResourcePermission userPermission : role.getPermissions()) {
|
||||
if (isPermitted(userPermission, requiredPermission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected boolean isPermitted(ResourcePermission userPermission, ResourcePermission resourcePermission) {
|
||||
return userPermission.implies(resourcePermission);
|
||||
}
|
||||
|
||||
}
|
||||
2
geode/security/src/main/resources/application.properties
Executable file
2
geode/security/src/main/resources/application.properties
Executable file
@@ -0,0 +1,2 @@
|
||||
spring.data.gemfire.security.username=scientist
|
||||
spring.data.gemfire.security.password=w0rk!ng4u
|
||||
42
geode/security/src/main/resources/shiro.ini
Executable file
42
geode/security/src/main/resources/shiro.ini
Executable file
@@ -0,0 +1,42 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
# Shiro INI configuration
|
||||
|
||||
# Objects and their properties are defined here, such as the securityManager, Realms and anything else needed
|
||||
# to build the SecurityManager
|
||||
[main]
|
||||
|
||||
# The 'users' section is for simple deployments when you only need a small number of statically-defined
|
||||
# set of User accounts.
|
||||
# username = password, roleName1, roleName2, …, roleNameN
|
||||
[users]
|
||||
root = s3cr3t!, ADMIN, DBA
|
||||
scientist = w0rk!ng4u, DATA_SCIENTIST
|
||||
analyst = p@55w0rd, DATA_ANALYST
|
||||
guest = guest, GUEST
|
||||
|
||||
# The 'roles' section is for simple deployments when you only need a small number of statically-defined roles.
|
||||
# rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN
|
||||
[roles]
|
||||
ADMIN = CLUSTER:MANAGE, CLUSTER:READ, CLUSTER:WRITE
|
||||
DBA = DATA:MANAGE, DATA:READ, DATA:WRITE
|
||||
DATA_SCIENTIST = DATA:READ, DATA:WRITE
|
||||
DATA_ANALYST = DATA:READ
|
||||
GUEST = NULL
|
||||
|
||||
# The 'urls' section is used for url-based security in web applications. We'll discuss this section
|
||||
# in the Web documentation
|
||||
[urls]
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS geode_security.roles_permissions (
|
||||
role_id INTEGER REFERENCES geode_security.roles(id),
|
||||
resource VARCHAR(32) NOT NUll,
|
||||
operation VARCHAR(32) NOT NULL,
|
||||
region_name VARCHAR(255),
|
||||
key_name VARCHAR(255)
|
||||
);
|
||||
4
geode/security/src/main/resources/sql/define-roles-table-ddl.sql
Executable file
4
geode/security/src/main/resources/sql/define-roles-table-ddl.sql
Executable file
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS geode_security.roles (
|
||||
id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1),
|
||||
name VARCHAR(32) NOT NULL
|
||||
);
|
||||
4
geode/security/src/main/resources/sql/define-users-roles-table-ddl.sql
Executable file
4
geode/security/src/main/resources/sql/define-users-roles-table-ddl.sql
Executable file
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS geode_security.users_roles (
|
||||
user_id INTEGER REFERENCES geode_security.users(id),
|
||||
role_id INTEGER REFERENCES geode_security.roles(id)
|
||||
);
|
||||
5
geode/security/src/main/resources/sql/define-users-table-ddl.sql
Executable file
5
geode/security/src/main/resources/sql/define-users-table-ddl.sql
Executable file
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS geode_security.users (
|
||||
id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
credentials VARCHAR(255)
|
||||
);
|
||||
1
geode/security/src/main/resources/sql/geode-security-schema-ddl.sql
Executable file
1
geode/security/src/main/resources/sql/geode-security-schema-ddl.sql
Executable file
@@ -0,0 +1 @@
|
||||
CREATE SCHEMA geode_security
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user