Skip to content

Commit

Permalink
DATAMONGO-36 - Added support for JSR-303 validation
Browse files Browse the repository at this point in the history
Added event listener that uses a java.validation.Validator to trigger validation on an entity before persisting it. This validation is enabled by default if the javx.validation API is present on the classpath. Added namespace attribute 'disable-validation' to allow disabling that auto enabling behavior.
  • Loading branch information
Maciej Walkowiak authored and odrotbohm committed Apr 27, 2012
1 parent d317bcf commit 5b13307
Show file tree
Hide file tree
Showing 15 changed files with 851 additions and 7 deletions.
16 changes: 16 additions & 0 deletions spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<mongo.version>2.7.1</mongo.version>
<querydsl.version>2.3.2</querydsl.version>
<cdi.version>1.0</cdi.version>
<validation.version>1.0.0.GA</validation.version>
<webbeans.version>1.1.3</webbeans.version>
</properties>

Expand Down Expand Up @@ -130,6 +131,21 @@
<scope>test</scope>
</dependency>

<!-- JSR 303 Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ public abstract class BeanNames {
static final String INDEX_HELPER = "indexCreationHelper";
static final String MONGO = "mongo";
static final String DB_FACTORY = "mongoDbFactory";
static final String VALIDATING_EVENT_LISTENER = "validatingMongoEventListener";
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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.mongodb.config;

import static org.springframework.data.mongodb.config.BeanNames.*;
Expand All @@ -30,12 +29,14 @@
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
Expand All @@ -52,18 +53,25 @@
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

/**
* @author Jon Brisbin <jbrisbin@vmware.com>
* Bean definition parser for the {@code mapping-converter} element.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Maciej Walkowiak
*/
public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {

private static final String BASE_PACKAGE = "base-package";
private static final boolean jsr303Present = ClassUtils.isPresent("javax.validation.Validator",
MappingMongoConverterParser.class.getClassLoader());

@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
Expand Down Expand Up @@ -107,9 +115,53 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa
registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition());
}

BeanDefinition validatingMongoEventListener = potentiallyCreateValidatingMongoEventListener(element, parserContext);

if (validatingMongoEventListener != null) {
registry.registerBeanDefinition(VALIDATING_EVENT_LISTENER, validatingMongoEventListener);
}

return converterBuilder.getBeanDefinition();
}

private BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) {

String disableValidation = element.getAttribute("disable-validation");
boolean validationDisabled = StringUtils.hasText(disableValidation) && Boolean.valueOf(disableValidation);

if (!validationDisabled) {

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
RuntimeBeanReference validator = getValidator(builder, parserContext);

if (validator != null) {

builder.getRawBeanDefinition().setBeanClass(ValidatingMongoEventListener.class);
builder.addConstructorArgValue(validator);

return builder.getBeanDefinition();
}
}

return null;
}

private RuntimeBeanReference getValidator(Object source, ParserContext parserContext) {

if (!jsr303Present) {
return null;
}

RootBeanDefinition validatorDef = new RootBeanDefinition(
"org.springframework.validation.beanvalidation.LocalValidatorFactoryBean");
validatorDef.setSource(source);
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));

return new RuntimeBeanReference(validatorName);
}

private String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
BeanDefinition conversionsDefinition) {

Expand Down
Original file line number Diff line number Diff line change
@@ -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.mongodb.core.mapping.event;

import java.util.Set;

import javax.validation.ConstraintViolationException;
import javax.validation.Validator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.mongodb.DBObject;

/**
* javax.validation dependant entities validator. When it is registered as Spring component its automatically invoked
* before entities are saved in database.
*
* @author Maciej Walkowiak
*/
public class ValidatingMongoEventListener extends AbstractMongoEventListener<Object> {

private static final Logger LOG = LoggerFactory.getLogger(ValidatingMongoEventListener.class);

private final Validator validator;

/**
* Creates a new {@link ValidatingMongoEventListener} using the given {@link Validator}.
*
* @param validator must not be {@literal null}.
*/
public ValidatingMongoEventListener(Validator validator) {
Assert.notNull(validator);
this.validator = validator;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeSave(java.lang.Object, com.mongodb.DBObject)
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void onBeforeSave(Object source, DBObject dbo) {

LOG.debug("Validating object: {}", source);
Set violations = validator.validate(source);

if (!violations.isEmpty()) {

LOG.info("During object: {} validation violations found: {}", source, violations);
throw new ConstraintViolationException(violations);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
http\://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd
http\://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd
http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd
http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd
Loading

0 comments on commit 5b13307

Please sign in to comment.