5
0
mirror of https://github.com/apache/sqoop.git synced 2025-05-09 13:29:04 +08:00

Sqoop2: Validations: Provide Validation annotations

This commit is contained in:
Abraham Elmahrek 2014-08-18 10:44:37 -07:00
parent d8e336b3ea
commit 1a0e04ec4b
16 changed files with 593 additions and 18 deletions

View File

@ -17,14 +17,25 @@
*/
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;
import java.lang.annotation.Target;
/**
* Class annotation. Each class that is used a configuration object where user
* is expected to provide input need to have this annotation.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfigurationClass {
/**
* List of validators associated with this Configuration class.
*
* @return
*/
Class<? extends Validator>[] validators() default {};
}

View File

@ -17,13 +17,18 @@
*/
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;
import java.lang.annotation.Target;
/**
* Denote configuration class
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FormClass {
/**
@ -32,4 +37,11 @@
* @return
*/
short defaultSize() default -1;
/**
* List of validators associated with this form.
*
* @return
*/
Class<? extends Validator>[] validators() default {};
}

View File

@ -17,14 +17,19 @@
*/
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;
import java.lang.annotation.Target;
/**
* Field annotation. Each field that user might change in configuration object
* need to have this annotation.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Input {
/**
* Sqoop framework will ensure that sensitive information will not be easily
@ -40,4 +45,11 @@
* @return Maximal length
*/
short size() default -1;
/**
* List of validators associated with this input.
*
* @return
*/
Class<? extends Validator>[] validators() default {};
}

View File

@ -0,0 +1,68 @@
/**
* 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;
/**
* Validation message.
*
* Validation message have always two parts - severity and textual information about what
* is wrong. It can be associated with Input, Form or Configuration class.
*/
public class Message {
private Status status;
private String message;
public Message(Status status, String message) {
this.status = status;
this.message = message;
}
public Status getStatus() {
return status;
}
public String getMessage() {
return message;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Message)) return false;
Message message1 = (Message) o;
if (message != null ? !message.equals(message1.message) : message1.message != null)
return false;
if (status != message1.status) return false;
return true;
}
@Override
public int hashCode() {
int result = status != null ? status.hashCode() : 0;
result = 31 * result + (message != null ? message.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "{" + status.name() + ": " + message + "}";
}
}

View File

@ -19,24 +19,26 @@
/**
* Status modes of a validation process.
*
* TODO: This should really be renamed to "severity"
*/
public enum Status {
/**
* There are no issues, no warnings. Everything is correct.
*/
FINE,
FINE, // TODO: Rename to "OK"
/**
* Validated entity is correct enough to be processed. There might be some
* warnings, but no errors.
*/
ACCEPTABLE,
ACCEPTABLE, // TODO: Rename to "WARNING"
/**
* There are serious issues with validated entity. We can't proceed until
* reported issues will be resolved.
*/
UNACCEPTABLE,
UNACCEPTABLE, // TODO: Rename to "ERROR"
;

View File

@ -0,0 +1,35 @@
/**
* 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.Message;
import org.apache.sqoop.validation.Status;
/**
* Ensure that given String Input is a class that is available to this JVM.
*/
public class ClassAvailable extends Validator<String> {
@Override
public void validate(String klass) {
try {
Class.forName(klass);
} catch (ClassNotFoundException e) {
addMessage(new Message(Status.UNACCEPTABLE, "Class not found"));
}
}
}

View File

@ -0,0 +1,34 @@
/**
* 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;
/**
* Ensure that given String is not empty.
*
* Will also ensure that the string is not null.
*/
public class NotEmpty extends Validator<String> {
@Override
public void validate(String instance) {
if (instance == null || instance.isEmpty()) {
addMessage(Status.UNACCEPTABLE, "Can't be null nor empty");
}
}
}

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;
/**
* Ensure that given object is never null.
*/
public class NotNull<T> extends Validator<T> {
@Override
public void validate(T instance) {
if (instance == null) {
addMessage(Status.UNACCEPTABLE, "Can't be null");
}
}
}

View File

@ -0,0 +1,69 @@
/**
* 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.Message;
import org.apache.sqoop.validation.Status;
import java.util.LinkedList;
import java.util.List;
/**
* Abstract validator class.
*
* Can be used to validate inputs, forms and configuration classes.
*/
abstract public class Validator<T> {
/**
* Validation check.
*
* To be implemented by our children.
*
* @param instance Object to validate (depending on what we are validating)
*/
abstract public void validate(T instance);
/**
* Messages generated during validation.
*/
private List<Message> messages;
public Validator() {
reset();
}
protected void addMessage(Message msg) {
messages.add(msg);
}
protected void addMessage(Status status, String msg) {
messages.add(new Message(status, msg));
}
public List<Message> getMessages() {
return messages;
}
/**
* Reset validator state (all previous messages).
*/
public void reset() {
messages = new LinkedList<Message>();
}
}

View File

@ -0,0 +1,50 @@
/**
* 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.Message;
import org.apache.sqoop.validation.Status;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
*/
public class TestClassAvailable {
Validator validator = new ClassAvailable();
@Test
public void test() {
List<Message> messages;
assertEquals(0, validator.getMessages().size());
validator.validate(ClassAvailable.class.getCanonicalName());
assertEquals(0, validator.getMessages().size());
validator.validate("java.lang.String");
assertEquals(0, validator.getMessages().size());
validator.validate("net.jarcec.super.private.project.Main");
assertEquals(1, validator.getMessages().size());
messages = validator.getMessages();
assertEquals(Status.UNACCEPTABLE, messages.get(0).getStatus());
}
}

View File

@ -0,0 +1,55 @@
/**
* 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.Message;
import org.apache.sqoop.validation.Status;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
*/
public class TestNotEmpty {
Validator validator = new NotEmpty();
@Test
public void test() {
List<Message> messages;
assertEquals(0, validator.getMessages().size());
validator.validate("Non empty");
assertEquals(0, validator.getMessages().size());
validator.validate("");
assertEquals(1, validator.getMessages().size());
messages = validator.getMessages();
assertEquals(Status.UNACCEPTABLE, messages.get(0).getStatus());
validator.reset();
assertEquals(0, validator.getMessages().size());
validator.validate(null);
assertEquals(1, validator.getMessages().size());
messages = validator.getMessages();
assertEquals(Status.UNACCEPTABLE, messages.get(0).getStatus());
}
}

View File

@ -0,0 +1,51 @@
/**
* 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.Message;
import org.apache.sqoop.validation.Status;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
*/
public class TestNotNull {
Validator validator = new NotNull();
@Test
public void test() {
assertEquals(0, validator.getMessages().size());
validator.validate("");
assertEquals(0, validator.getMessages().size());
validator.validate("Non empty");
assertEquals(0, validator.getMessages().size());
validator.validate(null);
assertEquals(1, validator.getMessages().size());
List<Message> messages = validator.getMessages();
Message msg = messages.get(0);
assertEquals(Status.UNACCEPTABLE, msg.getStatus());
}
}

View File

@ -0,0 +1,57 @@
/**
* 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.Message;
import org.apache.sqoop.validation.Status;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
*
*/
public class TestValidator {
public static class ValidatorImpl extends Validator<String> {
@Override
public void validate(String msg) {
addMessage(Status.FINE, msg);
addMessage(new Message(Status.UNACCEPTABLE, "Prefix: " + msg));
}
}
@Test
public void test() {
ValidatorImpl validator = new ValidatorImpl();
assertEquals(0, validator.getMessages().size());
validator.validate("X");
assertEquals(2, validator.getMessages().size());
Message msg = validator.getMessages().get(0);
assertEquals(Status.FINE, msg.getStatus());
assertEquals("X", msg.getMessage());
msg = validator.getMessages().get(1);
assertEquals(Status.UNACCEPTABLE, msg.getStatus());
assertEquals("Prefix: X", msg.getMessage());
validator.reset();
assertEquals(0, validator.getMessages().size());
}
}

View File

@ -19,17 +19,44 @@
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.validation.Status;
import org.apache.sqoop.validation.validators.NotEmpty;
import org.apache.sqoop.validation.validators.Validator;
import org.apache.sqoop.validation.validators.ClassAvailable;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;
/**
*
*/
@FormClass
@FormClass(validators = {ConnectionForm.FormValidator.class})
public class ConnectionForm {
@Input(size = 128) public String jdbcDriver;
@Input(size = 128) public String connectionString;
@Input(size = 40) public String username;
@Input(size = 40, sensitive = true) public String password;
@Input public Map<String, String> jdbcProperties;
@Input(size = 128, validators = {NotEmpty.class, ClassAvailable.class} )
public String jdbcDriver;
@Input(size = 128, validators = {NotEmpty.class} )
public String connectionString;
@Input(size = 40)
public String username;
@Input(size = 40, sensitive = true)
public String password;
@Input
public Map<String, String> jdbcProperties;
public static class FormValidator extends Validator<ConnectionForm> {
@Override
public void validate(ConnectionForm form) {
// See if we can connect to the database
try {
DriverManager.getConnection(form.connectionString, form.username, form.password);
} catch (SQLException e) {
addMessage(Status.ACCEPTABLE, "Can't connect to the database with given credentials: " + e.getMessage());
}
}
}
}

View File

@ -17,19 +17,59 @@
*/
package org.apache.sqoop.connector.jdbc.configuration;
import org.apache.sqoop.connector.jdbc.GenericJdbcConnectorConstants;
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.validation.Status;
import org.apache.sqoop.validation.validators.Validator;
/**
*
*/
@FormClass
@FormClass( validators = {FromTableForm.FormValidator.class})
public class FromTableForm {
@Input(size = 50) public String schemaName;
@Input(size = 50) public String tableName;
@Input(size = 2000) public String sql;
@Input(size = 50) public String columns;
@Input(size = 50) public String partitionColumn;
@Input public Boolean partitionColumnNull;
@Input(size = 50) public String boundaryQuery;
@Input(size = 50)
public String schemaName;
@Input(size = 50)
public String tableName;
@Input(size = 2000, validators = {SqlConditionTokenValidator.class})
public String sql;
@Input(size = 50)
public String columns;
@Input(size = 50)
public String partitionColumn;
@Input
public Boolean partitionColumnNull;
@Input(size = 50)
public String boundaryQuery;
public static class FormValidator extends Validator<FromTableForm> {
@Override
public void validate(FromTableForm form) {
if(form.tableName == null && form.sql == null) {
addMessage(Status.UNACCEPTABLE, "Either fromTable name or SQL must be specified");
}
if(form.tableName != null && form.sql != null) {
addMessage(Status.UNACCEPTABLE, "Both fromTable name and SQL cannot be specified");
}
if(form.schemaName != null && form.sql != null) {
addMessage(Status.UNACCEPTABLE, "Both schema name and SQL cannot be specified");
}
}
}
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,11 +19,13 @@
import org.apache.sqoop.model.FormClass;
import org.apache.sqoop.model.Input;
import org.apache.sqoop.validation.Status;
import org.apache.sqoop.validation.validators.Validator;
/**
*
*/
@FormClass
@FormClass(validators = {ToTableForm.FormValidator.class})
public class ToTableForm {
@Input(size = 50) public String schemaName;
@Input(size = 2000) public String tableName;
@ -31,4 +33,22 @@ public class ToTableForm {
@Input(size = 50) public String columns;
@Input(size = 2000) public String stageTableName;
@Input public Boolean clearStageTable;
public static class FormValidator extends Validator<ToTableForm> {
@Override
public void validate(ToTableForm form) {
if(form.tableName == null && form.sql == null) {
addMessage(Status.UNACCEPTABLE, "Either fromTable name or SQL must be specified");
}
if(form.tableName != null && form.sql != null) {
addMessage(Status.UNACCEPTABLE, "Both fromTable name and SQL cannot be specified");
}
if(form.tableName == null && form.stageTableName != null) {
addMessage(Status.UNACCEPTABLE, "Stage fromTable name cannot be specified without specifying fromTable name");
}
if(form.stageTableName == null && form.clearStageTable != null) {
addMessage(Status.UNACCEPTABLE, "Clear stage fromTable cannot be specified without specifying name of the stage fromTable.");
}
}
}
}