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).
This commit is contained in:
Oliver Gierke
2012-01-18 16:27:08 +00:00
parent 1a651e739b
commit 40a0a7f2a6
54 changed files with 4868 additions and 3 deletions

View File

@@ -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());
}
}

View File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -0,0 +1,5 @@
/**
* Implementations of Spring Data COmmons Core repository abstraction.
*/
package org.springframework.data.gemfire.repository;

View File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -0,0 +1,6 @@
package org.springframework.data.gemfire.repository.query;
interface Predicate {
String toString(String alias);
}

View File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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 File

@@ -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<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> context;
private final Regions regions;
/**
* Creates a new {@link GemfireRepositoryFactory}.
*
* @param regions must not be {@literal null}.
* @param context
*/
public GemfireRepositoryFactory(Iterable<Region<?, ?>> regions,
MappingContext<? extends GemfirePersistentEntity<?>, 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 <T, ID extends Serializable> GemfireEntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
GemfirePersistentEntity<T> entity = (GemfirePersistentEntity<T>) context.getPersistentEntity(domainClass);
return new DefaultGemfireEntityInformation<T, ID>(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<?, Serializable> 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);
}
};
}
}

View File

@@ -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<T extends Repository<S, ID>, S, ID extends Serializable> extends
RepositoryFactoryBeanSupport<T, S, ID> implements ApplicationContextAware {
private MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> context;
private Iterable<Region<?, ?>> 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<Region> regions = applicationContext.getBeansOfType(Region.class).values();
this.regions = Collections.unmodifiableCollection(regions);
}
/**
* @param context the context to set
*/
public void setMappingContext(MappingContext<? extends GemfirePersistentEntity<?>, 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);
}
}

View File

@@ -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<T, ID extends Serializable> implements GemfireRepository<T, ID> {
private final GemfireTemplate template;
private final EntityInformation<T, ID> 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<T, ID> 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<T> save(Iterable<? extends T> entities) {
List<T> result = new ArrayList<T>();
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<T> findAll() {
return template.execute(new GemfireCallback<Collection<T>>() {
@SuppressWarnings({ "rawtypes", "unchecked" })
public Collection<T> doInGemfire(Region region) {
return region.values();
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
*/
@Override
@SuppressWarnings("unchecked")
public Collection<T> findAll(Iterable<ID> ids) {
List<ID> parameters = new ArrayList<ID>();
for (ID id : ids) {
parameters.add(id);
}
return (Collection<T>) template.getAll(parameters).values();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.Repository#count()
*/
public long count() {
return template.execute(new GemfireCallback<Long>() {
@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<? extends T> entities) {
for (T entity : entities) {
delete(entity);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.Repository#deleteAll()
*/
public void deleteAll() {
template.execute(new GemfireCallback<Void>() {
@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<T, ID> wrapper) {
return template.put(wrapper.getKey(), wrapper.getEntity());
}
}