5
0
mirror of https://github.com/apache/sqoop.git synced 2025-05-07 00:03:22 +08:00

SQOOP-1468: Sqoop2: Validations: Introduce advanced validators that can be parametrized

This commit is contained in:
Jarek Jarcec Cecho 2014-09-03 11:17:48 -07:00 committed by Abraham Elmahrek
parent c0b22b1d62
commit e71dd75793
22 changed files with 373 additions and 57 deletions

View File

@ -17,8 +17,6 @@
*/
package org.apache.sqoop.model;
import org.apache.sqoop.validation.validators.Validator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -37,5 +35,5 @@
*
* @return
*/
Class<? extends Validator>[] validators() default {};
Validator[] validators() default {};
}

View File

@ -17,8 +17,6 @@
*/
package org.apache.sqoop.model;
import org.apache.sqoop.validation.validators.Validator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -43,5 +41,5 @@
*
* @return
*/
Class<? extends Validator>[] validators() default {};
Validator[] validators() default {};
}

View File

@ -17,8 +17,6 @@
*/
package org.apache.sqoop.model;
import org.apache.sqoop.validation.validators.Validator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -51,5 +49,5 @@
*
* @return
*/
Class<? extends Validator>[] validators() default {};
Validator[] validators() default {};
}

View File

@ -0,0 +1,45 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sqoop.model;
import org.apache.sqoop.validation.validators.AbstractValidator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Annotation for specifying validators
*
* Usage without any parameters:
* @Validator(ClassName.class)
*
* To specify string parameter call:
* @Validator(value = ClassName.class, strArg = "Hello World!")
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Validator {
/**
* Validator implementation that should be executed.
*/
Class<? extends AbstractValidator> value();
/**
* Optional argument that should be given to the validator before execution.
*/
String strArg() default AbstractValidator.DEFAULT_STRING_ARGUMENT;
}

View File

@ -17,7 +17,7 @@
*/
package org.apache.sqoop.validation;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.AbstractValidator;
import java.util.HashMap;
import java.util.List;
@ -49,7 +49,7 @@ public ValidationResult() {
* @param name Full name of the validated object
* @param validator Executed validator
*/
public void addValidator(String name, Validator validator) {
public void addValidator(String name, AbstractValidator validator) {
if(validator.getStatus() == Status.getDefault()) {
return;
}

View File

@ -22,8 +22,9 @@
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.FormUtils;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.model.Validator;
import org.apache.sqoop.utils.ClassUtils;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.AbstractValidator;
import java.lang.reflect.Field;
@ -114,14 +115,14 @@ public ValidationResult validateForm(String formName, Object form) {
*
* @param name Full name of the object
* @param object Input, Form or Class instance
* @param classes Validators array
* @param validators Validators array
* @return
*/
private ValidationResult validateArray(String name, Object object, Class<? extends Validator> []classes) {
private ValidationResult validateArray(String name, Object object, Validator[] validators) {
ValidationResult result = new ValidationResult();
for (Class<? extends Validator> klass : classes) {
Validator v = executeValidator(object, klass);
for (Validator validator : validators) {
AbstractValidator v = executeValidator(object, validator);
result.addValidator(name, v);
}
@ -132,11 +133,12 @@ private ValidationResult validateArray(String name, Object object, Class<? exten
* Execute single validator.
*
* @param object Input, Form or Class instance
* @param klass Validator's clas
* @param validator Validator annotation
* @return
*/
private Validator executeValidator(Object object, Class<? extends Validator> klass) {
Validator instance = (Validator) ClassUtils.instantiate(klass);
private AbstractValidator executeValidator(Object object, Validator validator) {
AbstractValidator instance = (AbstractValidator) ClassUtils.instantiate(validator.value());
instance.setStringArgument(validator.strArg());
instance.validate(object);
return instance;
}

View File

@ -28,7 +28,12 @@
*
* Can be used to validate inputs, forms and configuration classes.
*/
abstract public class Validator<T> {
abstract public class AbstractValidator<T> {
/**
* Default value of String argument.
*/
public static final String DEFAULT_STRING_ARGUMENT = "";
/**
* Validation check.
@ -49,7 +54,12 @@ abstract public class Validator<T> {
*/
private Status status;
public Validator() {
/**
* Optional String argument that can be defined for the Validator
*/
private String stringArgument;
public AbstractValidator() {
reset();
}
@ -70,11 +80,24 @@ public Status getStatus() {
return status;
}
public void setStringArgument(String arg) {
this.stringArgument = arg;
}
public String getStringArgument() {
return stringArgument;
}
/**
* Reset validator state (all previous messages).
* Reset validator state.
*
* * Previous messages
* * Status
* * Any stored arguments
*/
public void reset() {
messages = new LinkedList<Message>();
status = Status.getDefault();
stringArgument = DEFAULT_STRING_ARGUMENT;
}
}

View File

@ -23,7 +23,7 @@
/**
* Ensure that given String Input is a class that is available to this JVM.
*/
public class ClassAvailable extends Validator<String> {
public class ClassAvailable extends AbstractValidator<String> {
@Override
public void validate(String klass) {
try {

View File

@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sqoop.validation.validators;
import org.apache.sqoop.validation.Status;
/**
* String validator to verify presence of a sub string (provided as a String argument)
*/
public class Contains extends AbstractValidator<String> {
@Override
public void validate(String str) {
if(str == null || !str.contains(getStringArgument())) {
addMessage(Status.UNACCEPTABLE, "String must contain substring: " + getStringArgument());
}
}
}

View File

@ -24,7 +24,7 @@
*
* Will also ensure that the string is not null.
*/
public class NotEmpty extends Validator<String> {
public class NotEmpty extends AbstractValidator<String> {
@Override
public void validate(String instance) {
if (instance == null || instance.isEmpty()) {

View File

@ -22,7 +22,7 @@
/**
* Ensure that given object is never null.
*/
public class NotNull<T> extends Validator<T> {
public class NotNull<T> extends AbstractValidator<T> {
@Override
public void validate(T instance) {
if (instance == null) {

View File

@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sqoop.validation.validators;
import org.apache.sqoop.validation.Status;
/**
* String validator to verify if Input starts with given string (String argument).
*/
public class StartsWith extends AbstractValidator<String> {
@Override
public void validate(String str) {
if(str == null || !str.startsWith(getStringArgument())) {
addMessage(Status.UNACCEPTABLE, "String must start with: " + getStringArgument());
}
}
}

View File

@ -21,9 +21,11 @@
import org.apache.sqoop.model.Form;
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.model.Validator;
import org.apache.sqoop.validation.validators.Contains;
import org.apache.sqoop.validation.validators.NotEmpty;
import org.apache.sqoop.validation.validators.NotNull;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.AbstractValidator;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@ -33,12 +35,12 @@
*/
public class TestValidationRunner {
@FormClass(validators = {FormA.FormValidator.class})
@FormClass(validators = {@Validator(FormA.FormValidator.class)})
public static class FormA {
@Input(validators = {NotNull.class})
@Input(validators = {@Validator(NotNull.class)})
String notNull;
public static class FormValidator extends Validator<FormA> {
public static class FormValidator extends AbstractValidator<FormA> {
@Override
public void validate(FormA form) {
if(form.notNull == null) {
@ -80,7 +82,13 @@ public void testValidateForm() {
@FormClass
public static class FormB {
@Input(validators = {NotNull.class, NotEmpty.class})
@Input(validators = {@Validator(NotNull.class), @Validator(NotEmpty.class)})
String str;
}
@FormClass
public static class FormC {
@Input(validators = {@Validator(value = Contains.class, strArg = "findme")})
String str;
}
@ -98,14 +106,34 @@ public void testMultipleValidatorsOnSingleInput() {
assertEquals(2, result.getMessages().get("formName.str").size());
}
@ConfigurationClass(validators = {ConfigurationA.ClassValidator.class})
@Test
public void testValidatorWithParameters() {
FormC form = new FormC();
ValidationRunner runner = new ValidationRunner();
ValidationResult result;
// Sub string not found
form.str = "Mordor";
result = runner.validateForm("formName", form);
assertEquals(Status.UNACCEPTABLE, result.getStatus());
assertEquals(1, result.getMessages().size());
assertTrue(result.getMessages().containsKey("formName.str"));
// Sub string found
form.str = "Morfindmedor";
result = runner.validateForm("formName", form);
assertEquals(Status.FINE, result.getStatus());
assertEquals(0, result.getMessages().size());
}
@ConfigurationClass(validators = {@Validator(ConfigurationA.ClassValidator.class)})
public static class ConfigurationA {
@Form FormA formA;
public ConfigurationA() {
formA = new FormA();
}
public static class ClassValidator extends Validator<ConfigurationA> {
public static class ClassValidator extends AbstractValidator<ConfigurationA> {
@Override
public void validate(ConfigurationA conf) {
if("error".equals(conf.formA.notNull)) {

View File

@ -29,7 +29,7 @@
*/
public class TestClassAvailable {
Validator validator = new ClassAvailable();
AbstractValidator validator = new ClassAvailable();
@Test
public void test() {

View File

@ -0,0 +1,90 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sqoop.validation.validators;
import org.apache.sqoop.validation.Status;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
/**
*/
public class TestContains {
AbstractValidator validator = new Contains();
@Test
public void test() {
assertEquals(0, validator.getMessages().size());
// Default, no string argument set
validator.validate("str");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Searched substring is entire string
validator.reset();
validator.setStringArgument("str");
validator.validate("str");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Just starts with
validator.reset();
validator.setStringArgument("str");
validator.validate("straaaaa");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Ends with
validator.reset();
validator.setStringArgument("str");
validator.validate("aaastr");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// In the middle
validator.reset();
validator.setStringArgument("str");
validator.validate("aaastraaa");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Null string
validator.reset();
validator.setStringArgument("str");
validator.validate(null);
assertEquals(Status.UNACCEPTABLE, validator.getStatus());
assertEquals(1, validator.getMessages().size());
// Empty string
validator.reset();
validator.setStringArgument("str");
validator.validate("");
assertEquals(Status.UNACCEPTABLE, validator.getStatus());
assertEquals(1, validator.getMessages().size());
// "Random" string
validator.reset();
validator.setStringArgument("str");
validator.validate("Ahoj tady je meduza");
assertEquals(Status.UNACCEPTABLE, validator.getStatus());
assertEquals(1, validator.getMessages().size());
}
}

View File

@ -29,7 +29,7 @@
*/
public class TestNotEmpty {
Validator validator = new NotEmpty();
AbstractValidator validator = new NotEmpty();
@Test
public void test() {

View File

@ -29,7 +29,7 @@
*/
public class TestNotNull {
Validator validator = new NotNull();
AbstractValidator validator = new NotNull();
@Test
public void test() {

View File

@ -0,0 +1,74 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sqoop.validation.validators;
import org.apache.sqoop.validation.Status;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
/**
*/
public class TestStartsWith {
AbstractValidator validator = new StartsWith();
@Test
public void test() {
assertEquals(0, validator.getMessages().size());
// Default, no string argument set
validator.validate("str");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Searched substring is entire string
validator.validate("str");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Just starts with
validator.reset();
validator.setStringArgument("str");
validator.validate("strstr");
assertEquals(Status.FINE, validator.getStatus());
assertEquals(0, validator.getMessages().size());
// Null string
validator.reset();
validator.setStringArgument("str");
validator.validate(null);
assertEquals(Status.UNACCEPTABLE, validator.getStatus());
assertEquals(1, validator.getMessages().size());
// Empty string
validator.reset();
validator.setStringArgument("str");
validator.validate("");
assertEquals(Status.UNACCEPTABLE, validator.getStatus());
assertEquals(1, validator.getMessages().size());
// "Random" string
validator.reset();
validator.setStringArgument("str");
validator.validate("Ahoj tady je meduza");
assertEquals(Status.UNACCEPTABLE, validator.getStatus());
assertEquals(1, validator.getMessages().size());
}
}

View File

@ -27,7 +27,7 @@
*
*/
public class TestValidator {
public static class ValidatorImpl extends Validator<String> {
public static class ValidatorImpl extends AbstractValidator<String> {
@Override
public void validate(String msg) {
addMessage(Status.FINE, msg);

View File

@ -19,10 +19,12 @@
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.model.Validator;
import org.apache.sqoop.validation.Status;
import org.apache.sqoop.validation.validators.AbstractValidator;
import org.apache.sqoop.validation.validators.NotEmpty;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.ClassAvailable;
import org.apache.sqoop.validation.validators.StartsWith;
import java.sql.DriverManager;
import java.sql.SQLException;
@ -31,12 +33,12 @@
/**
*
*/
@FormClass(validators = {ConnectionForm.FormValidator.class})
@FormClass(validators = {@Validator(ConnectionForm.FormValidator.class)})
public class ConnectionForm {
@Input(size = 128, validators = {NotEmpty.class, ClassAvailable.class} )
@Input(size = 128, validators = {@Validator(NotEmpty.class), @Validator(ClassAvailable.class)} )
public String jdbcDriver;
@Input(size = 128, validators = {NotEmpty.class} )
@Input(size = 128, validators = {@Validator(value = StartsWith.class, strArg = "jdbc:")} )
public String connectionString;
@Input(size = 40)
@ -48,7 +50,7 @@ public class ConnectionForm {
@Input
public Map<String, String> jdbcProperties;
public static class FormValidator extends Validator<ConnectionForm> {
public static class FormValidator extends AbstractValidator<ConnectionForm> {
@Override
public void validate(ConnectionForm form) {
// See if we can connect to the database

View File

@ -20,13 +20,15 @@
import org.apache.sqoop.connector.jdbc.GenericJdbcConnectorConstants;
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.model.Validator;
import org.apache.sqoop.validation.Status;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.AbstractValidator;
import org.apache.sqoop.validation.validators.Contains;
/**
*
*/
@FormClass( validators = {FromTableForm.FormValidator.class})
@FormClass( validators = {@Validator(FromTableForm.FormValidator.class)})
public class FromTableForm {
@Input(size = 50)
public String schemaName;
@ -34,7 +36,7 @@ public class FromTableForm {
@Input(size = 50)
public String tableName;
@Input(size = 2000, validators = {SqlConditionTokenValidator.class})
@Input(size = 2000, validators = {@Validator(value = Contains.class, strArg = GenericJdbcConnectorConstants.SQL_CONDITIONS_TOKEN)})
public String sql;
@Input(size = 50)
@ -49,7 +51,7 @@ public class FromTableForm {
@Input(size = 50)
public String boundaryQuery;
public static class FormValidator extends Validator<FromTableForm> {
public static class FormValidator extends AbstractValidator<FromTableForm> {
@Override
public void validate(FromTableForm form) {
if(form.tableName == null && form.sql == null) {
@ -63,13 +65,4 @@ public void validate(FromTableForm form) {
}
}
}
public static class SqlConditionTokenValidator extends Validator<String> {
@Override
public void validate(String sql) {
if(sql != null && !sql.contains(GenericJdbcConnectorConstants.SQL_CONDITIONS_TOKEN)) {
addMessage(Status.UNACCEPTABLE, "SQL statement must contain placeholder for auto generated conditions - " + GenericJdbcConnectorConstants.SQL_CONDITIONS_TOKEN);
}
}
}
}

View File

@ -19,13 +19,14 @@
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.model.Validator;
import org.apache.sqoop.validation.Status;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.AbstractValidator;
/**
*
*/
@FormClass(validators = {ToTableForm.FormValidator.class})
@FormClass(validators = {@Validator(ToTableForm.FormValidator.class)})
public class ToTableForm {
@Input(size = 50) public String schemaName;
@Input(size = 2000) public String tableName;
@ -34,7 +35,7 @@ public class ToTableForm {
@Input(size = 2000) public String stageTableName;
@Input public Boolean clearStageTable;
public static class FormValidator extends Validator<ToTableForm> {
public static class FormValidator extends AbstractValidator<ToTableForm> {
@Override
public void validate(ToTableForm form) {
if(form.tableName == null && form.sql == null) {