Browse files

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).
  • Loading branch information...
1 parent 1a651e7 commit 40a0a7f2a636573007383d401789cd71a5d931d0 @olivergierke olivergierke committed Jan 18, 2012
Showing with 4,868 additions and 3 deletions.
  1. +7 −2 build.gradle
  2. +10 −0 docs/src/reference/docbook/index.xml
  3. +84 −0 docs/src/reference/docbook/reference/mapping.xml
  4. +218 −0 docs/src/reference/docbook/reference/repositories.xml
  5. +2 −0 gradle.properties
  6. +3 −0 src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java
  7. +50 −0 src/main/java/org/springframework/data/gemfire/mapping/GemfireMappingContext.java
  8. +57 −0 src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntity.java
  9. +52 −0 src/main/java/org/springframework/data/gemfire/mapping/GemfirePersistentProperty.java
  10. +51 −0 src/main/java/org/springframework/data/gemfire/mapping/GemfirePropertyValueProvider.java
  11. +156 −0 src/main/java/org/springframework/data/gemfire/mapping/MappingPdxSerializer.java
  12. +78 −0 src/main/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessor.java
  13. +42 −0 src/main/java/org/springframework/data/gemfire/mapping/Region.java
  14. +98 −0 src/main/java/org/springframework/data/gemfire/mapping/Regions.java
  15. +30 −0 src/main/java/org/springframework/data/gemfire/repository/GemfireRepository.java
  16. +34 −0 src/main/java/org/springframework/data/gemfire/repository/Query.java
  17. +95 −0 src/main/java/org/springframework/data/gemfire/repository/Wrapper.java
  18. +40 −0 src/main/java/org/springframework/data/gemfire/repository/config/GemfireRepositoryParser.java
  19. +135 −0 ...java/org/springframework/data/gemfire/repository/config/SimpleGemfireRepositoryConfiguration.java
  20. +5 −0 src/main/java/org/springframework/data/gemfire/repository/package-info.java
  21. +52 −0 src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java
  22. +37 −0 src/main/java/org/springframework/data/gemfire/repository/query/GemfireEntityInformation.java
  23. +143 −0 src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreator.java
  24. +86 −0 src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java
  25. +50 −0 src/main/java/org/springframework/data/gemfire/repository/query/GemfireRepositoryQuery.java
  26. +68 −0 src/main/java/org/springframework/data/gemfire/repository/query/PartTreeGemfireRepositoryQuery.java
  27. +6 −0 src/main/java/org/springframework/data/gemfire/repository/query/Predicate.java
  28. +173 −0 src/main/java/org/springframework/data/gemfire/repository/query/Predicates.java
  29. +53 −0 src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java
  30. +118 −0 src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java
  31. +103 −0 ...ain/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java
  32. +147 −0 src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactory.java
  33. +70 −0 src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java
  34. +174 −0 src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java
  35. +2 −1 src/main/resources/META-INF/spring.schemas
  36. +1,309 −0 src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd
  37. +66 −0 src/test/java/org/springframework/data/gemfire/mapping/GemfirePersistentEntityUnitTests.java
  38. +81 −0 src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerIntegrationTest.java
  39. +81 −0 src/test/java/org/springframework/data/gemfire/mapping/MappingPdxSerializerUnitTests.java
  40. +85 −0 src/test/java/org/springframework/data/gemfire/mapping/PdxReaderPropertyAccessorUnitTests.java
  41. +39 −0 .../java/org/springframework/data/gemfire/repository/config/NamespaceRepositoryIntegrationTests.java
  42. +54 −0 src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryCreatorUnitTests.java
  43. +75 −0 src/test/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethodUnitTests.java
  44. +76 −0 src/test/java/org/springframework/data/gemfire/repository/query/PredicatesUnitTests.java
  45. +68 −0 src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java
  46. +26 −0 src/test/java/org/springframework/data/gemfire/repository/sample/Address.java
  47. +57 −0 src/test/java/org/springframework/data/gemfire/repository/sample/Person.java
  48. +45 −0 src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java
  49. +120 −0 ...ngframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java
  50. +36 −0 ...org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java
  51. +104 −0 ...a/org/springframework/data/gemfire/repository/support/SimpleGemfireRepositoryIntegrationTest.java
  52. +1 −0 src/test/resources/log4j.properties
  53. +14 −0 src/test/resources/org/springframework/data/gemfire/repository/config/repo-context.xml
  54. +2 −0 template.mf
View
9 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
View
10 docs/src/reference/docbook/index.xml
@@ -13,6 +13,11 @@
<surname>Leau</surname>
<affiliation>SpringSource, a division of VMware</affiliation>
</author>
+ <author>
+ <firstname>Oliver</firstname>
+ <lastname>Gierke</lastname>
+ <affiliation>SpringSource, a division of VMware</affiliation>
+ </author>
</authorgroup>
@@ -41,6 +46,11 @@
<xi:include href="reference/bootstrap.xml"/>
<xi:include href="reference/data.xml"/>
<xi:include href="reference/serialization.xml"/>
+ <xi:include href="reference/mapping.xml"/>
+ <xi:include href="https://github.com/SpringSource/spring-data-commons/raw/master/src/docbkx/repositories.xml">
+ <xi:fallback href="../../../../../../spring-data-commons/src/docbkx/repositories.xml" />
+ </xi:include>
+ <xi:include href="reference/repositories.xml"/>
<xi:include href="reference/samples.xml"/>
</part>
View
84 docs/src/reference/docbook/reference/mapping.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter version="5.0" xml:id="mapping" xmlns="http://docbook.org/ns/docbook"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:ns5="http://www.w3.org/1999/xhtml"
+ xmlns:ns4="http://www.w3.org/2000/svg"
+ xmlns:ns3="http://www.w3.org/1998/Math/MathML"
+ xmlns:ns="http://docbook.org/ns/docbook">
+ <title>POJO mapping</title>
+
+ <section xml:id="mapping.entities">
+ <title>Entity mapping</title>
+
+ <para>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: </para>
+
+ <example>
+ <title>Mapping a domain class to Gemfire</title>
+
+ <programlisting language="java">@Region("myRegion")
+public class Person {
+
+ @Id Long id;
+ String firstname;
+ String lastname;
+
+ @PersistenceConstructor
+ public Person(String firstname, String lastname) {
+ // …
+ }
+
+ …
+} </programlisting>
+ </example>
+
+ <para>The first thing you see here is the
+ <interfacename>@Region</interfacename> annotation that can be used to
+ customize the region instances of the <classname>Person</classname> class
+ are stored in. The <interfacename>@Id</interfacename> annotation can be
+ used to annotate the property that shall be used as cache key. The
+ <interfacename>@PersistenceConstructor</interfacename> 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.</para>
+ </section>
+
+ <section xml:id="mapping.pdx-serializer">
+ <title>Mapping PDX serializer</title>
+
+ <para>Spring Data Gemfire provides a custom
+ <interfacename>PDXSerializer</interfacename> implementation that uses the
+ mapping information to customize entity serialization. Beyond that it
+ allows customizing the entity instantiation by using the Spring Data
+ <interfacename>EntityInstantiator</interfacename> abstraction. By default
+ the serializer uses a <classname>ReflectionEntityInstantiator</classname>
+ that will use the persistence constructor of the mapped entity (either the
+ single declared one or explicitly annoted with
+ <interfacename>@PersistenceConstructor</interfacename>). To provide values
+ for constructor parameters it will read fields with name of the
+ constructor parameters from the <interfacename>PDXReader</interfacename>
+ supplied.</para>
+
+ <example>
+ <title>Using @Value on entity constructor parameters</title>
+
+ <programlisting language="java">public class Person {
+
+ public Person(@Value("#root.foo") String firstname, @Value("bean") String lastname) {
+ // …
+ }
+
+ …
+} </programlisting>
+ </example>
+
+ <para>The entity annotated as such will get the field <code>foo</code>
+ read from the <interfacename>PDXReader</interfacename> and handed as
+ constructor parameter value for <code>firstname</code>. The value for
+ <code>lastname</code> will be the Spring bean with name
+ <code>bean</code>.</para>
+ </section>
+</chapter>
View
218 docs/src/reference/docbook/reference/repositories.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter version="5.0" xml:id="gemfire-repositories"
+ xmlns="http://docbook.org/ns/docbook"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:ns5="http://www.w3.org/1999/xhtml"
+ xmlns:ns4="http://www.w3.org/2000/svg"
+ xmlns:ns3="http://www.w3.org/1998/Math/MathML"
+ xmlns:ns="http://docbook.org/ns/docbook">
+ <title>Gemfire Repositories</title>
+
+ <section>
+ <title xml:id="gemfire-repositories.intro">Introduction</title>
+
+ <para>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 <xref linkend="repositories" />.</para>
+ </section>
+
+ <section xml:id="gemfire-repositories.spring-configuration">
+ <title>Spring configuration</title>
+
+ <para>To bootstrap Spring Data repositories you use the
+ <code>&lt;repositories /&gt;</code> element from the Gemfire
+ namespace:</para>
+
+ <example>
+ <title>Bootstrap Gemfire repositories</title>
+
+ <programlisting language="xml">&lt;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&gt;
+
+ &lt;gf:repositories base-package="com.acme.repository" /&gt;
+
+&lt;/beans&gt;</programlisting>
+ </example>
+
+ <para>This configuration snippet will look for interfaces below the
+ configured base package and create repository instances for those
+ interfaces backed by a <classname>SimpleGemfireRepository</classname>.
+ Note that you have to have your domain classes correctly mapped to
+ configured regions as the bottstrap process will fail otherwise.</para>
+ </section>
+
+ <section xml:id="gemfire-repositories.executing-queries">
+ <title>Executing OQL queries</title>
+
+ <para>The Gemfire repositories allow the definition of query methods to
+ easily execute OQL queries against the Region the managed entity is mapped
+ to.</para>
+
+ <example>
+ <title>Sample repository</title>
+
+ <programlisting language="java">@Region("myRegion")
+public class Person { … }</programlisting>
+
+ <programlisting language="java">public interface PersonRepository extends CrudRepository&lt;Person, Long&gt; {
+
+ Person findByEmailAddress(String emailAddress);
+
+ Collection&lt;Person&gt; findByFirstname(String firstname);
+
+ @Query("SELECT * FROM /Person p WHERE p.firstname = $1")
+ Collection&lt;Person&gt; findByFirstnameAnnotated(String firstname);
+
+ @Query("SELECT * FROM /Person p WHERE p.firstname IN SET $1")
+ Collection&lt;Person&gt; findByFirstnamesAnnotated(Collection&lt;String&gt; firstnames);
+}</programlisting>
+ </example>
+
+ <para>The first method listed here will cause the following query to be
+ derived: <code>SELECT x FROM /myRegion x WHERE x.emailAddress = $1</code>.
+ 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
+ <interfacename>@Query</interfacename> as seen for methods 3 and 4.</para>
+
+ <para><table>
+ <title>Supported keywords for query methods</title>
+
+ <tgroup cols="3">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2*" />
+
+ <colspec colwidth="2*" />
+
+ <thead>
+ <row>
+ <entry>Keyword</entry>
+
+ <entry>Sample</entry>
+
+ <entry>Logical result</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal>GreaterThan</literal></entry>
+
+ <entry><methodname>findByAgeGreaterThan(int
+ age)</methodname></entry>
+
+ <entry><code>x.age &gt; $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>GreaterThanEqual</literal></entry>
+
+ <entry><methodname>findByAgeGreaterThanEqual(int
+ age)</methodname></entry>
+
+ <entry><code>x.age &gt;= $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>LessThan</literal></entry>
+
+ <entry><methodname>findByAgeLessThan(int
+ age)</methodname></entry>
+
+ <entry><code>x.age &lt; $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>LessThanEqual</literal></entry>
+
+ <entry><methodname>findByAgeLessThanEqual(int
+ age)</methodname></entry>
+
+ <entry><code>x.age &lt;= $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>IsNotNull</literal>,
+ <literal>NotNull</literal></entry>
+
+ <entry><methodname>findByFirstnameNotNull()</methodname></entry>
+
+ <entry><code>x.firstname =! NULL</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>IsNull</literal>,
+ <literal>Null</literal></entry>
+
+ <entry><methodname>findByFirstnameNull()</methodname></entry>
+
+ <entry><code>x.firstname = NULL</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>In</literal></entry>
+
+ <entry><methodname>findByFirstnameIn(Collection&lt;String&gt;
+ x)</methodname></entry>
+
+ <entry><code>x.firstname IN SET $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>NotIn</literal></entry>
+
+ <entry><methodname>findByFirstnameNotIn(Collection&lt;String&gt;
+ x)</methodname></entry>
+
+ <entry><code>x.firstname NOT IN SET $1</code></entry>
+ </row>
+
+ <row>
+ <entry>(No keyword)</entry>
+
+ <entry><methodname>findByFirstname(String
+ name)</methodname></entry>
+
+ <entry><code>x.firstname = $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>Not</literal></entry>
+
+ <entry><methodname>findByFirstnameNot(String
+ name)</methodname></entry>
+
+ <entry><code>x.firstname != $1</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>IsTrue</literal>,
+ <literal>True</literal></entry>
+
+ <entry><code>findByActiveIsTrue()</code></entry>
+
+ <entry><code>x.active = true</code></entry>
+ </row>
+
+ <row>
+ <entry><literal>IsFalse</literal>,
+ <literal>False</literal></entry>
+
+ <entry><code>findByActiveIsFalse()</code></entry>
+
+ <entry><code>x.active = false</code></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table></para>
+ </section>
+</chapter>
View
2 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
View
3 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 @@ public void init() {
registerBeanDefinitionParser("transaction-manager", new TransactionManagerParser());
registerBeanDefinitionParser("cq-listener-container", new GemfireListenerContainerParser());
+
+ registerBeanDefinitionParser("repositories", new GemfireRepositoryParser());
}
}
View
50 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<GemfirePersistentEntity<?>, GemfirePersistentProperty> {
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
+ */
+ @Override
+ protected <T> GemfirePersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
+ return new GemfirePersistentEntity<T>(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);
+ }
+}
View
57 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<T> extends BasicPersistentEntity<T, GemfirePersistentProperty> {
+
+ private final String regionName;
+
+ /**
+ * Creates a new {@link GemfirePersistentEntity} for the given {@link TypeInformation}.
+ *
+ * @param information must not be {@literal null}.
+ */
+ public GemfirePersistentEntity(TypeInformation<T> information) {
+
+ super(information);
+
+ Class<T> 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;
+ }
+}
View
52 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<GemfirePersistentProperty> {
+
+ /**
+ * @param field
+ * @param propertyDescriptor
+ * @param owner
+ * @param simpleTypeHolder
+ */
+ public GemfirePersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
+ PersistentEntity<?, GemfirePersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
+ super(field, propertyDescriptor, owner, simpleTypeHolder);
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
+ */
+ @Override
+ protected Association<GemfirePersistentProperty> createAssociation() {
+ return null;
+ }
+}
View
51 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<GemfirePersistentProperty> {
+
+ 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> T getPropertyValue(GemfirePersistentProperty property) {
+ return (T) reader.readObject(property.getName());
+ }
+}
View
156 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<Class<?>, 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<GemfirePersistentProperty> provider = new PersistentEntityParameterValueProvider<GemfirePersistentProperty>(
+ entity, propertyValueProvider);
+ provider.setSpELEvaluator(new DefaultSpELExpressionEvaluator(reader, context));
+
+ Object instance = instantiator.createInstance(entity, provider);
+
+ final BeanWrapper<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(instance, conversionService);
+
+ entity.doWithProperties(new PropertyHandler<GemfirePersistentProperty>() {
+ 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<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(value, conversionService);
+
+ entity.doWithProperties(new PropertyHandler<GemfirePersistentProperty>() {
+ 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;
+ }
+}
View
78 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();
+ }
+}
View
42 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 "";
+}
View
98 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<Region<?, ?>> {
+
+ private final Map<String, Region<?, ?>> regions;
+ private final MappingContext<? extends GemfirePersistentEntity<?>, ?> 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<Region<?, ?>> regions, MappingContext<? extends GemfirePersistentEntity<?>, ?> context) {
+
+ Assert.notNull(regions);
+ Assert.notNull(context);
+
+ Map<String, com.gemstone.gemfire.cache.Region<?, ?>> regionMap = new HashMap<String, Region<?, ?>>();
+
+ 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 <T> Region<?, T> getRegion(Class<T> type) {
+
+ Assert.notNull(type);
+
+ GemfirePersistentEntity<?> entity = context.getPersistentEntity(type);
+ return (Region<?, T>) (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 <S, T> Region<S, T> getRegion(String name) {
+
+ Assert.notNull(name);
+
+ return (Region<S, T>) regions.get(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Iterable#iterator()
+ */
+ @Override
+ public Iterator<Region<?, ?>> iterator() {
+ return regions.values().iterator();
+ }
+}
View
30 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<T, ID extends Serializable> extends CrudRepository<T, ID> {
+
+ T save(Wrapper<T, ID> wrapper);
+}
View
34 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 "";
+}
View
95 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<T, KEY extends Serializable> {
+
+ 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;
+ }
+}
View
40 ...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<SimpleGemfireRepositoryConfiguration, GemfireRepositoryConfiguration> {
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser#getGlobalRepositoryConfigInformation(org.w3c.dom.Element)
+ */
+ @Override
+ protected SimpleGemfireRepositoryConfiguration getGlobalRepositoryConfigInformation(Element element) {
+ return new SimpleGemfireRepositoryConfiguration(element);
+ }
+}
View
135 .../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<GemfireRepositoryConfiguration, SimpleGemfireRepositoryConfiguration> {
+
+ 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<SimpleGemfireRepositoryConfiguration> {
+
+ String getGemfireTemplateRef();
+ }
+
+ static class ManualGemfireRepositoryConfiguration extends
+ ManualRepositoryConfigInformation<SimpleGemfireRepositoryConfiguration> 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<SimpleGemfireRepositoryConfiguration> 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();
+ }
+ }
+}
View
5 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;
+
View
52 ...va/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<T, ID extends Serializable> extends DelegatingEntityInformation<T, ID>
+ implements GemfireEntityInformation<T, ID> {
+
+ private final GemfirePersistentEntity<T> entity;
+
+ /**
+ * Creates a new {@link DefaultGemfireEntityInformation}.
+ *
+ * @param domainClass must not be {@literal null}.
+ */
+ public DefaultGemfireEntityInformation(GemfirePersistentEntity<T> entity) {
+ super(new ReflectionEntityInformation<T, ID>(entity.getType()));
+ this.entity = entity;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.gemfire.repository.query.GemfireEntityInformation#getRegionName()
+ */
+ @Override
+ public String getRegionName() {
+ return entity.getRegionName();
+ }
+}
View
37 ...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<T, ID extends Serializable> extends EntityInformation<T, ID> {
+
+ /**
+ * Returns the name of the {@link Region} the entity is held in.
+ *
+ * @return
+ */
+ String getRegionName();
+}
View
143 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<QueryString, Predicates> {
+
+ private static final Log LOG = LogFactory.getLog(GemfireQueryCreator.class);
+
+ private final QueryBuilder query;
+ private Iterator<Integer> 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<Object> 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<Object> 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<Integer> {
+
+ 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();
+ }
+ }
+}
View
86 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<? extends GemfirePersistentEntity<?>, 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;
+ }
+}
View
50 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;
+ }
+}
View
68 ...ava/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);
+ }
+}
View
6 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);
+}
View
173 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<Integer> 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<Integer> 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<Integer> 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));
+ }
+ }
+ }
+}
View
53 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;
+ }
+}
View
118 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<Integer> getInParameterIndexes() {
+
+ Pattern pattern = Pattern.compile(IN_PARAMETER_PATTERN);
+ Matcher matcher = pattern.matcher(query);
+ List<Integer> result = new ArrayList<Integer>();
+
+ while (matcher.find()) {
+ result.add(Integer.parseInt(matcher.group()));
+ }
+
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return query;
+ }
+}
View
103 .../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<Integer> 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);
+ }
+}
View
147 ...in/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.
+ */
+