Apache Geodeが1.0.0-incubatingとしてリリースされていますが、合わせてSpring Data Geodeも
1.0.0.INCUBATING-RELEASEになっています。
とはいえ、Spring Data Geode用のページはなくて、相変わらずSpring Data Gemfireな
わけですが。
Spring Data for Pivotal GemFire
Spring Data Geodeは、Spring Data GemfireのGitHubリポジトリ内で、別ブランチとして
管理されています。
GitHub - spring-projects/spring-data-gemfire at apache-geode
ドキュメント(Spring Data Gemfire)はこちら。
Spring Data GemFire Reference Guide
今回、Apache Geodeが1.0.0-incubatingになって初めてSpring Data Geode含めて扱うので、
まずは軽く試してみたいと思います。
本エントリとしてのお題は、
- Apache GeodeはClient/Server Modeで動かす
- Spring Data GeodeをSpring Bootを含めて起動できる環境を作る
- Spring Data Geodeで、GemfireRepositoryを使ってみる(Queryは含まない)
とします。要するに、Hello Worldレベルですね。
めっちゃハマりましたが。
では、いってみたいと思います。
準備
アプリケーション側(Maven)
Mavenの依存関係などは、こんな感じ。
<properties>
<projectbuildsourceEncoding>UTF-8</projectbuildsourceEncoding>
<javaversion>1.8</javaversion>
<mavencompilersource>1.8</mavencompilersource>
<mavencompilertarget>1.8</mavencompilertarget>
<springbootversion>1.4.4.RELEASE</springbootversion>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-geode</artifactId>
<version>1.0.0.INCUBATING-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Spring Bootは、1.4.4.RELEASEとしました。最新の1.5.1.RELEASEではありません。
また、Spring Data Geodeにはstarterがないので、直接Spring Data Geodeを依存関係に足しています。
LocatorとServerをそれぞれ起動しておきます。今回は、ひとつずつ起動しておくものとします。
Cacheの設定は、以下のようにしておきました。
cache.xml
xml version="1.0" encoding="UTF-8"
<cache
xmlns="http://geode.apache.org/schema/cache"
xmlnsxsi="http://www.w3.org/2001/XMLSchema-instance"
xsischemaLocation="http://geode.apache.org/schema/cache
http://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<region name="myRegion" refid="PARTITION_REDUNDANT">
</region>
</cache>
Locator、Server、そしてRegion。
gfsh>list members
Name | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:35:locator)<ec><v0>:1024
server-1fcdc39cf249 | 172.19.0.3(server-1fcdc39cf249:154)<v3>:1024
gfsh>list regions
List of regions
---------------
myRegion
アプリケーションの作成
では、Spring Data Geodeで作成するアプリケーションを作っていきます。
最初に、Apache Geodeに保存するエンティティを作成。お題は書籍とします。
src/main/java/org/littlewings/geode/spring/Book.java
package org.littlewings.geode.spring;
import java.io.Serializable;
import org.springframework.data.annotation.Id;
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String isbn;
private String title;
private Integer price;
public Book(String isbn, String title, Integer price) {
this.isbn = isbn;
this.title = title;
this.price = price;
}
public Book() {
}
}
Serializableを実装し、Spring Dataの@Idを付与している以外はふつうのJavaBeansです。
次に、このBookクラスを使うRepositoryを作成。今回は、GemfireRepositoryインターフェースを継承しているだけで、
追加メソッドは作成しません。また、@Regionでエンティティの保存先のRegionを指定しておきます。
src/main/java/org/littlewings/geode/spring/BookRepository.java
package org.littlewings.geode.spring;
import org.springframework.data.gemfire.mapping.Region;
import org.springframework.data.gemfire.repository.GemfireRepository;
@Region("myRegion")
public interface BookRepository extends GemfireRepository<Book, String> {
}
Spring Data Geodeの設定。@EnableGemfireRepositoriesアノテーションを付与するとともに、ClientCacheとRegionの
Bean定義を行います。
src/main/java/org/littlewings/geode/spring/GeodeConfig.java
package org.littlewings.geode.spring;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
@Configuration
@EnableGemfireRepositories
public class GeodeConfig {
@Bean
public ClientCacheFactoryBean geodeCache() throws Exception {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setCacheXml(new ClassPathResource("client-cache.xml"));
clientCacheFactory.afterPropertiesSet();
return clientCacheFactory;
}
@Bean
public ClientRegionFactoryBean<String, Book> region(ClientCache cache) throws Exception {
ClientRegionFactoryBean<String, Book> clientRegionFactory = new ClientRegionFactoryBean<>();
clientRegionFactory.setCache(cache);
clientRegionFactory.setRegionName("myRegion");
clientRegionFactory.afterPropertiesSet();
return clientRegionFactory;
}
}
Cacheの設定は、Cache XMLで指定するようにしました。
clientCacheFactory.setCacheXml(new ClassPathResource("client-cache.xml"));
また、RegionのBean定義はしておかないと、RepositoryがRegionを参照できなくてエラーになります…。
Cache XMLを使用しているからといって、Region自体のBeanの定義は省略できず行っておく必要があります、と。
ところで、Apache Geodeは1.0.0-incubatingになるに伴いパッケージ名が「org.apache.geode」になったの
ですが、Spring Data Geodeは未だにいろいろとGemfireなので混乱しますね?
Spring Boot有効化のためのクラス。特にすることはありません。
src/main/java/org/littlewings/geode/spring/App.java
package org.littlewings.geode.spring;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
public class App {
}
ClientCacheの設定
先ほどBean定義で参照していた、Cache XMLは以下のように定義しました。
src/test/resources/client-cache.xml
xml version="1.0" encoding="UTF-8"
<client-cache
xmlns="http://geode.apache.org/schema/cache"
xmlnsxsi="http://www.w3.org/2001/XMLSchema-instance"
xsischemaLocation="http://geode.apache.org/schema/cache
http://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<pool name="client-pool" subscription-enabled="true">
<locator host="localhost" port="10334"/>
</pool>
<region name="myRegion" refid="PROXY">
<region-attributes pool-name="client-pool"/>
</region>
</client-cache>
単純にServerに接続しにいくRegion定義です。
テストコード
では、テストコードを書いて確認してみます。
テストコードの雛形としては、以下のようなものを用意。
src/test/java/org/littlewings/geode/spring/SimpleDataGeodeTest.java
package org.littlewings.geode.spring;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.geode.cache.client.ServerOperationException;
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.dao.DataAccessResourceFailureException;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleDataGeodeTest {
@Autowired
BookRepository bookRepository;
}
作成したRepositoryは、@Autowiredでインジェクションしています。
まずは基本的なCRUD。
@Test
public void gettingStarted() {
Book book = new Book("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4320);
bookRepository.save(book);
Book foundBook = bookRepository.findOne("978-4798142470");
assertThat(foundBook.getTitle())
.isEqualTo("Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発");
assertThat(foundBook.getPrice())
.isEqualTo(4320);
bookRepository.delete("978-4798142470");
assertThat(bookRepository.findOne("978-4798142470"))
.isNull();
assertThat(bookRepository.count())
.isZero();
}
findAllなどの、Iterableを使うもの。
@Test
public void iterable() {
List<Book> books =
Arrays.asList(
new Book("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4320),
new Book("978-4774182179", "[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ", 4104),
new Book("978-4777519699", "はじめてのSpring Boot―スプリング・フレームワークで簡単Javaアプリ開発", 2700)
);
bookRepository.save(books);
Iterable<Book> foundBooks = bookRepository.findAll();
List<Book> foundBooksAsList = (List<Book>) foundBooks;
assertThat(foundBooksAsList)
.hasSize(3);
assertThat(foundBooksAsList.stream().map(b -> b.getIsbn()).collect(Collectors.toList()))
.containsAll(books.stream().map(b -> b.getIsbn()).collect(Collectors.toList()));
bookRepository.delete(books);
assertThat(bookRepository.count())
.isZero();
}
deleteAllはサポートしていなかったりします。
@Test
public void deleteAllUnsupported() {
assertThatThrownBy(() -> bookRepository.deleteAll())
.isInstanceOf(DataAccessResourceFailureException.class)
.hasCauseInstanceOf(ServerOperationException.class);
}
とりあえず、OKそうですね。
ハマったこと
それはもうハマりました。
まあ、ハマったといっても1点だけで、Spring Bootを1.5.1.RELEASEでSpring Data Geodeを合わせようとするとRepositoryにうまく
DIできなくなるようで、こんな感じでコケてしまいます…。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'region' defined in class path resource [org/littlewings/geode/spring/GeodeConfig.class]: Unsatisfied dependency expressed through method 'region' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bookRepository': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
Spring Bootのバージョンを下げるとうまく動くのですが、それに気づくまでにかなり時間がかかりました…。
なお、原因自体はちゃんと追えてません…。
まとめ
とりあえず触りだけですが、Spring Data Geodeの1.0.0.INCUBATING-RELEASEを試してみました。
Spring BootとSpring Data GeodeのRepositoryの関連にだいぶハマりましたが、最低限動いたので…とは言いきりにくい
ところがあります。
SpringのCache Abstractionとも、そのうち組み合わせてみようかな?(こっちは、Spring Boot 1.5.1.RELEASEで
動いて欲しい)
あと、Queryとかも試しておきたいですね。
まずはこんなところで。