Review Board 1.7.22


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

Review Request #8848 - Created Jan. 6, 2013 and submitted

Jarek Cecho
SQOOP-812
Reviewers
Sqoop
sqoop-sqoop2
I've fix methods toJson and fillValues() to work with new schema of configuration objects.
I've added new unit test.

Diff revision 2 (Latest)

1 2
1 2

  1. common/src/main/java/org/apache/sqoop/model/FormUtils.java: Loading...
  2. common/src/test/java/org/apache/sqoop/model/TestFormUtils.java: Loading...
common/src/main/java/org/apache/sqoop/model/FormUtils.java
Revision 585d02eee14a004df45d6fa77dcde4bd65132088 New Change
[20] 29 lines
[+20]
30
import java.util.List;
30
import java.util.List;
31
import java.util.Map;
31
import java.util.Map;
32

    
   
32

   
33
/**
33
/**
34
 * Util class for transforming data from correctly annotated configuration
34
 * Util class for transforming data from correctly annotated configuration
35
 * objects to form structures and vice-versa.
35
 * objects to different structures and vice-versa.
36
 *

   
37
 * TODO(jarcec): JSON methods needs rewrittion!

   
38
 */
36
 */
39
public class FormUtils {
37
public class FormUtils {
40

    
   
38

   
41
  /**
39
  /**
42
   * Transform correctly annotated configuration object to corresponding
40
   * Transform correctly annotated configuration object to corresponding
[+20] [20] 226 lines
[+20] [+] public static void applyValidation(List<MForm> forms, Validation validation) {
269
        }
267
        }
270
      }
268
      }
271
    }
269
    }
272
  }
270
  }
273

    
   
271

   

    
   
272
  /**

    
   
273
   * Convert configuration object to JSON. Only filled properties are serialized,

    
   
274
   * properties with null value are skipped.

    
   
275
   *

    
   
276
   * @param configuration Correctly annotated configuration object

    
   
277
   * @return String of JSON representation

    
   
278
   */
274
  @SuppressWarnings("unchecked")
279
  @SuppressWarnings("unchecked")
275
  public static String toJson(Object configuration) {
280
  public static String toJson(Object configuration) {
276
    Class klass = configuration.getClass();
281
    Class klass = configuration.getClass();
277

    
   
282

   
278
    ConfigurationClass global =
283
    ConfigurationClass global =
279
      (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class);
284
      (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class);
280

    
   
285

   
281
    // Each configuration object must have this class annotation
286
    // Each configuration object must have this class annotation
282
    if(global == null) {
287
    if(global == null) {
283
      throw new SqoopException(ModelError.MODEL_003,
288
      throw new SqoopException(ModelError.MODEL_003,
284
        "Missing annotation Configuration on class " + klass.getName());
289
        "Missing annotation Configuration on class " + klass.getName());
285
    }
290
    }
286

    
   
291

   
287
    JSONObject jsonObject = new JSONObject();
292
    JSONObject jsonOutput = new JSONObject();
288

    
   
293

   
289
    // Iterate over all declared fields
294
    // Iterate over all declared fields
290
    for (Field field : klass.getDeclaredFields()) {
295
    for (Field formField : klass.getDeclaredFields()) {
291
      field.setAccessible(true);
296
      formField.setAccessible(true);
292
      String fieldName = field.getName();
297
      String formName = formField.getName();
293

    
   
298

   
294
      // Each field that should be part of user input should have Input
299
      // We're processing only form validations
295
      // annotation.
300
      Form formAnnotation = formField.getAnnotation(Form.class);
296
      Input inputAnnotation = field.getAnnotation(Input.class);
301
      if(formAnnotation == null) {

    
   
302
        continue;

    
   
303
      }

    
   
304

   

    
   
305
      Object formValue;

    
   
306
      try {

    
   
307
        formValue = formField.get(configuration);

    
   
308
      } catch (IllegalAccessException e) {

    
   
309
        throw new SqoopException(ModelError.MODEL_005,

    
   
310
          "Issue with field " + formName, e);

    
   
311
      }

    
   
312

   

    
   
313
      JSONObject jsonForm = new JSONObject();

    
   
314

   

    
   
315
      // Now process each input on the form

    
   
316
      for(Field inputField : formField.getType().getDeclaredFields()) {

    
   
317
        inputField.setAccessible(true);

    
   
318
        String inputName = inputField.getName();
297

    
   
319

   
298
      Object value;
320
        Object value;
299
      try {
321
        try {
300
        value = field.get(configuration);
322
          value = inputField.get(formValue);
301
      } catch (IllegalAccessException e) {
323
        } catch (IllegalAccessException e) {
302
        throw new SqoopException(ModelError.MODEL_005,
324
          throw new SqoopException(ModelError.MODEL_005,
303
          "Issue with field " + field.getName(), e);
325
            "Issue with field " + formName + "." + inputName, e);
304
      }
326
        }
305

    
   
327

   

    
   
328
        Input inputAnnotation = inputField.getAnnotation(Input.class);

    
   
329

   
306
      // Do not serialize all values
330
        // Do not serialize all values
307
      if(inputAnnotation != null && value != null) {
331
        if(inputAnnotation != null && value != null) {
308
        Class type = field.getType();
332
          Class type = inputField.getType();
309

    
   
333

   
310
        // We need to support NULL, so we do not support primitive types
334
          // We need to support NULL, so we do not support primitive types
311
        if(type.isPrimitive()) {
335
          if(type.isPrimitive()) {
312
          throw new SqoopException(ModelError.MODEL_007,
336
            throw new SqoopException(ModelError.MODEL_007,
313
            "Detected primitive type " + type + " for field " + fieldName);
337
              "Detected primitive type " + type + " for field " + formName + "." + inputName);
314
        }
338
          }
315

    
   
339

   
316
        if(type == String.class) {
340
          if(type == String.class) {
317
          jsonObject.put(fieldName, value);
341
            jsonForm.put(inputName, value);
318
        } else if (type.isAssignableFrom(Map.class)) {
342
          } else if (type.isAssignableFrom(Map.class)) {
319
          JSONObject map = new JSONObject();
343
            JSONObject map = new JSONObject();
320
          for(Object key : ((Map)value).keySet()) {
344
            for(Object key : ((Map)value).keySet()) {
321
            map.put(key, map.get(key));
345
              map.put(key, ((Map)value).get(key));
322
          }
346
            }
323
          jsonObject.put(fieldName, map);
347
            jsonForm.put(inputName, map);
324
        } else if(type == Integer.class) {
348
          } else if(type == Integer.class) {
325
          jsonObject.put(fieldName, value);
349
            jsonForm.put(inputName, value);
326
        } else if(type.isEnum()) {
350
          } else if(type.isEnum()) {
327
          jsonObject.put(fieldName, value);
351
            jsonForm.put(inputName, value.toString());
328
        } else {
352
          } else {
329
          throw new SqoopException(ModelError.MODEL_004,
353
            throw new SqoopException(ModelError.MODEL_004,
330
            "Unsupported type " + type.getName() + " for input " + fieldName);
354
              "Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
331
        }
355
          }
332
      }
356
        }
333
    }
357
      }
334

    
   
358

   
335
    return jsonObject.toJSONString();
359
      jsonOutput.put(formName, jsonForm);
336
  }
360
    }
337

    
   
361

   
338
  // TODO(jarcec): This method currently do not iterate over all fields and
362
    return jsonOutput.toJSONString();
339
  // therefore some fields might have original values when original object will
363
  }
340
  // be reused. This is unfortunately not acceptable.
364

   

    
   
365
  /**

    
   
366
   * Parse given input JSON string and move it's values to given configuration

    
   
367
   * object.

    
   
368
   *

    
   
369
   * @param json JSON representation of the configuration object

    
   
370
   * @param configuration Configuration object to be filled

    
   
371
   */
341
  public static void fillValues(String json, Object configuration) {
372
  public static void fillValues(String json, Object configuration) {
342
    Class klass = configuration.getClass();
373
    Class klass = configuration.getClass();
343

    
   
374

   
344
    JSONObject jsonObject = (JSONObject) JSONValue.parse(json);
375
    JSONObject jsonForms = (JSONObject) JSONValue.parse(json);

    
   
376

   

    
   
377
    for(Field formField : klass.getDeclaredFields()) {

    
   
378
      formField.setAccessible(true);

    
   
379
      String formName = formField.getName();
345

    
   
380

   
346
    for(Object k : jsonObject.keySet()) {
381
      // We're processing only form validations
347
      String key = (String)k;
382
      Form formAnnotation = formField.getAnnotation(Form.class);

    
   
383
      if(formAnnotation == null) {

    
   
384
        continue;

    
   
385
      }
348

    
   
386

   
349
      Field field;

   
350
      try {
387
      try {
351
        field = klass.getDeclaredField(key);
388
        formField.set(configuration, formField.getType().newInstance());
352
      } catch (NoSuchFieldException e) {
389
      } catch (Exception e) {
353
        throw new SqoopException(ModelError.MODEL_006,
390
        throw new SqoopException(ModelError.MODEL_005,
354
          "Missing field " + key, e);
391
          "Issue with field " + formName, e);
355
      }
392
      }
356

    
   
393

   
357
      // We need to access this field even if it would be declared as private
394
      JSONObject jsonInputs = (JSONObject) jsonForms.get(formField.getName());
358
      field.setAccessible(true);
395
      if(jsonInputs == null) {
359
      Class type = field.getType();
396
        continue;

    
   
397
      }

    
   
398

   

    
   
399
      Object formValue;

    
   
400
      try {

    
   
401
        formValue = formField.get(configuration);

    
   
402
      } catch (IllegalAccessException e) {

    
   
403
        throw new SqoopException(ModelError.MODEL_005,

    
   
404
          "Issue with field " + formName, e);

    
   
405
      }

    
   
406

   

    
   
407
      for(Field inputField : formField.getType().getDeclaredFields()) {

    
   
408
        inputField.setAccessible(true);

    
   
409
        String inputName = inputField.getName();

    
   
410

   

    
   
411
        Input inputAnnotation = inputField.getAnnotation(Input.class);

    
   
412

   

    
   
413
        if(inputAnnotation == null || jsonInputs.get(inputName) == null) {

    
   
414
          try {

    
   
415
            inputField.set(formValue, null);

    
   
416
          } catch (IllegalAccessException e) {

    
   
417
            throw new SqoopException(ModelError.MODEL_005,

    
   
418
              "Issue with field " + formName + "." + inputName, e);

    
   
419
          }

    
   
420
          continue;

    
   
421
        }

    
   
422

   

    
   
423
        Class type = inputField.getType();
360

    
   
424

   
361
      try {
425
        try {
362
        if(type == String.class) {
426
          if(type == String.class) {
363
          field.set(configuration, jsonObject.get(key));
427
            inputField.set(formValue, jsonInputs.get(inputName));
364
        } else if (type.isAssignableFrom(Map.class)) {
428
          } else if (type.isAssignableFrom(Map.class)) {
365
          Map<String, String> map = new HashMap<String, String>();
429
            Map<String, String> map = new HashMap<String, String>();
366
          for(Object kk : jsonObject.keySet()) {
430
            JSONObject jsonObject = (JSONObject) jsonInputs.get(inputName);
367
            map.put((String)kk, (String)jsonObject.get(kk));
431
            for(Object key : jsonObject.keySet()) {

    
   
432
              map.put((String)key, (String)jsonObject.get(key));
368
          }
433
            }
369
          field.set(key, map);
434
            inputField.set(formValue, map);
370
        } else if(type == Integer.class) {
435
          } else if(type == Integer.class) {
371
          field.set(configuration, jsonObject.get(key));
436
            inputField.set(formValue, ((Long)jsonInputs.get(inputName)).intValue());
372
        } else if(type == Integer.class) {
437
          } else if(type.isEnum()) {
373
          field.set(configuration, Enum.valueOf((Class<? extends Enum>)field.getType(), (String) jsonObject.get(key)));
438
            inputField.set(formValue, Enum.valueOf((Class<? extends Enum>) inputField.getType(), (String) jsonInputs.get(inputName)));
374
        } else {
439
          } else {
375
          throw new SqoopException(ModelError.MODEL_004,
440
            throw new SqoopException(ModelError.MODEL_004,
376
            "Unsupported type " + type.getName() + " for input " + key);
441
              "Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
377
        }
442
          }
378
      } catch (IllegalAccessException e) {
443
        } catch (IllegalAccessException e) {
379
        throw new SqoopException(ModelError.MODEL_005,
444
          throw new SqoopException(ModelError.MODEL_005,
380
          "Issue with field " + field.getName(), e);
445
            "Issue with field " + formName + "." + inputName, e);

    
   
446
        }
381
      }
447
      }
382
    }
448
    }
383
  }
449
  }
384

    
   
450

   
385
}
451
}
common/src/test/java/org/apache/sqoop/model/TestFormUtils.java
Revision c80bd45947a364e9002db0e7ff0a2603ba75263d New Change
 
  1. common/src/main/java/org/apache/sqoop/model/FormUtils.java: Loading...
  2. common/src/test/java/org/apache/sqoop/model/TestFormUtils.java: Loading...