sqoop-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b...@apache.org
Subject [1/3] git commit: SQOOP-596: Implement connection resource end-to-end
Date Fri, 07 Sep 2012 19:48:36 GMT
Updated Branches:
  refs/heads/sqoop2 1f9bb28e0 -> 47cb311a5


SQOOP-596: Implement connection resource end-to-end


Project: http://git-wip-us.apache.org/repos/asf/sqoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/sqoop/commit/47cb311a
Tree: http://git-wip-us.apache.org/repos/asf/sqoop/tree/47cb311a
Diff: http://git-wip-us.apache.org/repos/asf/sqoop/diff/47cb311a

Branch: refs/heads/sqoop2
Commit: 47cb311a5f25791d9b58d3ffcd2a37c79fa4e7ac
Parents: 1f9bb28
Author: Bilung Lee <blee@apache.org>
Authored: Fri Sep 7 12:40:34 2012 -0700
Committer: Bilung Lee <blee@apache.org>
Committed: Fri Sep 7 12:40:34 2012 -0700

----------------------------------------------------------------------
 .../org/apache/sqoop/client/core/ClientError.java  |   10 +-
 .../apache/sqoop/client/display/FormDisplayer.java |   91 ----
 .../sqoop/client/request/ConnectionRequest.java    |   82 ++++
 .../sqoop/client/request/ConnectorRequest.java     |   15 +-
 .../sqoop/client/request/FrameworkRequest.java     |   10 +-
 .../org/apache/sqoop/client/request/Request.java   |   73 +++-
 .../sqoop/client/request/VersionRequest.java       |    2 +-
 .../apache/sqoop/client/shell/CreateCommand.java   |   58 +++
 .../client/shell/CreateConnectionFunction.java     |  156 ++++++
 .../apache/sqoop/client/shell/DeleteCommand.java   |   58 +++
 .../client/shell/DeleteConnectionFunction.java     |   67 +++
 .../org/apache/sqoop/client/shell/ShowCommand.java |    6 +-
 .../sqoop/client/shell/ShowConnectionFunction.java |  107 ++++
 .../sqoop/client/shell/ShowConnectorFunction.java  |   17 +-
 .../sqoop/client/shell/ShowFrameworkFunction.java  |   10 +-
 .../org/apache/sqoop/client/shell/SqoopShell.java  |    9 +-
 .../apache/sqoop/client/shell/UpdateCommand.java   |   58 +++
 .../client/shell/UpdateConnectionFunction.java     |  136 ++++++
 .../apache/sqoop/client/utils/FormDisplayer.java   |  135 ++++++
 .../org/apache/sqoop/client/utils/FormFiller.java  |  132 +++++
 .../org/apache/sqoop/common/ExceptionInfo.java     |   64 ---
 .../java/org/apache/sqoop/json/ConnectionBean.java |  177 +++++++
 .../java/org/apache/sqoop/json/ConnectorBean.java  |   32 +-
 .../java/org/apache/sqoop/json/ExceptionInfo.java  |   73 +++
 .../java/org/apache/sqoop/json/FrameworkBean.java  |   15 +-
 .../java/org/apache/sqoop/json/ValidationBean.java |  143 ++++++
 .../apache/sqoop/json/util/FormSerialization.java  |   33 +-
 .../json/util/ResourceBundleSerialization.java     |   83 ++++
 .../java/org/apache/sqoop/model/MConnection.java   |   39 ++-
 .../main/java/org/apache/sqoop/model/MInput.java   |   15 +
 .../java/org/apache/sqoop/model/MMapInput.java     |   10 +
 .../java/org/apache/sqoop/model/MStringInput.java  |   10 +
 .../org/apache/sqoop/utils/MapResourceBundle.java  |   49 ++
 .../java/org/apache/sqoop/validation/Status.java   |   69 +++
 .../org/apache/sqoop/json/TestConnectionBean.java  |   66 +++
 .../org/apache/sqoop/json/TestConnectorBean.java   |   65 +--
 .../org/apache/sqoop/json/TestFrameworkBean.java   |   54 +--
 .../test/java/org/apache/sqoop/json/TestUtil.java  |  137 ++++++
 .../org/apache/sqoop/json/TestValidationBean.java  |   69 +++
 .../apache/sqoop/utils/TestMapResourceBundle.java  |   41 ++
 .../org/apache/sqoop/validation/TestStatus.java    |   53 ++
 .../sqoop/connector/jdbc/GenericJdbcConnector.java |    3 +-
 .../jdbc/GenericJdbcConnectorConstants.java        |    3 +-
 .../sqoop/connector/jdbc/GenericJdbcValidator.java |   63 +++
 .../generic-jdbc-connector-resources.properties    |   12 +-
 .../connector/mysqljdbc/MySqlJdbcConnector.java    |    5 +-
 .../apache/sqoop/connector/ConnectorHandler.java   |    4 +
 .../apache/sqoop/connector/ConnectorManager.java   |   39 ++-
 .../apache/sqoop/framework/FrameworkConstants.java |    2 +
 .../apache/sqoop/framework/FrameworkManager.java   |   16 +
 .../apache/sqoop/repository/JdbcRepository.java    |  198 +++++---
 .../sqoop/repository/JdbcRepositoryHandler.java    |   55 +++
 .../org/apache/sqoop/repository/Repository.java    |   18 +-
 .../apache/sqoop/repository/RepositoryError.java   |   16 +-
 .../main/resources/framework-resources.properties  |   36 ++
 .../sqoop/repository/derby/DerbyRepoError.java     |   32 +-
 .../repository/derby/DerbyRepositoryHandler.java   |  374 ++++++++++++---
 .../sqoop/repository/derby/DerbySchemaQuery.java   |   62 +++-
 .../sqoop/handler/ConnectionRequestHandler.java    |  208 ++++++++
 .../sqoop/handler/ConnectorRequestHandler.java     |   30 +-
 .../sqoop/handler/FrameworkRequestHandler.java     |    5 +-
 .../org/apache/sqoop/server/RequestContext.java    |   51 ++
 .../apache/sqoop/server/SqoopProtocolServlet.java  |   58 ++-
 .../apache/sqoop/server/common/ServerError.java    |   51 ++
 .../apache/sqoop/server/v1/ConnectionServlet.java  |   56 +++
 server/src/main/webapp/WEB-INF/web.xml             |   12 +
 .../java/org/apache/sqoop/validation/Status.java   |   45 --
 .../org/apache/sqoop/validation/Validator.java     |    5 +-
 68 files changed, 3449 insertions(+), 539 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/core/ClientError.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/core/ClientError.java b/client/src/main/java/org/apache/sqoop/client/core/ClientError.java
index 91d1aee..1cf42e4 100644
--- a/client/src/main/java/org/apache/sqoop/client/core/ClientError.java
+++ b/client/src/main/java/org/apache/sqoop/client/core/ClientError.java
@@ -34,7 +34,15 @@ public enum ClientError implements ErrorCode {
   CLIENT_0003("An error has occurred when parsing options"),
 
   /** Unable to resolve the variables. */
-  CLIENT_0004("Unable to resolve the variables");
+  CLIENT_0004("Unable to resolve the variables"),
+
+  /** We're not able to get user input */
+  CLIENT_0005("Can't get user input"),
+
+  /** There occured exception on server side **/
+  CLIENT_0006("Server has returned exception"),
+
+  ;
 
   private final String message;
 

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/display/FormDisplayer.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/display/FormDisplayer.java b/client/src/main/java/org/apache/sqoop/client/display/FormDisplayer.java
deleted file mode 100644
index 6e632d4..0000000
--- a/client/src/main/java/org/apache/sqoop/client/display/FormDisplayer.java
+++ /dev/null
@@ -1,91 +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.client.display;
-
-import org.apache.sqoop.model.MForm;
-import org.apache.sqoop.model.MFramework;
-import org.apache.sqoop.model.MInput;
-import org.apache.sqoop.model.MInputType;
-import org.apache.sqoop.model.MJobForms;
-import org.apache.sqoop.model.MStringInput;
-import org.codehaus.groovy.tools.shell.IO;
-
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Convenience static methods for displaying form related information
- */
-public class FormDisplayer {
-
-  public static void displayFormDetails(IO io, MFramework framework) {
-    io.out.print("  Supported job types: ");
-    io.out.println(framework.getAllJobsForms().keySet().toString());
-
-    displayForms(io, framework.getConnectionForms().getForms(), "Connection");
-
-    for (MJobForms jobForms : framework.getAllJobsForms().values()) {
-      io.out.print("  Forms for job type ");
-      io.out.print(jobForms.getType().name());
-      io.out.println(":");
-
-      displayForms(io, jobForms.getForms(), "Job");
-    }
-  }
-
-  public static void displayForms(IO io, List<MForm> forms, String type) {
-    Iterator<MForm> fiter = forms.iterator();
-    int findx = 1;
-    while (fiter.hasNext()) {
-      io.out.print("    ");
-      io.out.print(type);
-      io.out.print(" form ");
-      io.out.print(findx++);
-      io.out.println(":");
-
-      MForm form = fiter.next();
-      io.out.print("      Name: ");
-      io.out.println(form.getName());
-
-      List<MInput<?>> inputs = form.getInputs();
-      Iterator<MInput<?>> iiter = inputs.iterator();
-      int iindx = 1;
-      while (iiter.hasNext()) {
-        io.out.print("      Input ");
-        io.out.print(iindx++);
-        io.out.println(":");
-
-        MInput<?> input = iiter.next();
-        io.out.print("        Name: ");
-        io.out.println(input.getName());
-        io.out.print("        Type: ");
-        io.out.println(input.getType());
-        if (input.getType() == MInputType.STRING) {
-          io.out.print("        Mask: ");
-          io.out.println(((MStringInput)input).isMasked());
-          io.out.print("        Size: ");
-          io.out.println(((MStringInput)input).getMaxLength());
-        }
-      }
-    }
-  }
-
-  private FormDisplayer() {
-    // Do not instantiate
-  }
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/request/ConnectionRequest.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/request/ConnectionRequest.java b/client/src/main/java/org/apache/sqoop/client/request/ConnectionRequest.java
new file mode 100644
index 0000000..80a84cd
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/request/ConnectionRequest.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.client.request;
+
+import org.apache.sqoop.json.ConnectionBean;
+import org.apache.sqoop.json.ValidationBean;
+import org.apache.sqoop.model.MConnection;
+import org.apache.sqoop.validation.Status;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+/**
+ * Provide CRUD semantics over RESTfull HTTP API for connections. All operations
+ * are normally supported.
+ */
+public class ConnectionRequest extends Request {
+
+  public static final String RESOURCE = "v1/connection/";
+
+  public ConnectionBean read(String serverUrl, String xid) {
+    String response = null;
+    if (xid == null) {
+      response = super.get(serverUrl + RESOURCE + "all");
+    } else {
+      response = super.get(serverUrl + RESOURCE + xid);
+    }
+    JSONObject jsonObject = (JSONObject)JSONValue.parse(response);
+
+    ConnectionBean connectionBean = new ConnectionBean();
+    connectionBean.restore(jsonObject);
+
+    return connectionBean;
+  }
+
+  public Status create(String serverUrl, MConnection connection) {
+
+    ConnectionBean connectionBean = new ConnectionBean(connection);
+    JSONObject connectionJson = connectionBean.extract();
+
+    String response = super.post(serverUrl + RESOURCE,
+                                 connectionJson.toJSONString());
+
+    ValidationBean validationBean = new ValidationBean(connection);
+    validationBean.restore((JSONObject) JSONValue.parse(response));
+
+    return validationBean.getStatus();
+  }
+
+  public Status update(String serverUrl, MConnection connection) {
+
+    ConnectionBean connectionBean = new ConnectionBean(connection);
+    JSONObject connectionJson = connectionBean.extract();
+
+    String response = super.put(serverUrl + RESOURCE
+                                  + connection.getPersistenceId(),
+                                connectionJson.toJSONString());
+
+    ValidationBean validationBean = new ValidationBean(connection);
+    validationBean.restore((JSONObject) JSONValue.parse(response));
+
+    return validationBean.getStatus();
+  }
+
+  public void delete(String serverUrl, long id) {
+     super.delete(serverUrl + RESOURCE + id);
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/request/ConnectorRequest.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/request/ConnectorRequest.java b/client/src/main/java/org/apache/sqoop/client/request/ConnectorRequest.java
index 2866700..9ea9d5d 100644
--- a/client/src/main/java/org/apache/sqoop/client/request/ConnectorRequest.java
+++ b/client/src/main/java/org/apache/sqoop/client/request/ConnectorRequest.java
@@ -21,14 +21,21 @@ import org.apache.sqoop.json.ConnectorBean;
 import org.json.simple.JSONObject;
 import org.json.simple.JSONValue;
 
+/**
+ * Provide cRud semantics over RESTfull HTTP API for connectors. Only read
+ * is supported as creation, update and delete might be done only directly on
+ * server side.
+ */
 public class ConnectorRequest extends Request
 {
-  public ConnectorBean doGet(String serverUrl, String cid) {
+  public static final String RESOURCE = "v1/connector/";
+
+  public ConnectorBean read(String serverUrl, String cid) {
     String response = null;
     if (cid == null) {
-      response = super.get(serverUrl + "v1/connector/all");
+      response = super.get(serverUrl + RESOURCE + "all");
     } else {
-      response = super.get(serverUrl + "v1/connector/" + cid);
+      response = super.get(serverUrl + RESOURCE + cid);
     }
     JSONObject jsonObject = (JSONObject)JSONValue.parse(response);
 
@@ -37,4 +44,4 @@ public class ConnectorRequest extends Request
 
     return connectorBean;
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/request/FrameworkRequest.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/request/FrameworkRequest.java b/client/src/main/java/org/apache/sqoop/client/request/FrameworkRequest.java
index c9ae901..06f9006 100644
--- a/client/src/main/java/org/apache/sqoop/client/request/FrameworkRequest.java
+++ b/client/src/main/java/org/apache/sqoop/client/request/FrameworkRequest.java
@@ -22,12 +22,16 @@ import org.json.simple.JSONObject;
 import org.json.simple.JSONValue;
 
 /**
- *
+ * Provide cRud semantics over RESTfull HTTP API for framework. Only read
+ * is supported as creation, update and delete is not allowed.
  */
 public class FrameworkRequest extends Request {
-  public FrameworkBean doGet(String serverUrl) {
+
+  public static final String RESOURCE = "v1/framework";
+
+  public FrameworkBean read(String serverUrl) {
     String response = null;
-    response = super.get(serverUrl + "v1/framework");
+    response = super.get(serverUrl + RESOURCE);
     JSONObject jsonObject = (JSONObject) JSONValue.parse(response);
 
     FrameworkBean frameworkBean = new FrameworkBean();

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/request/Request.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/request/Request.java b/client/src/main/java/org/apache/sqoop/client/request/Request.java
index bd3059d..5e381c9 100644
--- a/client/src/main/java/org/apache/sqoop/client/request/Request.java
+++ b/client/src/main/java/org/apache/sqoop/client/request/Request.java
@@ -20,21 +20,88 @@ package org.apache.sqoop.client.request;
 import javax.ws.rs.core.MediaType;
 
 import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.api.client.WebResource.Builder;
+import com.sun.jersey.api.client.filter.ClientFilter;
+import org.apache.sqoop.client.core.ClientError;
+import org.apache.sqoop.common.SqoopException;
+import org.apache.sqoop.common.SqoopProtocolConstants;
+import org.apache.sqoop.json.ExceptionInfo;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
 
 import java.util.Locale;
 
 public class Request
 {
-  public String get(String url) {
+  private static ServerExceptionFilter serverExceptionFilter;
+
+  static {
+    serverExceptionFilter = new ServerExceptionFilter();
+  }
+
+  protected Builder getBuilder(String url) {
     Client client = Client.create();
     WebResource resource = client.resource(url);
-    Builder builder = resource
+
+    // Provide filter that will rebuild exception that is sent from server
+    resource.addFilter(serverExceptionFilter);
+
+    return resource
       // Sqoop is using JSON for data transfers
       .accept(MediaType.APPLICATION_JSON_TYPE)
       // Transfer client locale to return client specific data
       .acceptLanguage(Locale.getDefault());
-    return builder.get(String.class);
+  }
+
+  public String get(String url) {
+    return getBuilder(url).get(String.class);
+  }
+
+  public String post(String url, String data) {
+    return getBuilder(url).post(String.class, data);
+  }
+
+  public String put(String url, String data) {
+    return getBuilder(url).put(String.class, data);
+  }
+
+  public void delete(String url) {
+    getBuilder(url).delete(String.class);
+  }
+
+  /**
+   * Client filter to intercepting exceptions sent by sqoop server and
+   * recreating them on client side. Current implementation will create new
+   * instance of SqoopException and will attach original error code and message.
+   */
+  private static class ServerExceptionFilter extends ClientFilter {
+    @Override
+    public ClientResponse handle(ClientRequest cr) {
+      ClientResponse resp = getNext().handle(cr);
+
+      // Special handling for 500 internal server error in case that server
+      // has sent us it's exception correctly. We're using default route
+      // for all other 500 occurrences.
+      if(resp.getClientResponseStatus()
+        == ClientResponse.Status.INTERNAL_SERVER_ERROR) {
+
+        if(resp.getHeaders().containsKey(
+          SqoopProtocolConstants.HEADER_SQOOP_INTERNAL_ERROR_CODE)) {
+
+          ExceptionInfo ex = new ExceptionInfo();
+
+          String responseText = resp.getEntity(String.class);
+          JSONObject json = (JSONObject) JSONValue.parse(responseText);
+          ex.restore(json);
+
+          throw new SqoopException(ClientError.CLIENT_0006, ex.toString());
+        }
+      }
+
+      return resp;
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/request/VersionRequest.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/request/VersionRequest.java b/client/src/main/java/org/apache/sqoop/client/request/VersionRequest.java
index 511878c..2b236ba 100644
--- a/client/src/main/java/org/apache/sqoop/client/request/VersionRequest.java
+++ b/client/src/main/java/org/apache/sqoop/client/request/VersionRequest.java
@@ -32,4 +32,4 @@ public class VersionRequest extends Request
 
     return versionBean;
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/CreateCommand.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/CreateCommand.java b/client/src/main/java/org/apache/sqoop/client/shell/CreateCommand.java
new file mode 100644
index 0000000..bf4b581
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/CreateCommand.java
@@ -0,0 +1,58 @@
+/**
+ * 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.client.shell;
+
+import org.apache.sqoop.client.core.ClientError;
+import org.apache.sqoop.common.SqoopException;
+import org.codehaus.groovy.tools.shell.Shell;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class CreateCommand extends SqoopCommand {
+
+  private CreateConnectionFunction connectionFunction;
+
+  public CreateCommand(Shell shell) {
+    super(shell, "create", "\\cr",
+      new String[] {"connection", "job"},
+      "Create", "info");
+  }
+
+  public Object execute(List args) {
+    if (args.size() == 0) {
+      io.out.println("Usage: create " + getUsage());
+      io.out.println();
+      return null;
+    }
+
+    String func = (String)args.get(0);
+    if (func.equals("connection")) {
+      if (connectionFunction == null) {
+        connectionFunction = new CreateConnectionFunction(io);
+      }
+      return connectionFunction.execute(args);
+
+    } else {
+      String msg = "Usage: create " + getUsage();
+      throw new SqoopException(ClientError.CLIENT_0002, msg);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/CreateConnectionFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/CreateConnectionFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/CreateConnectionFunction.java
new file mode 100644
index 0000000..aef4b92
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/CreateConnectionFunction.java
@@ -0,0 +1,156 @@
+/**
+ * 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.client.shell;
+
+import jline.ConsoleReader;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.sqoop.client.core.ClientError;
+import org.apache.sqoop.client.core.Environment;
+import org.apache.sqoop.client.request.ConnectionRequest;
+import org.apache.sqoop.client.request.ConnectorRequest;
+import org.apache.sqoop.client.request.FrameworkRequest;
+import org.apache.sqoop.common.SqoopException;
+import org.apache.sqoop.json.ConnectorBean;
+import org.apache.sqoop.json.FrameworkBean;
+import org.apache.sqoop.model.MConnection;
+import org.apache.sqoop.model.MConnector;
+import org.apache.sqoop.model.MFramework;
+import org.apache.sqoop.validation.Status;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import static org.apache.sqoop.client.utils.FormFiller.*;
+
+/**
+ *
+ */
+public class CreateConnectionFunction extends SqoopFunction {
+
+  private static final String CID = "cid";
+
+  private FrameworkRequest frameworkRequest;
+  private ConnectorRequest connectorRequest;
+  private ConnectionRequest connectionRequest;
+
+  private IO io;
+
+  @SuppressWarnings("static-access")
+  public CreateConnectionFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+      .withDescription("Connector ID")
+      .withLongOpt(CID)
+      .hasArg()
+      .create(CID.charAt(0)));
+  }
+
+  public Object execute(List<String> args) {
+    CommandLine line = parseOptions(this, 1, args);
+    if (!line.hasOption(CID)) {
+      io.out.println("Required argument --cid is missing.");
+      return null;
+    }
+
+    try {
+      createConnection(line.getOptionValue(CID));
+    } catch (IOException ex) {
+      throw new SqoopException(ClientError.CLIENT_0005, ex);
+    }
+
+    return null;
+  }
+
+  private void createConnection(String connectorId) throws IOException {
+    io.out.println("Creating connection for connector with id " + connectorId);
+
+    ConsoleReader reader = new ConsoleReader();
+
+    FrameworkBean frameworkBean = getFrameworkBean();
+    ConnectorBean connectorBean = getConnectorBean(connectorId);
+
+    MFramework framework = frameworkBean.getFramework();
+    ResourceBundle frameworkBundle = frameworkBean.getResourceBundle();
+
+    MConnector connector = connectorBean.getConnectors().get(0);
+    ResourceBundle connectorBundle = connectorBean.getResourceBundles().get(0);
+
+    MConnection connection = new MConnection(connector.getPersistenceId(),
+                                             connector.getConnectionForms(),
+                                             framework.getConnectionForms());
+
+    Status status = Status.FINE;
+
+    io.out.println("Please fill following values to create new connection"
+      + " object");
+
+    do {
+      if( !status.canProceed() ) {
+        io.out.println();
+        io.out.println("@|red There are issues with entered data, please"
+          + " revise your input:|@");
+      }
+
+      // Query connector forms
+      if(!fillForms(io, connection.getConnectorPart(),
+                    reader, connectorBundle)) {
+        return;
+      }
+
+      // Query framework forms
+      if(!fillForms(io, connection.getFrameworkPart(),
+                    reader, frameworkBundle)) {
+        return;
+      }
+
+      // Try to create
+      status = createConnection(connection);
+    } while(!status.canProceed());
+
+    io.out.println("New connection was successfully created with validation "
+      + "status " + status.name());
+  }
+
+  private FrameworkBean getFrameworkBean() {
+    if (frameworkRequest == null) {
+      frameworkRequest = new FrameworkRequest();
+    }
+
+    return frameworkRequest.read(Environment.getServerUrl());
+  }
+
+  private ConnectorBean getConnectorBean(String cid) {
+    if (connectorRequest == null) {
+      connectorRequest = new ConnectorRequest();
+    }
+
+    return connectorRequest.read(Environment.getServerUrl(), cid);
+  }
+
+  private Status createConnection(MConnection connection) {
+    if (connectionRequest == null) {
+      connectionRequest = new ConnectionRequest();
+    }
+
+    return connectionRequest.create(Environment.getServerUrl(), connection);
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/DeleteCommand.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/DeleteCommand.java b/client/src/main/java/org/apache/sqoop/client/shell/DeleteCommand.java
new file mode 100644
index 0000000..1616b4a
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/DeleteCommand.java
@@ -0,0 +1,58 @@
+/**
+ * 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.client.shell;
+
+import org.apache.sqoop.client.core.ClientError;
+import org.apache.sqoop.common.SqoopException;
+import org.codehaus.groovy.tools.shell.Shell;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class DeleteCommand extends SqoopCommand {
+
+  private DeleteConnectionFunction connectionFunction;
+
+  public DeleteCommand(Shell shell) {
+    super(shell, "delete", "\\d",
+      new String[] {"connection", "job"},
+      "Delete", "info");
+  }
+
+  @SuppressWarnings("unchecked")
+  public Object execute(List args) {
+    if (args.size() == 0) {
+      io.out.println("Usage: delete " + getUsage());
+      io.out.println();
+      return null;
+    }
+
+    String func = (String)args.get(0);
+    if (func.equals("connection")) {
+      if (connectionFunction == null) {
+        connectionFunction = new DeleteConnectionFunction(io);
+      }
+      return connectionFunction.execute(args);
+
+    } else {
+      String msg = "Usage: delete " + getUsage();
+      throw new SqoopException(ClientError.CLIENT_0002, msg);
+    }  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/DeleteConnectionFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/DeleteConnectionFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/DeleteConnectionFunction.java
new file mode 100644
index 0000000..43a4002
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/DeleteConnectionFunction.java
@@ -0,0 +1,67 @@
+/**
+ * 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.client.shell;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.sqoop.client.core.Environment;
+import org.apache.sqoop.client.request.ConnectionRequest;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class DeleteConnectionFunction extends SqoopFunction {
+
+  private IO io;
+
+  private ConnectionRequest connectionRequest;
+
+  private static final String XID = "xid";
+
+  @SuppressWarnings("static-access")
+  public DeleteConnectionFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+      .withDescription("Connection ID")
+      .withLongOpt(XID)
+      .hasArg()
+      .create('x'));
+  }
+
+  public Object execute(List<String> args) {
+    CommandLine line = parseOptions(this, 1, args);
+    if (!line.hasOption(XID)) {
+      io.out.println("Required argument --xid is missing.");
+      return null;
+    }
+
+    if (connectionRequest == null) {
+      connectionRequest = new ConnectionRequest();
+    }
+
+    connectionRequest.delete(Environment.getServerUrl(),
+                             Long.valueOf(line.getOptionValue(XID)));
+
+    return null;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/ShowCommand.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/ShowCommand.java b/client/src/main/java/org/apache/sqoop/client/shell/ShowCommand.java
index 584d04b..22182e9 100644
--- a/client/src/main/java/org/apache/sqoop/client/shell/ShowCommand.java
+++ b/client/src/main/java/org/apache/sqoop/client/shell/ShowCommand.java
@@ -29,6 +29,7 @@ public class ShowCommand extends SqoopCommand
   private ShowVersionFunction versionFunction;
   private ShowConnectorFunction connectorFunction;
   private ShowFrameworkFunction frameworkFunction;
+  private ShowConnectionFunction connectionFunction;
 
   protected ShowCommand(Shell shell) {
     super(shell, "show", "\\sh",
@@ -71,7 +72,10 @@ public class ShowCommand extends SqoopCommand
       return frameworkFunction.execute(args);
 
     } else if (func.equals("connection")) {
-      return null;
+      if (connectionFunction == null) {
+        connectionFunction = new ShowConnectionFunction(io);
+      }
+      return connectionFunction.execute(args);
 
     } else if (func.equals("job")) {
       return null;

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectionFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectionFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectionFunction.java
new file mode 100644
index 0000000..a499ff8
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectionFunction.java
@@ -0,0 +1,107 @@
+/**
+ * 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.client.shell;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.sqoop.client.core.Environment;
+import org.apache.sqoop.client.request.ConnectionRequest;
+import org.apache.sqoop.json.ConnectionBean;
+import org.apache.sqoop.model.MConnection;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import static org.apache.sqoop.client.utils.FormDisplayer.*;
+
+/**
+ *
+ */
+public class ShowConnectionFunction extends SqoopFunction {
+  public static final String ALL = "all";
+  public static final String XID = "xid";
+
+  private IO io;
+  private ConnectionRequest connectionRequest;
+
+  @SuppressWarnings("static-access")
+  protected ShowConnectionFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+        .withDescription("Display all connections")
+        .withLongOpt(ALL)
+        .create(ALL.charAt(0)));
+    this.addOption(OptionBuilder.hasArg().withArgName("xid")
+        .withDescription(  "Display the connection with xid" )
+        .withLongOpt(XID)
+        .create('x'));
+  }
+
+  public void printHelp(PrintWriter out) {
+    out.println("Usage: show connection");
+    super.printHelp(out);
+  }
+
+  public Object execute(List<String> args) {
+    if (args.size() == 1) {
+      printHelp(io.out);
+      io.out.println();
+      return null;
+    }
+
+    CommandLine line = parseOptions(this, 1, args);
+    if (line.hasOption(ALL)) {
+      showConnection(null);
+
+    } else if (line.hasOption(XID)) {
+      showConnection(line.getOptionValue(XID));
+    }
+
+    return null;
+  }
+
+  private void showConnection(String xid) {
+    if (connectionRequest == null) {
+      connectionRequest = new ConnectionRequest();
+    }
+    ConnectionBean connectionBean =
+      connectionRequest.read(Environment.getServerUrl(), xid);
+
+    List<MConnection> connections = connectionBean.getConnections();
+
+    io.out.println("@|bold " + connections.size()
+      + " connection(s) to show: |@");
+
+    for (MConnection connection : connections) {
+      io.out.println("Connection with id " + connection.getPersistenceId()
+        + " and name: " + connection.getName());
+
+      long connectorId = connection.getConnectorId();
+
+      // Display connector part
+      displayForms(io,
+                   connection.getConnectorPart().getForms(),
+                   connectionBean.getConnectorBundle(connectorId));
+      displayForms(io,
+                   connection.getFrameworkPart().getForms(),
+                   connectionBean.getFrameworkBundle());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectorFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectorFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectorFunction.java
index 140c78a..07c9c56 100644
--- a/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectorFunction.java
+++ b/client/src/main/java/org/apache/sqoop/client/shell/ShowConnectorFunction.java
@@ -19,6 +19,7 @@ package org.apache.sqoop.client.shell;
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.ResourceBundle;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.OptionBuilder;
@@ -26,10 +27,9 @@ import org.apache.sqoop.client.core.Environment;
 import org.apache.sqoop.client.request.ConnectorRequest;
 import org.apache.sqoop.json.ConnectorBean;
 import org.apache.sqoop.model.MConnector;
-import org.apache.sqoop.model.MJobForms;
 import org.codehaus.groovy.tools.shell.IO;
 
-import static org.apache.sqoop.client.display.FormDisplayer.*;
+import static org.apache.sqoop.client.utils.FormDisplayer.*;
 
 @SuppressWarnings("serial")
 public class ShowConnectorFunction extends SqoopFunction
@@ -82,12 +82,13 @@ public class ShowConnectorFunction extends SqoopFunction
       conntectorRequest = new ConnectorRequest();
     }
     ConnectorBean connectorBean =
-      conntectorRequest.doGet(Environment.getServerUrl(), cid);
-    MConnector[] connectors = connectorBean.getConnectors();
+      conntectorRequest.read(Environment.getServerUrl(), cid);
+    List<MConnector> connectors = connectorBean.getConnectors();
+    List<ResourceBundle> bundles = connectorBean.getResourceBundles();
 
-    io.out.println("@|bold " + connectors.length + " connector(s) to show: |@");
-    for (int i = 0; i < connectors.length; i++) {
-      MConnector connector = connectors[i];
+    io.out.println("@|bold " + connectors.size() + " connector(s) to show: |@");
+    for (int i = 0; i < connectors.size(); i++) {
+      MConnector connector = connectors.get(i);
 
       io.out.print("Connector with id ");
       io.out.print(connector.getPersistenceId());
@@ -98,7 +99,7 @@ public class ShowConnectorFunction extends SqoopFunction
       io.out.print("  Class: ");
       io.out.println(connector.getClassName());
 
-      displayFormDetails(io, connector);
+      displayFormMetadataDetails(io, connector, bundles.get(i));
 
     }
 

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/ShowFrameworkFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/ShowFrameworkFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/ShowFrameworkFunction.java
index bcb267e..532ff04 100644
--- a/client/src/main/java/org/apache/sqoop/client/shell/ShowFrameworkFunction.java
+++ b/client/src/main/java/org/apache/sqoop/client/shell/ShowFrameworkFunction.java
@@ -21,13 +21,13 @@ import org.apache.sqoop.client.core.Environment;
 import org.apache.sqoop.client.request.FrameworkRequest;
 import org.apache.sqoop.json.FrameworkBean;
 import org.apache.sqoop.model.MFramework;
-import org.apache.sqoop.model.MJobForms;
 import org.codehaus.groovy.tools.shell.IO;
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.ResourceBundle;
 
-import static org.apache.sqoop.client.display.FormDisplayer.*;
+import static org.apache.sqoop.client.utils.FormDisplayer.*;
 
 /**
  *
@@ -65,15 +65,17 @@ public class ShowFrameworkFunction extends SqoopFunction {
     }
 
     FrameworkBean frameworkBean =
-      frameworkRequest.doGet(Environment.getServerUrl());
+      frameworkRequest.read(Environment.getServerUrl());
+
     MFramework framework = frameworkBean.getFramework();
+    ResourceBundle bundle = frameworkBean.getResourceBundle();
 
     io.out.println("@|bold Framework specific options: |@");
 
     io.out.print("Persistent id: ");
     io.out.println(framework.getPersistenceId());
 
-    displayFormDetails(io, framework);
+    displayFormMetadataDetails(io, framework, bundle);
 
     io.out.println();
   }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/SqoopShell.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/SqoopShell.java b/client/src/main/java/org/apache/sqoop/client/shell/SqoopShell.java
index acf1222..91682d1 100644
--- a/client/src/main/java/org/apache/sqoop/client/shell/SqoopShell.java
+++ b/client/src/main/java/org/apache/sqoop/client/shell/SqoopShell.java
@@ -32,13 +32,13 @@ public class SqoopShell
 {
   private static final String banner =
       "@|green Sqoop Shell:|@ Type '@|bold help|@' or '@|bold \\h|@' for help.";
-  
+
   public static HashSet<String> commandsToKeep;
   static {
     commandsToKeep = new HashSet<String>();
     commandsToKeep.add("exit");
     commandsToKeep.add("history");
-  };
+  }
 
   public static void main (String[] args) throws Exception
   {
@@ -60,6 +60,9 @@ public class SqoopShell
     shell.register(new HelpCommand(shell));
     shell.register(new SetCommand(shell));
     shell.register(new ShowCommand(shell));
+    shell.register(new CreateCommand(shell));
+    shell.register(new DeleteCommand(shell));
+    shell.register(new UpdateCommand(shell));
 
     if (args.length == 0) {
       // Interactive mode:
@@ -88,4 +91,4 @@ public class SqoopShell
       }
     }
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/UpdateCommand.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/UpdateCommand.java b/client/src/main/java/org/apache/sqoop/client/shell/UpdateCommand.java
new file mode 100644
index 0000000..ad72a4e
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/UpdateCommand.java
@@ -0,0 +1,58 @@
+/**
+ * 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.client.shell;
+
+import org.apache.sqoop.client.core.ClientError;
+import org.apache.sqoop.common.SqoopException;
+import org.codehaus.groovy.tools.shell.Shell;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class UpdateCommand extends  SqoopCommand {
+
+  private UpdateConnectionFunction connectionFunction;
+
+  public UpdateCommand(Shell shell) {
+    super(shell, "update", "\\up",
+      new String[] {"connection", "job"},
+      "Update", "info");
+  }
+
+  public Object execute(List args) {
+    if (args.size() == 0) {
+      io.out.println("Usage: create " + getUsage());
+      io.out.println();
+      return null;
+    }
+
+    String func = (String)args.get(0);
+    if (func.equals("connection")) {
+      if (connectionFunction == null) {
+        connectionFunction = new UpdateConnectionFunction(io);
+      }
+      return connectionFunction.execute(args);
+
+    } else {
+      String msg = "Usage: update " + getUsage();
+      throw new SqoopException(ClientError.CLIENT_0002, msg);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/shell/UpdateConnectionFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/UpdateConnectionFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/UpdateConnectionFunction.java
new file mode 100644
index 0000000..c9ced51
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/UpdateConnectionFunction.java
@@ -0,0 +1,136 @@
+/**
+ * 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.client.shell;
+
+import jline.ConsoleReader;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.sqoop.client.core.ClientError;
+import org.apache.sqoop.client.core.Environment;
+import org.apache.sqoop.client.request.ConnectionRequest;
+import org.apache.sqoop.common.SqoopException;
+import org.apache.sqoop.json.ConnectionBean;
+import org.apache.sqoop.model.MConnection;
+import org.apache.sqoop.validation.Status;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import static org.apache.sqoop.client.utils.FormFiller.*;
+
+/**
+ *
+ */
+public class UpdateConnectionFunction extends SqoopFunction {
+
+  private static final String XID = "xid";
+
+  private ConnectionRequest connectionRequest;
+
+  private IO io;
+
+  @SuppressWarnings("static-access")
+  public UpdateConnectionFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+      .withDescription("Connection ID")
+      .withLongOpt(XID)
+      .hasArg()
+      .create(XID.charAt(0)));
+  }
+
+  public Object execute(List<String> args) {
+    CommandLine line = parseOptions(this, 1, args);
+    if (!line.hasOption(XID)) {
+      io.out.println("Required argument --xid is missing.");
+      return null;
+    }
+
+    try {
+      updateConnection(line.getOptionValue(XID));
+    } catch (IOException ex) {
+      throw new SqoopException(ClientError.CLIENT_0005, ex);
+    }
+
+    return null;
+  }
+
+  private void updateConnection(String connectionId) throws IOException {
+    io.out.println("Updating connection with id " + connectionId);
+
+    ConsoleReader reader = new ConsoleReader();
+
+    ConnectionBean connectionBean = readConnection(connectionId);
+
+    // TODO(jarcec): Check that we have expected data
+    MConnection connection = connectionBean.getConnections().get(0);
+    ResourceBundle frameworkBundle
+      = connectionBean.getFrameworkBundle();
+    ResourceBundle connectorBundle
+      = connectionBean.getConnectorBundle(connection.getConnectorId());
+
+    Status status = Status.FINE;
+
+    io.out.println("Please update connection metadata:");
+
+    do {
+      if( !status.canProceed() ) {
+        io.out.println();
+        io.out.println("@|red There are issues with entered data, please"
+          + " revise your input:|@");
+      }
+
+      // Query connector forms
+      if(!fillForms(io, connection.getConnectorPart(),
+                    reader, connectorBundle)) {
+        return;
+      }
+
+      // Query framework forms
+      if(!fillForms(io, connection.getFrameworkPart(),
+                    reader, frameworkBundle)) {
+        return;
+      }
+
+      // Try to create
+      status = updateConnection(connection);
+    } while(!status.canProceed());
+
+    io.out.println("Connection was sucessfully updated with status "
+      + status.name());
+  }
+
+  private Status updateConnection(MConnection connection) {
+    if (connectionRequest == null) {
+      connectionRequest = new ConnectionRequest();
+    }
+
+    return connectionRequest.update(Environment.getServerUrl(), connection);
+  }
+
+  private ConnectionBean readConnection(String connectionId) {
+    if (connectionRequest == null) {
+      connectionRequest = new ConnectionRequest();
+    }
+
+    return connectionRequest.read(Environment.getServerUrl(), connectionId);
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/utils/FormDisplayer.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/utils/FormDisplayer.java b/client/src/main/java/org/apache/sqoop/client/utils/FormDisplayer.java
new file mode 100644
index 0000000..02588c7
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/utils/FormDisplayer.java
@@ -0,0 +1,135 @@
+/**
+ * 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.client.utils;
+
+import org.apache.sqoop.model.MForm;
+import org.apache.sqoop.model.MFramework;
+import org.apache.sqoop.model.MInput;
+import org.apache.sqoop.model.MInputType;
+import org.apache.sqoop.model.MJobForms;
+import org.apache.sqoop.model.MStringInput;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+
+/**
+ * Convenience static methods for displaying form related information
+ */
+public class FormDisplayer {
+
+  public static void displayFormMetadataDetails(IO io,
+                                                MFramework framework,
+                                                ResourceBundle bundle) {
+    io.out.print("  Supported job types: ");
+    io.out.println(framework.getAllJobsForms().keySet().toString());
+
+    displayFormsMetadata(io,
+      framework.getConnectionForms().getForms(),
+      "Connection",
+      bundle);
+
+    for (MJobForms jobForms : framework.getAllJobsForms().values()) {
+      io.out.print("  Forms for job type ");
+      io.out.print(jobForms.getType().name());
+      io.out.println(":");
+
+      displayFormsMetadata(io, jobForms.getForms(), "Job", bundle);
+    }
+  }
+
+  public static void displayFormsMetadata(IO io,
+                                         List<MForm> forms,
+                                         String type,
+                                         ResourceBundle bundle) {
+    Iterator<MForm> fiter = forms.iterator();
+    int findx = 1;
+    while (fiter.hasNext()) {
+      io.out.print("    ");
+      io.out.print(type);
+      io.out.print(" form ");
+      io.out.print(findx++);
+      io.out.println(":");
+
+      MForm form = fiter.next();
+      io.out.print("      Name: ");
+      io.out.println(form.getName());
+
+      // Label
+      io.out.print("      Label: ");
+      io.out.println(bundle.getString(form.getLabelKey()));
+
+      // Help text
+      io.out.print("      Help: ");
+      io.out.println(bundle.getString(form.getHelpKey()));
+
+      List<MInput<?>> inputs = form.getInputs();
+      Iterator<MInput<?>> iiter = inputs.iterator();
+      int iindx = 1;
+      while (iiter.hasNext()) {
+        io.out.print("      Input ");
+        io.out.print(iindx++);
+        io.out.println(":");
+
+        MInput<?> input = iiter.next();
+        io.out.print("        Name: ");
+        io.out.println(input.getName());
+        io.out.print("        Label: ");
+        io.out.println(bundle.getString(input.getLabelKey()));
+        io.out.print("        Help: ");
+        io.out.println(bundle.getString(input.getHelpKey()));
+        io.out.print("        Type: ");
+        io.out.println(input.getType());
+        if (input.getType() == MInputType.STRING) {
+          io.out.print("        Mask: ");
+          io.out.println(((MStringInput)input).isMasked());
+          io.out.print("        Size: ");
+          io.out.println(((MStringInput)input).getMaxLength());
+        }
+      }
+    }
+  }
+
+  public static void displayForms(IO io,
+                                  List<MForm> forms,
+                                  ResourceBundle bundle) {
+    for(MForm form : forms) {
+      displayForm(io, form, bundle);
+    }
+  }
+
+  private static void displayForm(IO io, MForm form, ResourceBundle bundle) {
+    io.out.print("  ");
+    io.out.println(bundle.getString(form.getLabelKey()));
+
+    for (MInput<?> input : form.getInputs()) {
+      io.out.print("    ");
+      io.out.print(bundle.getString(input.getLabelKey()));
+      io.out.print(": ");
+      if(!input.isEmpty()) {
+        io.out.print(input.getUrlSafeValueString());
+      }
+      io.out.println("");
+    }
+  }
+
+  private FormDisplayer() {
+    // Do not instantiate
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java b/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java
new file mode 100644
index 0000000..5b6ebb9
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java
@@ -0,0 +1,132 @@
+/**
+ * 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.client.utils;
+
+import jline.ConsoleReader;
+import org.apache.sqoop.model.MConnectionForms;
+import org.apache.sqoop.model.MForm;
+import org.apache.sqoop.model.MInput;
+import org.apache.sqoop.model.MStringInput;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.io.IOException;
+import java.util.ResourceBundle;
+
+/**
+ *
+ */
+public class FormFiller {
+
+
+  public static boolean fillForms(IO io,
+                                  MConnectionForms formsMetadata,
+                                  ConsoleReader reader,
+                                  ResourceBundle bundle)
+    throws IOException {
+    for (MForm form : formsMetadata.getForms()) {
+      if(!fillForm(io, form, reader, bundle)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public static boolean fillForm(IO io,
+                                 MForm form,
+                                 ConsoleReader reader,
+                                 ResourceBundle bundle) throws IOException {
+    io.out.println("");
+    io.out.println(bundle.getString(form.getLabelKey()));
+    for (MInput input : form.getInputs()) {
+      if(!fillInput(io, input, reader, bundle)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public static boolean fillInput(IO io,
+                                  MInput input,
+                                  ConsoleReader reader,
+                                  ResourceBundle bundle) throws IOException {
+    // Print out warning or error message in case some validations were already
+    // performed.
+    switch (input.getValidationSeverity()) {
+      case ERROR:
+        io.out.println("Error message: @|red "
+          + input.getValidationMessage() + " |@");
+        break;
+      case WARNING:
+        io.out.println("Warning message: @|yellow "
+          + input.getValidationMessage() + " |@");
+        break;
+      default:
+        // Simply ignore all other states for the moment
+        break;
+    }
+
+    // Based on the input type, let's perform specific load
+    switch (input.getType()) {
+      case STRING:
+        return fillInputString(io, (MStringInput) input, reader, bundle);
+      //TODO(jarcec): Support MAP
+      default:
+        io.out.println("Unsupported data type " + input.getType());
+        return true;
+    }
+  }
+
+  public static boolean fillInputString(IO io,
+                                        MStringInput input,
+                                        ConsoleReader reader,
+                                        ResourceBundle bundle)
+                                        throws IOException {
+    // Print prompt
+    reader.printString(bundle.getString(input.getLabelKey()) + ": ");
+    reader.flushConsole();
+
+    // Fill already filled data when available
+    if(!input.isEmpty()) {
+      reader.putString(input.getValue());
+    }
+
+    // Get the data
+    String userTyped;
+    if(input.isMasked()) {
+       userTyped = reader.readLine('*');
+    } else {
+      userTyped = reader.readLine();
+    }
+
+    if (userTyped == null) {
+      return false;
+    } else if (userTyped.isEmpty()) {
+      input.setEmpty();
+    } else {
+      input.setValue(userTyped);
+    }
+
+    return true;
+  }
+
+  private FormFiller() {
+    // Do not instantiate
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/common/src/main/java/org/apache/sqoop/common/ExceptionInfo.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/common/ExceptionInfo.java b/common/src/main/java/org/apache/sqoop/common/ExceptionInfo.java
deleted file mode 100644
index b55111f..0000000
--- a/common/src/main/java/org/apache/sqoop/common/ExceptionInfo.java
+++ /dev/null
@@ -1,64 +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.common;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import org.apache.sqoop.json.JsonBean;
-import org.json.simple.JSONObject;
-
-public class ExceptionInfo implements JsonBean {
-
-  public static final String ERROR_CODE = "error-code";
-  public static final String ERROR_MESSAGE = "error-message";
-  public static final String STACK_TRACE = "stack-trace";
-
-  private String errorCode;
-  private String errorMessage;
-  private String stackTrace;
-
-  public ExceptionInfo(String code, String message, Exception ex) {
-    errorCode = code;
-    errorMessage = message;
-
-    StringWriter writer = new StringWriter();
-    ex.printStackTrace(new PrintWriter(writer));
-    writer.flush();
-
-    stackTrace = writer.getBuffer().toString();
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public JSONObject extract() {
-    JSONObject result = new JSONObject();
-    result.put(ERROR_CODE, errorCode);
-    result.put(ERROR_MESSAGE, errorMessage);
-    result.put(STACK_TRACE, stackTrace);
-
-    return result;
-  }
-
-  @Override
-  public void restore(JSONObject jsonObject) {
-    errorCode = (String) jsonObject.get(ERROR_CODE);
-    errorMessage = (String) jsonObject.get(ERROR_MESSAGE);
-    stackTrace = (String) jsonObject.get(STACK_TRACE);
-  }
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/common/src/main/java/org/apache/sqoop/json/ConnectionBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/ConnectionBean.java b/common/src/main/java/org/apache/sqoop/json/ConnectionBean.java
new file mode 100644
index 0000000..c793465
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/json/ConnectionBean.java
@@ -0,0 +1,177 @@
+/**
+ * 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;
+
+import org.apache.sqoop.model.MConnection;
+import org.apache.sqoop.model.MConnectionForms;
+import org.apache.sqoop.model.MForm;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import static org.apache.sqoop.json.util.FormSerialization.*;
+import static org.apache.sqoop.json.util.ResourceBundleSerialization.*;
+
+/**
+ * Connection representation that is being send across the network between
+ * Sqoop server and client. Server might optionally send resource bundles
+ * associated with the connections to spare client of sending another HTTP
+ * requests to obtain them.
+ */
+public class ConnectionBean implements JsonBean {
+
+  private static final String ALL = "all";
+  private static final String ID = "id";
+  private static final String NAME = "name";
+  private static final String CONNECTOR_ID = "connector-id";
+  private static final String CONNECTOR_PART = "connector";
+  private static final String FRAMEWORK_PART = "framework";
+
+  // Compulsory
+  private List<MConnection> connections;
+
+  // Optional
+  private Map<Long, ResourceBundle> connectorBundles;
+  private ResourceBundle frameworkBundle;
+
+  // For "extract"
+  public ConnectionBean(MConnection connection) {
+    this();
+    this.connections = new ArrayList<MConnection>();
+    this.connections.add(connection);
+  }
+
+  public ConnectionBean(List<MConnection> connections) {
+    this();
+    this.connections = connections;
+  }
+
+  // For "restore"
+  public ConnectionBean() {
+    connectorBundles = new HashMap<Long, ResourceBundle>();
+  }
+
+  public void setFrameworkBundle(ResourceBundle frameworkBundle) {
+    this.frameworkBundle = frameworkBundle;
+  }
+
+  public void addConnectorBundle(Long id, ResourceBundle connectorBundle) {
+    connectorBundles.put(id, connectorBundle);
+  }
+
+  public boolean hasConnectorBundle(Long id) {
+    return connectorBundles.containsKey(id);
+  }
+
+  public List<MConnection> getConnections() {
+    return connections;
+  }
+
+  public ResourceBundle getConnectorBundle(Long id) {
+    return connectorBundles.get(id);
+  }
+
+  public ResourceBundle getFrameworkBundle() {
+    return frameworkBundle;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public JSONObject extract() {
+    JSONArray array = new JSONArray();
+
+    for(MConnection connection : connections) {
+      JSONObject object = new JSONObject();
+
+      object.put(ID, connection.getPersistenceId());
+      object.put(NAME, connection.getName());
+      object.put(CONNECTOR_ID, connection.getConnectorId());
+      object.put(CONNECTOR_PART,
+        extractForms(connection.getConnectorPart().getForms()));
+      object.put(FRAMEWORK_PART,
+        extractForms(connection.getFrameworkPart().getForms()));
+
+      array.add(object);
+    }
+
+    JSONObject all = new JSONObject();
+    all.put(ALL, array);
+
+    if(!connectorBundles.isEmpty()) {
+      JSONObject bundles = new JSONObject();
+
+      for(Map.Entry<Long, ResourceBundle> entry : connectorBundles.entrySet()) {
+        bundles.put(entry.getKey().toString(),
+                    extractResourceBundle(entry.getValue()));
+      }
+
+      all.put(CONNECTOR_RESOURCES, bundles);
+    }
+    if(frameworkBundle != null) {
+      all.put(FRAMEWORK_RESOURCES,extractResourceBundle(frameworkBundle));
+    }
+    return all;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public void restore(JSONObject jsonObject) {
+    connections = new ArrayList<MConnection>();
+
+    JSONArray array = (JSONArray) jsonObject.get(ALL);
+
+    for (Object obj : array) {
+      JSONObject object = (JSONObject) obj;
+
+      long connectorId = (Long) object.get(CONNECTOR_ID);
+      JSONArray connectorPart = (JSONArray) object.get(CONNECTOR_PART);
+      JSONArray frameworkPart = (JSONArray) object.get(FRAMEWORK_PART);
+
+      List<MForm> connectorForms = restoreForms(connectorPart);
+      List<MForm> frameworkForms = restoreForms(frameworkPart);
+
+      MConnection connection = new MConnection(connectorId,
+        new MConnectionForms(connectorForms),
+        new MConnectionForms(frameworkForms));
+
+      connection.setPersistenceId((Long) object.get(ID));
+      connection.setName((String) object.get(NAME));
+
+      connections.add(connection);
+    }
+
+    if(jsonObject.containsKey(CONNECTOR_RESOURCES)) {
+      JSONObject bundles = (JSONObject) jsonObject.get(CONNECTOR_RESOURCES);
+      Set<Map.Entry<String, JSONObject>> entrySet = bundles.entrySet();
+      for (Map.Entry<String, JSONObject> entry : entrySet) {
+        connectorBundles.put(Long.parseLong(entry.getKey()),
+                             restoreResourceBundle(entry.getValue()));
+      }
+    }
+    if(jsonObject.containsKey(FRAMEWORK_RESOURCES)) {
+      frameworkBundle = restoreResourceBundle(
+        (JSONObject) jsonObject.get(FRAMEWORK_RESOURCES));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/common/src/main/java/org/apache/sqoop/json/ConnectorBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/ConnectorBean.java b/common/src/main/java/org/apache/sqoop/json/ConnectorBean.java
index 3bad175..f630f75 100644
--- a/common/src/main/java/org/apache/sqoop/json/ConnectorBean.java
+++ b/common/src/main/java/org/apache/sqoop/json/ConnectorBean.java
@@ -18,8 +18,10 @@
 package org.apache.sqoop.json;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.ResourceBundle;
 import java.util.Set;
 
 import org.apache.sqoop.model.MConnectionForms;
@@ -31,25 +33,33 @@ import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 
 import static org.apache.sqoop.json.util.FormSerialization.*;
+import static org.apache.sqoop.json.util.ResourceBundleSerialization.*;
 
 public class ConnectorBean implements JsonBean {
 
-  private MConnector[] connectors;
+  private List<MConnector> connectors;
+
+  private List<ResourceBundle> bundles;
 
   // for "extract"
-  public ConnectorBean(MConnector[] connectors) {
-    this.connectors = new MConnector[connectors.length];
-    System.arraycopy(connectors, 0, this.connectors, 0, connectors.length);
+  public ConnectorBean(List<MConnector> connectors,
+                       List<ResourceBundle> bundles) {
+    this.connectors = connectors;
+    this.bundles = bundles;
   }
 
   // for "restore"
   public ConnectorBean() {
   }
 
-  public MConnector[] getConnectors() {
+  public List<MConnector> getConnectors() {
     return connectors;
   }
 
+  public List<ResourceBundle> getResourceBundles() {
+    return bundles;
+  }
+
   @SuppressWarnings("unchecked")
   @Override
   public JSONObject extract() {
@@ -58,6 +68,7 @@ public class ConnectorBean implements JsonBean {
     JSONArray classArray = new JSONArray();
     JSONArray conFormsArray = new JSONArray();
     JSONArray jobFormsArray = new JSONArray();
+    JSONArray bundlesArray;
 
     for (MConnector connector : connectors) {
       idArray.add(connector.getPersistenceId());
@@ -72,12 +83,15 @@ public class ConnectorBean implements JsonBean {
       jobFormsArray.add(jobForms);
     }
 
+    bundlesArray = extractResourceBundles(bundles);
+
     JSONObject result = new JSONObject();
     result.put(ID, idArray);
     result.put(NAME, nameArray);
     result.put(CLASS, classArray);
     result.put(CON_FORMS, conFormsArray);
     result.put(JOB_FORMS, jobFormsArray);
+    result.put(RESOURCES, bundlesArray);
     return result;
   }
 
@@ -92,8 +106,8 @@ public class ConnectorBean implements JsonBean {
     JSONArray jobFormsArray =
         (JSONArray) jsonObject.get(JOB_FORMS);
 
-    connectors = new MConnector[idArray.size()];
-    for (int i = 0; i < connectors.length; i++) {
+    connectors = new LinkedList<MConnector>();
+    for (int i = 0; i < idArray.size(); i++) {
       long persistenceId = (Long) idArray.get(i);
       String uniqueName = (String) nameArray.get(i);
       String className = (String) classArray.get(i);
@@ -116,7 +130,9 @@ public class ConnectorBean implements JsonBean {
       MConnector connector = new MConnector(uniqueName, className,
         new MConnectionForms(connForms), jobs);
       connector.setPersistenceId(persistenceId);
-      connectors[i] = connector;
+      connectors.add(connector);
     }
+
+    bundles = restoreResourceBundles((JSONArray) jsonObject.get(RESOURCES));
   }
 }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/common/src/main/java/org/apache/sqoop/json/ExceptionInfo.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/ExceptionInfo.java b/common/src/main/java/org/apache/sqoop/json/ExceptionInfo.java
new file mode 100644
index 0000000..68de4b1
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/json/ExceptionInfo.java
@@ -0,0 +1,73 @@
+/**
+ * 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;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.json.simple.JSONObject;
+
+public class ExceptionInfo implements JsonBean {
+
+  public static final String ERROR_CODE = "error-code";
+  public static final String ERROR_MESSAGE = "error-message";
+  public static final String STACK_TRACE = "stack-trace";
+
+  private String errorCode;
+  private String errorMessage;
+  private String stackTrace;
+
+  // For "extract"
+  public ExceptionInfo(String code, String message, Exception ex) {
+    errorCode = code;
+    errorMessage = message;
+
+    StringWriter writer = new StringWriter();
+    ex.printStackTrace(new PrintWriter(writer));
+    writer.flush();
+
+    stackTrace = writer.getBuffer().toString();
+  }
+
+  // For "restore"
+  public ExceptionInfo() {
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public JSONObject extract() {
+    JSONObject result = new JSONObject();
+    result.put(ERROR_CODE, errorCode);
+    result.put(ERROR_MESSAGE, errorMessage);
+    result.put(STACK_TRACE, stackTrace);
+
+    return result;
+  }
+
+  @Override
+  public void restore(JSONObject jsonObject) {
+    errorCode = (String) jsonObject.get(ERROR_CODE);
+    errorMessage = (String) jsonObject.get(ERROR_MESSAGE);
+    stackTrace = (String) jsonObject.get(STACK_TRACE);
+  }
+
+  @Override
+  public String toString() {
+    return errorMessage;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/common/src/main/java/org/apache/sqoop/json/FrameworkBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/FrameworkBean.java b/common/src/main/java/org/apache/sqoop/json/FrameworkBean.java
index 47295e0..4e07b6c 100644
--- a/common/src/main/java/org/apache/sqoop/json/FrameworkBean.java
+++ b/common/src/main/java/org/apache/sqoop/json/FrameworkBean.java
@@ -28,9 +28,11 @@ import org.json.simple.JSONObject;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.ResourceBundle;
 import java.util.Set;
 
 import static org.apache.sqoop.json.util.FormSerialization.*;
+import static org.apache.sqoop.json.util.ResourceBundleSerialization.*;
 
 /**
  *
@@ -40,9 +42,12 @@ public class FrameworkBean implements JsonBean {
 
   private MFramework framework;
 
+  private ResourceBundle bundle;
+
   // for "extract"
-  public FrameworkBean(MFramework framework) {
+  public FrameworkBean(MFramework framework, ResourceBundle bundle) {
     this.framework = framework;
+    this.bundle = bundle;
   }
 
   // for "restore"
@@ -53,6 +58,10 @@ public class FrameworkBean implements JsonBean {
     return framework;
   }
 
+  public ResourceBundle getResourceBundle() {
+    return bundle;
+  }
+
   @SuppressWarnings("unchecked")
   @Override
   public JSONObject extract() {
@@ -68,10 +77,10 @@ public class FrameworkBean implements JsonBean {
     result.put(ID, framework.getPersistenceId());
     result.put(CON_FORMS, conForms);
     result.put(JOB_FORMS, jobForms);
+    result.put(RESOURCES, extractResourceBundle(bundle));
     return result;
   }
 
-
   @Override
   @SuppressWarnings("unchecked")
   public void restore(JSONObject jsonObject) {
@@ -94,6 +103,8 @@ public class FrameworkBean implements JsonBean {
 
     framework = new MFramework(new MConnectionForms(connForms), jobs);
     framework.setPersistenceId(id);
+
+    bundle = restoreResourceBundle((JSONObject) jsonObject.get(RESOURCES));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/common/src/main/java/org/apache/sqoop/json/ValidationBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/ValidationBean.java b/common/src/main/java/org/apache/sqoop/json/ValidationBean.java
new file mode 100644
index 0000000..73b73fa
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/json/ValidationBean.java
@@ -0,0 +1,143 @@
+/**
+ * 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;
+
+import org.apache.sqoop.model.MConnection;
+import org.apache.sqoop.model.MForm;
+import org.apache.sqoop.model.MInput;
+import org.apache.sqoop.model.MValidatedElement;
+import org.apache.sqoop.validation.Status;
+import org.json.simple.JSONObject;
+
+import java.util.List;
+
+/**
+ * Bean for sending validations across network. As is expected that both filled
+ * forms will be available on both ends (client and server), we're transferring
+ * only validation status and messages.
+ */
+public class ValidationBean implements JsonBean {
+
+  private static final String STATUS = "status";
+  private static final String CONNECTOR_PART = "connector";
+  private static final String FRAMEWORK_PART = "framework";
+
+  private static final String SEVERITY = "severity";
+  private static final String MESSAGE = "message";
+
+  private MConnection connection;
+  private Status status;
+
+  // For "extract"
+  public ValidationBean(MConnection connection, Status status) {
+    this.connection = connection;
+    this.status = status;
+  }
+
+  // For "restore"
+  public ValidationBean(MConnection connection) {
+    this.connection = connection;
+  }
+
+  public MConnection getConnection() {
+    return connection;
+  }
+
+  public Status getStatus() {
+    return status;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public JSONObject extract() {
+    JSONObject object = new JSONObject();
+
+    object.put(STATUS, status.name());
+    object.put(CONNECTOR_PART,
+      extractForms(connection.getConnectorPart().getForms()));
+    object.put(FRAMEWORK_PART,
+      extractForms(connection.getFrameworkPart().getForms()));
+
+    return object;
+  }
+
+  @SuppressWarnings("unchecked")
+  private JSONObject extractForms(List<MForm> forms) {
+    JSONObject ret = new JSONObject();
+
+    for (MForm form : forms) {
+      ret.put(form.getPersistenceId(), extractForm(form));
+    }
+    return ret;
+  }
+
+  @SuppressWarnings("unchecked")
+  private JSONObject extractForm(MForm form) {
+    JSONObject object = new JSONObject();
+
+    for (MInput input : form.getInputs()) {
+      if (input.getValidationSeverity() != MValidatedElement.Severity.OK) {
+        JSONObject validation = new JSONObject();
+        validation.put(SEVERITY, input.getValidationSeverity().name());
+        validation.put(MESSAGE, input.getValidationMessage());
+
+        object.put(input.getPersistenceId(), validation);
+      }
+    }
+
+    return object;
+  }
+
+  @Override
+  public void restore(JSONObject jsonObject) {
+    status = Status.valueOf((String) jsonObject.get(STATUS));
+
+    JSONObject connectorPart = (JSONObject) jsonObject.get(CONNECTOR_PART);
+    JSONObject frameworkPart = (JSONObject) jsonObject.get(FRAMEWORK_PART);
+
+    restoreForms(connectorPart, connection.getConnectorPart().getForms());
+    restoreForms(frameworkPart, connection.getFrameworkPart().getForms());
+  }
+
+  private void restoreForms(JSONObject json, List<MForm> forms) {
+    for (MForm form : forms) {
+      String id = Long.toString(form.getPersistenceId());
+      if (json.containsKey(id)) {
+        restoreForm((JSONObject) json.get(id), form);
+      }
+    }
+  }
+
+  private void restoreForm(JSONObject json, MForm form) {
+    for (MInput input : form.getInputs()) {
+      String id = Long.toString(input.getPersistenceId());
+      if (json.containsKey(id)) {
+        JSONObject validation = (JSONObject) json.get(id);
+
+        MValidatedElement.Severity severity =
+          MValidatedElement.Severity.valueOf((String) validation.get(SEVERITY));
+        String message = (String) validation.get(MESSAGE);
+
+        input.setValidationMessage(severity, message);
+      } else {
+        input.setValidationMessage(MValidatedElement.Severity.OK, null);
+      }
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/47cb311a/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
index ba9cf02..6b7332a 100644
--- a/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java
+++ b/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java
@@ -28,7 +28,6 @@ import org.json.simple.JSONObject;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Convenient static methods for serializing forms.
@@ -48,6 +47,7 @@ public class FormSerialization {
   public static final String FORM_INPUT_TYPE = "type";
   public static final String FORM_INPUT_MASK = "mask";
   public static final String FORM_INPUT_SIZE = "size";
+  public static final String FORM_INPUT_VALUE = "value";
 
   /**
    * Transform given list of forms to JSON Array object.
@@ -75,6 +75,7 @@ public class FormSerialization {
   @SuppressWarnings("unchecked")
   public static JSONObject extractForm(MForm mForm) {
     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();
@@ -83,15 +84,22 @@ public class FormSerialization {
     for (MInput<?> mInput : mForm.getInputs()) {
       JSONObject input = new JSONObject();
       mInputs.add(input);
-
+      input.put(ID, mInput.getPersistenceId());
       input.put(FORM_INPUT_NAME, mInput.getName());
       input.put(FORM_INPUT_TYPE, mInput.getType().toString());
+
+      // String specific serialization
       if (mInput.getType() == MInputType.STRING) {
         input.put(FORM_INPUT_MASK,
             ((MStringInput)mInput).isMasked());
         input.put(FORM_INPUT_SIZE,
             ((MStringInput)mInput).getMaxLength());
       }
+
+      // Serialize value if is there
+      if(!mInput.isEmpty()) {
+        input.put(FORM_INPUT_VALUE, mInput.getUrlSafeValueString());
+      }
     }
 
     return form;
@@ -127,25 +135,36 @@ public class FormSerialization {
       JSONObject input = (JSONObject) inputs.get(i);
       MInputType type =
           MInputType.valueOf((String) input.get(FORM_INPUT_TYPE));
+      MInput mInput = null;
       switch (type) {
         case STRING: {
           String name = (String) input.get(FORM_INPUT_NAME);
           boolean mask = (Boolean) input.get(FORM_INPUT_MASK);
           long size = (Long) input.get(FORM_INPUT_SIZE);
-          MInput<String> mInput = new MStringInput(name, mask, (short) size);
-          mInputs.add(mInput);
+          mInput = new MStringInput(name, mask, (short) size);
           break;
         }
         case MAP: {
           String name = (String) input.get(FORM_INPUT_NAME);
-          MInput<Map<String, String>> mInput = new MMapInput(name);
-          mInputs.add(mInput);
+          mInput = new MMapInput(name);
           break;
         }
       }
+
+      // Propagate form ID
+      mInput.setPersistenceId((Long)input.get(ID));
+
+      // Propagate form optional value
+      if(input.containsKey(FORM_INPUT_VALUE)) {
+        mInput.restoreFromUrlSafeValueString(
+          (String) input.get(FORM_INPUT_VALUE));
+      }
+      mInputs.add(mInput);
     }
 
-    return new MForm((String) form.get(FORM_NAME), mInputs);
+    MForm mForm = new MForm((String) form.get(FORM_NAME), mInputs);
+    mForm.setPersistenceId((Long) form.get(ID));
+    return mForm;
   }
 
   private FormSerialization() {


Mime
View raw message