You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sqoop.apache.org by ab...@apache.org on 2014/10/03 08:53:26 UTC

[12/13] SQOOP-1498: Sqoop2: Repository Object refactoring (objects prefixed with M)

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java b/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java
new file mode 100644
index 0000000..cec46f6
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java
@@ -0,0 +1,226 @@
+/**
+ * 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.json.util;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sqoop.common.SqoopException;
+import org.apache.sqoop.model.MBooleanInput;
+import org.apache.sqoop.model.MEnumInput;
+import org.apache.sqoop.model.MConfig;
+import org.apache.sqoop.model.MConfigType;
+import org.apache.sqoop.model.MInput;
+import org.apache.sqoop.model.MInputType;
+import org.apache.sqoop.model.MIntegerInput;
+import org.apache.sqoop.model.MMapInput;
+import org.apache.sqoop.model.MStringInput;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Convenient static methods for serializing config objects.
+ */
+public final class ConfigSerialization {
+
+  public static final String ALL = "all";
+  public static final String ID = "id";
+  public static final String NAME = "name";
+  public static final String VERSION = "version";
+  public static final String CLASS = "class";
+  public static final String ENABLED = "enabled";
+  public static final String CREATION_USER = "creation-user";
+  public static final String CREATION_DATE = "creation-date";
+  public static final String UPDATE_USER = "update-user";
+  public static final String UPDATE_DATE = "update-date";
+  // TODO(VB): Move these constants to connector bean
+  public static final String CONNECTOR_LINK_CONFIG = "link-config";
+  public static final String CONNECTOR_JOB_CONFIG = "job-config";
+  // TODO:move these configs to driver bean
+  public static final String DRIVER_VERSION = "driver-version";
+  public static final String DRIVER_CONFIG = "driver-config";
+
+  public static final String CONFIG_NAME = "name";
+  public static final String CONFIG_TYPE = "type";
+  public static final String CONFIG_INPUTS = "inputs";
+  public static final String CONFIG_INPUT_NAME = "name";
+  public static final String CONFIG_INPUT_TYPE = "type";
+  public static final String CONFIG_INPUT_SENSITIVE = "sensitive";
+  public static final String CONFIG_INPUT_SIZE = "size";
+  public static final String CONFIG_INPUT_VALUE = "value";
+  public static final String CONFIG_INPUT_VALUES = "values";
+
+  /**
+   * Transform given list of configs to JSON Array object.
+   *
+   * @param mConfigs List of configs.
+   * @return JSON object with serialized config of the list.
+   */
+  @SuppressWarnings("unchecked")
+  public static JSONArray extractConfigList(List<MConfig> mConfigs, boolean skipSensitive) {
+    JSONArray configs = new JSONArray();
+
+    for (MConfig mConfig : mConfigs) {
+      configs.add(extractConfig(mConfig, skipSensitive));
+    }
+
+    return configs;
+  }
+
+  /**
+   * Transform given config to JSON Object.
+   *
+   * @param mConfig Given MConfig instance
+   * @param skipSensitive conditionally add sensitive input values
+   * @return Serialized JSON object.
+   */
+  @SuppressWarnings("unchecked")
+  static JSONObject extractConfig(MConfig mConfig, boolean skipSensitive) {
+    JSONObject config = new JSONObject();
+    config.put(ID, mConfig.getPersistenceId());
+    config.put(CONFIG_NAME, mConfig.getName());
+    config.put(CONFIG_TYPE, MConfigType.LINK.toString());
+    JSONArray mInputs = new JSONArray();
+    config.put(CONFIG_INPUTS, mInputs);
+
+    for (MInput<?> mInput : mConfig.getInputs()) {
+      JSONObject input = new JSONObject();
+      input.put(ID, mInput.getPersistenceId());
+      input.put(CONFIG_INPUT_NAME, mInput.getName());
+      input.put(CONFIG_INPUT_TYPE, mInput.getType().toString());
+      input.put(CONFIG_INPUT_SENSITIVE, mInput.isSensitive());
+
+      // String specific serialization
+      if (mInput.getType() == MInputType.STRING) {
+        input.put(CONFIG_INPUT_SIZE,
+            ((MStringInput)mInput).getMaxLength());
+      }
+
+      // Enum specific serialization
+      if(mInput.getType() == MInputType.ENUM) {
+        input.put(CONFIG_INPUT_VALUES,
+          StringUtils.join(((MEnumInput)mInput).getValues(), ","));
+      }
+
+      // Serialize value if is there
+      // Skip if sensitive
+      if (!mInput.isEmpty() && !(skipSensitive && mInput.isSensitive())) {
+        if (mInput.getType() == MInputType.MAP) {
+          input.put(CONFIG_INPUT_VALUE, mInput.getValue());
+        } else {
+          input.put(CONFIG_INPUT_VALUE, mInput.getUrlSafeValueString());
+        }
+      }
+
+      mInputs.add(input);
+    }
+
+    return config;
+  }
+
+  /**
+   * Restore List of MConfigs from JSON Array.
+   *
+   * @param configs JSON array representing list of MConfigs
+   * @return Restored list of MConfigs
+   */
+  public static List<MConfig> restoreConfigList(JSONArray configs) {
+    List<MConfig> mConfigs = new ArrayList<MConfig>();
+
+    for (int i = 0; i < configs.size(); i++) {
+      mConfigs.add(restoreConfig((JSONObject) configs.get(i)));
+    }
+
+    return mConfigs;
+  }
+
+  /**
+   * Restore one MConfig from JSON Object.
+   *
+   * @param config JSON representation of the MConfig.
+   * @return Restored MConfig.
+   */
+  static MConfig restoreConfig(JSONObject config) {
+    JSONArray inputs = (JSONArray) config.get(CONFIG_INPUTS);
+
+    List<MInput<?>> mInputs = new ArrayList<MInput<?>>();
+    for (int i = 0; i < inputs.size(); i++) {
+      JSONObject input = (JSONObject) inputs.get(i);
+      MInputType type =
+          MInputType.valueOf((String) input.get(CONFIG_INPUT_TYPE));
+      String name = (String) input.get(CONFIG_INPUT_NAME);
+      Boolean sensitive = (Boolean) input.get(CONFIG_INPUT_SENSITIVE);
+      MInput mInput = null;
+      switch (type) {
+        case STRING: {
+          long size = (Long) input.get(CONFIG_INPUT_SIZE);
+          mInput = new MStringInput(name, sensitive.booleanValue(), (short) size);
+          break;
+        }
+        case MAP: {
+          mInput = new MMapInput(name, sensitive.booleanValue());
+          break;
+        }
+        case INTEGER: {
+          mInput = new MIntegerInput(name, sensitive.booleanValue());
+          break;
+        }
+        case BOOLEAN: {
+          mInput = new MBooleanInput(name, sensitive.booleanValue());
+          break;
+        }
+        case ENUM: {
+          String values = (String) input.get(CONFIG_INPUT_VALUES);
+          mInput = new MEnumInput(name, sensitive.booleanValue(), values.split(","));
+          break;
+        }
+      }
+
+      // Propagate config ID
+      mInput.setPersistenceId((Long)input.get(ID));
+
+      // Propagate config optional value
+      if(input.containsKey(CONFIG_INPUT_VALUE)) {
+        switch (type) {
+        case MAP:
+          try {
+            mInput.setValue((Map<String, String>)input.get(CONFIG_INPUT_VALUE));
+          } catch (ClassCastException e) {
+            throw new SqoopException(SerializationError.SERIALIZATION_001, name + " requires a 'map' value.");
+          }
+          break;
+        default:
+          mInput.restoreFromUrlSafeValueString(
+              (String) input.get(CONFIG_INPUT_VALUE));
+          break;
+        }
+      }
+      mInputs.add(mInput);
+    }
+
+    MConfig mConfig = new MConfig((String) config.get(CONFIG_NAME), mInputs);
+    mConfig.setPersistenceId((Long) config.get(ID));
+    return mConfig;
+  }
+
+  private ConfigSerialization() {
+    // Do not instantiate
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java b/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java
deleted file mode 100644
index 77f6191..0000000
--- a/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- * 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.json.util;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.sqoop.common.SqoopException;
-import org.apache.sqoop.model.MBooleanInput;
-import org.apache.sqoop.model.MEnumInput;
-import org.apache.sqoop.model.MForm;
-import org.apache.sqoop.model.MFormType;
-import org.apache.sqoop.model.MInput;
-import org.apache.sqoop.model.MInputType;
-import org.apache.sqoop.model.MIntegerInput;
-import org.apache.sqoop.model.MMapInput;
-import org.apache.sqoop.model.MStringInput;
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Convenient static methods for serializing forms.
- */
-public final class FormSerialization {
-
-  public static final String ALL = "all";
-  public static final String ID = "id";
-  public static final String NAME = "name";
-  public static final String VERSION = "version";
-  public static final String DRIVER_VERSION = "driver-version";
-  public static final String CLASS = "class";
-  public static final String ENABLED = "enabled";
-  public static final String CREATION_USER = "creation-user";
-  public static final String CREATION_DATE = "creation-date";
-  public static final String UPDATE_USER = "update-user";
-  public static final String UPDATE_DATE = "update-date";
-  public static final String CON_FORMS = "con-forms";
-  public static final String JOB_FORMS = "job-forms";
-
-  public static final String FORM_NAME = "name";
-  public static final String FORM_TYPE = "type";
-  public static final String FORM_INPUTS = "inputs";
-  public static final String FORM_INPUT_NAME = "name";
-  public static final String FORM_INPUT_TYPE = "type";
-  public static final String FORM_INPUT_SENSITIVE = "sensitive";
-  public static final String FORM_INPUT_SIZE = "size";
-  public static final String FORM_INPUT_VALUE = "value";
-  public static final String FORM_INPUT_VALUES = "values";
-
-  /**
-   * Transform given list of forms to JSON Array object.
-   *
-   * @param mForms List of forms.
-   * @return JSON object with serialized form of the list.
-   */
-  @SuppressWarnings("unchecked")
-  public static JSONArray extractForms(List<MForm> mForms, boolean skipSensitive) {
-    JSONArray forms = new JSONArray();
-
-    for (MForm mForm : mForms) {
-      forms.add(extractForm(mForm, skipSensitive));
-    }
-
-    return forms;
-  }
-
-  /**
-   * Transform given form to JSON Object.
-   *
-   * @param mForm Given MForm instance
-   * @param skipSensitive conditionally add sensitive input values
-   * @return Serialized JSON object.
-   */
-  @SuppressWarnings("unchecked")
-  public static JSONObject extractForm(MForm mForm, boolean skipSensitive) {
-    JSONObject form = new JSONObject();
-    form.put(ID, mForm.getPersistenceId());
-    form.put(FORM_NAME, mForm.getName());
-    form.put(FORM_TYPE, MFormType.CONNECTION.toString());
-    JSONArray mInputs = new JSONArray();
-    form.put(FORM_INPUTS, mInputs);
-
-    for (MInput<?> mInput : mForm.getInputs()) {
-      JSONObject input = new JSONObject();
-      input.put(ID, mInput.getPersistenceId());
-      input.put(FORM_INPUT_NAME, mInput.getName());
-      input.put(FORM_INPUT_TYPE, mInput.getType().toString());
-      input.put(FORM_INPUT_SENSITIVE, mInput.isSensitive());
-
-      // String specific serialization
-      if (mInput.getType() == MInputType.STRING) {
-        input.put(FORM_INPUT_SIZE,
-            ((MStringInput)mInput).getMaxLength());
-      }
-
-      // Enum specific serialization
-      if(mInput.getType() == MInputType.ENUM) {
-        input.put(FORM_INPUT_VALUES,
-          StringUtils.join(((MEnumInput)mInput).getValues(), ","));
-      }
-
-      // Serialize value if is there
-      // Skip if sensitive
-      if (!mInput.isEmpty() && !(skipSensitive && mInput.isSensitive())) {
-        if (mInput.getType() == MInputType.MAP) {
-          input.put(FORM_INPUT_VALUE, mInput.getValue());
-        } else {
-          input.put(FORM_INPUT_VALUE, mInput.getUrlSafeValueString());
-        }
-      }
-
-      mInputs.add(input);
-    }
-
-    return form;
-  }
-
-  /**
-   * Restore List of MForms from JSON Array.
-   *
-   * @param forms JSON array representing list of MForms
-   * @return Restored list of MForms
-   */
-  public static List<MForm> restoreForms(JSONArray forms) {
-    List<MForm> mForms = new ArrayList<MForm>();
-
-    for (int i = 0; i < forms.size(); i++) {
-      mForms.add(restoreForm((JSONObject) forms.get(i)));
-    }
-
-    return mForms;
-  }
-
-  /**
-   * Restore one MForm from JSON Object.
-   *
-   * @param form JSON representation of the MForm.
-   * @return Restored MForm.
-   */
-  public static MForm restoreForm(JSONObject form) {
-    JSONArray inputs = (JSONArray) form.get(FORM_INPUTS);
-
-    List<MInput<?>> mInputs = new ArrayList<MInput<?>>();
-    for (int i = 0; i < inputs.size(); i++) {
-      JSONObject input = (JSONObject) inputs.get(i);
-      MInputType type =
-          MInputType.valueOf((String) input.get(FORM_INPUT_TYPE));
-      String name = (String) input.get(FORM_INPUT_NAME);
-      Boolean sensitive = (Boolean) input.get(FORM_INPUT_SENSITIVE);
-      MInput mInput = null;
-      switch (type) {
-        case STRING: {
-          long size = (Long) input.get(FORM_INPUT_SIZE);
-          mInput = new MStringInput(name, sensitive.booleanValue(), (short) size);
-          break;
-        }
-        case MAP: {
-          mInput = new MMapInput(name, sensitive.booleanValue());
-          break;
-        }
-        case INTEGER: {
-          mInput = new MIntegerInput(name, sensitive.booleanValue());
-          break;
-        }
-        case BOOLEAN: {
-          mInput = new MBooleanInput(name, sensitive.booleanValue());
-          break;
-        }
-        case ENUM: {
-          String values = (String) input.get(FORM_INPUT_VALUES);
-          mInput = new MEnumInput(name, sensitive.booleanValue(), values.split(","));
-          break;
-        }
-      }
-
-      // Propagate form ID
-      mInput.setPersistenceId((Long)input.get(ID));
-
-      // Propagate form optional value
-      if(input.containsKey(FORM_INPUT_VALUE)) {
-        switch (type) {
-        case MAP:
-          try {
-            mInput.setValue((Map<String, String>)input.get(FORM_INPUT_VALUE));
-          } catch (ClassCastException e) {
-            throw new SqoopException(SerializationError.SERIALIZATION_001, name + " requires a 'map' value.");
-          }
-          break;
-        default:
-          mInput.restoreFromUrlSafeValueString(
-              (String) input.get(FORM_INPUT_VALUE));
-          break;
-        }
-      }
-      mInputs.add(mInput);
-    }
-
-    MForm mForm = new MForm((String) form.get(FORM_NAME), mInputs);
-    mForm.setPersistenceId((Long) form.get(ID));
-    return mForm;
-  }
-
-  private FormSerialization() {
-    // Do not instantiate
-  }
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java b/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java
index 20ee5f3..f27d81d 100644
--- a/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java
+++ b/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java
@@ -43,7 +43,6 @@ public final class ResourceBundleSerialization {
     for (ResourceBundle bundle : bundles) {
       array.add(extractResourceBundle(bundle));
     }
-
     return array;
   }
 
@@ -58,7 +57,6 @@ public final class ResourceBundleSerialization {
     return json;
   }
 
-  @SuppressWarnings("unchecked")
   public static List<ResourceBundle> restoreResourceBundles(JSONArray array) {
     List<ResourceBundle> bundles = new LinkedList<ResourceBundle>();
     for (Object item : array) {

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/Config.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/Config.java b/common/src/main/java/org/apache/sqoop/model/Config.java
new file mode 100644
index 0000000..46e7268
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/Config.java
@@ -0,0 +1,28 @@
+/**
+ * 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denote config in Configuration class
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Config {
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/ConfigClass.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/ConfigClass.java b/common/src/main/java/org/apache/sqoop/model/ConfigClass.java
new file mode 100644
index 0000000..f925759
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/ConfigClass.java
@@ -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 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 ConfigClass {
+
+  /**
+   * Default size for Inputs in this config.
+   *
+   * @return
+   */
+  short defaultSize() default -1;
+
+  /**
+   * List of validators associated with this config.
+   *
+   * @return
+   */
+  Validator[] validators() default {};
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java b/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java
new file mode 100644
index 0000000..290e7fc
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java
@@ -0,0 +1,565 @@
+/**
+ * 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.common.SqoopException;
+import org.apache.sqoop.utils.ClassUtils;
+import org.apache.sqoop.validation.Message;
+import org.apache.sqoop.validation.Status;
+import org.apache.sqoop.validation.ConfigValidator;
+import org.apache.sqoop.validation.ConfigValidationResult;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Util class for transforming data from correctly annotated configuration
+ * objects to different structures and vice-versa.
+ *
+ * TODO: This class should see some overhaul into more reusable code, especially expose and re-use the methods at the end.
+ */
+public class ConfigUtils {
+
+  /**
+   * Transform correctly annotated configuration object to corresponding
+   * list of configs.
+   *
+   * Configs will be order according to the occurrence in the configuration
+   * class. Inputs will be also ordered based on occurrence.
+   *
+   * @param configuration Annotated arbitrary configuration object
+   * @return Corresponding list of configs
+   */
+  public static List<MConfig> toConfigs(Object configuration) {
+    return toConfigs(configuration.getClass(), configuration);
+  }
+
+  public static List<MConfig> toConfigs(Class klass) {
+    return toConfigs(klass, null);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static List<MConfig> toConfigs(Class klass, Object configuration) {
+    ConfigurationClass configurationClass =
+      (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class);
+
+    // Each configuration object must have this class annotation
+    if(configurationClass == null) {
+      throw new SqoopException(ModelError.MODEL_003,
+        "Missing annotation ConfigurationClass on class " + klass.getName());
+    }
+
+    List<MConfig> configs = new LinkedList<MConfig>();
+
+    // Iterate over all declared fields
+    for (Field field : klass.getDeclaredFields()) {
+      field.setAccessible(true);
+
+      String formName = field.getName();
+
+      // Each field that should be part of user input should have Input
+      // annotation.
+      Config formAnnotation = field.getAnnotation(Config.class);
+
+      if(formAnnotation != null) {
+        Class type = field.getType();
+
+        Object value = null;
+        if(configuration != null) {
+          try {
+            value = field.get(configuration);
+          } catch (IllegalAccessException e) {
+            throw new SqoopException(ModelError.MODEL_005,
+              "Can't retrieve value from " + field.getName(), e);
+          }
+        }
+
+        configs.add(toConfig(formName, type, value));
+      }
+    }
+
+    return configs;
+  }
+
+  @SuppressWarnings("unchecked")
+  private static MConfig toConfig(String formName, Class klass, Object object) {
+     ConfigClass global =
+      (ConfigClass)klass.getAnnotation(ConfigClass.class);
+
+    // Each configuration object must have this class annotation
+    if(global == null) {
+      throw new SqoopException(ModelError.MODEL_003,
+        "Missing annotation ConfigClass on class " + klass.getName());
+    }
+
+    // Intermediate list of inputs
+    List<MInput<?>> inputs = new LinkedList<MInput<?>>();
+
+    // Iterate over all declared fields
+    for (Field field : klass.getDeclaredFields()) {
+      field.setAccessible(true);
+
+      String fieldName = field.getName();
+      String inputName = formName + "." + fieldName;
+
+      // Each field that should be part of user input should have Input
+      // annotation.
+      Input inputAnnotation = field.getAnnotation(Input.class);
+
+      if(inputAnnotation != null) {
+        boolean sensitive = inputAnnotation.sensitive();
+        short maxLen = inputAnnotation.size();
+        Class type = field.getType();
+
+        MInput input;
+
+        // 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);
+        }
+
+        // Instantiate corresponding MInput<?> structure
+        if(type == String.class) {
+          input = new MStringInput(inputName, sensitive, maxLen);
+        } else if (type.isAssignableFrom(Map.class)) {
+          input = new MMapInput(inputName, sensitive);
+        } else if(type == Integer.class) {
+          input = new MIntegerInput(inputName, sensitive);
+        } else if(type == Boolean.class) {
+          input = new MBooleanInput(inputName, sensitive);
+        } else if(type.isEnum()) {
+          input = new MEnumInput(inputName, sensitive, ClassUtils.getEnumStrings(type));
+        } else {
+          throw new SqoopException(ModelError.MODEL_004,
+            "Unsupported type " + type.getName() + " for input " + fieldName);
+        }
+
+        // Move value if it's present in original configuration object
+        if(object != null) {
+          Object value;
+          try {
+            value = field.get(object);
+          } catch (IllegalAccessException e) {
+            throw new SqoopException(ModelError.MODEL_005,
+              "Can't retrieve value from " + field.getName(), e);
+          }
+          if(value == null) {
+            input.setEmpty();
+          } else {
+            input.setValue(value);
+          }
+        }
+
+        inputs.add(input);
+      }
+    }
+
+    return new MConfig(formName, inputs);
+  }
+
+  /**
+   * Move config values from config list into corresponding configuration object.
+   *
+   * @param configs Input config list
+   * @param configuration Output configuration object
+   */
+  public static void fromConfigs(List<MConfig> configs, Object configuration) {
+    Class klass = configuration.getClass();
+
+    for(MConfig config : configs) {
+      Field configField;
+      try {
+        configField = klass.getDeclaredField(config.getName());
+      } catch (NoSuchFieldException e) {
+        throw new SqoopException(ModelError.MODEL_006,
+          "Missing field " + config.getName() + " on config class " + klass.getCanonicalName(), e);
+      }
+
+      // We need to access this field even if it would be declared as private
+      configField.setAccessible(true);
+
+      Class configClass = configField.getType();
+      Object newValue = ClassUtils.instantiate(configClass);
+
+      if(newValue == null) {
+        throw new SqoopException(ModelError.MODEL_006,
+          "Can't instantiate new config " + configClass);
+      }
+
+      for(MInput input : config.getInputs()) {
+        String[] splitNames = input.getName().split("\\.");
+        if(splitNames.length != 2) {
+          throw new SqoopException(ModelError.MODEL_009,
+            "Invalid name: " + input.getName());
+        }
+
+        String inputName = splitNames[1];
+        // TODO(jarcec): Names structures fix, handle error cases
+        Field inputField;
+        try {
+          inputField = configClass.getDeclaredField(inputName);
+        } catch (NoSuchFieldException e) {
+          throw new SqoopException(ModelError.MODEL_006,
+            "Missing field " + input.getName(), e);
+        }
+
+        // We need to access this field even if it would be declared as private
+        inputField.setAccessible(true);
+
+        try {
+          if(input.isEmpty()) {
+            inputField.set(newValue, null);
+          } else {
+            if (input.getType() == MInputType.ENUM) {
+              inputField.set(newValue, Enum.valueOf((Class<? extends Enum>)inputField.getType(), (String) input.getValue()));
+            } else {
+              inputField.set(newValue, input.getValue());
+            }
+          }
+        } catch (IllegalAccessException e) {
+          throw new SqoopException(ModelError.MODEL_005,
+            "Issue with field " + inputField.getName(), e);
+        }
+      }
+
+      try {
+        configField.set(configuration, newValue);
+      } catch (IllegalAccessException e) {
+        throw new SqoopException(ModelError.MODEL_005,
+          "Issue with field " + configField.getName(), e);
+      }
+    }
+  }
+
+  /**
+   * Apply validations on the configs.
+   *
+   * @param configs Configs that should be updated
+   * @param validation Validation that we should apply
+   */
+  public static void applyValidation(List<MConfig> configs, ConfigValidator validation) {
+    Map<ConfigValidator.ConfigInput, ConfigValidator.Message> messages = validation.getMessages();
+
+    for(MConfig config : configs) {
+      applyValidation(config, messages);
+
+      for(MInput input : config.getInputs()) {
+        applyValidation(input, messages);
+      }
+    }
+  }
+
+  /**
+   * Apply validation on given validated element.
+   *
+   * @param element Element on what we're applying the validations
+   * @param messages Map of all validation messages
+   */
+  public static void applyValidation(MValidatedElement element, Map<ConfigValidator.ConfigInput, ConfigValidator.Message> messages) {
+    ConfigValidator.ConfigInput name = new ConfigValidator.ConfigInput(element.getName());
+
+    if(messages.containsKey(name)) {
+      ConfigValidator.Message message = messages.get(name);
+      element.addValidationMessage(new Message(message.getStatus(), message.getMessage()));
+    } else {
+      element.addValidationMessage(new Message(Status.getDefault(), null));
+    }
+  }
+
+
+  /**
+   * Apply given validations on list of configs.
+   *
+   * @param configs
+   * @param result
+   */
+  public static void applyValidation(List<MConfig> configs, ConfigValidationResult result) {
+    for(MConfig config : configs) {
+      applyValidation(config, result);
+
+      for(MInput input : config.getInputs()) {
+        applyValidation(input, result);
+      }
+    }
+  }
+
+  /**
+   * Apply validation messages on given element.
+   *
+   * Element's state will be set to default if there are no associated messages.
+   *
+   * @param element
+   * @param result
+   */
+  public static void applyValidation(MValidatedElement element, ConfigValidationResult result) {
+    List<Message> messages = result.getMessages().get(element.getName());
+
+    if(messages != null) {
+      element.setValidationMessages(messages);
+    } else {
+      element.resetValidationMessages();
+    }
+  }
+
+  /**
+   * 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();
+
+    ConfigurationClass configurationClass =
+      (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class);
+
+    // Each configuration object must have this class annotation
+    if(configurationClass == null) {
+      throw new SqoopException(ModelError.MODEL_003,
+        "Missing annotation ConfigurationGroup on class " + klass.getName());
+    }
+
+    JSONObject jsonOutput = new JSONObject();
+
+    // Iterate over all declared fields
+    for (Field formField : klass.getDeclaredFields()) {
+      formField.setAccessible(true);
+      String formName = formField.getName();
+
+      // We're processing only config validations
+      Config formAnnotation = formField.getAnnotation(Config.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 jsonConfig = new JSONObject();
+
+      // Now process each input on the config
+      for(Field inputField : formField.getType().getDeclaredFields()) {
+        inputField.setAccessible(true);
+        String inputName = inputField.getName();
+
+        Object value;
+        try {
+          value = inputField.get(formValue);
+        } catch (IllegalAccessException e) {
+          throw new SqoopException(ModelError.MODEL_005,
+            "Issue with field " + formName + "." + inputName, e);
+        }
+
+        Input inputAnnotation = inputField.getAnnotation(Input.class);
+
+        // Do not serialize all values
+        if(inputAnnotation != null && value != null) {
+          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 " + formName + "." + inputName);
+          }
+
+          if(type == String.class) {
+            jsonConfig.put(inputName, value);
+          } else if (type.isAssignableFrom(Map.class)) {
+            JSONObject map = new JSONObject();
+            for(Object key : ((Map)value).keySet()) {
+              map.put(key, ((Map)value).get(key));
+            }
+            jsonConfig.put(inputName, map);
+          } else if(type == Integer.class) {
+            jsonConfig.put(inputName, value);
+          } else if(type.isEnum()) {
+            jsonConfig.put(inputName, value.toString());
+          } else if(type == Boolean.class) {
+            jsonConfig.put(inputName, value);
+          }else {
+            throw new SqoopException(ModelError.MODEL_004,
+              "Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
+          }
+        }
+      }
+
+      jsonOutput.put(formName, jsonConfig);
+    }
+
+    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 ConfigurationGroup object to be filled
+   */
+  public static void fillValues(String json, Object configuration) {
+    Class klass = configuration.getClass();
+
+    JSONObject jsonConfigs = (JSONObject) JSONValue.parse(json);
+
+    for(Field configField : klass.getDeclaredFields()) {
+      configField.setAccessible(true);
+      String configName = configField.getName();
+
+      // We're processing only config validations
+      Config formAnnotation = configField.getAnnotation(Config.class);
+      if(formAnnotation == null) {
+        continue;
+      }
+
+      try {
+        configField.set(configuration, configField.getType().newInstance());
+      } catch (Exception e) {
+        throw new SqoopException(ModelError.MODEL_005,
+          "Issue with field " + configName, e);
+      }
+
+      JSONObject jsonInputs = (JSONObject) jsonConfigs.get(configField.getName());
+      if(jsonInputs == null) {
+        continue;
+      }
+
+      Object configValue;
+      try {
+        configValue = configField.get(configuration);
+      } catch (IllegalAccessException e) {
+        throw new SqoopException(ModelError.MODEL_005,
+          "Issue with field " + configName, e);
+      }
+
+      for(Field inputField : configField.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(configValue, null);
+          } catch (IllegalAccessException e) {
+            throw new SqoopException(ModelError.MODEL_005,
+              "Issue with field " + configName + "." + inputName, e);
+          }
+          continue;
+        }
+
+        Class type = inputField.getType();
+
+        try {
+          if(type == String.class) {
+            inputField.set(configValue, jsonInputs.get(inputName));
+          } else if (type.isAssignableFrom(Map.class)) {
+            Map<String, String> map = new HashMap<String, String>();
+            JSONObject jsonObject = (JSONObject) jsonInputs.get(inputName);
+            for(Object key : jsonObject.keySet()) {
+              map.put((String)key, (String)jsonObject.get(key));
+            }
+            inputField.set(configValue, map);
+          } else if(type == Integer.class) {
+            inputField.set(configValue, ((Long)jsonInputs.get(inputName)).intValue());
+          } else if(type.isEnum()) {
+            inputField.set(configValue, Enum.valueOf((Class<? extends Enum>) inputField.getType(), (String) jsonInputs.get(inputName)));
+          } else if(type == Boolean.class) {
+            inputField.set(configValue, (Boolean) jsonInputs.get(inputName));
+          }else {
+            throw new SqoopException(ModelError.MODEL_004,
+              "Unsupported type " + type.getName() + " for input " + configName + "." + inputName);
+          }
+        } catch (IllegalAccessException e) {
+          throw new SqoopException(ModelError.MODEL_005,
+            "Issue with field " + configName + "." + inputName, e);
+        }
+      }
+    }
+  }
+
+  public static String getName(Field input, Input annotation) {
+    return input.getName();
+  }
+
+  public static String getName(Field config, Config annotation) {
+    return config.getName();
+  }
+
+  public static ConfigurationClass getConfigurationClassAnnotation(Object object, boolean strict) {
+    ConfigurationClass annotation = object.getClass().getAnnotation(ConfigurationClass.class);
+
+    if(strict && annotation == null) {
+      throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationGroupClass on class " + object.getClass().getName());
+    }
+
+    return annotation;
+  }
+
+  public static ConfigClass getConfigClassAnnotation(Object object, boolean strict) {
+    ConfigClass annotation = object.getClass().getAnnotation(ConfigClass.class);
+
+    if(strict && annotation == null) {
+      throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationGroupClass on class " + object.getClass().getName());
+    }
+
+    return annotation;
+  }
+
+  public static Config getConfigAnnotation(Field field, boolean strict) {
+    Config annotation = field.getAnnotation(Config.class);
+
+    if(strict && annotation == null) {
+      throw new SqoopException(ModelError.MODEL_003, "Missing annotation Config on Field " + field.getName() + " on class " + field.getDeclaringClass().getName());
+    }
+
+    return annotation;
+  }
+
+  public static Input getInputAnnotation(Field field, boolean strict) {
+    Input annotation = field.getAnnotation(Input.class);
+
+    if(strict && annotation == null) {
+      throw new SqoopException(ModelError.MODEL_003, "Missing annotation Input on Field " + field.getName() + " on class " + field.getDeclaringClass().getName());
+    }
+
+    return annotation;
+  }
+
+  public static Object getFieldValue(Field field, Object object) {
+    try {
+      field.setAccessible(true);
+      return field.get(object);
+    } catch (IllegalAccessException e) {
+      throw new SqoopException(ModelError.MODEL_012, e);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java b/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java
index 73374d8..c65c478 100644
--- a/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java
+++ b/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java
@@ -23,15 +23,16 @@ 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.
+ * Class annotation to represent configuration for the connectors
+ * Each class that is used a configuration group object, the connector developer
+ * is expected to provide the inputs needed for this annotation
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface ConfigurationClass {
 
   /**
-   * List of validators associated with this Configuration class.
+   * List of validators associated with this Configuration group class.
    *
    * @return
    */

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/Form.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/Form.java b/common/src/main/java/org/apache/sqoop/model/Form.java
deleted file mode 100644
index 4321582..0000000
--- a/common/src/main/java/org/apache/sqoop/model/Form.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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 java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Denote form in Configuration class
- */
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Form {
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/FormClass.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/FormClass.java b/common/src/main/java/org/apache/sqoop/model/FormClass.java
deleted file mode 100644
index 6048d03..0000000
--- a/common/src/main/java/org/apache/sqoop/model/FormClass.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * 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 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 {
-
-  /**
-   * Default size for Inputs in this form.
-   *
-   * @return
-   */
-  short defaultSize() default -1;
-
-  /**
-   * List of validators associated with this form.
-   *
-   * @return
-   */
-  Validator[] validators() default {};
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/FormUtils.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/FormUtils.java b/common/src/main/java/org/apache/sqoop/model/FormUtils.java
deleted file mode 100644
index ae025ab..0000000
--- a/common/src/main/java/org/apache/sqoop/model/FormUtils.java
+++ /dev/null
@@ -1,565 +0,0 @@
-/**
- * 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.common.SqoopException;
-import org.apache.sqoop.utils.ClassUtils;
-import org.apache.sqoop.validation.Message;
-import org.apache.sqoop.validation.Status;
-import org.apache.sqoop.validation.Validation;
-import org.apache.sqoop.validation.ValidationResult;
-import org.json.simple.JSONObject;
-import org.json.simple.JSONValue;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Util class for transforming data from correctly annotated configuration
- * objects to different structures and vice-versa.
- *
- * TODO: This class should see some overhaul into more reusable code, especially expose and re-use the methods at the end.
- */
-public class FormUtils {
-
-  /**
-   * Transform correctly annotated configuration object to corresponding
-   * list of forms.
-   *
-   * Forms will be order according to the occurrence in the configuration
-   * class. Inputs will be also ordered based on occurrence.
-   *
-   * @param configuration Annotated arbitrary configuration object
-   * @return Corresponding list of forms
-   */
-  public static List<MForm> toForms(Object configuration) {
-    return toForms(configuration.getClass(), configuration);
-  }
-
-  public static List<MForm> toForms(Class klass) {
-    return toForms(klass, null);
-  }
-
-  @SuppressWarnings("unchecked")
-  public static List<MForm> toForms(Class klass, Object configuration) {
-    ConfigurationClass global =
-      (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class);
-
-    // Each configuration object must have this class annotation
-    if(global == null) {
-      throw new SqoopException(ModelError.MODEL_003,
-        "Missing annotation ConfigurationClass on class " + klass.getName());
-    }
-
-    List<MForm> forms = new LinkedList<MForm>();
-
-    // Iterate over all declared fields
-    for (Field field : klass.getDeclaredFields()) {
-      field.setAccessible(true);
-
-      String formName = field.getName();
-
-      // Each field that should be part of user input should have Input
-      // annotation.
-      Form formAnnotation = field.getAnnotation(Form.class);
-
-      if(formAnnotation != null) {
-        Class type = field.getType();
-
-        Object value = null;
-        if(configuration != null) {
-          try {
-            value = field.get(configuration);
-          } catch (IllegalAccessException e) {
-            throw new SqoopException(ModelError.MODEL_005,
-              "Can't retrieve value from " + field.getName(), e);
-          }
-        }
-
-        forms.add(toForm(formName, type, value));
-      }
-    }
-
-    return forms;
-  }
-
-  @SuppressWarnings("unchecked")
-  private static MForm toForm(String formName, Class klass, Object object) {
-     FormClass global =
-      (FormClass)klass.getAnnotation(FormClass.class);
-
-    // Each configuration object must have this class annotation
-    if(global == null) {
-      throw new SqoopException(ModelError.MODEL_003,
-        "Missing annotation FormClass on class " + klass.getName());
-    }
-
-    // Intermediate list of inputs
-    List<MInput<?>> inputs = new LinkedList<MInput<?>>();
-
-    // Iterate over all declared fields
-    for (Field field : klass.getDeclaredFields()) {
-      field.setAccessible(true);
-
-      String fieldName = field.getName();
-      String inputName = formName + "." + fieldName;
-
-      // Each field that should be part of user input should have Input
-      // annotation.
-      Input inputAnnotation = field.getAnnotation(Input.class);
-
-      if(inputAnnotation != null) {
-        boolean sensitive = inputAnnotation.sensitive();
-        short maxLen = inputAnnotation.size();
-        Class type = field.getType();
-
-        MInput input;
-
-        // 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);
-        }
-
-        // Instantiate corresponding MInput<?> structure
-        if(type == String.class) {
-          input = new MStringInput(inputName, sensitive, maxLen);
-        } else if (type.isAssignableFrom(Map.class)) {
-          input = new MMapInput(inputName, sensitive);
-        } else if(type == Integer.class) {
-          input = new MIntegerInput(inputName, sensitive);
-        } else if(type == Boolean.class) {
-          input = new MBooleanInput(inputName, sensitive);
-        } else if(type.isEnum()) {
-          input = new MEnumInput(inputName, sensitive, ClassUtils.getEnumStrings(type));
-        } else {
-          throw new SqoopException(ModelError.MODEL_004,
-            "Unsupported type " + type.getName() + " for input " + fieldName);
-        }
-
-        // Move value if it's present in original configuration object
-        if(object != null) {
-          Object value;
-          try {
-            value = field.get(object);
-          } catch (IllegalAccessException e) {
-            throw new SqoopException(ModelError.MODEL_005,
-              "Can't retrieve value from " + field.getName(), e);
-          }
-          if(value == null) {
-            input.setEmpty();
-          } else {
-            input.setValue(value);
-          }
-        }
-
-        inputs.add(input);
-      }
-    }
-
-    return new MForm(formName, inputs);
-  }
-
-  /**
-   * Move form values from form list into corresponding configuration object.
-   *
-   * @param forms Input form list
-   * @param configuration Output configuration object
-   */
-  public static void fromForms(List<MForm> forms, Object configuration) {
-    Class klass = configuration.getClass();
-
-    for(MForm form : forms) {
-      Field formField;
-      try {
-        formField = klass.getDeclaredField(form.getName());
-      } catch (NoSuchFieldException e) {
-        throw new SqoopException(ModelError.MODEL_006,
-          "Missing field " + form.getName() + " on form class " + klass.getCanonicalName(), e);
-      }
-
-      // We need to access this field even if it would be declared as private
-      formField.setAccessible(true);
-
-      Class formClass = formField.getType();
-      Object newValue = ClassUtils.instantiate(formClass);
-
-      if(newValue == null) {
-        throw new SqoopException(ModelError.MODEL_006,
-          "Can't instantiate new form " + formClass);
-      }
-
-      for(MInput input : form.getInputs()) {
-        String[] splitNames = input.getName().split("\\.");
-        if(splitNames.length != 2) {
-          throw new SqoopException(ModelError.MODEL_009,
-            "Invalid name: " + input.getName());
-        }
-
-        String inputName = splitNames[1];
-        // TODO(jarcec): Names structures fix, handle error cases
-        Field inputField;
-        try {
-          inputField = formClass.getDeclaredField(inputName);
-        } catch (NoSuchFieldException e) {
-          throw new SqoopException(ModelError.MODEL_006,
-            "Missing field " + input.getName(), e);
-        }
-
-        // We need to access this field even if it would be declared as private
-        inputField.setAccessible(true);
-
-        try {
-          if(input.isEmpty()) {
-            inputField.set(newValue, null);
-          } else {
-            if (input.getType() == MInputType.ENUM) {
-              inputField.set(newValue, Enum.valueOf((Class<? extends Enum>)inputField.getType(), (String) input.getValue()));
-            } else {
-              inputField.set(newValue, input.getValue());
-            }
-          }
-        } catch (IllegalAccessException e) {
-          throw new SqoopException(ModelError.MODEL_005,
-            "Issue with field " + inputField.getName(), e);
-        }
-      }
-
-      try {
-        formField.set(configuration, newValue);
-      } catch (IllegalAccessException e) {
-        throw new SqoopException(ModelError.MODEL_005,
-          "Issue with field " + formField.getName(), e);
-      }
-    }
-  }
-
-  /**
-   * Apply validations on the forms.
-   *
-   * @param forms Forms that should be updated
-   * @param validation Validation that we should apply
-   */
-  public static void applyValidation(List<MForm> forms, Validation validation) {
-    Map<Validation.FormInput, Validation.Message> messages = validation.getMessages();
-
-    for(MForm form : forms) {
-      applyValidation(form, messages);
-
-      for(MInput input : form.getInputs()) {
-        applyValidation(input, messages);
-      }
-    }
-  }
-
-  /**
-   * Apply validation on given validated element.
-   *
-   * @param element Element on what we're applying the validations
-   * @param messages Map of all validation messages
-   */
-  public static void applyValidation(MValidatedElement element, Map<Validation.FormInput, Validation.Message> messages) {
-    Validation.FormInput name = new Validation.FormInput(element.getName());
-
-    if(messages.containsKey(name)) {
-      Validation.Message message = messages.get(name);
-      element.addValidationMessage(new Message(message.getStatus(), message.getMessage()));
-    } else {
-      element.addValidationMessage(new Message(Status.getDefault(), null));
-    }
-  }
-
-
-  /**
-   * Apply given validations on list of forms.
-   *
-   * @param forms
-   * @param result
-   */
-  public static void applyValidation(List<MForm> forms, ValidationResult result) {
-    for(MForm form : forms) {
-      applyValidation(form, result);
-
-      for(MInput input : form.getInputs()) {
-        applyValidation(input, result);
-      }
-    }
-  }
-
-  /**
-   * Apply validation messages on given element.
-   *
-   * Element's state will be set to default if there are no associated messages.
-   *
-   * @param element
-   * @param result
-   */
-  public static void applyValidation(MValidatedElement element, ValidationResult result) {
-    List<Message> messages = result.getMessages().get(element.getName());
-
-    if(messages != null) {
-      element.setValidationMessages(messages);
-    } else {
-      element.resetValidationMessages();
-    }
-  }
-
-  /**
-   * 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();
-
-    ConfigurationClass global =
-      (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class);
-
-    // Each configuration object must have this class annotation
-    if(global == null) {
-      throw new SqoopException(ModelError.MODEL_003,
-        "Missing annotation Configuration on class " + klass.getName());
-    }
-
-    JSONObject jsonOutput = new JSONObject();
-
-    // Iterate over all declared fields
-    for (Field formField : klass.getDeclaredFields()) {
-      formField.setAccessible(true);
-      String formName = formField.getName();
-
-      // 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 = inputField.get(formValue);
-        } catch (IllegalAccessException e) {
-          throw new SqoopException(ModelError.MODEL_005,
-            "Issue with field " + formName + "." + inputName, e);
-        }
-
-        Input inputAnnotation = inputField.getAnnotation(Input.class);
-
-        // Do not serialize all values
-        if(inputAnnotation != null && value != null) {
-          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 " + formName + "." + inputName);
-          }
-
-          if(type == String.class) {
-            jsonForm.put(inputName, value);
-          } else if (type.isAssignableFrom(Map.class)) {
-            JSONObject map = new JSONObject();
-            for(Object key : ((Map)value).keySet()) {
-              map.put(key, ((Map)value).get(key));
-            }
-            jsonForm.put(inputName, map);
-          } else if(type == Integer.class) {
-            jsonForm.put(inputName, value);
-          } else if(type.isEnum()) {
-            jsonForm.put(inputName, value.toString());
-          } else if(type == Boolean.class) {
-            jsonForm.put(inputName, value);
-          }else {
-            throw new SqoopException(ModelError.MODEL_004,
-              "Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
-          }
-        }
-      }
-
-      jsonOutput.put(formName, jsonForm);
-    }
-
-    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 jsonForms = (JSONObject) JSONValue.parse(json);
-
-    for(Field formField : klass.getDeclaredFields()) {
-      formField.setAccessible(true);
-      String formName = formField.getName();
-
-      // We're processing only form validations
-      Form formAnnotation = formField.getAnnotation(Form.class);
-      if(formAnnotation == null) {
-        continue;
-      }
-
-      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) {
-            inputField.set(formValue, jsonInputs.get(inputName));
-          } else if (type.isAssignableFrom(Map.class)) {
-            Map<String, String> map = new HashMap<String, String>();
-            JSONObject jsonObject = (JSONObject) jsonInputs.get(inputName);
-            for(Object key : jsonObject.keySet()) {
-              map.put((String)key, (String)jsonObject.get(key));
-            }
-            inputField.set(formValue, map);
-          } else if(type == Integer.class) {
-            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 if(type == Boolean.class) {
-            inputField.set(formValue, (Boolean) jsonInputs.get(inputName));
-          }else {
-            throw new SqoopException(ModelError.MODEL_004,
-              "Unsupported type " + type.getName() + " for input " + formName + "." + inputName);
-          }
-        } catch (IllegalAccessException e) {
-          throw new SqoopException(ModelError.MODEL_005,
-            "Issue with field " + formName + "." + inputName, e);
-        }
-      }
-    }
-  }
-
-  public static String getName(Field input, Input annotation) {
-    return input.getName();
-  }
-
-  public static String getName(Field form, Form annotation) {
-    return form.getName();
-  }
-
-  public static ConfigurationClass getConfigurationClassAnnotation(Object object, boolean strict) {
-    ConfigurationClass annotation = object.getClass().getAnnotation(ConfigurationClass.class);
-
-    if(strict && annotation == null) {
-      throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationClass on class " + object.getClass().getName());
-    }
-
-    return annotation;
-  }
-
-  public static FormClass getFormClassAnnotation(Object object, boolean strict) {
-    FormClass annotation = object.getClass().getAnnotation(FormClass.class);
-
-    if(strict && annotation == null) {
-      throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationClass on class " + object.getClass().getName());
-    }
-
-    return annotation;
-  }
-
-  public static Form getFormAnnotation(Field field, boolean strict) {
-    Form annotation = field.getAnnotation(Form.class);
-
-    if(strict && annotation == null) {
-      throw new SqoopException(ModelError.MODEL_003, "Missing annotation Form on Field " + field.getName() + " on class " + field.getDeclaringClass().getName());
-    }
-
-    return annotation;
-  }
-
-  public static Input getInputAnnotation(Field field, boolean strict) {
-    Input annotation = field.getAnnotation(Input.class);
-
-    if(strict && annotation == null) {
-      throw new SqoopException(ModelError.MODEL_003, "Missing annotation Input on Field " + field.getName() + " on class " + field.getDeclaringClass().getName());
-    }
-
-    return annotation;
-  }
-
-  public static Object getFieldValue(Field field, Object object) {
-    try {
-      field.setAccessible(true);
-      return field.get(object);
-    } catch (IllegalAccessException e) {
-      throw new SqoopException(ModelError.MODEL_012, e);
-    }
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/MConfig.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MConfig.java b/common/src/main/java/org/apache/sqoop/model/MConfig.java
new file mode 100644
index 0000000..b5d2afd
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/MConfig.java
@@ -0,0 +1,117 @@
+/**
+ * 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.common.SqoopException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a group of inputs that are processed together. This allows the
+ * input gathering process to be broken down into multiple steps that can be
+ * then paged through by the user interface.
+ */
+public final class MConfig extends MValidatedElement implements MClonable {
+
+  private final List<MInput<?>> inputs;
+
+  public MConfig(String name, List<MInput<?>> inputs) {
+    super(name);
+
+    this.inputs = inputs;
+  }
+
+  public List<MInput<?>> getInputs() {
+    return inputs;
+  }
+
+  public MInput<?> getInput(String inputName) {
+    for(MInput<?> input: inputs) {
+      if(inputName.equals(input.getName())) {
+        return input;
+      }
+    }
+
+    throw new SqoopException(ModelError.MODEL_011, "Input name: " + inputName);
+  }
+
+  public MStringInput getStringInput(String inputName) {
+    return (MStringInput)getInput(inputName);
+  }
+
+  public MEnumInput getEnumInput(String inputName) {
+    return (MEnumInput)getInput(inputName);
+  }
+
+  public MIntegerInput getIntegerInput(String inputName) {
+    return (MIntegerInput)getInput(inputName);
+  }
+
+  public MBooleanInput getBooleanInput(String inputName) {
+    return (MBooleanInput)getInput(inputName);
+  }
+
+  public MMapInput getMapInput(String inputName) {
+    return (MMapInput)getInput(inputName);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("config-").append(getName());
+    sb.append(":").append(getPersistenceId()).append(":").append(inputs);
+
+    return sb.toString();
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+
+    if (!(other instanceof MConfig)) {
+      return false;
+    }
+
+    MConfig mf = (MConfig) other;
+    return getName().equals(mf.getName())
+        && inputs.equals(mf.inputs);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = 17;
+    result = 31 * result + getName().hashCode();
+    for (MInput<?> mi : inputs) {
+      result = 31 * result + mi.hashCode();
+    }
+
+    return result;
+  }
+
+  @Override
+  public MConfig clone(boolean cloneWithValue) {
+    List<MInput<?>> copyInputs = new ArrayList<MInput<?>>();
+    for(MInput<?> itr : this.getInputs()) {
+      copyInputs.add((MInput<?>)itr.clone(cloneWithValue));
+    }
+    MConfig copyConfig = new MConfig(this.getName(), copyInputs);
+    return copyConfig;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/MConfigList.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MConfigList.java b/common/src/main/java/org/apache/sqoop/model/MConfigList.java
new file mode 100644
index 0000000..8747b55
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/MConfigList.java
@@ -0,0 +1,124 @@
+/**
+ * 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.common.SqoopException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Arbitrary list of config objects.
+ */
+public class MConfigList implements MClonable {
+
+  private final List<MConfig> configObjects;
+
+  public MConfigList(List<MConfig> configObjects) {
+    this.configObjects = configObjects;
+  }
+
+  public List<MConfig> getConfigs() {
+    return configObjects;
+  }
+
+  public MConfig getConfig(String configName) {
+    for(MConfig config: configObjects) {
+      if(configName.equals(config.getName())) {
+        return config;
+      }
+    }
+
+    throw new SqoopException(ModelError.MODEL_010, "config name: " + configName);
+  }
+
+  public MInput getInput(String name) {
+    String []parts = name.split("\\.");
+    if(parts.length != 2) {
+      throw new SqoopException(ModelError.MODEL_009, name);
+    }
+
+    return getConfig(parts[0]).getInput(name);
+  }
+
+  public MStringInput getStringInput(String name) {
+    return (MStringInput)getInput(name);
+  }
+
+  public MEnumInput getEnumInput(String name) {
+    return (MEnumInput)getInput(name);
+  }
+
+  public MIntegerInput getIntegerInput(String name) {
+    return (MIntegerInput)getInput(name);
+  }
+
+  public MMapInput getMapInput(String name) {
+    return (MMapInput)getInput(name);
+  }
+
+  public MBooleanInput getBooleanInput(String name) {
+    return (MBooleanInput)getInput(name);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof MConfigList)) return false;
+
+    MConfigList mConfigList = (MConfigList) o;
+
+    if (!configObjects.equals(mConfigList.configObjects)) return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    for(MConfig config : configObjects) {
+      result = 31 * result + config.hashCode();
+    }
+
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Configs: ");
+    for(MConfig config : configObjects) {
+      sb.append(config.toString());
+    }
+    return sb.toString();
+  }
+
+  @Override
+  public MConfigList clone(boolean cloneWithValue) {
+    List<MConfig> copyConfigs = null;
+    if(this.getConfigs() != null) {
+      copyConfigs = new ArrayList<MConfig>();
+      for(MConfig itr : this.getConfigs()) {
+        MConfig newConfig = itr.clone(cloneWithValue);
+        newConfig.setPersistenceId(itr.getPersistenceId());
+        copyConfigs.add(newConfig);
+      }
+    }
+    MConfigList copyConfigList = new MConfigList(copyConfigs);
+    return copyConfigList;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/MConfigType.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MConfigType.java b/common/src/main/java/org/apache/sqoop/model/MConfigType.java
new file mode 100644
index 0000000..de05332
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/MConfigType.java
@@ -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.model;
+
+/**
+ * Represents the various config types supported by the system.
+ */
+public enum MConfigType {
+
+  /** Unknown config type */
+  OTHER,
+
+  /** link config type */
+  LINK,
+
+  /** Job config type */
+  JOB;
+
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java b/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java
deleted file mode 100644
index 457ccdb..0000000
--- a/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * 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 java.util.List;
-
-/**
- * Metadata describing all required information to build up an connection
- * object for one part. Both connector and framework need to supply this object
- * to build up entire connection.
- */
-public class MConnectionForms extends MFormList {
-
-  public MConnectionForms(List<MForm> forms) {
-    super(forms);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("Connection: ");
-    sb.append(super.toString());
-    return sb.toString();
-  }
-
-  @Override
-  public boolean equals(Object other) {
-    if (other == this) {
-      return true;
-    }
-
-    return super.equals(other);
-  }
-
-  @Override
-  public MConnectionForms clone(boolean cloneWithValue) {
-    MConnectionForms copy = new MConnectionForms(super.clone(cloneWithValue).getForms());
-    return copy;
-  }
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/MConnector.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MConnector.java b/common/src/main/java/org/apache/sqoop/model/MConnector.java
index 7999b08..2f42191 100644
--- a/common/src/main/java/org/apache/sqoop/model/MConnector.java
+++ b/common/src/main/java/org/apache/sqoop/model/MConnector.java
@@ -23,28 +23,27 @@ import org.apache.sqoop.common.SqoopException;
 import org.apache.sqoop.common.SupportedDirections;
 
 /**
- * Connector metadata.
- *
- * Includes unique id that identifies connector in metadata store, unique human
- * readable name, corresponding name and all forms for all supported job types.
+ * Connector entity supports the FROM/TO {@link Transferable} Includes unique id
+ * that identifies connector in the repository, unique human readable name,
+ * corresponding name and all configs to support the from and to data sources
  */
 public final class MConnector extends MPersistableEntity implements MClonable {
 
   private final String uniqueName;
   private final String className;
-  private final MConnectionForms connectionForms;
-  private final MJobForms fromJobForms;
-  private final MJobForms toJobForms;
-  String version;
-
-  public MConnector(String uniqueName, String className,
-                    String version, MConnectionForms connectionForms,
-                    MJobForms fromJobForms, MJobForms toJobForms) {
+  private final String version;
+  private final MLinkConfig linkConfig;
+  private final MFromConfig fromConfig;
+  private final MToConfig toConfig;
+
+  public MConnector(String uniqueName, String className, String version, MLinkConfig linkConfig,
+      MFromConfig fromConfig, MToConfig toConfig) {
     this.version = version;
-    this.connectionForms = connectionForms;
-    this.fromJobForms = fromJobForms;
-    this.toJobForms = toJobForms;
+    this.linkConfig = linkConfig;
+    this.fromConfig = fromConfig;
+    this.toConfig = toConfig;
 
+    // Why are we abusing NPE?
     if (uniqueName == null || className == null) {
       throw new NullPointerException();
     }
@@ -63,17 +62,15 @@ public final class MConnector extends MPersistableEntity implements MClonable {
 
   @Override
   public String toString() {
-    MJobForms fromJobForms = this.getJobForms(Direction.FROM);
-    MJobForms toJobForms = this.getJobForms(Direction.TO);
     StringBuilder sb = new StringBuilder("connector-");
     sb.append(uniqueName).append(":").append(getPersistenceId()).append(":");
     sb.append(className);
-    sb.append(", ").append(getConnectionForms().toString());
-    if (fromJobForms != null) {
-      sb.append(", ").append(fromJobForms.toString());
+    sb.append(", ").append(getLinkConfig().toString());
+    if (getConfig(Direction.FROM) != null) {
+      sb.append(", ").append(getConfig(Direction.FROM).toString());
     }
-    if (toJobForms != null) {
-      sb.append(", ").append(toJobForms.toString());
+    if (getConfig(Direction.TO) != null) {
+      sb.append(", ").append(getConfig(Direction.TO).toString());
     }
     return sb.toString();
   }
@@ -94,41 +91,39 @@ public final class MConnector extends MPersistableEntity implements MClonable {
 
     if (supportedDirections.isDirectionSupported(Direction.FROM)
         && mcSupportedDirections.isDirectionSupported(Direction.FROM)
-        && !getJobForms(Direction.FROM).equals(mc.getJobForms(Direction.FROM))) {
+        && !getFromConfig().equals(mc.getFromConfig())) {
       return false;
     }
 
-    if (supportedDirections.isDirectionSupported(Direction.FROM)
-        != mcSupportedDirections.isDirectionSupported(Direction.FROM)) {
+    if (supportedDirections.isDirectionSupported(Direction.FROM) != mcSupportedDirections
+        .isDirectionSupported(Direction.FROM)) {
       return false;
     }
 
     if (supportedDirections.isDirectionSupported(Direction.TO)
         && mcSupportedDirections.isDirectionSupported(Direction.TO)
-        && !getJobForms(Direction.TO).equals(mc.getJobForms(Direction.TO))) {
+        && !getToConfig().equals(mc.getToConfig())) {
       return false;
     }
 
-    if (supportedDirections.isDirectionSupported(Direction.TO)
-        != mcSupportedDirections.isDirectionSupported(Direction.TO)) {
+    if (supportedDirections.isDirectionSupported(Direction.TO) != mcSupportedDirections
+        .isDirectionSupported(Direction.TO)) {
       return false;
     }
 
-    return uniqueName.equals(mc.uniqueName)
-        && className.equals(mc.className)
-        && version.equals(mc.version)
-        && connectionForms.equals(mc.getConnectionForms());
+    return uniqueName.equals(mc.uniqueName) && className.equals(mc.className)
+        && version.equals(mc.version) && linkConfig.equals((mc.getLinkConfig()));
   }
 
   @Override
   public int hashCode() {
     SupportedDirections supportedDirections = getSupportedDirections();
-    int result = getConnectionForms().hashCode();
+    int result = getLinkConfig().hashCode();
     if (supportedDirections.isDirectionSupported(Direction.FROM)) {
-      result = 31 * result + getJobForms(Direction.FROM).hashCode();
+      result = 31 * result + getFromConfig().hashCode();
     }
     if (supportedDirections.isDirectionSupported(Direction.TO)) {
-      result = 31 * result + getJobForms(Direction.TO).hashCode();
+      result = 31 * result + getToConfig().hashCode();
     }
     result = 31 * result + version.hashCode();
     result = 31 * result + uniqueName.hashCode();
@@ -137,58 +132,57 @@ public final class MConnector extends MPersistableEntity implements MClonable {
   }
 
   public MConnector clone(boolean cloneWithValue) {
-    //Connector never have any values filled
+    // Connector never have any values filled
     cloneWithValue = false;
 
-    MJobForms fromJobForms = this.getJobForms(Direction.FROM);
-    MJobForms toJobForms = this.getJobForms(Direction.TO);
+    MFromConfig fromConfig = this.getFromConfig();
+    MToConfig toConfig = this.getToConfig();
 
-    if (fromJobForms != null) {
-      fromJobForms = fromJobForms.clone(cloneWithValue);
+    if (fromConfig != null) {
+      fromConfig = fromConfig.clone(cloneWithValue);
     }
 
-    if (toJobForms != null) {
-      toJobForms = toJobForms.clone(cloneWithValue);
+    if (toConfig != null) {
+      toConfig = toConfig.clone(cloneWithValue);
     }
 
-    MConnector copy = new MConnector(
-        this.getUniqueName(),
-        this.getClassName(),
-        this.getVersion(),
-        this.getConnectionForms().clone(cloneWithValue),
-        fromJobForms,
-        toJobForms);
+    MConnector copy = new MConnector(this.getUniqueName(), this.getClassName(), this.getVersion(),
+        this.getLinkConfig().clone(cloneWithValue), fromConfig, toConfig);
     copy.setPersistenceId(this.getPersistenceId());
     return copy;
   }
 
-  public MConnectionForms getConnectionForms() {
-    return connectionForms;
+  public MLinkConfig getLinkConfig() {
+    return linkConfig;
   }
 
-  public MJobForms getJobForms(Direction type) {
-    switch(type) {
-      case FROM:
-        return fromJobForms;
+  public MConfigList getConfig(Direction type) {
+    switch (type) {
+    case FROM:
+      return fromConfig;
 
-      case TO:
-        return toJobForms;
+    case TO:
+      return toConfig;
 
-      default:
-        throw new SqoopException(DirectionError.DIRECTION_0000, "Direction: " + type);
+    default:
+      throw new SqoopException(DirectionError.DIRECTION_0000, "Direction: " + type);
     }
   }
 
-  public String getVersion() {
-    return version;
+  public MFromConfig getFromConfig() {
+    return fromConfig;
   }
 
-  public void setVersion(String version) {
-    this.version = version;
+  public MToConfig getToConfig() {
+    return toConfig;
+  }
+
+  public String getVersion() {
+    return version;
   }
 
   public SupportedDirections getSupportedDirections() {
-    return new SupportedDirections(this.getJobForms(Direction.FROM) != null,
-        this.getJobForms(Direction.TO) != null);
+    return new SupportedDirections(this.getConfig(Direction.FROM) != null,
+        this.getConfig(Direction.TO) != null);
   }
 }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/f63c080d/common/src/main/java/org/apache/sqoop/model/MDriver.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MDriver.java b/common/src/main/java/org/apache/sqoop/model/MDriver.java
new file mode 100644
index 0000000..685439e
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/MDriver.java
@@ -0,0 +1,82 @@
+/**
+ * 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 java.sql.Driver;
+
+/**
+ * Describes the configs associated with the {@link Driver} for executing sqoop jobs.
+ */
+public class MDriver extends MPersistableEntity implements MClonable {
+
+  private final MDriverConfig driverConfig;
+  private final String version;
+
+  public MDriver(MDriverConfig driverConfig, String version) {
+    this.driverConfig = driverConfig;
+    this.version = version;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("driver-");
+    sb.append(getPersistenceId()).append(":");
+    sb.append("version = " + version);
+    sb.append(", ").append(driverConfig.toString());
+
+    return sb.toString();
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+
+    if (!(other instanceof MDriver)) {
+      return false;
+    }
+
+    MDriver driver = (MDriver) other;
+    return version.equals(driver.getVersion()) &&
+        driverConfig.equals(driver.driverConfig);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = driverConfig.hashCode();
+    result = 31 * result + version.hashCode();
+    return result;
+  }
+
+  public MDriverConfig getDriverConfig() {
+    return driverConfig;
+  }
+
+  @Override
+  public MDriver clone(boolean cloneWithValue) {
+    cloneWithValue = false;
+    MDriver copy = new MDriver(this.driverConfig.clone(cloneWithValue), this.version);
+    copy.setPersistenceId(this.getPersistenceId());
+    return copy;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+}
\ No newline at end of file