DATACOUCH-519 - Reinstate template auditing
This reinstates the auditing that was present before the major refactoring. Additional testing for createdBy, createdDate, lastModifiedBy, lastModifiedDate has been added as well.
This commit is contained in:
committed by
Michael Nitschinger
parent
ebb8d39afd
commit
e8e13ffebc
@@ -23,6 +23,7 @@ package org.springframework.data.couchbase.config;
|
||||
*
|
||||
* @author Michael Nitschinger
|
||||
* @author Simon Baslé
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public class BeanNames {
|
||||
|
||||
@@ -48,4 +49,8 @@ public class BeanNames {
|
||||
*/
|
||||
public static final String COUCHBASE_MAPPING_CONTEXT = "couchbaseMappingContext";
|
||||
|
||||
/**
|
||||
* The name for the bean that will handle audit trail marking of entities.
|
||||
*/
|
||||
public static final String COUCHBASE_AUDITING_HANDLER = "couchbaseAuditingHandler";
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.*;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.couchbase.CouchbaseClientFactory;
|
||||
@@ -33,6 +31,14 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
import com.couchbase.client.java.Collection;
|
||||
|
||||
/**
|
||||
* Implements Couchbase operations
|
||||
* findBy, insertBy, upsertBy, replaceBy, removeBy, existsBy
|
||||
*
|
||||
* @author Michael Nitschinger
|
||||
* @author Michael Reiche
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContextAware {
|
||||
|
||||
private final CouchbaseClientFactory clientFactory;
|
||||
@@ -161,6 +167,8 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
prepareIndexCreator(applicationContext);
|
||||
templateSupport.setApplicationContext(applicationContext);
|
||||
reactiveCouchbaseTemplate.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
private void prepareIndexCreator(ApplicationContext context) {
|
||||
|
||||
@@ -16,23 +16,41 @@
|
||||
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
|
||||
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.core.mapping.event.*;
|
||||
import org.springframework.data.couchbase.repository.support.MappingCouchbaseEntityInformation;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class CouchbaseTemplateSupport {
|
||||
/**
|
||||
* encode/decode support for CouchbaseTemplate
|
||||
*
|
||||
* @author Michael Nitschinger
|
||||
* @author Michael Reiche
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CouchbaseTemplateSupport implements ApplicationContextAware {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CouchbaseTemplateSupport.class);
|
||||
|
||||
private final CouchbaseConverter converter;
|
||||
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
|
||||
// TODO: this should be replaced I think
|
||||
private final TranslationService translationService;
|
||||
private ApplicationContext applicationContext;
|
||||
EntityCallbacks entityCallbacks;
|
||||
|
||||
public CouchbaseTemplateSupport(final CouchbaseConverter converter) {
|
||||
this.converter = converter;
|
||||
@@ -41,8 +59,12 @@ public class CouchbaseTemplateSupport {
|
||||
}
|
||||
|
||||
public CouchbaseDocument encodeEntity(final Object entityToEncode) {
|
||||
maybeEmitEvent(new BeforeConvertEvent<>(entityToEncode));
|
||||
Object maybeNewEntity = maybeCallBeforeConvert(entityToEncode, "");
|
||||
final CouchbaseDocument converted = new CouchbaseDocument();
|
||||
converter.write(entityToEncode, converted);
|
||||
converter.write(maybeNewEntity, converted);
|
||||
maybeCallAfterConvert(entityToEncode, converted, "");
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted));
|
||||
return converted;
|
||||
}
|
||||
|
||||
@@ -52,7 +74,8 @@ public class CouchbaseTemplateSupport {
|
||||
|
||||
T readEntity = converter.read(entityClass, (CouchbaseDocument) translationService.decode(source, converted));
|
||||
final ConvertingPropertyAccessor<T> accessor = getPropertyAccessor(readEntity);
|
||||
CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(readEntity.getClass());
|
||||
CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(
|
||||
readEntity.getClass());
|
||||
|
||||
if (persistentEntity.getVersionProperty() != null) {
|
||||
accessor.setProperty(persistentEntity.getVersionProperty(), cas);
|
||||
@@ -62,7 +85,8 @@ public class CouchbaseTemplateSupport {
|
||||
|
||||
public void applyUpdatedCas(final Object entity, final long cas) {
|
||||
final ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(entity);
|
||||
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(entity.getClass());
|
||||
final CouchbasePersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(
|
||||
entity.getClass());
|
||||
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
|
||||
|
||||
if (versionProperty != null) {
|
||||
@@ -82,4 +106,66 @@ public class CouchbaseTemplateSupport {
|
||||
return new ConvertingPropertyAccessor<>(accessor, converter.getConversionService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
if (entityCallbacks == null) {
|
||||
setEntityCallbacks(EntityCallbacks.create(applicationContext));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link EntityCallbacks} instance to use when invoking
|
||||
* {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link BeforeConvertCallback}.
|
||||
* <p/>
|
||||
* Overrides potentially existing {@link EntityCallbacks}.
|
||||
*
|
||||
* @param entityCallbacks must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if the given instance is {@literal null}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
|
||||
Assert.notNull(entityCallbacks, "EntityCallbacks must not be null!");
|
||||
this.entityCallbacks = entityCallbacks;
|
||||
}
|
||||
|
||||
void maybeEmitEvent(CouchbaseMappingEvent<?> event) {
|
||||
if (canPublishEvent()) {
|
||||
try {
|
||||
this.applicationContext.publishEvent(event);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
LOG.info("maybeEmitEvent called, but CouchbaseTemplate not initialized with applicationContext");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean canPublishEvent() {
|
||||
return this.applicationContext != null;
|
||||
}
|
||||
|
||||
protected <T> T maybeCallBeforeConvert(T object, String collection) {
|
||||
if (entityCallbacks != null) {
|
||||
try {
|
||||
return entityCallbacks.callback(BeforeConvertCallback.class, object, collection);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
LOG.info("maybeCallBeforeConvert called, but CouchbaseTemplate not initialized with applicationContext");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
protected <T> T maybeCallAfterConvert(T object, CouchbaseDocument document, String collection) {
|
||||
if (null != entityCallbacks) {
|
||||
return entityCallbacks.callback(AfterConvertCallback.class, object, document, collection);
|
||||
} else {
|
||||
LOG.info("maybeCallAfterConvert called, but CouchbaseTemplate not initialized with applicationContext");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.couchbase.CouchbaseClientFactory;
|
||||
@@ -23,7 +27,13 @@ import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
|
||||
import com.couchbase.client.java.Collection;
|
||||
|
||||
public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations {
|
||||
/**
|
||||
* template class for Reactive Couchbase operations
|
||||
*
|
||||
* @author Michael Nitschinger
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, ApplicationContextAware {
|
||||
|
||||
private final CouchbaseClientFactory clientFactory;
|
||||
private final CouchbaseConverter converter;
|
||||
@@ -132,4 +142,9 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations {
|
||||
return resolved == null ? ex : resolved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
templateSupport.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
|
||||
@@ -50,6 +52,7 @@ import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
|
||||
@@ -63,6 +66,7 @@ import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A mapping converter for Couchbase. The converter is responsible for reading from and writing to entities and
|
||||
@@ -72,6 +76,7 @@ import org.springframework.util.CollectionUtils;
|
||||
* @author Oliver Gierke
|
||||
* @author Geoffrey Mina
|
||||
* @author Mark Paluch
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implements ApplicationContextAware {
|
||||
|
||||
@@ -104,6 +109,12 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
*/
|
||||
protected CouchbaseTypeMapper typeMapper;
|
||||
|
||||
/**
|
||||
* Callbacks for Audit Mechanism
|
||||
*/
|
||||
private @Nullable
|
||||
EntityCallbacks entityCallbacks;
|
||||
|
||||
public MappingCouchbaseConverter() {
|
||||
super(new DefaultConversionService());
|
||||
|
||||
@@ -127,7 +138,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
* attribute.
|
||||
*
|
||||
* @param mappingContext the mapping context to use.
|
||||
* @param typeKey the attribute name to use to store complex types class name.
|
||||
* @param typeKey the attribute name to use to store complex types class name.
|
||||
*/
|
||||
public MappingCouchbaseConverter(
|
||||
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
|
||||
@@ -156,7 +167,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Check if one class is a subtype of the other.
|
||||
*
|
||||
* @param left the first class.
|
||||
* @param left the first class.
|
||||
* @param right the second class.
|
||||
* @return true if it is a subtype, false otherwise.
|
||||
*/
|
||||
@@ -182,9 +193,9 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Read an incoming {@link CouchbaseDocument} into the target entity.
|
||||
*
|
||||
* @param type the type information of the target entity.
|
||||
* @param type the type information of the target entity.
|
||||
* @param source the document to convert.
|
||||
* @param <R> the entity type.
|
||||
* @param <R> the entity type.
|
||||
* @return the converted entity.
|
||||
*/
|
||||
protected <R> R read(final TypeInformation<R> type, final CouchbaseDocument source) {
|
||||
@@ -194,10 +205,10 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Read an incoming {@link CouchbaseDocument} into the target entity.
|
||||
*
|
||||
* @param type the type information of the target entity.
|
||||
* @param type the type information of the target entity.
|
||||
* @param source the document to convert.
|
||||
* @param parent an optional parent object.
|
||||
* @param <R> the entity type.
|
||||
* @param <R> the entity type.
|
||||
* @return the converted entity.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -217,8 +228,8 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
return (R) readMap(typeToUse, source, parent);
|
||||
}
|
||||
|
||||
CouchbasePersistentEntity<R> entity = (CouchbasePersistentEntity<R>) mappingContext
|
||||
.getRequiredPersistentEntity(typeToUse);
|
||||
CouchbasePersistentEntity<R> entity = (CouchbasePersistentEntity<R>) mappingContext.getRequiredPersistentEntity(
|
||||
typeToUse);
|
||||
return read(entity, source, parent);
|
||||
}
|
||||
|
||||
@@ -232,10 +243,11 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
* @param entity the target entity.
|
||||
* @param source the document to convert.
|
||||
* @param parent an optional parent object.
|
||||
* @param <R> the entity type.
|
||||
* @param <R> the entity type.
|
||||
* @return the converted entity.
|
||||
*/
|
||||
protected <R> R read(final CouchbasePersistentEntity<R> entity, final CouchbaseDocument source, final Object parent) {
|
||||
protected <R> R read(final CouchbasePersistentEntity<R> entity, final CouchbaseDocument source,
|
||||
final Object parent) {
|
||||
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(source, spELContext);
|
||||
ParameterValueProvider<CouchbasePersistentProperty> provider = getParameterProvider(entity, source, evaluator,
|
||||
parent);
|
||||
@@ -247,8 +259,8 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
|
||||
if (!doesPropertyExistInSource(prop) || entity.isConstructorArgument(prop) || isIdConstructionProperty(prop)
|
||||
|| prop.isAnnotationPresent(N1qlJoin.class)) {
|
||||
if (!doesPropertyExistInSource(prop) || entity.isConstructorArgument(prop) || isIdConstructionProperty(
|
||||
prop) || prop.isAnnotationPresent(N1qlJoin.class)) {
|
||||
return;
|
||||
}
|
||||
Object obj = prop.isIdProperty() ? source.getId() : getValueInternal(prop, source, instance);
|
||||
@@ -277,8 +289,8 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
* Loads the property value through the value provider.
|
||||
*
|
||||
* @param property the source property.
|
||||
* @param source the source document.
|
||||
* @param parent the optional parent.
|
||||
* @param source the source document.
|
||||
* @param parent the optional parent.
|
||||
* @return the actual property value.
|
||||
*/
|
||||
protected Object getValueInternal(final CouchbasePersistentProperty property, final CouchbaseDocument source,
|
||||
@@ -289,10 +301,10 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Creates a new parameter provider.
|
||||
*
|
||||
* @param entity the persistent entity.
|
||||
* @param source the source document.
|
||||
* @param entity the persistent entity.
|
||||
* @param source the source document.
|
||||
* @param evaluator the SPEL expression evaluator.
|
||||
* @param parent the optional parent.
|
||||
* @param parent the optional parent.
|
||||
* @return a new parameter value provider.
|
||||
*/
|
||||
private ParameterValueProvider<CouchbasePersistentProperty> getParameterProvider(
|
||||
@@ -309,7 +321,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Recursively parses the a map from the source document.
|
||||
*
|
||||
* @param type the type information for the document.
|
||||
* @param type the type information for the document.
|
||||
* @param source the source document.
|
||||
* @param parent the optional parent.
|
||||
* @return the recursively parsed map.
|
||||
@@ -350,7 +362,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Potentially convert simple values like ENUMs.
|
||||
*
|
||||
* @param value the value to convert.
|
||||
* @param value the value to convert.
|
||||
* @param target the target object.
|
||||
* @return the potentially converted object.
|
||||
*/
|
||||
@@ -394,15 +406,16 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
|
||||
writeInternal(source, target, type);
|
||||
if (target.getId() == null) {
|
||||
throw new MappingException("An ID property is needed, but not found/could not be generated on this entity.");
|
||||
throw new MappingException(
|
||||
"An ID property is needed, but not found/could not be generated on this entity.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a source object into a {@link CouchbaseDocument} target.
|
||||
*
|
||||
* @param source the source object.
|
||||
* @param target the target document.
|
||||
* @param source the source object.
|
||||
* @param target the target document.
|
||||
* @param typeHint the type information for the source.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -552,7 +565,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
*
|
||||
* @param source the source object.
|
||||
* @param target the target document.
|
||||
* @param prop the property information.
|
||||
* @param prop the property information.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void writePropertyInternal(final Object source, final CouchbaseDocument target,
|
||||
@@ -577,6 +590,18 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueType.getType().equals(java.util.Optional.class)) {
|
||||
if (source == null)
|
||||
return;
|
||||
Optional<String> o = (Optional<String>) source;
|
||||
if (o.isPresent()) {
|
||||
writeSimpleInternal(o.get(), target, prop.getFieldName());
|
||||
} else {
|
||||
writeSimpleInternal(null, target, prop.getFieldName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Class<?>> basicTargetType = conversions.getCustomWriteTarget(source.getClass());
|
||||
if (basicTargetType.isPresent()) {
|
||||
|
||||
@@ -590,9 +615,9 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
CouchbaseDocument propertyDoc = new CouchbaseDocument();
|
||||
addCustomTypeKeyIfNecessary(type, source, propertyDoc);
|
||||
|
||||
CouchbasePersistentEntity<?> entity = isSubtype(prop.getType(), source.getClass())
|
||||
? mappingContext.getRequiredPersistentEntity(source.getClass())
|
||||
: mappingContext.getRequiredPersistentEntity(type);
|
||||
CouchbasePersistentEntity<?> entity = isSubtype(prop.getType(), source.getClass()) ?
|
||||
mappingContext.getRequiredPersistentEntity(source.getClass()) :
|
||||
mappingContext.getRequiredPersistentEntity(type);
|
||||
writeInternal(source, propertyDoc, entity);
|
||||
target.put(name, propertyDoc);
|
||||
}
|
||||
@@ -600,7 +625,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
/**
|
||||
* Wrapper method to create the underlying map.
|
||||
*
|
||||
* @param map the source map.
|
||||
* @param map the source map.
|
||||
* @param prop the persistent property.
|
||||
* @return the written couchbase document.
|
||||
*/
|
||||
@@ -616,7 +641,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
*
|
||||
* @param source the source object.
|
||||
* @param target the target document.
|
||||
* @param type the type information for the document.
|
||||
* @param type the type information for the document.
|
||||
* @return the written couchbase document.
|
||||
*/
|
||||
private CouchbaseDocument writeMapInternal(final Map<Object, Object> source, final CouchbaseDocument target,
|
||||
@@ -635,7 +660,9 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
new CouchbaseList(conversions.getSimpleTypeHolder()), type.getMapValueType()));
|
||||
} else {
|
||||
CouchbaseDocument embeddedDoc = new CouchbaseDocument();
|
||||
TypeInformation<?> valueTypeInfo = type.isMap() ? type.getMapValueType() : ClassTypeInformation.OBJECT;
|
||||
TypeInformation<?> valueTypeInfo = type.isMap() ?
|
||||
type.getMapValueType() :
|
||||
ClassTypeInformation.OBJECT;
|
||||
writeInternal(val, embeddedDoc, valueTypeInfo);
|
||||
target.put(simpleKey, embeddedDoc);
|
||||
}
|
||||
@@ -651,7 +678,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
* Helper method to create the underlying collection/list.
|
||||
*
|
||||
* @param collection the collection to write.
|
||||
* @param prop the property information.
|
||||
* @param prop the property information.
|
||||
* @return the created couchbase list.
|
||||
*/
|
||||
private CouchbaseList createCollection(final Collection<?> collection, final CouchbasePersistentProperty prop) {
|
||||
@@ -664,7 +691,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
*
|
||||
* @param source the source object.
|
||||
* @param target the target document.
|
||||
* @param type the type information for the document.
|
||||
* @param type the type information for the document.
|
||||
* @return the created couchbase list.
|
||||
*/
|
||||
private CouchbaseList writeCollectionInternal(final Collection<?> source, final CouchbaseList target,
|
||||
@@ -677,8 +704,8 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
if (elementType == null || conversions.isSimpleType(elementType)) {
|
||||
target.put(getPotentiallyConvertedSimpleWrite(element));
|
||||
} else if (element instanceof Collection || elementType.isArray()) {
|
||||
target.put(writeCollectionInternal(asCollection(element), new CouchbaseList(conversions.getSimpleTypeHolder()),
|
||||
componentType));
|
||||
target.put(writeCollectionInternal(asCollection(element),
|
||||
new CouchbaseList(conversions.getSimpleTypeHolder()), componentType));
|
||||
} else {
|
||||
|
||||
CouchbaseDocument embeddedDoc = new CouchbaseDocument();
|
||||
@@ -695,12 +722,13 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
* Read a collection from the source object.
|
||||
*
|
||||
* @param targetType the target type.
|
||||
* @param source the list as source.
|
||||
* @param parent the optional parent.
|
||||
* @param source the list as source.
|
||||
* @param parent the optional parent.
|
||||
* @return the instantiated collection.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object readCollection(final TypeInformation<?> targetType, final CouchbaseList source, final Object parent) {
|
||||
private Object readCollection(final TypeInformation<?> targetType, final CouchbaseList source,
|
||||
final Object parent) {
|
||||
Assert.notNull(targetType, "Target type must not be null!");
|
||||
|
||||
Class<?> collectionType = targetType.getType();
|
||||
@@ -709,8 +737,9 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
}
|
||||
|
||||
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
|
||||
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
|
||||
: CollectionFactory.createCollection(collectionType, source.size(false));
|
||||
Collection<Object> items = targetType.getType().isArray() ?
|
||||
new ArrayList<Object>() :
|
||||
CollectionFactory.createCollection(collectionType, source.size(false));
|
||||
TypeInformation<?> componentType = targetType.getComponentType();
|
||||
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
|
||||
|
||||
@@ -735,7 +764,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
*
|
||||
* @param source the source object.
|
||||
* @param target the target document.
|
||||
* @param key the key of the object.
|
||||
* @param key the key of the object.
|
||||
*/
|
||||
private void writeSimpleInternal(final Object source, final CouchbaseDocument target, final String key) {
|
||||
target.put(key, getPotentiallyConvertedSimpleWrite(source));
|
||||
@@ -748,14 +777,14 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
|
||||
Optional<Class<?>> customTarget = conversions.getCustomWriteTarget(value.getClass());
|
||||
|
||||
return customTarget.map(it -> (Object) conversionService.convert(value, it))
|
||||
.orElseGet(() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value);
|
||||
return customTarget.map(it -> (Object) conversionService.convert(value, it)).orElseGet(
|
||||
() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom type key if needed.
|
||||
*
|
||||
* @param type the type information.
|
||||
* @param type the type information.
|
||||
* @param source th the source object.
|
||||
* @param target the target document.
|
||||
*/
|
||||
@@ -772,15 +801,34 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
if (entityCallbacks == null) {
|
||||
setEntityCallbacks(EntityCallbacks.create(applicationContext));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* COPIED
|
||||
* Set the {@link EntityCallbacks} instance to use when invoking
|
||||
* {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link AfterConvertCallback}.
|
||||
* <p/>
|
||||
* Overrides potentially existing {@link EntityCallbacks}.
|
||||
*
|
||||
* @param entityCallbacks must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if the given instance is {@literal null}.
|
||||
* @since 3.0
|
||||
*/
|
||||
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
|
||||
Assert.notNull(entityCallbacks, "EntityCallbacks must not be null!");
|
||||
this.entityCallbacks = entityCallbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to read the value based on the value type.
|
||||
*
|
||||
* @param value the value to convert.
|
||||
* @param type the type information.
|
||||
* @param value the value to convert.
|
||||
* @param type the type information.
|
||||
* @param parent the optional parent.
|
||||
* @param <R> the target type.
|
||||
* @param <R> the target type.
|
||||
* @return the converted object.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -913,8 +961,8 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|
||||
private final Object parent;
|
||||
|
||||
public ConverterAwareSpELExpressionParameterValueProvider(final SpELExpressionEvaluator evaluator,
|
||||
final ConversionService conversionService, final ParameterValueProvider<CouchbasePersistentProperty> delegate,
|
||||
final Object parent) {
|
||||
final ConversionService conversionService,
|
||||
final ParameterValueProvider<CouchbasePersistentProperty> delegate, final Object parent) {
|
||||
super(evaluator, conversionService, delegate);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2020 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.couchbase.core.mapping.event;
|
||||
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.mapping.callback.EntityCallback;
|
||||
|
||||
/**
|
||||
* Callback being invoked after a domain object is materialized from a {@link Document} when reading results.
|
||||
*
|
||||
* @author Roman Puchkovskiy
|
||||
* @author Mark Paluch
|
||||
* @author Michael Reiche
|
||||
* @see org.springframework.data.mapping.callback.EntityCallbacks
|
||||
* @since 3.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AfterConvertCallback<T> extends EntityCallback<T> {
|
||||
|
||||
/**
|
||||
* Entity callback method invoked after a domain object is materialized from a {@link Document}. Can return either the
|
||||
* same or a modified instance of the domain object.
|
||||
*
|
||||
* @param entity the domain object (the result of the conversion).
|
||||
* @param document must not be {@literal null}.
|
||||
* @param collection name of the collection.
|
||||
* @return the domain object that is the result of reading it from the {@link Document}.
|
||||
*/
|
||||
T onAfterConvert(T entity, Document document, String collection);
|
||||
}
|
||||
@@ -31,11 +31,16 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @author Simon Baslé
|
||||
* @author Mark Paluch
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public class AuditingEventListener implements ApplicationListener<BeforeConvertEvent<Object>> {
|
||||
|
||||
private final ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory;
|
||||
|
||||
public AuditingEventListener() {
|
||||
this.auditingHandlerFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AuditingEventListener} using the given {@link MappingContext} and {@link AuditingHandler}
|
||||
* provided by the given {@link ObjectFactory}.
|
||||
@@ -51,8 +56,8 @@ public class AuditingEventListener implements ApplicationListener<BeforeConvertE
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(BeforeConvertEvent<Object> event) {
|
||||
|
||||
Optional.ofNullable(event.getSource())//
|
||||
.ifPresent(it -> auditingHandlerFactory.getObject().markAudited(it));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.couchbase.core.mapping.event;
|
||||
|
||||
import org.springframework.data.mapping.callback.EntityCallback;
|
||||
|
||||
/**
|
||||
* Callback being invoked before a domain object is converted to be persisted.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Michael Reiche
|
||||
* @see org.springframework.data.mapping.callback.EntityCallbacks
|
||||
* @since 2.2
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BeforeConvertCallback<T> extends EntityCallback<T> {
|
||||
|
||||
/**
|
||||
* Entity callback method invoked before a domain object is converted to be persisted. Can return either the same or a
|
||||
* modified instance of the domain object.
|
||||
*
|
||||
* @param entity the domain object to save.
|
||||
* @param collection name of the collection.
|
||||
* @return the domain object to be persisted.
|
||||
*/
|
||||
T onBeforeConvert(T entity, String collection);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.couchbase.repository.auditing;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.auditing.AuditingHandler;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.auditing.config.AuditingConfiguration;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.couchbase.config.BeanNames;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
|
||||
import org.springframework.data.couchbase.core.mapping.event.AuditingEventListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A support registrar that allows to set up auditing for Couchbase (including {@link AuditingHandler}
|
||||
* and { IsNewStrategyFactory} set up). See {@link EnableCouchbaseAuditing} for the associated annotation.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Simon Baslé
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public class CouchbaseAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableCouchbaseAuditing.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAuditingHandlerBeanName() {
|
||||
return BeanNames.COUCHBASE_AUDITING_HANDLER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
|
||||
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
|
||||
|
||||
ensureMappingContext(registry, annotationMetadata);
|
||||
super.registerBeanDefinitions(annotationMetadata, registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
|
||||
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
|
||||
builder.addConstructorArgReference(BeanNames.COUCHBASE_MAPPING_CONTEXT);
|
||||
return configureDefaultAuditHandlerAttributes(configuration, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
|
||||
BeanDefinitionRegistry registry) {
|
||||
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
|
||||
|
||||
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AuditingEventListener.class);
|
||||
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(
|
||||
getAuditingHandlerBeanName(), registry));
|
||||
|
||||
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
|
||||
AuditingEventListener.class.getName(), registry);
|
||||
}
|
||||
|
||||
private void ensureMappingContext(BeanDefinitionRegistry registry, Object source) {
|
||||
if (!registry.containsBeanDefinition(BeanNames.COUCHBASE_MAPPING_CONTEXT)) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(CouchbaseMappingContext.class);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
definition.setSource(source);
|
||||
|
||||
registry.registerBeanDefinition(BeanNames.COUCHBASE_MAPPING_CONTEXT, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.couchbase.repository.auditing;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.auditing.DateTimeProvider;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
|
||||
/**
|
||||
* Annotation to enable auditing in Couchbase via annotation configuration.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Simon Baslé
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(CouchbaseAuditingRegistrar.class)
|
||||
public @interface EnableCouchbaseAuditing {
|
||||
/**
|
||||
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
|
||||
*/
|
||||
String auditorAwareRef() default "auditorAwareRef";
|
||||
|
||||
/**
|
||||
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
|
||||
*/
|
||||
boolean setDates() default true;
|
||||
|
||||
/**
|
||||
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
|
||||
*/
|
||||
boolean modifyOnCreate() default true;
|
||||
|
||||
/**
|
||||
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
|
||||
* used for setting creation and modification dates.
|
||||
*/
|
||||
String dateTimeProviderRef() default "dateTimeProviderRef";
|
||||
}
|
||||
Reference in New Issue
Block a user