From 40a0a7f2a636573007383d401789cd71a5d931d0 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 18 Jan 2012 16:27:08 +0000 Subject: [PATCH 01/21] SGF-82, SGF-83 - Initial draft of repository integration. Added support for annotation based entity mapping (@Region, @Id, @PersistenceConstructor). Added support for Spring Data repositories (query execution, query derivation). --- build.gradle | 9 +- docs/src/reference/docbook/index.xml | 10 + .../reference/docbook/reference/mapping.xml | 84 ++ .../docbook/reference/repositories.xml | 218 +++ gradle.properties | 2 + .../config/GemfireNamespaceHandler.java | 3 + .../mapping/GemfireMappingContext.java | 50 + .../mapping/GemfirePersistentEntity.java | 57 + .../mapping/GemfirePersistentProperty.java | 52 + .../mapping/GemfirePropertyValueProvider.java | 51 + .../gemfire/mapping/MappingPdxSerializer.java | 156 ++ .../mapping/PdxReaderPropertyAccessor.java | 78 + .../data/gemfire/mapping/Region.java | 42 + .../data/gemfire/mapping/Regions.java | 98 ++ .../gemfire/repository/GemfireRepository.java | 30 + .../data/gemfire/repository/Query.java | 34 + .../data/gemfire/repository/Wrapper.java | 95 ++ .../config/GemfireRepositoryParser.java | 40 + .../SimpleGemfireRepositoryConfiguration.java | 135 ++ .../data/gemfire/repository/package-info.java | 5 + .../DefaultGemfireEntityInformation.java | 52 + .../query/GemfireEntityInformation.java | 37 + .../repository/query/GemfireQueryCreator.java | 143 ++ .../repository/query/GemfireQueryMethod.java | 86 ++ .../query/GemfireRepositoryQuery.java | 50 + .../query/PartTreeGemfireRepositoryQuery.java | 68 + .../gemfire/repository/query/Predicate.java | 6 + .../gemfire/repository/query/Predicates.java | 173 +++ .../repository/query/QueryBuilder.java | 53 + .../gemfire/repository/query/QueryString.java | 118 ++ .../StringBasedGemfireRepositoryQuery.java | 103 ++ .../support/GemfireRepositoryFactory.java | 147 ++ .../support/GemfireRepositoryFactoryBean.java | 70 + .../support/SimpleGemfireRepository.java | 174 +++ src/main/resources/META-INF/spring.schemas | 3 +- .../gemfire/config/spring-gemfire-1.2.xsd | 1309 +++++++++++++++++ .../GemfirePersistentEntityUnitTests.java | 66 + .../MappingPdxSerializerIntegrationTest.java | 81 + .../MappingPdxSerializerUnitTests.java | 81 + .../PdxReaderPropertyAccessorUnitTests.java | 85 ++ .../NamespaceRepositoryIntegrationTests.java | 39 + .../query/GemfireQueryCreatorUnitTests.java | 54 + .../query/GemfireQueryMethodUnitTests.java | 75 + .../repository/query/PredicatesUnitTests.java | 76 + .../query/QueryStringUnitTests.java | 68 + .../gemfire/repository/sample/Address.java | 26 + .../gemfire/repository/sample/Person.java | 57 + .../repository/sample/PersonRepository.java | 45 + ...fireRepositoryFactoryIntegrationTests.java | 120 ++ ...fireRepositoryFactoryIntegrationTests.java | 36 + ...impleGemfireRepositoryIntegrationTest.java | 104 ++ src/test/resources/log4j.properties | 1 + .../repository/config/repo-context.xml | 14 + template.mf | 2 + 54 files changed, 4868 insertions(+), 3 deletions(-) create mode 100644 docs/src/reference/docbook/reference/mapping.xml create mode 100644 docs/src/reference/docbook/reference/repositories.xml create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/GemfireMappingContext.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/GemfirePropertyValueProvider.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessor.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/Region.java create mode 100644 src/main/java/org/springframework/data/gemfire/mapping/Regions.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/GemfireRepository.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/Query.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/Wrapper.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryParser.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/config/SimpleGemfireRepositoryConfiguration.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/package-info.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreator.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/GemfireRepositoryQuery.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/Predicate.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/Predicates.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java create mode 100644 src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java create mode 100644 src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd create mode 100644 src/test/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntityUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessorUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/config/NamespaceRepositoryIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreatorUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/query/PredicatesUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/Address.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/Person.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java create mode 100644 src/test/resources/org/springframework/data/gemfire/repository/config/repo-context.xml diff --git a/build.gradle b/build.gradle index 27e33509..03aafdc9 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ allprojects { mavenRepo name: "spring-snapshot", urls: "http://maven.springframework.org/snapshot" mavenRepo name: "sonatype-snapshot", urls: "http://oss.sonatype.org/content/repositories/snapshots" mavenRepo name: "ext-snapshots", urls: "http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/" - mavenRepo name: "gemstone-com-release", urls: "http://dist.gemstone.com/maven/release" + mavenRepo name: "gemstone-com-release", urls: "http://repo.springsource.org/gemstone-release" } } @@ -87,10 +87,15 @@ dependencies { compile("com.gemstone.gemfire:gemfire:$gemfireVersion") // Testing - testCompile "junit:junit:$junitVersion" + testCompile "junit:junit-dep:$junitVersion" testCompile "org.mockito:mockito-core:$mockitoVersion" + testCompile "org.hamcrest:hamcrest-core:$hamcrestVersion" + testCompile "org.hamcrest:hamcrest-library:$hamcrestVersion" testCompile "org.springframework:spring-test:$springVersion" testCompile("javax.annotation:jsr250-api:1.0") { optional = true } + + // Spring Data + compile "org.springframework.data:spring-data-commons-core:${springDataCommonsVersion}" } javaprojects = rootProject diff --git a/docs/src/reference/docbook/index.xml b/docs/src/reference/docbook/index.xml index b6bc2654..48119e0b 100644 --- a/docs/src/reference/docbook/index.xml +++ b/docs/src/reference/docbook/index.xml @@ -13,6 +13,11 @@ Leau SpringSource, a division of VMware + + Oliver + Gierke + SpringSource, a division of VMware + @@ -41,6 +46,11 @@ + + + + + diff --git a/docs/src/reference/docbook/reference/mapping.xml b/docs/src/reference/docbook/reference/mapping.xml new file mode 100644 index 00000000..03409452 --- /dev/null +++ b/docs/src/reference/docbook/reference/mapping.xml @@ -0,0 +1,84 @@ + + + POJO mapping + +
+ Entity mapping + + Spring Data Gemfire provides support to map entities to be stored in + a Gemfire grid. The mapping metadata is define by using annotations at the + domain classes just like this: + + + Mapping a domain class to Gemfire + + @Region("myRegion") +public class Person { + + @Id Long id; + String firstname; + String lastname; + + @PersistenceConstructor + public Person(String firstname, String lastname) { + // … + } + + … +} + + + The first thing you see here is the + @Region annotation that can be used to + customize the region instances of the Person class + are stored in. The @Id annotation can be + used to annotate the property that shall be used as cache key. The + @PersistenceConstructor annotation actually + helps disambiguing multiple potentially available constructors taking + parameters and explicitly marking the one annotated as the one to be used + to create entities. With none or only a single constructor you can omit + the annotation. +
+ +
+ Mapping PDX serializer + + Spring Data Gemfire provides a custom + PDXSerializer implementation that uses the + mapping information to customize entity serialization. Beyond that it + allows customizing the entity instantiation by using the Spring Data + EntityInstantiator abstraction. By default + the serializer uses a ReflectionEntityInstantiator + that will use the persistence constructor of the mapped entity (either the + single declared one or explicitly annoted with + @PersistenceConstructor). To provide values + for constructor parameters it will read fields with name of the + constructor parameters from the PDXReader + supplied. + + + Using @Value on entity constructor parameters + + public class Person { + + public Person(@Value("#root.foo") String firstname, @Value("bean") String lastname) { + // … + } + + … +} + + + The entity annotated as such will get the field foo + read from the PDXReader and handed as + constructor parameter value for firstname. The value for + lastname will be the Spring bean with name + bean. +
+
diff --git a/docs/src/reference/docbook/reference/repositories.xml b/docs/src/reference/docbook/reference/repositories.xml new file mode 100644 index 00000000..1e4c0cbe --- /dev/null +++ b/docs/src/reference/docbook/reference/repositories.xml @@ -0,0 +1,218 @@ + + + Gemfire Repositories + +
+ Introduction + + Spring Data Gemfire provides support to use the Spring Data + repository abstraction to easily persist entities into Gemfire and execute + queries. A general introduction into the repository programmin model has + been provided in . +
+ +
+ Spring configuration + + To bootstrap Spring Data repositories you use the + <repositories /> element from the Gemfire + namespace: + + + Bootstrap Gemfire repositories + + <beans xmlns="http://www.springframework.org/schema/beans" + xmlns:gf="http://www.springframework.org/schema/gemfire" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/gemfire + http://www.springframework.org/schema/gemfire/spring-gemfire.xsd> + + <gf:repositories base-package="com.acme.repository" /> + +</beans> + + + This configuration snippet will look for interfaces below the + configured base package and create repository instances for those + interfaces backed by a SimpleGemfireRepository. + Note that you have to have your domain classes correctly mapped to + configured regions as the bottstrap process will fail otherwise. +
+ +
+ Executing OQL queries + + The Gemfire repositories allow the definition of query methods to + easily execute OQL queries against the Region the managed entity is mapped + to. + + + Sample repository + + @Region("myRegion") +public class Person { … } + + public interface PersonRepository extends CrudRepository<Person, Long> { + + Person findByEmailAddress(String emailAddress); + + Collection<Person> findByFirstname(String firstname); + + @Query("SELECT * FROM /Person p WHERE p.firstname = $1") + Collection<Person> findByFirstnameAnnotated(String firstname); + + @Query("SELECT * FROM /Person p WHERE p.firstname IN SET $1") + Collection<Person> findByFirstnamesAnnotated(Collection<String> firstnames); +} + + + The first method listed here will cause the following query to be + derived: SELECT x FROM /myRegion x WHERE x.emailAddress = $1. + The second method works the same way except it's returning all entities + found whereas the first one expects a single result value. In case the + supported keywords are not sufficient to declare your query or the method + name gets to verbose you can annotate the query methods with + @Query as seen for methods 3 and 4. + + + Supported keywords for query methods + + + + + + + + + + + Keyword + + Sample + + Logical result + + + + + + GreaterThan + + findByAgeGreaterThan(int + age) + + x.age > $1 + + + + GreaterThanEqual + + findByAgeGreaterThanEqual(int + age) + + x.age >= $1 + + + + LessThan + + findByAgeLessThan(int + age) + + x.age < $1 + + + + LessThanEqual + + findByAgeLessThanEqual(int + age) + + x.age <= $1 + + + + IsNotNull, + NotNull + + findByFirstnameNotNull() + + x.firstname =! NULL + + + + IsNull, + Null + + findByFirstnameNull() + + x.firstname = NULL + + + + In + + findByFirstnameIn(Collection<String> + x) + + x.firstname IN SET $1 + + + + NotIn + + findByFirstnameNotIn(Collection<String> + x) + + x.firstname NOT IN SET $1 + + + + (No keyword) + + findByFirstname(String + name) + + x.firstname = $1 + + + + Not + + findByFirstnameNot(String + name) + + x.firstname != $1 + + + + IsTrue, + True + + findByActiveIsTrue() + + x.active = true + + + + IsFalse, + False + + findByActiveIsFalse() + + x.active = false + + + +
+
+
diff --git a/gradle.properties b/gradle.properties index cc97c3f2..db667a51 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,11 +6,13 @@ slf4jVersion = 1.6.4 # Common libraries springVersion = 3.1.0.RELEASE +springDataCommonsVersion = 1.3.0.M1 gemfireVersion = 6.6.1 # Testing junitVersion = 4.8.1 mockitoVersion = 1.8.5 +hamcrestVersion = 1.2.1 # Manifest properties diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java index 57eeafe6..68f52bfa 100644 --- a/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java +++ b/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java @@ -17,6 +17,7 @@ package org.springframework.data.gemfire.config; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; +import org.springframework.data.gemfire.repository.config.GemfireRepositoryParser; /** * Namespace handler for GemFire definitions. @@ -40,5 +41,7 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("transaction-manager", new TransactionManagerParser()); registerBeanDefinitionParser("cq-listener-container", new GemfireListenerContainerParser()); + + registerBeanDefinitionParser("repositories", new GemfireRepositoryParser()); } } \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/mapping/GemfireMappingContext.java b/src/main/java/org/springframework/data/gemfire/mapping/GemfireMappingContext.java new file mode 100644 index 00000000..bc5c47fb --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/GemfireMappingContext.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; + +import org.springframework.data.mapping.context.AbstractMappingContext; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.util.TypeInformation; + +/** + * + * @author Oliver Gierke + */ +public class GemfireMappingContext extends + AbstractMappingContext, GemfirePersistentProperty> { + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) + */ + @Override + protected GemfirePersistentEntity createPersistentEntity(TypeInformation typeInformation) { + return new GemfirePersistentEntity(typeInformation); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder) + */ + @Override + protected GemfirePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, + GemfirePersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + return new GemfirePersistentProperty(field, descriptor, owner, simpleTypeHolder); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java new file mode 100644 index 00000000..9cb5878e --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.StringUtils; + +/** + * {@link PersistentEntity} implementation adding custom Gemfire related metadata, such as the region the entity is + * mapped to etc. + * + * @author Oliver Gierke + */ +public class GemfirePersistentEntity extends BasicPersistentEntity { + + private final String regionName; + + /** + * Creates a new {@link GemfirePersistentEntity} for the given {@link TypeInformation}. + * + * @param information must not be {@literal null}. + */ + public GemfirePersistentEntity(TypeInformation information) { + + super(information); + + Class rawType = information.getType(); + Region region = rawType.getAnnotation(Region.class); + String fallbackName = rawType.getSimpleName(); + + this.regionName = region == null || !StringUtils.hasText(region.value()) ? fallbackName : region.value(); + } + + /** + * Returns the name of the region the entity shall be stored in. + * + * @return + */ + public String getRegionName() { + return this.regionName; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java new file mode 100644 index 00000000..ea04d880 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; + +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.model.SimpleTypeHolder; + +/** + * {@link PersistentProperty} implementation to for Gemfire related metadata. + * + * @author Oliver Gierke + */ +public class GemfirePersistentProperty extends AnnotationBasedPersistentProperty { + + /** + * @param field + * @param propertyDescriptor + * @param owner + * @param simpleTypeHolder + */ + public GemfirePersistentProperty(Field field, PropertyDescriptor propertyDescriptor, + PersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + super(field, propertyDescriptor, owner, simpleTypeHolder); + } + + /* (non-Javadoc) + * @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation() + */ + @Override + protected Association createAssociation() { + return null; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePropertyValueProvider.java b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePropertyValueProvider.java new file mode 100644 index 00000000..e674a47d --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePropertyValueProvider.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import org.springframework.data.mapping.model.PropertyValueProvider; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.pdx.PdxReader; + +/** + * {@link PropertyValueProvider} to read property values from a {@link PdxReader}. + * + * @author Oliver Gierke + */ +class GemfirePropertyValueProvider implements PropertyValueProvider { + + private final PdxReader reader; + + /** + * Creates a new {@link GemfirePropertyValueProvider} with the given {@link PdxReader}. + * + * @param reader must not be {@literal null}. + */ + public GemfirePropertyValueProvider(PdxReader reader) { + Assert.notNull(reader); + this.reader = reader; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty) + */ + @Override + @SuppressWarnings("unchecked") + public T getPropertyValue(GemfirePersistentProperty property) { + return (T) reader.readObject(property.getName()); + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java new file mode 100644 index 00000000..439b92d2 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java @@ -0,0 +1,156 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import java.util.Map; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.convert.EntityInstantiator; +import org.springframework.data.convert.EntityInstantiators; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.model.BeanWrapper; +import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; +import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; +import org.springframework.data.mapping.model.SpELContext; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.pdx.PdxReader; +import com.gemstone.gemfire.pdx.PdxSerializer; +import com.gemstone.gemfire.pdx.PdxWriter; + +/** + * {@link PdxSerializer} implementation that uses a {@link GemfireMappingContext} to read and write entities. + * + * @author Oliver Gierke + */ +public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAware { + + private final GemfireMappingContext mappingContext; + private final ConversionService conversionService; + + private EntityInstantiators instantiators; + private SpELContext context; + + /** + * Creates a new {@link MappingPdxSerializer} using the given {@link GemfireMappingContext} and + * {@link ConversionService}. + * + * @param mappingContext must not be {@literal null}. + * @param conversionService must not be {@literal null}. + */ + public MappingPdxSerializer(GemfireMappingContext mappingContext, ConversionService conversionService) { + + Assert.notNull(mappingContext); + Assert.notNull(conversionService); + + this.mappingContext = mappingContext; + this.conversionService = conversionService; + this.instantiators = new EntityInstantiators(); + this.context = new SpELContext(PdxReaderPropertyAccessor.INSTANCE); + } + + /** + * Configures the {@link EntityInstantiator}s to be used to create the instances to be read. + * + * @param gemfireInstantiators must not be {@literal null}. + */ + public void setGemfireInstantiators(Map, EntityInstantiator> gemfireInstantiators) { + Assert.notNull(gemfireInstantiators); + this.instantiators = new EntityInstantiators(gemfireInstantiators); + } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = new SpELContext(context, applicationContext); + } + + /* + * (non-Javadoc) + * @see com.gemstone.gemfire.pdx.PdxSerializer#fromData(java.lang.Class, com.gemstone.gemfire.pdx.PdxReader) + */ + public Object fromData(Class type, final PdxReader reader) { + + final GemfirePersistentEntity entity = mappingContext.getPersistentEntity(type); + EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); + GemfirePropertyValueProvider propertyValueProvider = new GemfirePropertyValueProvider(reader); + + PersistentEntityParameterValueProvider provider = new PersistentEntityParameterValueProvider( + entity, propertyValueProvider); + provider.setSpELEvaluator(new DefaultSpELExpressionEvaluator(reader, context)); + + Object instance = instantiator.createInstance(entity, provider); + + final BeanWrapper, Object> wrapper = BeanWrapper.create(instance, conversionService); + + entity.doWithProperties(new PropertyHandler() { + public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) { + + if (entity.isConstructorArgument(persistentProperty)) { + return; + } + + Object value = reader.readField(persistentProperty.getName()); + + try { + wrapper.setProperty(persistentProperty, value); + } catch (Exception e) { + throw new MappingException("Could not read value " + value.toString(), e); + } + } + }); + + return wrapper.getBean(); + } + + /* + * (non-Javadoc) + * @see com.gemstone.gemfire.pdx.PdxSerializer#toData(java.lang.Object, com.gemstone.gemfire.pdx.PdxWriter) + */ + public boolean toData(Object value, final PdxWriter writer) { + + GemfirePersistentEntity entity = mappingContext.getPersistentEntity(value.getClass()); + final BeanWrapper, Object> wrapper = BeanWrapper.create(value, conversionService); + + entity.doWithProperties(new PropertyHandler() { + public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) { + + try { + Object value = wrapper.getProperty(persistentProperty); + writer.writeObject(persistentProperty.getName(), value); + } catch (Exception e) { + throw new MappingException("Could not write value for property " + persistentProperty.toString(), e); + } + } + }); + + GemfirePersistentProperty idProperty = entity.getIdProperty(); + + if (idProperty != null) { + writer.markIdentityField(idProperty.getName()); + } + + return true; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessor.java b/src/main/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessor.java new file mode 100644 index 00000000..4e52e7b9 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessor.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.PropertyAccessor; +import org.springframework.expression.TypedValue; + +import com.gemstone.gemfire.pdx.PdxReader; + +/** + * {@link PropertyAccessor} to read values from a {@link PdxReader}. + * + * @author Oliver Gierke + */ +enum PdxReaderPropertyAccessor implements PropertyAccessor { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.expression.PropertyAccessor#getSpecificTargetClasses() + */ + @Override + public Class[] getSpecificTargetClasses() { + return new Class[] { PdxReader.class }; + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.PropertyAccessor#canRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String) + */ + @Override + public boolean canRead(EvaluationContext context, Object target, String name) { + return ((PdxReader) target).hasField(name); + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.PropertyAccessor#read(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String) + */ + @Override + public TypedValue read(EvaluationContext context, Object target, String name) { + Object object = ((PdxReader) target).readObject(name); + return object == null ? TypedValue.NULL : new TypedValue(object); + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.PropertyAccessor#canWrite(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String) + */ + @Override + public boolean canWrite(EvaluationContext context, Object target, String name) { + return false; + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.PropertyAccessor#write(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String, java.lang.Object) + */ + @Override + public void write(EvaluationContext context, Object target, String name, Object newValue) { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/mapping/Region.java b/src/main/java/org/springframework/data/gemfire/mapping/Region.java new file mode 100644 index 00000000..726c0478 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/Region.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to define the region an entity will be stored in. + * + * @author Oliver Gierke + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface Region { + + /** + * The name of the {@link com.gemstone.gemfire.cache.Region} the entity shall be stored in. + * + * @return the name of the region the entity shall be persisted in. + */ + String value() default ""; +} diff --git a/src/main/java/org/springframework/data/gemfire/mapping/Regions.java b/src/main/java/org/springframework/data/gemfire/mapping/Regions.java new file mode 100644 index 00000000..70c3ebb6 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/mapping/Regions.java @@ -0,0 +1,98 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.util.Assert; + +import com.gemstone.bp.edu.emory.mathcs.backport.java.util.Collections; +import com.gemstone.gemfire.cache.Region; + +/** + * Simple value object to abstract access to regions by name and mapped type. + * + * @author Oliver Gierke + */ +public class Regions implements Iterable> { + + private final Map> regions; + private final MappingContext, ?> context; + + /** + * Creates a new {@link Regions} wrapper for the given {@link Region}s and {@link MappingContext}. + * + * @param regions must not be {@literal null}. + * @param context must not be {@literal null}. + */ + @SuppressWarnings("unchecked") + public Regions(Iterable> regions, MappingContext, ?> context) { + + Assert.notNull(regions); + Assert.notNull(context); + + Map> regionMap = new HashMap>(); + + for (Region region : regions) { + regionMap.put(region.getName(), region); + } + + this.regions = Collections.unmodifiableMap(regionMap); + this.context = context; + } + + /** + * Returns the {@link Region} the given type is mapped to. Will try to find a {@link Region} with the simple class + * name in case no mapping information is found. + * + * @param type must not be {@literal null}. + * @return + */ + @SuppressWarnings("unchecked") + public Region getRegion(Class type) { + + Assert.notNull(type); + + GemfirePersistentEntity entity = context.getPersistentEntity(type); + return (Region) (entity == null ? regions.get(type.getSimpleName()) : regions.get(entity.getRegionName())); + } + + /** + * Returns the region with the given name. + * + * @param name must not be {@literal null}. + * @return + */ + @SuppressWarnings("unchecked") + public Region getRegion(String name) { + + Assert.notNull(name); + + return (Region) regions.get(name); + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator> iterator() { + return regions.values().iterator(); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/GemfireRepository.java b/src/main/java/org/springframework/data/gemfire/repository/GemfireRepository.java new file mode 100644 index 00000000..ec4b935c --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/GemfireRepository.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository; + +import java.io.Serializable; + +import org.springframework.data.repository.CrudRepository; + +/** + * Gemfire-specific extension of the {@link CrudRepository} interface. + * + * @author Oliver Gierke + */ +public interface GemfireRepository extends CrudRepository { + + T save(Wrapper wrapper); +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/Query.java b/src/main/java/org/springframework/data/gemfire/repository/Query.java new file mode 100644 index 00000000..b6e6cfd2 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/Query.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Oliver Gierke + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Query { + + String value() default ""; +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/Wrapper.java b/src/main/java/org/springframework/data/gemfire/repository/Wrapper.java new file mode 100644 index 00000000..97a664a6 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/Wrapper.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository; + +import java.io.Serializable; + +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * Simple value object to hold an entity alongside an external key the entity shall be stored under. + * + * @author Oliver Gierke + */ +public final class Wrapper { + + private final KEY key; + private final T entity; + + /** + * The entity to handle as well as the key. + * + * @param entity + * @param key must not be {@literal null}. + */ + public Wrapper(T entity, KEY key) { + + Assert.notNull(key); + + this.entity = entity; + this.key = key; + } + + /** + * @return the key + */ + public KEY getKey() { + return key; + } + + /** + * @return the entity + */ + public T getEntity() { + return entity; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object value) { + + if (this == value) { + return true; + } + + if (!(value instanceof Wrapper)) { + return false; + } + + Wrapper that = (Wrapper) value; + + return this.key.equals(that.key) && ObjectUtils.nullSafeEquals(this.entity, that.entity); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + + int result = 17; + + result += 31 * key.hashCode(); + result += 31 * ObjectUtils.nullSafeHashCode(entity); + + return result; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryParser.java b/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryParser.java new file mode 100644 index 00000000..7b6c40dd --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryParser.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.config; + +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.data.gemfire.repository.config.SimpleGemfireRepositoryConfiguration.GemfireRepositoryConfiguration; +import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean; +import org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser; +import org.w3c.dom.Element; + +/** + * {@link BeanDefinitionParser} to create {@link GemfireRepositoryFactoryBean}. + * + * @author Oliver Gierke + */ +public class GemfireRepositoryParser extends + AbstractRepositoryConfigDefinitionParser { + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser#getGlobalRepositoryConfigInformation(org.w3c.dom.Element) + */ + @Override + protected SimpleGemfireRepositoryConfiguration getGlobalRepositoryConfigInformation(Element element) { + return new SimpleGemfireRepositoryConfiguration(element); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/config/SimpleGemfireRepositoryConfiguration.java b/src/main/java/org/springframework/data/gemfire/repository/config/SimpleGemfireRepositoryConfiguration.java new file mode 100644 index 00000000..afa04ff7 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/config/SimpleGemfireRepositoryConfiguration.java @@ -0,0 +1,135 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.config; + +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.repository.config.SimpleGemfireRepositoryConfiguration.GemfireRepositoryConfiguration; +import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean; +import org.springframework.data.repository.config.AutomaticRepositoryConfigInformation; +import org.springframework.data.repository.config.ManualRepositoryConfigInformation; +import org.springframework.data.repository.config.RepositoryConfig; +import org.springframework.data.repository.config.SingleRepositoryConfigInformation; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Repository configuration implementation. + * + * @author Oliver Gierke + */ +class SimpleGemfireRepositoryConfiguration extends + RepositoryConfig { + + private static final String GEMFIRE_TEMPLATE_REF = "gemfire-template-ref"; + + /** + * Creates a new {@link SimpleGemfireRepositoryConfiguration} for the given {@link Element}. + * + * @param repositoriesElement must not be {@literal null}. + */ + protected SimpleGemfireRepositoryConfiguration(Element repositoriesElement) { + super(repositoriesElement, GemfireRepositoryFactoryBean.class.getName()); + } + + /** + * Returns the bean name of the {@link GemfireTemplate} to be used. + * + * @return + */ + String getGemfireTemplateRef() { + + String attribute = getSource().getAttribute(GEMFIRE_TEMPLATE_REF); + return StringUtils.hasText(attribute) ? attribute : null; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.GlobalRepositoryConfigInformation#getAutoconfigRepositoryInformation(java.lang.String) + */ + @Override + public GemfireRepositoryConfiguration getAutoconfigRepositoryInformation(String interfaceName) { + return new AutomaticGemfireRepositoryConfiguration(interfaceName, this); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.CommonRepositoryConfigInformation#getNamedQueriesLocation() + */ + @Override + public String getNamedQueriesLocation() { + return "classpath*:META-INF/gemfire-named-queries.properties"; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfig#createSingleRepositoryConfigInformationFor(org.w3c.dom.Element) + */ + @Override + protected GemfireRepositoryConfiguration createSingleRepositoryConfigInformationFor(Element element) { + return new ManualGemfireRepositoryConfiguration(element, this); + } + + public interface GemfireRepositoryConfiguration extends + SingleRepositoryConfigInformation { + + String getGemfireTemplateRef(); + } + + static class ManualGemfireRepositoryConfiguration extends + ManualRepositoryConfigInformation implements GemfireRepositoryConfiguration { + + /** + * @param element + * @param parent + */ + public ManualGemfireRepositoryConfiguration(Element element, SimpleGemfireRepositoryConfiguration parent) { + super(element, parent); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.gemfire.config.GemfireRepositoryParser.SimpleGemfireRepositoryConfiguration.GemfireRepositoryConfiguration#getGemfireTemplateRef() + */ + @Override + public String getGemfireTemplateRef() { + return getAttribute(GEMFIRE_TEMPLATE_REF); + } + + } + + static class AutomaticGemfireRepositoryConfiguration extends + AutomaticRepositoryConfigInformation implements + GemfireRepositoryConfiguration { + + /** + * @param interfaceName + * @param parent + */ + public AutomaticGemfireRepositoryConfiguration(String interfaceName, SimpleGemfireRepositoryConfiguration parent) { + super(interfaceName, parent); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.gemfire.config.GemfireRepositoryParser.SimpleGemfireRepositoryConfiguration.GemfireRepositoryConfiguration#getGemfireTemplateRef() + */ + @Override + public String getGemfireTemplateRef() { + + return getParent().getGemfireTemplateRef(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/repository/package-info.java b/src/main/java/org/springframework/data/gemfire/repository/package-info.java new file mode 100644 index 00000000..d5292d78 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/package-info.java @@ -0,0 +1,5 @@ +/** + * Implementations of Spring Data COmmons Core repository abstraction. + */ +package org.springframework.data.gemfire.repository; + diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java b/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java new file mode 100644 index 00000000..944f0d9c --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.io.Serializable; + +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.repository.core.support.DelegatingEntityInformation; +import org.springframework.data.repository.core.support.ReflectionEntityInformation; + +/** + * Implementation of {@link GemfireEntityInformation} using reflection to lookup region names. + * + * @author Oliver Gierke + */ +public class DefaultGemfireEntityInformation extends DelegatingEntityInformation + implements GemfireEntityInformation { + + private final GemfirePersistentEntity entity; + + /** + * Creates a new {@link DefaultGemfireEntityInformation}. + * + * @param domainClass must not be {@literal null}. + */ + public DefaultGemfireEntityInformation(GemfirePersistentEntity entity) { + super(new ReflectionEntityInformation(entity.getType())); + this.entity = entity; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.gemfire.repository.query.GemfireEntityInformation#getRegionName() + */ + @Override + public String getRegionName() { + return entity.getRegionName(); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java new file mode 100644 index 00000000..1b44fde7 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.io.Serializable; + +import org.springframework.data.repository.core.EntityInformation; + +import com.gemstone.gemfire.cache.Region; + +/** + * {@link EntityInformation} to capture Gemfire specific information. + * + * @author Oliver Gierke + */ +public interface GemfireEntityInformation extends EntityInformation { + + /** + * Returns the name of the {@link Region} the entity is held in. + * + * @return + */ + String getRegionName(); +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreator.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreator.java new file mode 100644 index 00000000..0d28e039 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreator.java @@ -0,0 +1,143 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.util.Iterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.domain.Sort; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.repository.query.parser.AbstractQueryCreator; +import org.springframework.data.repository.query.parser.Part; +import org.springframework.data.repository.query.parser.PartTree; + +/** + * Query creator to create {@link QueryString} instances. + * + * @author Oliver Gierke + */ +class GemfireQueryCreator extends AbstractQueryCreator { + + private static final Log LOG = LogFactory.getLog(GemfireQueryCreator.class); + + private final QueryBuilder query; + private Iterator indexes; + + /** + * Creates a new {@link GemfireQueryCreator} using the given {@link PartTree} and domain class. + * + * @param tree must not be {@literal null}. + * @param entity must not be {@literal null}. + */ + public GemfireQueryCreator(PartTree tree, GemfirePersistentEntity entity) { + + super(tree); + + this.query = new QueryBuilder(entity); + this.indexes = new IndexProvider(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#createQuery(org.springframework.data.domain.Sort) + */ + @Override + public QueryString createQuery(Sort dynamicSort) { + + this.indexes = new IndexProvider(); + return super.createQuery(dynamicSort); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator) + */ + @Override + protected Predicates create(Part part, Iterator iterator) { + return Predicates.create(part, this.indexes); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator) + */ + @Override + protected Predicates and(Part part, Predicates base, Iterator iterator) { + return base.and(Predicates.create(part, this.indexes)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object) + */ + @Override + protected Predicates or(Predicates base, Predicates criteria) { + return base.or(criteria); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#complete(java.lang.Object, org.springframework.data.domain.Sort) + */ + @Override + protected QueryString complete(Predicates criteria, Sort sort) { + + QueryString result = query.create(criteria); + + if (LOG.isDebugEnabled()) { + LOG.debug("Created query: " + result.toString()); + } + + return result; + } + + private static class IndexProvider implements Iterator { + + private int index; + + public IndexProvider() { + this.index = 1; + } + + /* + * (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + @Override + public boolean hasNext() { + return index <= Integer.MAX_VALUE; + } + + /* + * (non-Javadoc) + * @see java.util.Iterator#next() + */ + @Override + public Integer next() { + return index++; + } + + /* + * (non-Javadoc) + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java new file mode 100644 index 00000000..cab19a3e --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java @@ -0,0 +1,86 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.lang.reflect.Method; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.mapping.GemfirePersistentProperty; +import org.springframework.data.gemfire.repository.Query; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Gemfire specific {@link QueryMethod}. + * + * @author Oliver Gierke + */ +public class GemfireQueryMethod extends QueryMethod { + + private final Method method; + private final GemfirePersistentEntity entity; + + /** + * Creates a new {@link GemfireQueryMethod} from the given {@link Method} and {@link RepositoryMetadata}. + * + * @param method must not be {@literal null}. + * @param metadata must not be {@literal null}. + * @param context must not be {@literal null}. + */ + public GemfireQueryMethod(Method method, RepositoryMetadata metadata, + MappingContext, GemfirePersistentProperty> context) { + + super(method, metadata); + + Assert.notNull(context); + + this.method = method; + this.entity = context.getPersistentEntity(getDomainClass()); + } + + /** + * Returns whether the query method contains an annotated, non-empty query. + * + * @return + */ + public boolean hasAnnotatedQuery() { + return StringUtils.hasText(getAnnotatedQuery()); + } + + /** + * @return the entity + */ + public GemfirePersistentEntity getPersistentEntity() { + return entity; + } + + /** + * Returns the query annotated to the query method. + * + * @return the annotated query or {@literal null} in case it's empty or none available. + */ + String getAnnotatedQuery() { + + Query query = method.getAnnotation(Query.class); + String queryString = query == null ? null : (String) AnnotationUtils.getValue(query); + + return StringUtils.hasText(queryString) ? queryString : null; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireRepositoryQuery.java new file mode 100644 index 00000000..0018bb5e --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireRepositoryQuery.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.util.Assert; + +/** + * Base class for GemFire specific {@link RepositoryQuery} implementations. + * + * @author Oliver Gierke + */ +abstract class GemfireRepositoryQuery implements RepositoryQuery { + + private final GemfireQueryMethod queryMethod; + + /** + * Creates a new {@link GemfireRepositoryQuery} using the given {@link GemfireQueryMethod}. + * + * @param queryMethod must not be {@literal null}. + */ + public GemfireRepositoryQuery(GemfireQueryMethod queryMethod) { + + Assert.notNull(queryMethod); + this.queryMethod = queryMethod; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() + */ + @Override + public QueryMethod getQueryMethod() { + return this.queryMethod; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java new file mode 100644 index 00000000..56a1982f --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.data.repository.query.parser.PartTree; + +/** + * {@link GemfireRepositoryQuery} backed by a {@link PartTree} and thus, deriving an OQL query from the backing query + * method's name. + * + * @author Oliver Gierke + */ +public class PartTreeGemfireRepositoryQuery extends GemfireRepositoryQuery { + + private final GemfireQueryMethod method; + private final PartTree tree; + private final GemfireTemplate template; + + /** + * Creates a new {@link PartTreeGemfireRepositoryQuery} using the given {@link GemfireQueryMethod} and + * {@link GemfireTemplate}. + * + * @param method must not be {@literal null}. + * @param template must not be {@literal null}. + */ + public PartTreeGemfireRepositoryQuery(GemfireQueryMethod method, GemfireTemplate template) { + + super(method); + + Class domainClass = method.getEntityInformation().getJavaType(); + + this.tree = new PartTree(method.getName(), domainClass); + this.method = method; + this.template = template; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) + */ + @Override + public Object execute(Object[] parameters) { + + ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(method.getParameters(), parameters); + QueryString query = new GemfireQueryCreator(tree, method.getPersistentEntity()).createQuery(parameterAccessor + .getSort()); + + RepositoryQuery repositoryQuery = new StringBasedGemfireRepositoryQuery(query.toString(), method, template); + + return repositoryQuery.execute(parameters); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/Predicate.java b/src/main/java/org/springframework/data/gemfire/repository/query/Predicate.java new file mode 100644 index 00000000..c07b5412 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/Predicate.java @@ -0,0 +1,6 @@ +package org.springframework.data.gemfire.repository.query; + +interface Predicate { + + String toString(String alias); +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/Predicates.java b/src/main/java/org/springframework/data/gemfire/repository/query/Predicates.java new file mode 100644 index 00000000..8d42093d --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/Predicates.java @@ -0,0 +1,173 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.util.Iterator; + +import org.springframework.data.repository.query.parser.Part; +import org.springframework.data.repository.query.parser.Part.Type; +import org.springframework.util.Assert; + +class Predicates implements Predicate { + + private final Predicate current; + + /** + * Creates a new {@link Predicates} wrapper instance. + * + * @param predicate must not be {@literal null}. + */ + private Predicates(Predicate predicate) { + this.current = predicate; + } + + private static Predicates create(Predicate predicate) { + return new Predicates(predicate); + } + + /** + * Creates a new Predicate for the given {@link Part} and index iterator. + * + * @param part must not be {@literal null}. + * @param value must not be {@literal null}. + * @return + */ + public static Predicates create(Part part, Iterator value) { + return create(new AtomicPredicate(part, value)); + } + + /** + * And-concatenates the given {@link Predicate} to the current one. + * + * @param predicate must not be {@literal null}. + * @return + */ + public Predicates and(final Predicate predicate) { + + return create(new Predicate() { + @Override + public String toString(String alias) { + return String.format("%s AND %s", Predicates.this.current.toString(alias), predicate.toString(alias)); + } + }); + } + + /** + * Or-concatenates the given {@link Predicate} to the current one. + * + * @param predicate must not be {@literal null}. + * @return + */ + public Predicates or(final Predicate predicate) { + + return create(new Predicate() { + @Override + public String toString(String alias) { + return String.format("%s OR %s", Predicates.this.current.toString(alias), predicate.toString(alias)); + } + }); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.gemfire.repository.query.Predicate#toString(java.lang.String) + */ + @Override + public String toString(String alias) { + return current.toString(alias); + } + + /** + * Predicate to create a predicate expression for a {@link Part}. + * + * @author Oliver Gierke + */ + public static class AtomicPredicate implements Predicate { + + private final Part part; + private final Iterator value; + + /** + * Creates a new {@link AtomicPredicate}. + * + * @param part must not be {@literal null}. + * @param value must not be {@literal null}. + */ + public AtomicPredicate(Part part, Iterator value) { + + Assert.notNull(part); + Assert.notNull(value); + + this.part = part; + this.value = value; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.gemfire.repository.query.Predicate#toString(java.lang.String) + */ + @Override + public String toString(String alias) { + + Type type = part.getType(); + return String.format("%s.%s %s", alias == null ? QueryBuilder.DEFAULT_ALIAS : alias, part.getProperty() + .toDotPath(), toClause(type)); + } + + private String toClause(Type type) { + + switch (type) { + case IS_NULL: + case IS_NOT_NULL: + return String.format("%s NULL", getOperator(type)); + default: + return String.format("%s $%s", getOperator(type), value.next()); + } + } + + /** + * Maps the given {@link Type} to an OQL operator. + * + * @param type + * @return + */ + private String getOperator(Type type) { + + switch (type) { + case IN: + return "IN SET"; + case NOT_IN: + return "NOT IN SET"; + case GREATER_THAN: + return ">"; + case GREATER_THAN_EQUAL: + return ">="; + case LESS_THAN: + return "<"; + case LESS_THAN_EQUAL: + return "<="; + case IS_NOT_NULL: + case NEGATING_SIMPLE_PROPERTY: + return "!="; + case IS_NULL: + case SIMPLE_PROPERTY: + return "="; + default: + throw new IllegalArgumentException(String.format("Unsupported operator %s!", type)); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java b/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java new file mode 100644 index 00000000..3d1978f9 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.util.Assert; + +/** + * + * @author Oliver Gierke + */ +class QueryBuilder { + + static final String DEFAULT_ALIAS = "x"; + + private final String query; + + public QueryBuilder(String source) { + Assert.hasText(source); + this.query = source; + } + + public QueryBuilder(GemfirePersistentEntity entity) { + this(String.format("SELECT * FROM /%s %s", entity.getRegionName(), DEFAULT_ALIAS)); + } + + public QueryString create(Predicate predicate) { + + return new QueryString(query + " WHERE " + predicate.toString(DEFAULT_ALIAS)); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return query; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java b/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java new file mode 100644 index 00000000..ee4ca7cc --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java @@ -0,0 +1,118 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.gemstone.gemfire.cache.Region; + +/** + * Value object to work with OQL query strings. + * + * @author Oliver Gierke + */ +class QueryString { + + private static final String REGION_PATTERN = "(?<=\\/)%s"; + private static final String IN_PARAMETER_PATTERN = "(?<=IN (SET|LIST) \\$)\\d"; + private static final String IN_PATTERN = "(?<=IN (SET|LIST) )\\$\\d"; + + private final String query; + + /** + * Creates a {@link QueryString} from the given {@link String} query. + * + * @param source + */ + public QueryString(String source) { + + Assert.hasText(source); + this.query = source; + } + + /** + * Creates a {@literal SELECT} query for the given domain class. + * + * @param domainClass must not be {@literal null}. + */ + public QueryString(Class domainClass) { + this(String.format("SELECT * FROM /%s", domainClass.getSimpleName())); + } + + /** + * Replaces the domain classes referenced inside the current query with the given {@link Region}. + * + * @param domainClass must not be {@literal null}. + * @param region must not be {@literal null}. + * @return + */ + public QueryString forRegion(Class domainClass, Region region) { + + String pattern = String.format(REGION_PATTERN, domainClass.getSimpleName()); + return new QueryString(query.replaceAll(pattern, region.getName())); + } + + /** + * Binds the given values to the {@literal IN} parameter keyword by expanding the given values into a comma-separated + * {@link String}. + * + * @param values the values to bind, returns the {@link QueryString} as is if {@literal null} is given. + * @return + */ + public QueryString bindIn(Collection values) { + + if (values == null) { + return this; + } + + String valueString = StringUtils.collectionToDelimitedString(values, ", ", "'", "'"); + return new QueryString(query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString))); + } + + /** + * Returns the parameter indexes used in this query. + * + * @return the parameter indexes used in this query or an empty {@link Iterable} if none are used. + */ + public Iterable getInParameterIndexes() { + + Pattern pattern = Pattern.compile(IN_PARAMETER_PATTERN); + Matcher matcher = pattern.matcher(query); + List result = new ArrayList(); + + while (matcher.find()) { + result.add(Integer.parseInt(matcher.group())); + } + + return result; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return query; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java new file mode 100644 index 00000000..2c417943 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java @@ -0,0 +1,103 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +/** + * {@link GemfireRepositoryQuery} using plain {@link String} based OQL queries. + * + * @author Oliver Gierke + */ +public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { + + private final QueryString query; + private final GemfireQueryMethod method; + private final GemfireTemplate template; + + /** + * Creates a new {@link StringBasedGemfireRepositoryQuery} using the given {@link GemfireQueryMethod} and + * {@link GemfireTemplate}. The actual query {@link String} will be looked up from the query method. + * + * @param method must not be {@literal null}. + * @param template must not be {@literal null}. + */ + public StringBasedGemfireRepositoryQuery(GemfireQueryMethod method, GemfireTemplate template) { + this(method.getAnnotatedQuery(), method, template); + } + + /** + * Creates a new {@link StringBasedGemfireRepositoryQuery} using the given query {@link String}, + * {@link GemfireQueryMethod} and {@link GemfireTemplate}. + * + * @param query will fall back to the query annotated to the given {@link GemfireQueryMethod} if {@literal null} is + * given. + * @param method must not be {@literal null}. + * @param template must not be {@literal null}. + */ + public StringBasedGemfireRepositoryQuery(String query, GemfireQueryMethod method, GemfireTemplate template) { + + super(method); + + Assert.notNull(template); + + this.query = new QueryString(StringUtils.hasText(query) ? query : method.getAnnotatedQuery()); + this.method = method; + this.template = template; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) + */ + @Override + public Object execute(Object[] parameters) { + + ParametersParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), parameters); + QueryString query = this.query.forRegion(method.getEntityInformation().getJavaType(), template.getRegion()); + + Iterator indexes = query.getInParameterIndexes().iterator(); + while (indexes.hasNext()) { + query = query.bindIn(toCollection(accessor.getBindableValue(indexes.next() - 1))); + } + + return template.find(query.toString(), parameters); + } + + /** + * Returns the given object as collection. Collections will be returned as is, Arrays will be converted into a + * collection and all other objects will be wrapped into a single-element collection. + * + * @param source + * @return + */ + private Collection toCollection(Object source) { + + if (source instanceof Collection) { + return (Collection) source; + } + + return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java new file mode 100644 index 00000000..cafb940a --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java @@ -0,0 +1,147 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.support; + +import java.io.Serializable; +import java.lang.reflect.Method; + +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.mapping.GemfirePersistentProperty; +import org.springframework.data.gemfire.mapping.Regions; +import org.springframework.data.gemfire.repository.query.DefaultGemfireEntityInformation; +import org.springframework.data.gemfire.repository.query.GemfireEntityInformation; +import org.springframework.data.gemfire.repository.query.GemfireQueryMethod; +import org.springframework.data.gemfire.repository.query.PartTreeGemfireRepositoryQuery; +import org.springframework.data.gemfire.repository.query.StringBasedGemfireRepositoryQuery; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.core.NamedQueries; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryLookupStrategy.Key; +import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.cache.Region; + +/** + * {@link RepositoryFactorySupport} implementation creating repository proxies for Gemfire. + * + * @author Oliver Gierke + */ +public class GemfireRepositoryFactory extends RepositoryFactorySupport { + + private final MappingContext, GemfirePersistentProperty> context; + private final Regions regions; + + /** + * Creates a new {@link GemfireRepositoryFactory}. + * + * @param regions must not be {@literal null}. + * @param context + */ + public GemfireRepositoryFactory(Iterable> regions, + MappingContext, GemfirePersistentProperty> context) { + + Assert.notNull(regions); + + this.context = context == null ? new GemfireMappingContext() : context; + this.regions = new Regions(regions, this.context); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class) + */ + @Override + @SuppressWarnings("unchecked") + public GemfireEntityInformation getEntityInformation(Class domainClass) { + + GemfirePersistentEntity entity = (GemfirePersistentEntity) context.getPersistentEntity(domainClass); + return new DefaultGemfireEntityInformation(entity); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata) + */ + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected Object getTargetRepository(RepositoryMetadata metadata) { + + GemfireEntityInformation entityInformation = getEntityInformation(metadata.getDomainClass()); + GemfireTemplate gemfireTemplate = getTemplate(metadata); + + return new SimpleGemfireRepository(gemfireTemplate, entityInformation); + } + + private GemfireTemplate getTemplate(RepositoryMetadata metadata) { + + Class domainClass = metadata.getDomainClass(); + Region region = regions.getRegion(domainClass); + + GemfirePersistentEntity entity = context.getPersistentEntity(domainClass); + + Class regionKeyType = region.getAttributes().getKeyConstraint(); + Class entityIdType = metadata.getIdClass(); + + if (regionKeyType != null && entity.getIdProperty() != null) { + Assert.isTrue(regionKeyType.isAssignableFrom(entityIdType), String.format( + "The region referenced only supports keys of type %s but the entity to be stored has an id of type %s!", + regionKeyType, entityIdType)); + } + + return new GemfireTemplate(region); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata) + */ + @Override + protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { + return SimpleGemfireRepository.class; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key) + */ + @Override + protected QueryLookupStrategy getQueryLookupStrategy(Key key) { + return new QueryLookupStrategy() { + @Override + public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) { + + GemfireQueryMethod queryMethod = new GemfireQueryMethod(method, metadata, context); + GemfireTemplate template = getTemplate(metadata); + + if (queryMethod.hasAnnotatedQuery()) { + return new StringBasedGemfireRepositoryQuery(queryMethod, template); + } + + String namedQueryName = queryMethod.getNamedQueryName(); + if (namedQueries.hasQuery(namedQueryName)) { + return new StringBasedGemfireRepositoryQuery(namedQueries.getQuery(namedQueryName), queryMethod, template); + } + + return new PartTreeGemfireRepositoryQuery(queryMethod, template); + } + }; + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java new file mode 100644 index 00000000..1dfa5079 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.support; + +import java.io.Serializable; +import java.util.Collection; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.mapping.GemfirePersistentProperty; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; + +import com.gemstone.bp.edu.emory.mathcs.backport.java.util.Collections; +import com.gemstone.gemfire.cache.Region; + +/** + * + * @author Oliver Gierke + */ +public class GemfireRepositoryFactoryBean, S, ID extends Serializable> extends + RepositoryFactoryBeanSupport implements ApplicationContextAware { + + private MappingContext, GemfirePersistentProperty> context; + private Iterable> regions; + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + Collection regions = applicationContext.getBeansOfType(Region.class).values(); + this.regions = Collections.unmodifiableCollection(regions); + } + + /** + * @param context the context to set + */ + public void setMappingContext(MappingContext, GemfirePersistentProperty> context) { + this.context = context; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory() + */ + @Override + protected RepositoryFactorySupport createRepositoryFactory() { + return new GemfireRepositoryFactory(regions, context); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java new file mode 100644 index 00000000..65b8b5b0 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java @@ -0,0 +1,174 @@ +package org.springframework.data.gemfire.repository.support; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.data.gemfire.GemfireCallback; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.repository.GemfireRepository; +import org.springframework.data.gemfire.repository.Wrapper; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.GemFireCheckedException; +import com.gemstone.gemfire.GemFireException; +import com.gemstone.gemfire.cache.Region; + +/** + * Basic repository implementation. + * + * @author Oliver Gierke + */ +public class SimpleGemfireRepository implements GemfireRepository { + + private final GemfireTemplate template; + private final EntityInformation entityInformation; + + /** + * Creates a new {@link SimpleGemfireRepository}. + * + * @param template must not be {@literal null}. + * @param entityInformation must not be {@literal null}. + */ + public SimpleGemfireRepository(GemfireTemplate template, EntityInformation entityInformation) { + + Assert.notNull(template); + Assert.notNull(entityInformation); + + this.template = template; + this.entityInformation = entityInformation; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#save(java.lang.Object) + */ + public T save(T entity) { + template.put(entityInformation.getId(entity), entity); + return entity; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#save(java.lang.Iterable) + */ + public Iterable save(Iterable entities) { + List result = new ArrayList(); + for (T entity : entities) { + result.add(save(entity)); + } + return result; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#findOne(java.io.Serializable) + */ + @SuppressWarnings("unchecked") + public T findOne(ID id) { + Object object = template.get(id); + return (T) object; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#exists(java.io.Serializable) + */ + public boolean exists(ID id) { + return findOne(id) != null; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#findAll() + */ + public Collection findAll() { + return template.execute(new GemfireCallback>() { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Collection doInGemfire(Region region) { + return region.values(); + } + }); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) + */ + @Override + @SuppressWarnings("unchecked") + public Collection findAll(Iterable ids) { + + List parameters = new ArrayList(); + for (ID id : ids) { + parameters.add(id); + } + + return (Collection) template.getAll(parameters).values(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#count() + */ + public long count() { + return template.execute(new GemfireCallback() { + @SuppressWarnings("rawtypes") + public Long doInGemfire(Region region) throws GemFireCheckedException, GemFireException { + return Long.valueOf(region.keySet().size()); + } + }); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#delete(java.lang.Object) + */ + public void delete(T entity) { + template.remove(entityInformation.getId(entity)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#delete(java.lang.Iterable) + */ + public void delete(Iterable entities) { + for (T entity : entities) { + delete(entity); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.Repository#deleteAll() + */ + public void deleteAll() { + + template.execute(new GemfireCallback() { + @SuppressWarnings("rawtypes") + public Void doInGemfire(Region region) { + region.clear(); + return null; + } + }); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable) + */ + public void delete(ID id) { + template.remove(id); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.gemfire.repository.GemfireRepository#save(org.springframework.data.gemfire.repository.Wrapper) + */ + @Override + public T save(Wrapper wrapper) { + return template.put(wrapper.getKey(), wrapper.getEntity()); + } +} diff --git a/src/main/resources/META-INF/spring.schemas b/src/main/resources/META-INF/spring.schemas index 2bb1ca58..5bff7b59 100644 --- a/src/main/resources/META-INF/spring.schemas +++ b/src/main/resources/META-INF/spring.schemas @@ -1,3 +1,4 @@ http\://www.springframework.org/schema/gemfire/spring-gemfire-1.0.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.0.xsd http\://www.springframework.org/schema/gemfire/spring-gemfire-1.1.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd -http\://www.springframework.org/schema/gemfire/spring-gemfire.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd \ No newline at end of file +http\://www.springframework.org/schema/gemfire/spring-gemfire-1.2.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd +http\://www.springframework.org/schema/gemfire/spring-gemfire.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd \ No newline at end of file diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd new file mode 100644 index 00000000..70ab629b --- /dev/null +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd @@ -0,0 +1,1309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + namespace and its 'properties' element. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The reference to a GemfireTemplate. Will default to 'gemfireTemplate'. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntityUnitTests.java b/src/test/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntityUnitTests.java new file mode 100644 index 00000000..8515c585 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntityUnitTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.data.util.ClassTypeInformation; + +/** + * Unit tests for {@link GemfirePersistentEntity}. + * + * @author Oliver Gierke + */ +public class GemfirePersistentEntityUnitTests { + + @Test + public void defaultsRegionNameToClassName() { + GemfirePersistentEntity entity = new GemfirePersistentEntity( + ClassTypeInformation.from(UnannotatedRegion.class)); + assertThat(entity.getRegionName(), is(UnannotatedRegion.class.getSimpleName())); + } + + @Test + public void defaultsAnnotatedRegionToCLassName() { + GemfirePersistentEntity entity = new GemfirePersistentEntity( + ClassTypeInformation.from(UnnamedRegion.class)); + assertThat(entity.getRegionName(), is(UnnamedRegion.class.getSimpleName())); + } + + @Test + public void readsRegionNameFromAnnotation() { + + GemfirePersistentEntity entity = new GemfirePersistentEntity( + ClassTypeInformation.from(AnnotatedRegion.class)); + assertThat(entity.getRegionName(), is("Foo")); + } + + static class UnannotatedRegion { + + } + + @Region("Foo") + static class AnnotatedRegion { + + } + + @Region + static class UnnamedRegion { + + } +} diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java new file mode 100644 index 00000000..92553a30 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.mapping.MappingPdxSerializer; +import org.springframework.data.gemfire.repository.sample.Address; +import org.springframework.data.gemfire.repository.sample.Person; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionFactory; + +/** + * Integration tests for {@link MappingPdxSerializer}. + * + * @author Oliver Gierke + */ +public class MappingPdxSerializerIntegrationTest { + + Region region; + + @Before + public void setUp() { + + MappingPdxSerializer serializer = new MappingPdxSerializer(new GemfireMappingContext(), + new DefaultConversionService()); + + CacheFactory factory = new CacheFactory(); + factory.setPdxSerializer(serializer); + factory.setPdxPersistent(true); + + Cache cache = factory.create(); + + RegionFactory regionFactory = cache.createRegionFactory(); + regionFactory.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); + region = regionFactory.create("foo"); + } + + @Test + public void serializeAndDeserializeCorrectly() { + + Address address = new Address(); + address.zipCode = "01234"; + address.city = "London"; + + Person person = new Person(1L, "Oliver", "Gierke"); + person.address = address; + + region.put(1L, person); + Object result = region.get(1L); + + assertThat(result instanceof Person, is(true)); + + Person reference = person; + assertThat(reference.getFirstname(), is(person.getFirstname())); + assertThat(reference.getLastname(), is(person.getLastname())); + assertThat(reference.address, is(person.address)); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java new file mode 100644 index 00000000..a6f43ace --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.convert.EntityInstantiator; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.mapping.model.ParameterValueProvider; + +import com.gemstone.gemfire.pdx.PdxReader; + +/** + * Unit tests for {@link MappingPdxSerializer}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class MappingPdxSerializerUnitTests { + + GemfireMappingContext context; + ConversionService conversionService; + MappingPdxSerializer serializer; + + @Mock + EntityInstantiator instantiator; + @Mock + PdxReader reader; + + @Before + public void setUp() { + + context = new GemfireMappingContext(); + conversionService = new GenericConversionService(); + serializer = new MappingPdxSerializer(context, conversionService); + } + + @Test + @SuppressWarnings("unchecked") + public void usesRegisteredInstantiator() { + + Person person = new Person(1L, "Oliver", "Gierke"); + + ParameterValueProvider provider = any(ParameterValueProvider.class); + GemfirePersistentEntity entity = any(GemfirePersistentEntity.class); + when(instantiator.createInstance(entity, provider)).thenReturn(person); + + Map, EntityInstantiator> instantiators = new HashMap, EntityInstantiator>(); + instantiators.put(Person.class, instantiator); + + serializer.setGemfireInstantiators(instantiators); + serializer.fromData(Person.class, reader); + + verify(instantiator, times(1)).createInstance(eq(context.getPersistentEntity(Person.class)), + any(ParameterValueProvider.class)); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessorUnitTests.java b/src/test/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessorUnitTests.java new file mode 100644 index 00000000..5bfffdde --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessorUnitTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.expression.TypedValue; + +import com.gemstone.bp.edu.emory.mathcs.backport.java.util.Arrays; +import com.gemstone.gemfire.pdx.PdxReader; + +/** + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class PdxReaderPropertyAccessorUnitTests { + + @Mock + PdxReader reader; + + @Test + @SuppressWarnings("unchecked") + public void appliesToPdxReadersOnly() { + List> classes = Arrays.asList(PdxReaderPropertyAccessor.INSTANCE.getSpecificTargetClasses()); + assertThat(classes, hasItem(PdxReader.class)); + } + + @Test + public void canReadPropertyIfReaderHasField() { + + when(reader.hasField("key")).thenReturn(true); + assertThat(PdxReaderPropertyAccessor.INSTANCE.canRead(null, reader, "key"), is(true)); + + when(reader.hasField("key")).thenReturn(false); + assertThat(PdxReaderPropertyAccessor.INSTANCE.canRead(null, reader, "key"), is(false)); + } + + @Test + public void returnsTypedNullIfNullIsReadFromReader() { + + when(reader.readObject("key")).thenReturn(null); + assertThat(PdxReaderPropertyAccessor.INSTANCE.read(null, reader, "key"), is(TypedValue.NULL)); + } + + @Test + public void returnsTypeValueWithValueReadFromReader() { + + when(reader.readObject("key")).thenReturn("String"); + + TypedValue result = PdxReaderPropertyAccessor.INSTANCE.read(null, reader, "key"); + + assertThat(result.getTypeDescriptor(), is(TypeDescriptor.valueOf(String.class))); + assertThat(result.getValue(), is((Object) "String")); + } + + @Test(expected = UnsupportedOperationException.class) + public void doesNotSupportWrites() { + + assertThat(PdxReaderPropertyAccessor.INSTANCE.canWrite(null, null, null), is(false)); + PdxReaderPropertyAccessor.INSTANCE.write(null, null, null, reader); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/config/NamespaceRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/config/NamespaceRepositoryIntegrationTests.java new file mode 100644 index 00000000..8b75f00d --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/config/NamespaceRepositoryIntegrationTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.mapping.Regions; +import org.springframework.data.gemfire.repository.sample.PersonRepository; +import org.springframework.data.gemfire.repository.support.AbstractGemfireRepositoryFactoryIntegrationTests; +import org.springframework.test.context.ContextConfiguration; + +/** + * Integration tests for namespace usage. + * + * @author Oliver Gierke + */ +@ContextConfiguration("repo-context.xml") +public class NamespaceRepositoryIntegrationTests extends AbstractGemfireRepositoryFactoryIntegrationTests { + + @Autowired + PersonRepository repository; + + @Override + protected PersonRepository getRepository(Regions regions) { + return repository; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreatorUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreatorUnitTests.java new file mode 100644 index 00000000..95d673c8 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreatorUnitTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.repository.query.parser.PartTree; + +/** + * Unit tests for {@link GemfireQueryCreator}. + * + * @author Oliver Gierke + */ +public class GemfireQueryCreatorUnitTests { + + GemfirePersistentEntity entity; + + @Before + @SuppressWarnings("unchecked") + public void setUp() { + + GemfireMappingContext context = new GemfireMappingContext(); + entity = (GemfirePersistentEntity) context.getPersistentEntity(Person.class); + } + + @Test + public void createsQueryForSimplePropertyReferenceCorrectly() { + + PartTree partTree = new PartTree("findByFirstname", Person.class); + GemfireQueryCreator creator = new GemfireQueryCreator(partTree, entity); + + QueryString query = creator.createQuery(); + assertThat(query.toString(), is("SELECT * FROM /simple x WHERE x.firstname = $1")); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java new file mode 100644 index 00000000..f0b7d64b --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Method; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.repository.Query; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.repository.core.RepositoryMetadata; + +/** + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class GemfireQueryMethodUnitTests { + + @Mock + RepositoryMetadata metadata; + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void detectsAnnotatedQueryCorrectly() throws Exception { + + GemfireMappingContext context = new GemfireMappingContext(); + when(metadata.getDomainClass()).thenReturn((Class) Person.class); + when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) Person.class); + + GemfireQueryMethod method = new GemfireQueryMethod(Sample.class.getMethod("annotated"), metadata, context); + assertThat(method.hasAnnotatedQuery(), is(true)); + assertThat(method.getAnnotatedQuery(), is("foo")); + + method = new GemfireQueryMethod(Sample.class.getMethod("annotatedButEmpty"), metadata, context); + assertThat(method.hasAnnotatedQuery(), is(false)); + assertThat(method.getAnnotatedQuery(), is(nullValue())); + + method = new GemfireQueryMethod(Sample.class.getMethod("notAnnotated"), metadata, context); + assertThat(method.hasAnnotatedQuery(), is(false)); + assertThat(method.getAnnotatedQuery(), is(nullValue())); + } + + interface Sample { + + @Query("foo") + void annotated(); + + @Query("") + void annotatedButEmpty(); + + void notAnnotated(); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/PredicatesUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/query/PredicatesUnitTests.java new file mode 100644 index 00000000..5bf25632 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/query/PredicatesUnitTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.Iterator; + +import org.junit.Test; +import org.springframework.data.gemfire.repository.query.Predicates.AtomicPredicate; +import org.springframework.data.repository.query.parser.Part; + +/** + * + * @author Oliver Gierke + */ +public class PredicatesUnitTests { + + @Test + public void atomicPredicateDefaultsAlias() { + + Part part = new Part("firstname", Person.class); + + Iterable indexes = Arrays.asList(1); + + Predicate predicate = new AtomicPredicate(part, indexes.iterator()); + assertThat(predicate.toString(null), is("x.firstname = $1")); + } + + @Test + public void concatenatesAndPredicateCorrectly() { + + Part left = new Part("firstname", Person.class); + Part right = new Part("lastname", Person.class); + Iterator indexes = Arrays.asList(1, 2).iterator(); + + Predicates predicate = Predicates.create(left, indexes); + predicate = predicate.and(new AtomicPredicate(right, indexes)); + + assertThat(predicate.toString(null), is("x.firstname = $1 AND x.lastname = $2")); + } + + @Test + public void concatenatesOrPredicateCorrectly() { + + Part left = new Part("firstname", Person.class); + Part right = new Part("lastname", Person.class); + Iterator indexes = Arrays.asList(1, 2).iterator(); + + Predicates predicate = Predicates.create(left, indexes); + predicate = predicate.or(new AtomicPredicate(right, indexes)); + + assertThat(predicate.toString(null), is("x.firstname = $1 OR x.lastname = $2")); + } + + static class Person { + + String firstname; + String lastname; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java new file mode 100644 index 00000000..dc8daaec --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.gemfire.repository.sample.Person; + +import com.gemstone.gemfire.cache.Region; + +/** + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class QueryStringUnitTests { + + @Mock + @SuppressWarnings("rawtypes") + Region region; + + @Test + public void replacesDomainObjectWithRegionNameCorrectly() { + + QueryString query = new QueryString("SELECT * FROM /Person p WHERE p.firstname = $1"); + + when(region.getName()).thenReturn("foo"); + assertThat(query.forRegion(Person.class, region).toString(), is("SELECT * FROM /foo p WHERE p.firstname = $1")); + } + + @Test + public void bindsInValuesCorrectly() { + + QueryString query = new QueryString("SELECT * FROM /Person p WHERE p.firstname IN SET $1"); + List values = Arrays.asList(1, 2, 3); + assertThat(query.bindIn(values).toString(), is("SELECT * FROM /Person p WHERE p.firstname IN SET ('1', '2', '3')")); + } + + @Test + public void detectsInParameterIndexesCorrectly() { + + QueryString query = new QueryString("IN SET $1 OR IN SET $2"); + Iterable indexes = query.getInParameterIndexes(); + assertThat(indexes, is((Iterable) Arrays.asList(1, 2))); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java b/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java new file mode 100644 index 00000000..0de38379 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/Address.java @@ -0,0 +1,26 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.sample; + +/** + * + * @author Oliver Gierke + */ +public class Address { + + public String zipCode; + public String city; +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java b/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java new file mode 100644 index 00000000..372b9ee5 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/Person.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.sample; + +import java.io.Serializable; + +import org.springframework.data.annotation.Id; +import org.springframework.data.gemfire.mapping.Region; + +/** + * + * @author Oliver Gierke + */ +@Region("simple") +public class Person implements Serializable { + + private static final long serialVersionUID = 508843183613325255L; + + @Id + public Long id; + public String firstname; + public String lastname; + public Address address; + + public Person(Long id, String firstname, String lastname) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + } + + /** + * @return the firstname + */ + public String getFirstname() { + return firstname; + } + + /** + * @return the lastname + */ + public String getLastname() { + return lastname; + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java new file mode 100644 index 00000000..7fe55fd2 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.sample; + +import java.util.Collection; + +import org.springframework.data.gemfire.repository.Query; +import org.springframework.data.repository.Repository; + +/** + * + * + * @author Oliver Gierke + */ +public interface PersonRepository extends Repository { + + @Query("SELECT * FROM /Person p WHERE p.firstname = $1") + Collection findByFirstnameAnnotated(String firstname); + + @Query("SELECT * FROM /Person p WHERE p.firstname IN SET $1") + Collection findByFirstnamesAnnotated(Collection firstnames); + + Collection findByFirstname(String firstname); + + Collection findByFirstnameIn(Collection firstnames); + + Collection findByFirstnameIn(String... firstnames); + + Collection findByFirstnameAndLastname(String firstname, String lastname); + + Collection findByFirstnameOrLastname(String firstname, String lastname); +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java new file mode 100644 index 00000000..c53ab4f4 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.support; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.mapping.Regions; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.gemfire.repository.sample.PersonRepository; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.gemstone.gemfire.cache.Region; + +/** + * Integration test for {@link GemfireRepositoryFactory}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +public abstract class AbstractGemfireRepositoryFactoryIntegrationTests { + + @Autowired + List> regions; + + Person dave, carter, boyd, stefan, leroi, jeff; + PersonRepository repository; + + @Before + public void setUp() { + + dave = new Person(1L, "Dave", "Matthews"); + carter = new Person(2L, "Carter", "Beauford"); + boyd = new Person(3L, "Boyd", "Tinsley"); + stefan = new Person(4L, "Stefan", "Lessard"); + leroi = new Person(5L, "Leroi", "Moore"); + jeff = new Person(6L, "Jeff", "Coffin"); + + GemfireMappingContext context = new GemfireMappingContext(); + + Regions regions = new Regions(this.regions, context); + GemfireTemplate template = new GemfireTemplate(regions.getRegion(Person.class)); + + template.put(dave.id, dave); + template.put(carter.id, carter); + template.put(boyd.id, boyd); + template.put(stefan.id, stefan); + template.put(leroi.id, leroi); + template.put(jeff.id, jeff); + + repository = getRepository(regions); + } + + protected abstract PersonRepository getRepository(Regions regions); + + @Test + public void foo() { + assertResultsFound(repository.findByFirstnameAnnotated("Dave"), dave); + } + + @Test + public void executesAnnotatedInQueryMethodCorrectly() { + assertResultsFound(repository.findByFirstnamesAnnotated(Arrays.asList("Carter", "Dave")), carter, dave); + } + + @Test + public void executesInQueryMethodCorrectly() { + assertResultsFound(repository.findByFirstnameIn(Arrays.asList("Carter", "Dave")), carter, dave); + } + + @Test + public void executesDerivedQueryCorrectly() { + assertResultsFound(repository.findByFirstname("Carter"), carter); + assertResultsFound(repository.findByFirstnameIn(Arrays.asList("Stefan", "Boyd")), stefan, boyd); + assertResultsFound(repository.findByFirstnameIn("Leroi"), leroi); + } + + @Test + public void executesDerivedQueryWithAndCorrectly() { + assertResultsFound(repository.findByFirstnameAndLastname("Carter", "Beauford"), carter); + } + + @Test + public void executesDerivedQueryWithOrCorrectly() { + assertResultsFound(repository.findByFirstnameOrLastname("Carter", "Matthews"), carter, dave); + } + + private void assertResultsFound(Collection result, T... expected) { + + assertThat(result, is(notNullValue())); + assertThat(result.size(), is(expected.length)); + + for (T element : expected) { + assertThat(result.contains(element), is(true)); + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java new file mode 100644 index 00000000..bd31ad4a --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.support; + +import org.springframework.data.gemfire.mapping.Regions; +import org.springframework.data.gemfire.repository.sample.PersonRepository; +import org.springframework.test.context.ContextConfiguration; + +/** + * Integration test for {@link GemfireRepositoryFactory}. + * + * @author Oliver Gierke + */ +@ContextConfiguration("../config/repo-context.xml") +public class GemfireRepositoryFactoryIntegrationTests extends AbstractGemfireRepositoryFactoryIntegrationTests { + + @Override + protected PersonRepository getRepository(Regions regions) { + + GemfireRepositoryFactory factory = new GemfireRepositoryFactory(regions, null); + return factory.getRepository(PersonRepository.class); + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java new file mode 100644 index 00000000..d2360998 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012 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 + * + * 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. + */ +package org.springframework.data.gemfire.repository.support; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.support.ReflectionEntityInformation; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.gemstone.gemfire.cache.query.SelectResults; + +/** + * Integration tests for {@link SimpleGemfireRepository}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("../../basic-template.xml") +public class SimpleGemfireRepositoryIntegrationTest { + + @Autowired + GemfireTemplate template; + + SimpleGemfireRepository repository; + + @Before + public void setUp() { + + EntityInformation information = new ReflectionEntityInformation(Person.class); + repository = new SimpleGemfireRepository(template, information); + } + + @Test + public void storeAndDeleteEntity() { + + Person person = new Person(1L, "Oliver", "Gierke"); + + repository.save(person); + + assertThat(repository.count(), is(1L)); + assertThat(repository.findOne(person.id), is(person)); + assertThat(repository.findAll().size(), is(1)); + + repository.delete(person); + + assertThat(repository.count(), is(0L)); + assertThat(repository.findOne(person.id), is(nullValue())); + assertThat(repository.findAll().size(), is(0)); + } + + @Test + public void queryRegion() throws Exception { + + Person person = new Person(1L, "Oliver", "Gierke"); + + template.put(1L, person); + + SelectResults persons = template.find("SELECT * FROM /simple s WHERE s.firstname = $1", person.firstname); + + assertThat(persons.size(), is(1)); + assertThat(persons.iterator().next(), is(person)); + } + + @Test + public void findAllWithGivenIds() { + + Person dave = new Person(1L, "Dave", "Matthews"); + Person carter = new Person(2L, "Carter", "Beauford"); + Person leroi = new Person(3L, "Leroi", "Moore"); + + template.put(dave.id, dave); + template.put(carter.id, carter); + template.put(leroi.id, leroi); + + Collection result = repository.findAll(Arrays.asList(carter.id, leroi.id)); + assertThat(result, hasItems(carter, leroi)); + assertThat(result, not(hasItems(dave))); + } +} diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties index 1f7cee2f..5b8659e2 100644 --- a/src/test/resources/log4j.properties +++ b/src/test/resources/log4j.properties @@ -5,6 +5,7 @@ log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n log4j.category.org.springframework.data.gemfire.listener=TRACE +log4j.category.org.springframework.data.gemfire.repository=DEBUG # for debugging datasource initialization # log4j.category.test.jdbc=DEBUG diff --git a/src/test/resources/org/springframework/data/gemfire/repository/config/repo-context.xml b/src/test/resources/org/springframework/data/gemfire/repository/config/repo-context.xml new file mode 100644 index 00000000..6c8c4a44 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/repository/config/repo-context.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/template.mf b/template.mf index fff33d8c..d29cfb28 100644 --- a/template.mf +++ b/template.mf @@ -12,6 +12,8 @@ Import-Template: org.springframework.context.*;version=${spring.range}, org.springframework.core.*;version=${spring.range}, org.springframework.dao.*;version=${spring.range}, + org.springframework.data.*;version="${springDataCommonsVersion:[=.=.=.=,+1.0.0)}", + org.springframework.expression.*;version="${springVersion:[=.=.=,+1.0.0)}", org.springframework.util.*;version=${spring.range}, org.springframework.transaction.*;version=${spring.range}, com.gemstone.gemfire.*;version=${gemfire.range}, From d0a683555689c919605f057c7225cf348798a85f Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 1 Mar 2012 17:52:10 +0100 Subject: [PATCH 02/21] SGF-78 - Changed namespace attributes from boolean to string. If namespace element attributes are defined as xsd:boolean in the XSD the become unusable with property placeholders or SpEL. Thus we changed it back to strings. --- .../springframework/data/gemfire/config/spring-gemfire-1.2.xsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd index 70ab629b..318b29d5 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd @@ -1077,7 +1077,7 @@ The client subscription configuration that is used to control a clients use of s - + Date: Thu, 8 Mar 2012 18:49:09 +0200 Subject: [PATCH 03/21] - backport from 1.1.x f523b602114b42898eff24b1ba91a2542aba1167 35ce3d7ed74cb32e468afed818255f9a2ee96241 fix for SGF-80 fix test plus add extra check for client cache --- .../gemfire/client/ClientRegionFactoryBean.java | 14 ++++++++++++-- .../data/gemfire/client/client-cache.xml | 2 +- .../data/gemfire/config/client-ns.xml | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java index 362add1b..d10968bc 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java @@ -38,6 +38,7 @@ import com.gemstone.gemfire.cache.client.ClientCache; import com.gemstone.gemfire.cache.client.ClientRegionFactory; import com.gemstone.gemfire.cache.client.ClientRegionShortcut; import com.gemstone.gemfire.cache.client.Pool; +import com.gemstone.gemfire.internal.cache.GemFireCacheImpl; /** * Client extension for GemFire regions. @@ -74,8 +75,12 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean @Override protected Region lookupFallback(GemFireCache cache, String regionName) throws Exception { Assert.isTrue(cache instanceof ClientCache, "Unable to create regions from " + cache); - ClientCache c = (ClientCache) cache; + + if (cache instanceof GemFireCacheImpl) { + Assert.isTrue(((GemFireCacheImpl) cache).isClient(), "A client-cache instance is required"); + } + // first look at shortcut ClientRegionShortcut s = null; @@ -88,8 +93,13 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)) { s = ClientRegionShortcut.LOCAL_PERSISTENT; } + else { + s = ClientRegionShortcut.LOCAL; + } + } + else { + s = ClientRegionShortcut.LOCAL; } - s = ClientRegionShortcut.LOCAL; } else { s = shortcut; } diff --git a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml b/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml index 4534f6a7..5cfeb3ae 100644 --- a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml +++ b/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml @@ -12,6 +12,6 @@ --> - + diff --git a/src/test/resources/org/springframework/data/gemfire/config/client-ns.xml b/src/test/resources/org/springframework/data/gemfire/config/client-ns.xml index ec14d7fa..f2a229e8 100644 --- a/src/test/resources/org/springframework/data/gemfire/config/client-ns.xml +++ b/src/test/resources/org/springframework/data/gemfire/config/client-ns.xml @@ -9,7 +9,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> - + From a194942d899062a491401192d18ca4c8b86dce9b Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Mon, 19 Mar 2012 21:03:40 +0200 Subject: [PATCH 04/21] backport from 1.1.x 33e3a6c - c20602d meaning: c20602d 7f9a096 d439f9b 33e3a6c changed xsd:boolean to string SGF-78 handle CACHING_PROXY in client FB SGF-80 update spring & gemfire dependencies update changelog re-add handle CACHING_PROXY in client FB SGF-80 --- docs/src/info/changelog.txt | 10 +++++++++- gradle.properties | 6 +++--- samples/hello-world/gradle.properties | 2 +- .../data/gemfire/client/ClientRegionFactoryBean.java | 3 +++ .../data/gemfire/config/spring-gemfire-1.1.xsd | 4 ++-- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/src/info/changelog.txt b/docs/src/info/changelog.txt index 757eb528..d8628bbf 100644 --- a/docs/src/info/changelog.txt +++ b/docs/src/info/changelog.txt @@ -2,11 +2,19 @@ SPRING DATA GEMFIRE CHANGELOG ============================= http://www.springsource.org/spring-gemfire -Changes in version 1.1.0.RELEASE (2011-12-14) +Changes in version 1.1.1.RELEASE (2012-03-20) --------------------------------------------- +General +* Upgraded to GemFire 6.6.2 +* Upgraded to Spring Framework 3.1.1 GA + Package org.springframework.data.gemfire * Fixed incorrect parsing of pdx-serializer (from value to reference) +* Fixed incorrect parsing of use-bean-factory-locator + +Package org.springframework.data.gemfire.client +* Fixed bug that caused client namespace to create only local regions Changes in version 1.1.0.RELEASE (2011-12-14) diff --git a/gradle.properties b/gradle.properties index db667a51..2e08bd17 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ log4jVersion = 1.2.16 slf4jVersion = 1.6.4 # Common libraries -springVersion = 3.1.0.RELEASE +springVersion = 3.1.1.RELEASE springDataCommonsVersion = 1.3.0.M1 -gemfireVersion = 6.6.1 +gemfireVersion = 6.6.2 # Testing junitVersion = 4.8.1 @@ -17,7 +17,7 @@ hamcrestVersion = 1.2.1 # Manifest properties ## OSGi ranges -spring.range = "[3.1.0, 4.0.0)" +spring.range = "[3.0.0, 4.0.0)" gemfire.range = "[6.5, 7.0)" # -------------------- diff --git a/samples/hello-world/gradle.properties b/samples/hello-world/gradle.properties index 048ae069..16890fe1 100644 --- a/samples/hello-world/gradle.properties +++ b/samples/hello-world/gradle.properties @@ -1,3 +1,3 @@ junitVersion = 4.8.1 -springVersion = 3.1.0.RELEASE +springVersion = 3.1.1.RELEASE version = 1.2.0.BUILD-SNAPSHOT diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java index d10968bc..705d7b84 100644 --- a/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/ClientRegionFactoryBean.java @@ -93,6 +93,9 @@ public class ClientRegionFactoryBean extends RegionLookupFactoryBean else if (DataPolicy.PERSISTENT_REPLICATE.equals(dataPolicy)) { s = ClientRegionShortcut.LOCAL_PERSISTENT; } + else if (DataPolicy.NORMAL.equals(this.dataPolicy)) { + s = ClientRegionShortcut.CACHING_PROXY; + } else { s = ClientRegionShortcut.LOCAL; } diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd index 6f1d1a9c..37fe5a6f 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd @@ -6,7 +6,7 @@ targetNamespace="http://www.springframework.org/schema/gemfire" elementFormDefault="qualified" attributeFormDefault="unqualified" - version="1.1"> + version="1.1.1"> @@ -1074,7 +1074,7 @@ The client subscription configuration that is used to control a clients use of s - + Date: Mon, 19 Mar 2012 21:04:50 +0200 Subject: [PATCH 05/21] update changelog --- docs/src/info/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/info/changelog.txt b/docs/src/info/changelog.txt index d8628bbf..fc47709b 100644 --- a/docs/src/info/changelog.txt +++ b/docs/src/info/changelog.txt @@ -2,8 +2,8 @@ SPRING DATA GEMFIRE CHANGELOG ============================= http://www.springsource.org/spring-gemfire -Changes in version 1.1.1.RELEASE (2012-03-20) ---------------------------------------------- +Changes in version 1.2.0.M1 (2012-03-20) +---------------------------------------- General * Upgraded to GemFire 6.6.2 From 8743519c96dc8a2f82f2530b573803bb64d8504b Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Mon, 19 Mar 2012 21:44:04 +0200 Subject: [PATCH 06/21] change gemstone repo --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 03aafdc9..cf091d31 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,8 @@ allprojects { mavenRepo name: "spring-snapshot", urls: "http://maven.springframework.org/snapshot" mavenRepo name: "sonatype-snapshot", urls: "http://oss.sonatype.org/content/repositories/snapshots" mavenRepo name: "ext-snapshots", urls: "http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/" - mavenRepo name: "gemstone-com-release", urls: "http://repo.springsource.org/gemstone-release" + mavenRepo name: "gemstone-com-release", urls: "http://dist.gemstone.com/maven/release" + //mavenRepo name: "gemstone-com-release", urls: "http://repo.springsource.org/gemstone-release" } } From 9d5de9222b45269f33699297cddd0e50383f0780 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 06:02:05 +0200 Subject: [PATCH 07/21] replace tabs with spaces --- .../springframework/data/gemfire/config/spring-gemfire-1.2.xsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd index 318b29d5..60797d8d 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd @@ -12,7 +12,7 @@ + schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd"/> Date: Tue, 20 Mar 2012 06:14:11 +0200 Subject: [PATCH 08/21] backport from 1.1.x 18761a8c - 6a05270b GemfireTransactionCommitException should extend Spring's extension SGF-84 improve handling of GemFire 6.5 TX exceptions --- .../gemfire/GemfireTransactionCommitException.java | 10 +--------- .../data/gemfire/GemfireTransactionManager.java | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/GemfireTransactionCommitException.java b/src/main/java/org/springframework/data/gemfire/GemfireTransactionCommitException.java index d9c1f0ef..d1545cde 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireTransactionCommitException.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireTransactionCommitException.java @@ -16,7 +16,7 @@ package org.springframework.data.gemfire; -import com.gemstone.gemfire.cache.TransactionException; +import org.springframework.transaction.TransactionException; /** * Gemfire-specific subclass of {@link org.springframework.transaction.TransactionException}, indicating a transaction failure at commit time. @@ -25,10 +25,6 @@ import com.gemstone.gemfire.cache.TransactionException; */ public class GemfireTransactionCommitException extends TransactionException { - public GemfireTransactionCommitException() { - super(); - } - public GemfireTransactionCommitException(String message, Throwable cause) { super(message, cause); } @@ -36,8 +32,4 @@ public class GemfireTransactionCommitException extends TransactionException { public GemfireTransactionCommitException(String message) { super(message); } - - public GemfireTransactionCommitException(Throwable cause) { - super(cause); - } } diff --git a/src/main/java/org/springframework/data/gemfire/GemfireTransactionManager.java b/src/main/java/org/springframework/data/gemfire/GemfireTransactionManager.java index 8a0f2d45..d7c17f9f 100644 --- a/src/main/java/org/springframework/data/gemfire/GemfireTransactionManager.java +++ b/src/main/java/org/springframework/data/gemfire/GemfireTransactionManager.java @@ -30,7 +30,6 @@ import org.springframework.util.Assert; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheTransactionManager; -import com.gemstone.gemfire.cache.CommitConflictException; import com.gemstone.gemfire.cache.Region; /** @@ -136,7 +135,7 @@ public class GemfireTransactionManager extends AbstractPlatformTransactionManage } catch (IllegalStateException ex) { throw new NoTransactionException( "No transaction associated with the current thread; are there multiple transaction managers ?", ex); - } catch (CommitConflictException ex) { + } catch (TransactionException ex) { throw new GemfireTransactionCommitException("Unexpected failure on commit of Cache local transaction", ex); } } From 0bf79e1d0cc2f39863642d8643787028c4eca975 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 06:18:43 +0200 Subject: [PATCH 09/21] update changelog --- docs/src/info/changelog.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/src/info/changelog.txt b/docs/src/info/changelog.txt index fc47709b..fcd8e745 100644 --- a/docs/src/info/changelog.txt +++ b/docs/src/info/changelog.txt @@ -5,6 +5,14 @@ http://www.springsource.org/spring-gemfire Changes in version 1.2.0.M1 (2012-03-20) ---------------------------------------- +General +* Introduced support for annotation-based entity mapping (@Id, @PersistenceConstructor, @Id) +* Introduced support for Spring Data Repositories (query exception & derivation) + + +Changes in version 1.1.1.RELEASE (2012-03-20) +--------------------------------------------- + General * Upgraded to GemFire 6.6.2 * Upgraded to Spring Framework 3.1.1 GA @@ -12,6 +20,8 @@ General Package org.springframework.data.gemfire * Fixed incorrect parsing of pdx-serializer (from value to reference) * Fixed incorrect parsing of use-bean-factory-locator +* Fixed GemfireTransactionCommitException class hierarchy +* Improved handling of GemFire 6.5+ transaction exceptions Package org.springframework.data.gemfire.client * Fixed bug that caused client namespace to create only local regions From 7224ecadb78e4411d64f3238949fcc510cd2d8e2 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 08:38:47 +0200 Subject: [PATCH 10/21] fix reference name rendering --- docs/src/reference/docbook/index.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference/docbook/index.xml b/docs/src/reference/docbook/index.xml index 48119e0b..ec0600d1 100644 --- a/docs/src/reference/docbook/index.xml +++ b/docs/src/reference/docbook/index.xml @@ -15,7 +15,7 @@ Oliver - Gierke + Gierke SpringSource, a division of VMware From 53405371dffafb8ae30a5857f73a83a8041eea33 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 11:23:43 +0200 Subject: [PATCH 11/21] prepare 1.2.0 M1 release --- gradle.properties | 2 +- samples/hello-world/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2e08bd17..b808632c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,4 @@ gemfire.range = "[6.5, 7.0)" # -------------------- # Project wide version # -------------------- -springGemfireVersion=1.2.0.BUILD-SNAPSHOT \ No newline at end of file +springGemfireVersion=1.2.0.M1 \ No newline at end of file diff --git a/samples/hello-world/gradle.properties b/samples/hello-world/gradle.properties index 16890fe1..c2da9549 100644 --- a/samples/hello-world/gradle.properties +++ b/samples/hello-world/gradle.properties @@ -1,3 +1,3 @@ junitVersion = 4.8.1 springVersion = 3.1.1.RELEASE -version = 1.2.0.BUILD-SNAPSHOT +version = 1.2.0.M1 From a6e5374afe4fbd5b53a7438998c530c23a936031 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 11:44:03 +0200 Subject: [PATCH 12/21] improve maven info --- maven.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/maven.gradle b/maven.gradle index 3707b53f..cbd78b3a 100644 --- a/maven.gradle +++ b/maven.gradle @@ -112,6 +112,18 @@ def customizePom(pom) { distribution 'repo' } } + scm { + url = 'http://github.com/SpringSource/spring-gemfire' + connection = 'scm:git:git://github.com/SpringSource/spring-gemfire' + developerConnection = 'scm:git:git://github.com/SpringSource/spring-gemfire' + } + developers { + developer { + id = 'costin' + name = 'Costin Leau' + email = 'cleau@vmware.com' + } + } // similar to Spring's configuration dependencies { From 08757733019c3fd54f2bb9f8c5e84f44746bd391 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 12:46:57 +0200 Subject: [PATCH 13/21] fix maven description a bit more --- maven.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/maven.gradle b/maven.gradle index cbd78b3a..4c5d933f 100644 --- a/maven.gradle +++ b/maven.gradle @@ -105,6 +105,14 @@ def customizePom(pom) { } pom.project { + name = project.description + description = project.description + url = 'http://github.com/SpringSource/spring-gemfire' + organization { + name = 'SpringSource' + url = 'http://www.springsource.org/spring-gemfire' + } + licenses { license { name 'The Apache Software License, Version 2.0' From f5042aafda00dc04bb6062b9059ce50b5f4d6c06 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 13:15:57 +0200 Subject: [PATCH 14/21] bump version --- gradle.properties | 2 +- samples/hello-world/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index b808632c..2e08bd17 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,4 @@ gemfire.range = "[6.5, 7.0)" # -------------------- # Project wide version # -------------------- -springGemfireVersion=1.2.0.M1 \ No newline at end of file +springGemfireVersion=1.2.0.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/hello-world/gradle.properties b/samples/hello-world/gradle.properties index c2da9549..16890fe1 100644 --- a/samples/hello-world/gradle.properties +++ b/samples/hello-world/gradle.properties @@ -1,3 +1,3 @@ junitVersion = 4.8.1 springVersion = 3.1.1.RELEASE -version = 1.2.0.M1 +version = 1.2.0.BUILD-SNAPSHOT From a8fc8fab7ca9c8efff42d59f42e3cbd3dcf8950d Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Mar 2012 13:21:22 +0200 Subject: [PATCH 15/21] update Readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2ff5959..f6911840 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,9 @@ dependencies { } ~~~~~ -Latest GA release is _1.1.0.RELEASE_ -Latest nightly build is _1.1.1.BUILD-SNAPSHOT_ +Latest GA release is _1.1.1.RELEASE_ +Latest milestone release is _1.2.0.M1_ +Latest nightly build is _1.2.0.BUILD-SNAPSHOT_ * Configure a GemFire cache and Region (replicated, partitioned, client and so on): From a7e48f4b0c8bdaf2603b0a5c39fea5a786369e6e Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 4 Apr 2012 13:03:30 +0300 Subject: [PATCH 16/21] fix incorrect parsing of pdx-disk-store attribute SGF-85 (cherry picked from commit 8e37173472703b5ad6724ef9a2d68eab982d0745) --- .../org/springframework/data/gemfire/config/CacheParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/gemfire/config/CacheParser.java b/src/main/java/org/springframework/data/gemfire/config/CacheParser.java index 40b20142..357306c7 100644 --- a/src/main/java/org/springframework/data/gemfire/config/CacheParser.java +++ b/src/main/java/org/springframework/data/gemfire/config/CacheParser.java @@ -45,7 +45,7 @@ class CacheParser extends AbstractSingleBeanDefinitionParser { ParsingUtils.setPropertyValue(element, builder, "cache-xml-location", "cacheXml"); ParsingUtils.setPropertyReference(element, builder, "properties-ref", "properties"); ParsingUtils.setPropertyReference(element, builder, "pdx-serializer", "pdxSerializer"); - ParsingUtils.setPropertyValue(element, builder, "pdx-disk-store", "pdxDiskStore"); + ParsingUtils.setPropertyValue(element, builder, "pdx-disk-store", "pdxDiskStoreName"); ParsingUtils.setPropertyValue(element, builder, "pdx-persistent", "pdxPersistent"); ParsingUtils.setPropertyValue(element, builder, "pdx-read-serialized", "pdxReadSerialized"); ParsingUtils.setPropertyValue(element, builder, "pdx-ignore-unread-fields", "pdxIgnoreUnreadFields"); From 1c98377844bd6503c4cd25d7b7d30140da0bb576 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 20 Mar 2012 09:43:19 +0100 Subject: [PATCH 17/21] SGF-87 - Upgrade to Spring Data Commons 1.3.0.RC1. Hand null as parent object to PersistentEntityParameterValueProvider as the PdxSerializer abstraction does not allow accessing the parent object of the one currently to be created. Adapted changes in CrudRepository. Polished JavaDoc in SimpleGemfireRepository. --- gradle.properties | 2 +- .../gemfire/mapping/MappingPdxSerializer.java | 2 +- .../support/SimpleGemfireRepository.java | 30 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2e08bd17..cc1bd447 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ slf4jVersion = 1.6.4 # Common libraries springVersion = 3.1.1.RELEASE -springDataCommonsVersion = 1.3.0.M1 +springDataCommonsVersion = 1.3.0.RC1 gemfireVersion = 6.6.2 # Testing diff --git a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java index 439b92d2..5d65d809 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java @@ -97,7 +97,7 @@ public class MappingPdxSerializer implements PdxSerializer, ApplicationContextAw GemfirePropertyValueProvider propertyValueProvider = new GemfirePropertyValueProvider(reader); PersistentEntityParameterValueProvider provider = new PersistentEntityParameterValueProvider( - entity, propertyValueProvider); + entity, propertyValueProvider, null); provider.setSpELEvaluator(new DefaultSpELExpressionEvaluator(reader, context)); Object instance = instantiator.createInstance(entity, provider); diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java index 65b8b5b0..30a3ed41 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java @@ -41,22 +41,22 @@ public class SimpleGemfireRepository implements Gemf this.entityInformation = entityInformation; } - /* + /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#save(java.lang.Object) + * @see org.springframework.data.repository.CrudRepository#save(S) */ - public T save(T entity) { + public U save(U entity) { template.put(entityInformation.getId(entity), entity); return entity; } - /* + /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#save(java.lang.Iterable) + * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ - public Iterable save(Iterable entities) { - List result = new ArrayList(); - for (T entity : entities) { + public Iterable save(Iterable entities) { + List result = new ArrayList(); + for (U entity : entities) { result.add(save(entity)); } return result; @@ -64,7 +64,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#findOne(java.io.Serializable) + * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable) */ @SuppressWarnings("unchecked") public T findOne(ID id) { @@ -74,7 +74,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#exists(java.io.Serializable) + * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable) */ public boolean exists(ID id) { return findOne(id) != null; @@ -82,7 +82,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#findAll() + * @see org.springframework.data.repository.CrudRepository#findAll() */ public Collection findAll() { return template.execute(new GemfireCallback>() { @@ -111,7 +111,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#count() + * @see org.springframework.data.repository.CrudRepository#count() */ public long count() { return template.execute(new GemfireCallback() { @@ -124,7 +124,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#delete(java.lang.Object) + * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) */ public void delete(T entity) { template.remove(entityInformation.getId(entity)); @@ -132,7 +132,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#delete(java.lang.Iterable) + * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) */ public void delete(Iterable entities) { for (T entity : entities) { @@ -142,7 +142,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * @see org.springframework.data.repository.Repository#deleteAll() + * @see org.springframework.data.repository.CrudRepository#deleteAll() */ public void deleteAll() { From c5ca33d0c66d4a13b21d5eca33d83bf4c6199a92 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 7 May 2012 11:49:40 +0200 Subject: [PATCH 18/21] SGF-87 - Upgrade to Spring Data Commons 1.3.0.BUILD-SNAPSHOT. Make sure we can compile against the next major version. --- gradle.properties | 2 +- .../support/GemfireRepositoryFactory.java | 14 +++++++------- .../query/GemfireQueryMethodUnitTests.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gradle.properties b/gradle.properties index cc1bd447..9acce116 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ slf4jVersion = 1.6.4 # Common libraries springVersion = 3.1.1.RELEASE -springDataCommonsVersion = 1.3.0.RC1 +springDataCommonsVersion = 1.3.0.BUILD-SNAPSHOT gemfireVersion = 6.6.2 # Testing diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java index cafb940a..40f4d93b 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java @@ -64,7 +64,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { this.regions = new Regions(regions, this.context); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class) */ @@ -76,7 +76,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { return new DefaultGemfireEntityInformation(entity); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata) */ @@ -84,7 +84,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { @SuppressWarnings({ "rawtypes", "unchecked" }) protected Object getTargetRepository(RepositoryMetadata metadata) { - GemfireEntityInformation entityInformation = getEntityInformation(metadata.getDomainClass()); + GemfireEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); GemfireTemplate gemfireTemplate = getTemplate(metadata); return new SimpleGemfireRepository(gemfireTemplate, entityInformation); @@ -92,13 +92,13 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { private GemfireTemplate getTemplate(RepositoryMetadata metadata) { - Class domainClass = metadata.getDomainClass(); + Class domainClass = metadata.getDomainType(); Region region = regions.getRegion(domainClass); GemfirePersistentEntity entity = context.getPersistentEntity(domainClass); Class regionKeyType = region.getAttributes().getKeyConstraint(); - Class entityIdType = metadata.getIdClass(); + Class entityIdType = metadata.getIdType(); if (regionKeyType != null && entity.getIdProperty() != null) { Assert.isTrue(regionKeyType.isAssignableFrom(entityIdType), String.format( @@ -109,7 +109,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { return new GemfireTemplate(region); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata) */ @@ -118,7 +118,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { return SimpleGemfireRepository.class; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key) */ diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java index f0b7d64b..2fb1ea73 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java @@ -46,7 +46,7 @@ public class GemfireQueryMethodUnitTests { public void detectsAnnotatedQueryCorrectly() throws Exception { GemfireMappingContext context = new GemfireMappingContext(); - when(metadata.getDomainClass()).thenReturn((Class) Person.class); + when(metadata.getDomainType()).thenReturn((Class) Person.class); when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) Person.class); GemfireQueryMethod method = new GemfireQueryMethod(Sample.class.getMethod("annotated"), metadata, context); From 77478fe0b66c980d07668fe2f89d37c6ff96ba38 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 7 May 2012 11:52:29 +0200 Subject: [PATCH 19/21] SGF-89 - Fixed invalid invocation of ContinuousQueryListeners. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ContinuousQueryListenerAdapter did not return after the invocation of the handleEvent(…) method of the listener delegate implements ContinousQueryListener. This caused an exception if the listener implementation did not one of the supported method signatures for reflection invocation. Fixed that by adding the necessary return statement. Added according test cases. --- .../ContinuousQueryListenerAdapter.java | 6 +-- .../adapter/QueryListenerAdapterTest.java | 50 ++++++++++++++----- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/listener/adapter/ContinuousQueryListenerAdapter.java b/src/main/java/org/springframework/data/gemfire/listener/adapter/ContinuousQueryListenerAdapter.java index a6f98fe4..406ff2af 100644 --- a/src/main/java/org/springframework/data/gemfire/listener/adapter/ContinuousQueryListenerAdapter.java +++ b/src/main/java/org/springframework/data/gemfire/listener/adapter/ContinuousQueryListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2012 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. @@ -68,6 +68,7 @@ import com.gemstone.gemfire.cache.query.CqQuery; * * @author Juergen Hoeller * @author Costin Leau + * @author Oliver Gierke * @see org.springframework.jms.listener.adapter.MessageListenerAdapter */ public class ContinuousQueryListenerAdapter implements ContinuousQueryListener { @@ -260,7 +261,6 @@ public class ContinuousQueryListenerAdapter implements ContinuousQueryListener { * @param event the incoming GemFire event * @see #handleListenerException */ - @SuppressWarnings("unchecked") public void onEvent(CqEvent event) { try { @@ -269,6 +269,7 @@ public class ContinuousQueryListenerAdapter implements ContinuousQueryListener { if (delegate != this) { if (delegate instanceof ContinuousQueryListener) { ((ContinuousQueryListener) delegate).onEvent(event); + return; } } @@ -283,7 +284,6 @@ public class ContinuousQueryListenerAdapter implements ContinuousQueryListener { + "override the 'getListenerMethodName' method."); } - invokeListenerMethod(event, methodName); } catch (Throwable th) { handleListenerException(th); diff --git a/src/test/java/org/springframework/data/gemfire/listener/adapter/QueryListenerAdapterTest.java b/src/test/java/org/springframework/data/gemfire/listener/adapter/QueryListenerAdapterTest.java index 5e43592e..a3126ce4 100644 --- a/src/test/java/org/springframework/data/gemfire/listener/adapter/QueryListenerAdapterTest.java +++ b/src/test/java/org/springframework/data/gemfire/listener/adapter/QueryListenerAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2012 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. @@ -16,11 +16,9 @@ package org.springframework.data.gemfire.listener.adapter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import org.junit.Before; import org.junit.Test; @@ -31,9 +29,9 @@ import com.gemstone.gemfire.cache.query.CqEvent; import com.gemstone.gemfire.cache.query.CqQuery; import com.gemstone.gemfire.cache.query.internal.CqQueryImpl; - /** * @author Costin Leau + * @author Oliver Gierke */ public class QueryListenerAdapterTest { @@ -92,16 +90,17 @@ public class QueryListenerAdapterTest { void handleOperation(Operation op); void handleArray(byte[] ba); - + void handleKey(Object key); - + void handleKV(Object k, Object v); - + void handleEx(Throwable th); - + void handleOps(Operation base, Operation query); - void handleAll(CqEvent event, CqQuery query, byte[] ba, Object key, Operation op, Throwable th, Operation qOp, Object v); + void handleAll(CqEvent event, CqQuery query, byte[] ba, Object key, Operation op, Throwable th, Operation qOp, + Object v); void handleInvalid(Object o1, Object o2, Object o3); } @@ -212,4 +211,31 @@ public class QueryListenerAdapterTest { doThrow(new IllegalArgumentException()).when(mock); } + /** + * @see SGF-89 + */ + @Test + public void triggersListenerImplementingInterfaceCorrectly() { + + SampleListener listener = new SampleListener(); + + ContinuousQueryListener listenerAdapter = new ContinuousQueryListenerAdapter(listener) { + protected void handleListenerException(Throwable ex) { + throw new RuntimeException(ex); + } + }; + + listenerAdapter.onEvent(event()); + assertThat(listener.count, is(1)); + } + + class SampleListener implements ContinuousQueryListener { + + int count; + + @Override + public void onEvent(CqEvent event) { + count++; + } + } } \ No newline at end of file From f3cf6f013e233074fbffd4a5fa8c8e008c5061bc Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 7 May 2012 12:06:11 +0200 Subject: [PATCH 20/21] Polished Javadoc in repository subsystem. --- .../data/gemfire/mapping/GemfirePersistentEntity.java | 2 +- .../springframework/data/gemfire/mapping/Regions.java | 8 ++++---- .../query/DefaultGemfireEntityInformation.java | 6 +++--- .../repository/query/GemfireEntityInformation.java | 2 +- .../gemfire/repository/query/GemfireQueryMethod.java | 6 ++++-- .../support/GemfireRepositoryFactoryBean.java | 10 +++++++--- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java index 9cb5878e..2012cac1 100644 --- a/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java +++ b/src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java @@ -49,7 +49,7 @@ public class GemfirePersistentEntity extends BasicPersistentEntity> { * name in case no mapping information is found. * * @param type must not be {@literal null}. - * @return + * @return the {@link Region} the given type is mapped to. */ @SuppressWarnings("unchecked") public Region getRegion(Class type) { @@ -74,10 +74,10 @@ public class Regions implements Iterable> { } /** - * Returns the region with the given name. + * Returns the {@link Region} with the given name. * * @param name must not be {@literal null}. - * @return + * @return the {@link Region} with the given name. */ @SuppressWarnings("unchecked") public Region getRegion(String name) { @@ -87,7 +87,7 @@ public class Regions implements Iterable> { return (Region) regions.get(name); } - /* + /* * (non-Javadoc) * @see java.lang.Iterable#iterator() */ diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java b/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java index 944f0d9c..056ea1f1 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java @@ -27,21 +27,21 @@ import org.springframework.data.repository.core.support.ReflectionEntityInformat * @author Oliver Gierke */ public class DefaultGemfireEntityInformation extends DelegatingEntityInformation - implements GemfireEntityInformation { +implements GemfireEntityInformation { private final GemfirePersistentEntity entity; /** * Creates a new {@link DefaultGemfireEntityInformation}. * - * @param domainClass must not be {@literal null}. + * @param entity must not be {@literal null}. */ public DefaultGemfireEntityInformation(GemfirePersistentEntity entity) { super(new ReflectionEntityInformation(entity.getType())); this.entity = entity; } - /* + /* * (non-Javadoc) * @see org.springframework.data.gemfire.repository.query.GemfireEntityInformation#getRegionName() */ diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java index 1b44fde7..2b043f95 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java @@ -31,7 +31,7 @@ public interface GemfireEntityInformation extends En /** * Returns the name of the {@link Region} the entity is held in. * - * @return + * @return the name of the {@link Region} the entity is held in. */ String getRegionName(); } diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java index cab19a3e..69655726 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java @@ -58,14 +58,16 @@ public class GemfireQueryMethod extends QueryMethod { /** * Returns whether the query method contains an annotated, non-empty query. * - * @return + * @return whether the query method contains an annotated, non-empty query. */ public boolean hasAnnotatedQuery() { return StringUtils.hasText(getAnnotatedQuery()); } /** - * @return the entity + * Returns the {@link GemfirePersistentEntity} the method deals with. + * + * @return the {@link GemfirePersistentEntity} the method deals with. */ public GemfirePersistentEntity getPersistentEntity() { return entity; diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java index 1dfa5079..1152b123 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java @@ -19,6 +19,7 @@ import java.io.Serializable; import java.util.Collection; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.FactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; @@ -32,16 +33,17 @@ import com.gemstone.bp.edu.emory.mathcs.backport.java.util.Collections; import com.gemstone.gemfire.cache.Region; /** + * {@link FactoryBean} adapter for {@link GemfireRepositoryFactory}. * * @author Oliver Gierke */ public class GemfireRepositoryFactoryBean, S, ID extends Serializable> extends - RepositoryFactoryBeanSupport implements ApplicationContextAware { +RepositoryFactoryBeanSupport implements ApplicationContextAware { private MappingContext, GemfirePersistentProperty> context; private Iterable> regions; - /* + /* * (non-Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ @@ -53,13 +55,15 @@ public class GemfireRepositoryFactoryBean, S, ID ext } /** + * Configures the {@link MappingContext} to be used. + * * @param context the context to set */ public void setMappingContext(MappingContext, GemfirePersistentProperty> context) { this.context = context; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory() */ From 78820a2a70e9068ba0cb4fc0e976605af6b9c61d Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 7 May 2012 13:10:45 +0200 Subject: [PATCH 21/21] SGF-92 - Improved error message in case no region can be found. If the region referenced by an entity managed by a repository cannot be found in the application context we now throw an exception with an error message indicating the missing configuration. --- .../repository/support/GemfireRepositoryFactory.java | 7 ++++++- .../GemfireRepositoryFactoryIntegrationTests.java | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java index 40f4d93b..7c09dc8c 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java @@ -93,9 +93,14 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport { private GemfireTemplate getTemplate(RepositoryMetadata metadata) { Class domainClass = metadata.getDomainType(); + GemfirePersistentEntity entity = context.getPersistentEntity(domainClass); + Region region = regions.getRegion(domainClass); - GemfirePersistentEntity entity = context.getPersistentEntity(domainClass); + if (region == null) { + throw new IllegalStateException(String.format("No region '%s' found for domain class %s! Make sure you have " + + "configured a Gemfire region of that name in your application context!", entity.getRegionName(), domainClass)); + } Class regionKeyType = region.getAttributes().getKeyConstraint(); Class entityIdType = metadata.getIdType(); diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java index bd31ad4a..11ef0405 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java @@ -15,10 +15,13 @@ */ package org.springframework.data.gemfire.repository.support; +import org.junit.Test; import org.springframework.data.gemfire.mapping.Regions; import org.springframework.data.gemfire.repository.sample.PersonRepository; import org.springframework.test.context.ContextConfiguration; +import com.gemstone.bp.edu.emory.mathcs.backport.java.util.Collections; + /** * Integration test for {@link GemfireRepositoryFactory}. * @@ -33,4 +36,12 @@ public class GemfireRepositoryFactoryIntegrationTests extends AbstractGemfireRep GemfireRepositoryFactory factory = new GemfireRepositoryFactory(regions, null); return factory.getRepository(PersonRepository.class); } + + @Test(expected = IllegalStateException.class) + @SuppressWarnings("unchecked") + public void throwsExceptionIfReferencedRegionIsNotConfigured() { + + GemfireRepositoryFactory factory = new GemfireRepositoryFactory(Collections.emptySet(), null); + factory.getRepository(PersonRepository.class); + } }