Java tutorial
/* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.gemfire.repository.support; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Optional; import org.apache.geode.cache.Region; import org.springframework.data.gemfire.GemfireTemplate; 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.projection.ProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryInformation; 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.QueryMethod; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * {@link RepositoryFactorySupport} implementation creating repository proxies * for Gemfire. * * @author Oliver Gierke * @author David Turanski * @author John Blum */ public class GemfireRepositoryFactory extends RepositoryFactorySupport { private static final Class<org.springframework.data.gemfire.mapping.annotation.Region> REGION_ANNOTATION = org.springframework.data.gemfire.mapping.annotation.Region.class; static final String REGION_NOT_FOUND = "Region [%1$s] for Domain Type [%2$s] using Repository [%3$s] was not found;" + " You must configure a Region with name [%1$s] in the application context"; static final String REGION_REPOSITORY_ID_TYPE_MISMATCH = "Region [%1$s] requires keys of type [%2$s], but Repository [%3$s] declared an id of type [%4$s]"; static final String REPOSITORY_ENTITY_ID_TYPE_MISMATCH = "Repository [%1$s] declared an id of type [%2$s], but entity [%3$s] has an id of type [%4$s]"; private final MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> mappingContext; private final Regions regions; /** * Constructs a new instance of {@link GemfireRepositoryFactory} initialized with the given collection * of configured {@link Region Regions} and the {@link MappingContext}. * * @param regions {@link Iterable} collection of configured {@link Region Regions} used by this application; * must not be {@literal null}. * @param mappingContext {@link MappingContext} used to map entities to the underlying data store, * must not be {@literal null}. * @throws IllegalArgumentException if either {@link Regions} or the {@link MappingContext} is {@literal null}. * @see org.springframework.data.gemfire.mapping.GemfireMappingContext * @see org.springframework.data.gemfire.mapping.Regions * @see org.springframework.data.mapping.context.MappingContext */ public GemfireRepositoryFactory(@NonNull Iterable<Region<?, ?>> regions, @NonNull MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> mappingContext) { Assert.notNull(regions, "Regions are required"); Assert.notNull(mappingContext, "MappingContext is required"); this.regions = new Regions(regions, mappingContext); this.mappingContext = mappingContext; } /** * Returns a reference to the Pivotal GemFire {@link MappingContext} used to provide mapping meta-data * between {@link Class entity types} and the data store. * * @return a reference to the Pivotal GemFire {@link MappingContext}. * @see org.springframework.data.gemfire.mapping.GemfireMappingContext * @see org.springframework.data.mapping.context.MappingContext */ protected MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> getMappingContext() { return this.mappingContext; } /** * Returns a reference to the configured, application-defined {@link Region Regions}. * * @return a reference to the configured, application-defined {@link Region Regions}. * @see org.springframework.data.gemfire.mapping.Regions */ protected Regions getRegions() { return this.regions; } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class) */ @Override public <T, ID> GemfireEntityInformation<T, ID> getEntityInformation(Class<T> domainClass) { return new DefaultGemfireEntityInformation<>(resolvePersistentEntity(domainClass)); } /* * (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#getTargetRepository(org.springframework.data.repository.core.RepositoryInformation) */ @Override protected Object getTargetRepository(RepositoryInformation repositoryInformation) { GemfireEntityInformation<?, Serializable> entityInformation = getEntityInformation( repositoryInformation.getDomainType()); GemfireTemplate gemfireTemplate = newTemplate(repositoryInformation); return getTargetRepositoryViaReflection(repositoryInformation, gemfireTemplate, entityInformation); } /** * Constructs a new instance of {@link GemfireTemplate} initialized with the identified {@link Region} * used to back all persistent, data access operations defined by the {@link Repository}. * * @param repositoryMetadata {@link RepositoryMetadata} containing meta-data about the {@link Repository}. * @return a new instance of {@link GemfireTemplate} initialized with the identified {@link Region}. * @see #resolvePersistentEntity(Class) * @see #resolveRegion(RepositoryMetadata, GemfirePersistentEntity) * @see #validate(RepositoryMetadata, GemfirePersistentEntity, Region) * @see org.springframework.data.repository.core.RepositoryMetadata * @see org.springframework.data.gemfire.GemfireTemplate * @see org.apache.geode.cache.Region */ protected GemfireTemplate newTemplate(RepositoryMetadata repositoryMetadata) { GemfirePersistentEntity<?> entity = resolvePersistentEntity(repositoryMetadata.getDomainType()); return new GemfireTemplate(validate(repositoryMetadata, entity, resolveRegion(repositoryMetadata, entity))); } @Nullable @SuppressWarnings("unchecked") private <T> GemfirePersistentEntity<T> resolvePersistentEntity(Class<?> domainType) { return (GemfirePersistentEntity<T>) getMappingContext().getPersistentEntity(domainType); } private Region<?, ?> resolveRegion(RepositoryMetadata repositoryMetadata, GemfirePersistentEntity entity) { String resolvedRegionName = getRepositoryRegionName(repositoryMetadata) .orElseGet(() -> getEntityRegionName(repositoryMetadata, entity)); return resolveRegion(repositoryMetadata, resolvedRegionName); } private Region<?, ?> validate(RepositoryMetadata repositoryMetadata, GemfirePersistentEntity<?> entity, Region<?, ?> region) { Assert.notNull(region, "Region is required"); Class<?> repositoryIdType = repositoryMetadata.getIdType(); Optional.ofNullable(region.getAttributes().getKeyConstraint()) .ifPresent(regionKeyType -> Assert.isTrue(regionKeyType.isAssignableFrom(repositoryIdType), () -> String.format(REGION_REPOSITORY_ID_TYPE_MISMATCH, region.getFullPath(), regionKeyType.getName(), repositoryMetadata.getRepositoryInterface().getName(), repositoryIdType.getName()))); Optional.ofNullable(entity).map(GemfirePersistentEntity::getIdProperty) .ifPresent(entityIdProperty -> Assert.isTrue( repositoryIdType.isAssignableFrom(entityIdProperty.getType()), () -> String.format(REPOSITORY_ENTITY_ID_TYPE_MISMATCH, repositoryMetadata.getRepositoryInterface().getName(), repositoryIdType.getName(), entityIdProperty.getOwner().getName(), entityIdProperty.getTypeName()))); return region; } String getEntityRegionName(@NonNull RepositoryMetadata repositoryMetadata, @Nullable GemfirePersistentEntity entity) { Optional<GemfirePersistentEntity> optionalEntity = Optional.ofNullable(entity); return optionalEntity.map(GemfirePersistentEntity::getRegionName).filter(StringUtils::hasText) .orElseGet(() -> optionalEntity.map(GemfirePersistentEntity::getType).map(Class::getSimpleName) .orElseGet(() -> repositoryMetadata.getDomainType().getSimpleName())); } Optional<String> getRepositoryRegionName(@NonNull RepositoryMetadata repositoryMetadata) { return Optional.ofNullable(repositoryMetadata).map(RepositoryMetadata::getRepositoryInterface) .filter(repositoryInterface -> repositoryInterface.isAnnotationPresent(REGION_ANNOTATION)) .map(repositoryInterface -> repositoryInterface.getAnnotation(REGION_ANNOTATION)) .map(regionAnnotation -> regionAnnotation.value()).filter(StringUtils::hasText); } Region<?, ?> resolveRegion(@NonNull RepositoryMetadata repositoryMetadata, String regionNamePath) { return Optional.ofNullable(getRegions().getRegion(regionNamePath)) .orElseThrow(() -> newIllegalStateException(REGION_NOT_FOUND, regionNamePath, repositoryMetadata.getDomainType().getName(), repositoryMetadata.getRepositoryInterface().getName())); } /* * (non-Javadoc) * * @see org.springframework.data.repository.core.support.RepositoryFactorySupport * #getQueryLookupStrategy(Key, EvaluationContextProvider) */ @Override protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) { return Optional.of((Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory projectionFactory, NamedQueries namedQueries) -> { GemfireQueryMethod queryMethod = newQueryMethod(method, repositoryMetadata, projectionFactory, evaluationContextProvider); GemfireTemplate template = newTemplate(repositoryMetadata); if (queryMethod.hasAnnotatedQuery()) { return new StringBasedGemfireRepositoryQuery(queryMethod, template).asUserDefinedQuery(); } String namedQueryName = queryMethod.getNamedQueryName(); if (namedQueries.hasQuery(namedQueryName)) { return new StringBasedGemfireRepositoryQuery(namedQueries.getQuery(namedQueryName), queryMethod, template).asUserDefinedQuery(); } return new PartTreeGemfireRepositoryQuery(queryMethod, template); }); } @SuppressWarnings({ "unchecked", "unused" }) protected <T extends QueryMethod> T newQueryMethod(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory projectionFactory, QueryMethodEvaluationContextProvider evaluationContextProvider) { return (T) new GemfireQueryMethod(method, repositoryMetadata, projectionFactory, getMappingContext()); } }