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

SQOOP-812: Serialization of Configuration objects to and from json is not working properly

(Jarek Jarcec Cecho via Cheolsoo Park)
This commit is contained in:
Cheolsoo Park 2013-01-22 16:00:27 -08:00
parent 52dece8fa8
commit 612060139a
2 changed files with 178 additions and 68 deletions

View File

@ -32,9 +32,7 @@
/**
* Util class for transforming data from correctly annotated configuration
* objects to form structures and vice-versa.
*
* TODO(jarcec): JSON methods needs rewrittion!
* objects to different structures and vice-versa.
*/
public class FormUtils {
@ -271,6 +269,13 @@ public static void applyValidation(List<MForm> forms, Validation validation) {
}
}
/**
* Convert configuration object to JSON. Only filled properties are serialized,
* properties with null value are skipped.
*
* @param configuration Correctly annotated configuration object
* @return String of JSON representation
*/
@SuppressWarnings("unchecked")
public static String toJson(Object configuration) {
Class klass = configuration.getClass();
@ -284,100 +289,161 @@ public static String toJson(Object configuration) {
"Missing annotation Configuration on class " + klass.getName());
}
JSONObject jsonObject = new JSONObject();
JSONObject jsonOutput = new JSONObject();
// Iterate over all declared fields
for (Field field : klass.getDeclaredFields()) {
field.setAccessible(true);
String fieldName = field.getName();
for (Field formField : klass.getDeclaredFields()) {
formField.setAccessible(true);
String formName = formField.getName();
// Each field that should be part of user input should have Input
// annotation.
Input inputAnnotation = field.getAnnotation(Input.class);
// We're processing only form validations
Form formAnnotation = formField.getAnnotation(Form.class);
if(formAnnotation == null) {
continue;
}
Object formValue;
try {
formValue = formField.get(configuration);
} catch (IllegalAccessException e) {
throw new SqoopException(ModelError.MODEL_005,
"Issue with field " + formName, e);
}
JSONObject jsonForm = new JSONObject();
// Now process each input on the form
for(Field inputField : formField.getType().getDeclaredFields()) {
inputField.setAccessible(true);
String inputName = inputField.getName();
Object value;
try {
value = field.get(configuration);
value = inputField.get(formValue);
} catch (IllegalAccessException e) {
throw new SqoopException(ModelError.MODEL_005,
"Issue with field " + field.getName(), e);
"Issue with field " + formName + "." + inputName, e);
}
Input inputAnnotation = inputField.getAnnotation(Input.class);
// Do not serialize all values
if(inputAnnotation != null && value != null) {
Class type = field.getType();
Class type = inputField.getType();
// We need to support NULL, so we do not support primitive types
if(type.isPrimitive()) {
throw new SqoopException(ModelError.MODEL_007,
"Detected primitive type " + type + " for field " + fieldName);
"Detected primitive type " + type + " for field " + formName + "." + inputName);
}
if(type == String.class) {
jsonObject.put(fieldName, value);
jsonForm.put(inputName, value);
} else if (type.isAssignableFrom(Map.class)) {
JSONObject map = new JSONObject();
for(Object key : ((Map)value).keySet()) {
map.put(key, map.get(key));
map.put(key, ((Map)value).get(key));
}
jsonObject.put(fieldName, map);
jsonForm.put(inputName, map);
} else if(type == Integer.class) {
jsonObject.put(fieldName, value);
jsonForm.put(inputName, value);
} else if(type.isEnum()) {
jsonObject.put(fieldName, value);
jsonForm.put(inputName, value.toString());
} else {
throw new SqoopException(ModelError.MODEL_004,
"Unsupported type " + type.getName() + " for input " + fieldName);
"Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
}
}
}
return jsonObject.toJSONString();
jsonOutput.put(formName, jsonForm);
}
// TODO(jarcec): This method currently do not iterate over all fields and
// therefore some fields might have original values when original object will
// be reused. This is unfortunately not acceptable.
return jsonOutput.toJSONString();
}
/**
* Parse given input JSON string and move it's values to given configuration
* object.
*
* @param json JSON representation of the configuration object
* @param configuration Configuration object to be filled
*/
public static void fillValues(String json, Object configuration) {
Class klass = configuration.getClass();
JSONObject jsonObject = (JSONObject) JSONValue.parse(json);
JSONObject jsonForms = (JSONObject) JSONValue.parse(json);
for(Object k : jsonObject.keySet()) {
String key = (String)k;
for(Field formField : klass.getDeclaredFields()) {
formField.setAccessible(true);
String formName = formField.getName();
Field field;
try {
field = klass.getDeclaredField(key);
} catch (NoSuchFieldException e) {
throw new SqoopException(ModelError.MODEL_006,
"Missing field " + key, e);
// We're processing only form validations
Form formAnnotation = formField.getAnnotation(Form.class);
if(formAnnotation == null) {
continue;
}
// We need to access this field even if it would be declared as private
field.setAccessible(true);
Class type = field.getType();
try {
formField.set(configuration, formField.getType().newInstance());
} catch (Exception e) {
throw new SqoopException(ModelError.MODEL_005,
"Issue with field " + formName, e);
}
JSONObject jsonInputs = (JSONObject) jsonForms.get(formField.getName());
if(jsonInputs == null) {
continue;
}
Object formValue;
try {
formValue = formField.get(configuration);
} catch (IllegalAccessException e) {
throw new SqoopException(ModelError.MODEL_005,
"Issue with field " + formName, e);
}
for(Field inputField : formField.getType().getDeclaredFields()) {
inputField.setAccessible(true);
String inputName = inputField.getName();
Input inputAnnotation = inputField.getAnnotation(Input.class);
if(inputAnnotation == null || jsonInputs.get(inputName) == null) {
try {
inputField.set(formValue, null);
} catch (IllegalAccessException e) {
throw new SqoopException(ModelError.MODEL_005,
"Issue with field " + formName + "." + inputName, e);
}
continue;
}
Class type = inputField.getType();
try {
if(type == String.class) {
field.set(configuration, jsonObject.get(key));
inputField.set(formValue, jsonInputs.get(inputName));
} else if (type.isAssignableFrom(Map.class)) {
Map<String, String> map = new HashMap<String, String>();
for(Object kk : jsonObject.keySet()) {
map.put((String)kk, (String)jsonObject.get(kk));
JSONObject jsonObject = (JSONObject) jsonInputs.get(inputName);
for(Object key : jsonObject.keySet()) {
map.put((String)key, (String)jsonObject.get(key));
}
field.set(key, map);
inputField.set(formValue, map);
} else if(type == Integer.class) {
field.set(configuration, jsonObject.get(key));
} else if(type == Integer.class) {
field.set(configuration, Enum.valueOf((Class<? extends Enum>)field.getType(), (String) jsonObject.get(key)));
inputField.set(formValue, ((Long)jsonInputs.get(inputName)).intValue());
} else if(type.isEnum()) {
inputField.set(formValue, Enum.valueOf((Class<? extends Enum>) inputField.getType(), (String) jsonInputs.get(inputName)));
} else {
throw new SqoopException(ModelError.MODEL_004,
"Unsupported type " + type.getName() + " for input " + key);
"Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
}
} catch (IllegalAccessException e) {
throw new SqoopException(ModelError.MODEL_005,
"Issue with field " + field.getName(), e);
"Issue with field " + formName + "." + inputName, e);
}
}
}
}

View File

@ -114,6 +114,39 @@ public void testApplyValidation() {
forms.get(0).getInputs().get(1).getValidationMessage());
}
public void testJson() {
Config config = new Config();
config.aForm.a1 = "A";
config.bForm.b2 = "B";
config.cForm.intValue = 4;
config.cForm.map.put("C", "D");
config.cForm.enumeration = Enumeration.X;
String json = FormUtils.toJson(config);
Config targetConfig = new Config();
// Old values from should be always removed
targetConfig.aForm.a2 = "X";
targetConfig.bForm.b1 = "Y";
// Nulls in forms shouldn't be an issue either
targetConfig.cForm = null;
FormUtils.fillValues(json, targetConfig);
assertEquals("A", targetConfig.aForm.a1);
assertNull(targetConfig.aForm.a2);
assertNull(targetConfig.bForm.b1);
assertEquals("B", targetConfig.bForm.b2);
assertEquals((Integer)4, targetConfig.cForm.intValue);
assertEquals(1, targetConfig.cForm.map.size());
assertTrue(targetConfig.cForm.map.containsKey("C"));
assertEquals("D", targetConfig.cForm.map.get("C"));
assertEquals(Enumeration.X, targetConfig.cForm.enumeration);
}
protected Validation getValidation() {
Map<Validation.FormInput, Validation.Message> messages
= new HashMap<Validation.FormInput, Validation.Message>();
@ -153,6 +186,7 @@ protected List<MForm> getForms() {
inputs = new LinkedList<MInput<?>>();
inputs.add(new MIntegerInput("cForm.intValue"));
inputs.add(new MMapInput("cForm.map"));
inputs.add(new MEnumInput("cForm.enumeration", new String[]{"X", "Y"}));
ret.add(new MForm("cForm", inputs));
return ret;
@ -193,6 +227,11 @@ public static class BForm {
public static class CForm {
@Input Integer intValue;
@Input Map<String, String> map;
@Input Enumeration enumeration;
public CForm() {
map = new HashMap<String, String>();
}
}
@FormClass
@ -202,4 +241,9 @@ public static class DForm {
public static class ConfigWithout {
}
enum Enumeration {
X,
Y,
}
}