sqoop-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b...@apache.org
Subject [1/4] git commit: SQOOP-656 End to end submission engine (Jarek Jarcec Cecho)
Date Fri, 02 Nov 2012 21:58:28 GMT
Updated Branches:
  refs/heads/sqoop2 eac799f45 -> 1cd3c3734


SQOOP-656 End to end submission engine
(Jarek Jarcec Cecho)


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

Branch: refs/heads/sqoop2
Commit: 1cd3c3734c9be8dc27805a7ea725988fcd5c4a7d
Parents: eac799f
Author: Bilung Lee <blee@apache.org>
Authored: Fri Nov 2 14:54:58 2012 -0700
Committer: Bilung Lee <blee@apache.org>
Committed: Fri Nov 2 14:54:58 2012 -0700

----------------------------------------------------------------------
 .../org/apache/sqoop/client/core/Environment.java  |    2 +-
 .../org/apache/sqoop/client/core/RequestCache.java |   30 +
 .../org/apache/sqoop/client/request/Request.java   |    4 +-
 .../sqoop/client/request/SubmissionRequest.java    |   62 ++
 .../client/shell/CreateConnectionFunction.java     |    2 -
 .../org/apache/sqoop/client/shell/SqoopShell.java  |    1 +
 .../sqoop/client/shell/SubmissionCommand.java      |   69 +++
 .../client/shell/SubmissionStartFunction.java      |   61 ++
 .../client/shell/SubmissionStatusFunction.java     |   61 ++
 .../sqoop/client/shell/SubmissionStopFunction.java |   61 ++
 .../apache/sqoop/client/utils/FormFiller.java.orig |  432 ---------------
 .../sqoop/client/utils/SubmissionDisplayer.java    |   76 +++
 .../sqoop/client/utils/ThrowableDisplayer.java     |    4 +
 .../org/apache/sqoop/common/ImmutableContext.java  |   70 +++
 .../java/org/apache/sqoop/common/MapContext.java   |  114 ++++
 .../org/apache/sqoop/common/MutableContext.java    |   33 ++
 .../org/apache/sqoop/common/MutableMapContext.java |   49 ++
 .../main/java/org/apache/sqoop/json/JobBean.java   |    6 +-
 .../java/org/apache/sqoop/json/SubmissionBean.java |  141 +++++
 .../java/org/apache/sqoop/json/ThrowableBean.java  |   21 +-
 .../java/org/apache/sqoop/model/FormUtils.java     |  113 ++++
 .../java/org/apache/sqoop/model/MSubmission.java   |  181 ++++++
 .../apache/sqoop/submission/SubmissionStatus.java  |   75 +++
 .../apache/sqoop/submission/counter/Counter.java   |   47 ++
 .../sqoop/submission/counter/CounterGroup.java     |   54 ++
 .../apache/sqoop/submission/counter/Counters.java  |   47 ++
 .../org/apache/sqoop/utils/ClassLoadingUtils.java  |   86 ---
 .../java/org/apache/sqoop/utils/ClassUtils.java    |  137 +++++
 .../org/apache/sqoop/json/TestSubmissionBean.java  |  148 +++++
 .../org/apache/sqoop/json/TestThrowableBean.java   |    2 +
 .../apache/sqoop/utils/TestClassLoadingUtils.java  |   68 ---
 .../org/apache/sqoop/utils/TestClassUtils.java     |   68 +++
 connector/connector-generic-jdbc/pom.xml           |    6 +-
 .../sqoop/connector/jdbc/GenericJdbcExecutor.java  |    7 +-
 .../connector/jdbc/GenericJdbcExportDestroyer.java |    4 +-
 .../jdbc/GenericJdbcExportInitializer.java         |    5 +-
 .../connector/jdbc/GenericJdbcExportLoader.java    |    4 +-
 .../connector/jdbc/GenericJdbcImportDestroyer.java |    4 +-
 .../connector/jdbc/GenericJdbcImportExtractor.java |    8 +-
 .../jdbc/GenericJdbcImportInitializer.java         |   91 ++--
 .../connector/jdbc/GenericJdbcImportPartition.java |    5 +
 .../jdbc/GenericJdbcImportPartitioner.java         |    8 +-
 .../configuration/ConnectionConfiguration.java     |    5 +-
 .../jdbc/configuration/ExportJobConfiguration.java |    3 +
 .../jdbc/configuration/ImportJobConfiguration.java |    3 +
 .../generic-jdbc-connector-resources.properties    |    4 +
 .../sqoop/connector/jdbc/TestImportExtractor.java  |   18 +-
 .../connector/jdbc/TestImportInitializer.java      |   15 +-
 .../connector/jdbc/TestImportPartitioner.java      |   16 +-
 .../apache/sqoop/connector/ConnectorHandler.java   |    6 +-
 .../apache/sqoop/connector/ConnectorManager.java   |   16 +-
 .../main/java/org/apache/sqoop/core/Context.java   |   67 ---
 .../org/apache/sqoop/core/SqoopConfiguration.java  |    5 +-
 .../apache/sqoop/framework/FrameworkConstants.java |   35 +-
 .../org/apache/sqoop/framework/FrameworkError.java |   16 +-
 .../apache/sqoop/framework/FrameworkManager.java   |  404 +++++++++++++-
 .../apache/sqoop/framework/SubmissionEngine.java   |  105 ++++
 .../apache/sqoop/framework/SubmissionRequest.java  |  223 ++++++++
 .../configuration/ImportJobConfiguration.java      |    3 +
 .../java/org/apache/sqoop/job/JobConstants.java    |   42 +-
 .../main/java/org/apache/sqoop/job/JobEngine.java  |   37 --
 .../java/org/apache/sqoop/job/PrefixContext.java   |   62 ++
 .../java/org/apache/sqoop/job/etl/EtlContext.java  |   43 --
 .../org/apache/sqoop/job/etl/EtlFramework.java     |  148 -----
 .../apache/sqoop/job/etl/EtlMutableContext.java    |   43 --
 .../java/org/apache/sqoop/job/etl/EtlOptions.java  |  165 ------
 .../sqoop/job/etl/HdfsSequenceImportLoader.java    |   11 +-
 .../apache/sqoop/job/etl/HdfsTextImportLoader.java |   11 +-
 .../apache/sqoop/job/mr/ConfigurationUtils.java    |   65 +++
 .../java/org/apache/sqoop/job/mr/MrExecution.java  |  153 -----
 .../org/apache/sqoop/job/mr/SqoopInputFormat.java  |   23 +-
 .../java/org/apache/sqoop/job/mr/SqoopMapper.java  |   22 +-
 .../job/mr/SqoopOutputFormatLoadExecutor.java      |   32 +-
 .../java/org/apache/sqoop/job/mr/SqoopSplit.java   |    4 +-
 .../apache/sqoop/repository/JdbcRepository.java    |  103 +++-
 .../sqoop/repository/JdbcRepositoryContext.java    |   10 +-
 .../sqoop/repository/JdbcRepositoryHandler.java    |   51 ++
 .../sqoop/repository/JdbcRepositoryProvider.java   |   10 +-
 .../org/apache/sqoop/repository/Repository.java    |   65 ++-
 .../apache/sqoop/repository/RepositoryError.java   |    9 +
 .../apache/sqoop/repository/RepositoryManager.java |    8 +-
 .../sqoop/repository/RepositoryProvider.java       |    4 +-
 .../main/resources/framework-resources.properties  |    3 +
 .../java/org/apache/sqoop/job/TestHdfsLoad.java    |    9 +-
 .../java/org/apache/sqoop/job/TestJobEngine.java   |   17 +-
 .../java/org/apache/sqoop/job/TestMapReduce.java   |   12 +-
 dist/src/main/server/conf/sqoop.properties         |   23 +
 pom.xml                                            |    1 +
 .../sqoop/repository/derby/DerbyRepoError.java     |   15 +
 .../repository/derby/DerbyRepositoryHandler.java   |  181 ++++++
 .../repository/derby/DerbySchemaConstants.java     |   18 +
 .../sqoop/repository/derby/DerbySchemaQuery.java   |   66 +++
 .../sqoop/repository/derby/DerbyTestCase.java      |   17 +
 .../repository/derby/TestSubmissionHandling.java   |  166 ++++++
 server/pom.xml                                     |    6 +
 .../sqoop/handler/ConnectionRequestHandler.java    |    6 +-
 .../sqoop/handler/ConnectorRequestHandler.java     |    2 +-
 .../apache/sqoop/handler/JobRequestHandler.java    |   10 +-
 .../sqoop/handler/SubmissionRequestHandler.java    |  101 ++++
 .../org/apache/sqoop/server/RequestContext.java    |    7 +
 .../org/apache/sqoop/server/ServerInitializer.java |    4 +
 .../apache/sqoop/server/v1/SubmissionServlet.java  |   51 ++
 server/src/main/webapp/WEB-INF/web.xml             |   13 +
 .../org/apache/sqoop/job/etl/CallbackBase.java     |   49 ++
 .../java/org/apache/sqoop/job/etl/Context.java     |   27 -
 .../java/org/apache/sqoop/job/etl/Destroyer.java   |    5 +-
 .../java/org/apache/sqoop/job/etl/Exporter.java    |   19 +-
 .../java/org/apache/sqoop/job/etl/Extractor.java   |    8 +-
 .../java/org/apache/sqoop/job/etl/Importer.java    |   20 +-
 .../java/org/apache/sqoop/job/etl/Initializer.java |   32 +-
 .../main/java/org/apache/sqoop/job/etl/Loader.java |    3 +-
 .../org/apache/sqoop/job/etl/MutableContext.java   |   27 -
 .../java/org/apache/sqoop/job/etl/Options.java     |   27 -
 .../java/org/apache/sqoop/job/etl/Partition.java   |    7 +
 .../java/org/apache/sqoop/job/etl/Partitioner.java |    6 +-
 submission/mapreduce/pom.xml                       |   67 +++
 .../sqoop/submission/mapreduce/Constants.java      |   33 ++
 .../mapreduce/MapreduceSubmissionEngine.java       |  311 +++++++++++
 .../mapreduce/MapreduceSubmissionError.java        |   50 ++
 submission/pom.xml                                 |   36 ++
 120 files changed, 4361 insertions(+), 1626 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/core/Environment.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/core/Environment.java b/client/src/main/java/org/apache/sqoop/client/core/Environment.java
index cb92998..aa8c989 100644
--- a/client/src/main/java/org/apache/sqoop/client/core/Environment.java
+++ b/client/src/main/java/org/apache/sqoop/client/core/Environment.java
@@ -30,7 +30,7 @@ public final class Environment
   private static String serverPort;
   private static String serverWebapp;
 
-  private static final String HOST_DEFAULT = "localhost";
+  private static final String HOST_DEFAULT = "vm-sqoop2";
   private static final String PORT_DEFAULT = "8080";
   private static final String WEBAPP_DEFAULT = "sqoop";
 

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/core/RequestCache.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/core/RequestCache.java b/client/src/main/java/org/apache/sqoop/client/core/RequestCache.java
index 03b1eb0..a5d70c5 100644
--- a/client/src/main/java/org/apache/sqoop/client/core/RequestCache.java
+++ b/client/src/main/java/org/apache/sqoop/client/core/RequestCache.java
@@ -21,14 +21,17 @@ 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.client.request.JobRequest;
+import org.apache.sqoop.client.request.SubmissionRequest;
 import org.apache.sqoop.json.ConnectionBean;
 import org.apache.sqoop.json.ConnectorBean;
 import org.apache.sqoop.json.FrameworkBean;
 import org.apache.sqoop.json.JobBean;
+import org.apache.sqoop.json.SubmissionBean;
 import org.apache.sqoop.json.ValidationBean;
 import org.apache.sqoop.model.FormUtils;
 import org.apache.sqoop.model.MConnection;
 import org.apache.sqoop.model.MJob;
+import org.apache.sqoop.model.MSubmission;
 import org.apache.sqoop.validation.Status;
 import org.apache.sqoop.validation.Validation;
 
@@ -41,6 +44,7 @@ public final class RequestCache {
   private static ConnectorRequest connectorRequest;
   private static ConnectionRequest connectionRequest;
   private static JobRequest jobRequest;
+  private static SubmissionRequest submissionRequest;
 
   public static FrameworkRequest getFrameworkRequest() {
     if (frameworkRequest == null) {
@@ -74,6 +78,14 @@ public final class RequestCache {
     return jobRequest;
   }
 
+  public static SubmissionRequest getSubmissionRequest() {
+    if (submissionRequest == null) {
+      submissionRequest = new SubmissionRequest();
+    }
+
+    return submissionRequest;
+  }
+
   public static FrameworkBean readFramework() {
     return getFrameworkRequest().read(Environment.getServerUrl());
   }
@@ -193,6 +205,24 @@ public final class RequestCache {
     getJobRequest().delete(Environment.getServerUrl(), jid);
   }
 
+  public static MSubmission readSubmission(String jid) {
+    return getSubmissionRequest()
+      .read(Environment.getServerUrl(), jid)
+      .getSubmission();
+  }
+
+  public static MSubmission createSubmission(String jid) {
+    return getSubmissionRequest()
+      .create(Environment.getServerUrl(), jid)
+      .getSubmission();
+  }
+
+  public static MSubmission deleteSubmission(String jid) {
+    return getSubmissionRequest()
+      .delete(Environment.getServerUrl(), jid)
+      .getSubmission();
+  }
+
   private RequestCache() {
     // Instantiation is prohibited
   }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/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 b243dfd..1720507 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
@@ -68,8 +68,8 @@ public class Request
     return getBuilder(url).put(String.class, data);
   }
 
-  public void delete(String url) {
-    getBuilder(url).delete(String.class);
+  public String delete(String url) {
+    return getBuilder(url).delete(String.class);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/request/SubmissionRequest.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/request/SubmissionRequest.java b/client/src/main/java/org/apache/sqoop/client/request/SubmissionRequest.java
new file mode 100644
index 0000000..60dcbb2
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/request/SubmissionRequest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.SubmissionBean;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+/**
+ * Provide CRD semantics over RESTfull HTTP API for submissions. Please note
+ * that "update" is not supported as client can't update submission status.
+ */
+public class SubmissionRequest extends  Request {
+
+  public static final String RESOURCE = "v1/submission/";
+
+  public static final String ACTION = RESOURCE + "action/";
+
+  public SubmissionBean read(String serverUrl, String jid) {
+    String response = super.get(serverUrl + ACTION + jid);
+
+    JSONObject jsonObject = (JSONObject) JSONValue.parse(response);
+
+    SubmissionBean submissionBean = new SubmissionBean();
+    submissionBean.restore(jsonObject);
+
+    return submissionBean;
+  }
+
+  public SubmissionBean create(String serverUrl, String jid) {
+    String response = super.post(serverUrl + ACTION + jid, null);
+
+    SubmissionBean submissionBean = new SubmissionBean();
+    submissionBean.restore((JSONObject) JSONValue.parse(response));
+
+    return submissionBean;
+  }
+
+  public SubmissionBean delete(String serverUrl, String id) {
+     String response = super.delete(serverUrl + ACTION + id);
+
+    SubmissionBean submissionBean = new SubmissionBean();
+    submissionBean.restore((JSONObject) JSONValue.parse(response));
+
+    return submissionBean;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/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
index ea1e5d8..734276d 100644
--- a/client/src/main/java/org/apache/sqoop/client/shell/CreateConnectionFunction.java
+++ b/client/src/main/java/org/apache/sqoop/client/shell/CreateConnectionFunction.java
@@ -116,6 +116,4 @@ public class CreateConnectionFunction extends SqoopFunction {
       + "status " + status.name() + " and persistent id "
       + connection.getPersistenceId());
   }
-
-
 }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/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 c9ac848..5ac8dd2 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
@@ -71,6 +71,7 @@ public final class SqoopShell
     shell.register(new DeleteCommand(shell));
     shell.register(new UpdateCommand(shell));
     shell.register(new CloneCommand(shell));
+    shell.register(new SubmissionCommand(shell));
 
     if (args.length == 0) {
       // Interactive mode:

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/shell/SubmissionCommand.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/SubmissionCommand.java b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionCommand.java
new file mode 100644
index 0000000..39a2b31
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionCommand.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sqoop.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 SubmissionCommand  extends SqoopCommand {
+
+  private SubmissionStartFunction startFunction;
+  private SubmissionStopFunction stopFunction;
+  private SubmissionStatusFunction statusFunction;
+
+  public SubmissionCommand(Shell shell) {
+    super(shell, "submission", "\\sub",
+      new String[] {"start", "stop", "status"},
+      "Submission", "info");
+  }
+
+  public Object execute(List args) {
+    if (args.size() == 0) {
+      io.out.println("Usage: submission " + getUsage());
+      io.out.println();
+      return null;
+    }
+
+    String func = (String)args.get(0);
+    if (func.equals("start")) {
+      if (startFunction == null) {
+        startFunction = new SubmissionStartFunction(io);
+      }
+      return startFunction.execute(args);
+    } else if (func.equals("stop")) {
+        if (stopFunction == null) {
+          stopFunction = new SubmissionStopFunction(io);
+        }
+        return stopFunction.execute(args);
+    } else if (func.equals("status")) {
+      if (statusFunction == null) {
+        statusFunction = new SubmissionStatusFunction(io);
+      }
+      return statusFunction.execute(args);
+    } else {
+      String msg = "Usage: status " + getUsage();
+      throw new SqoopException(ClientError.CLIENT_0002, msg);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStartFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStartFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStartFunction.java
new file mode 100644
index 0000000..74ce905
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStartFunction.java
@@ -0,0 +1,61 @@
+/**
+ * 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.RequestCache;
+import org.apache.sqoop.client.utils.SubmissionDisplayer;
+import org.apache.sqoop.model.MSubmission;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class SubmissionStartFunction extends SqoopFunction {
+  private static final String JID = "jid";
+
+  private IO io;
+
+  @SuppressWarnings("static-access")
+  public SubmissionStartFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+      .withDescription("Job ID")
+      .withLongOpt(JID)
+      .hasArg()
+      .create(JID.charAt(0)));
+  }
+
+  public Object execute(List<String> args) {
+    CommandLine line = parseOptions(this, 1, args);
+    if (!line.hasOption(JID)) {
+      io.out.println("Required argument --jid is missing.");
+      return null;
+    }
+
+    MSubmission submission =
+      RequestCache.createSubmission(line.getOptionValue(JID));
+
+    SubmissionDisplayer.display(io, submission);
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStatusFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStatusFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStatusFunction.java
new file mode 100644
index 0000000..4d683c0
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStatusFunction.java
@@ -0,0 +1,61 @@
+/**
+ * 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.RequestCache;
+import org.apache.sqoop.client.utils.SubmissionDisplayer;
+import org.apache.sqoop.model.MSubmission;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class SubmissionStatusFunction extends  SqoopFunction {
+  private static final String JID = "jid";
+
+  private IO io;
+
+  @SuppressWarnings("static-access")
+  public SubmissionStatusFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+      .withDescription("Job ID")
+      .withLongOpt(JID)
+      .hasArg()
+      .create(JID.charAt(0)));
+  }
+
+  public Object execute(List<String> args) {
+    CommandLine line = parseOptions(this, 1, args);
+    if (!line.hasOption(JID)) {
+      io.out.println("Required argument --jid is missing.");
+      return null;
+    }
+
+    MSubmission submission =
+      RequestCache.readSubmission(line.getOptionValue(JID));
+
+    SubmissionDisplayer.display(io, submission);
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStopFunction.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStopFunction.java b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStopFunction.java
new file mode 100644
index 0000000..97628f7
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/shell/SubmissionStopFunction.java
@@ -0,0 +1,61 @@
+/**
+ * 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.RequestCache;
+import org.apache.sqoop.client.utils.SubmissionDisplayer;
+import org.apache.sqoop.model.MSubmission;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.util.List;
+
+/**
+ *
+ */
+public class SubmissionStopFunction extends SqoopFunction {
+  private static final String JID = "jid";
+
+  private IO io;
+
+  @SuppressWarnings("static-access")
+  public SubmissionStopFunction(IO io) {
+    this.io = io;
+
+    this.addOption(OptionBuilder
+      .withDescription("Job ID")
+      .withLongOpt(JID)
+      .hasArg()
+      .create(JID.charAt(0)));
+  }
+
+  public Object execute(List<String> args) {
+    CommandLine line = parseOptions(this, 1, args);
+    if (!line.hasOption(JID)) {
+      io.out.println("Required argument --jid is missing.");
+      return null;
+    }
+
+    MSubmission submission =
+      RequestCache.deleteSubmission(line.getOptionValue(JID));
+
+    SubmissionDisplayer.display(io, submission);
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java.orig
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java.orig b/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java.orig
deleted file mode 100644
index d2fb6ce..0000000
--- a/client/src/main/java/org/apache/sqoop/client/utils/FormFiller.java.orig
+++ /dev/null
@@ -1,432 +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.utils;
-
-import jline.ConsoleReader;
-import org.apache.sqoop.client.core.Environment;
-import org.apache.sqoop.model.MConnection;
-import org.apache.sqoop.model.MForm;
-import org.apache.sqoop.model.MInput;
-import org.apache.sqoop.model.MIntegerInput;
-import org.apache.sqoop.model.MMapInput;
-import org.apache.sqoop.model.MJob;
-import org.apache.sqoop.model.MStringInput;
-import org.codehaus.groovy.tools.shell.IO;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.ResourceBundle;
-
-/**
- * Convenient methods for retrieving user input.
- */
-public final class FormFiller {
-
-  /**
-   * Internal input that will be reused for loading names for connection and
-   * job objects.
-   */
-  private static MStringInput nameInput =
-    new MStringInput("object-name", false, (short)25);
-
-  /**
-   * Fill job object based on user input.
-   *
-   * @param io Shell's io object
-   * @param reader Associated console reader object
-   * @param job Job that user is suppose to fill in
-   * @param connectorBundle Connector resource bundle
-   * @param frameworkBundle Framework resource bundle
-   * @return True if we filled all inputs, false if user has stopped processing
-   * @throws IOException
-   */
-  public static boolean fillJob(IO io,
-                                ConsoleReader reader,
-                                MJob job,
-                                ResourceBundle connectorBundle,
-                                ResourceBundle frameworkBundle)
-                                throws IOException {
-
-    job.setName(getName(io, reader, job.getName()));
-
-    // Fill in data from user
-     return fillForms(io,
-                      reader,
-                      job.getConnectorPart().getForms(),
-                      connectorBundle,
-                      job.getFrameworkPart().getForms(),
-                      frameworkBundle);
-  }
-
-  /**
-   * Fill connection object based on user input.
-   *
-   * @param io Shell's io object
-   * @param reader Associated console reader object
-   * @param connection Connection that user is suppose to fill in
-   * @param connectorBundle Connector resource bundle
-   * @param frameworkBundle Framework resouce bundle
-   * @return True if we filled all inputs, false if user has stopped processing
-   * @throws IOException
-   */
-  public static boolean fillConnection(IO io,
-                                       ConsoleReader reader,
-                                       MConnection connection,
-                                       ResourceBundle connectorBundle,
-                                       ResourceBundle frameworkBundle)
-                                       throws IOException {
-
-    connection.setName(getName(io, reader, connection.getName()));
-
-    // Fill in data from user
-     return fillForms(io,
-                      reader,
-                      connection.getConnectorPart().getForms(),
-                      connectorBundle,
-                      connection.getFrameworkPart().getForms(),
-                      frameworkBundle);
-  }
-
-  public static boolean fillForms(IO io,
-                                  ConsoleReader reader,
-                                  List<MForm> connectorForms,
-                                  ResourceBundle connectorBundle,
-                                  List<MForm> frameworkForms,
-                                  ResourceBundle frameworkBundle
-                                  ) throws IOException {
-
-
-    // Query connector forms
-    if(!fillForms(io, connectorForms, reader, connectorBundle)) {
-      return false;
-    }
-
-    // Query framework forms
-    if(!fillForms(io, frameworkForms, reader, frameworkBundle)) {
-      return false;
-    }
-
-    return true;
-  }
-
-  public static boolean fillForms(IO io,
-                                  List<MForm> forms,
-                                  ConsoleReader reader,
-                                  ResourceBundle bundle)
-    throws IOException {
-    for (MForm form : forms) {
-      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:
-        errorMessage(io, input.getValidationMessage());
-        break;
-      case WARNING:
-        warningMessage(io, 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);
-      case INTEGER:
-        return fillInputInteger(io, (MIntegerInput) input, reader, bundle);
-      case MAP:
-        return fillInputMap(io, (MMapInput)input, reader, bundle);
-      default:
-        io.out.println("Unsupported data type " + input.getType());
-        return true;
-    }
-  }
-
-  /**
-   * Load user input for map type.
-   *
-   * This implementation will load one map entry at the time. Current flows is
-   * as follows: if user did not enter anything (empty input) finish loading
-   * and return from function. If user specified input with equal sign (=),
-   * lets add new key value pair. Otherwise consider entire input as a key name
-   * and try to remove it from the map.
-   *
-   * Please note that following code do not supports equal sign in property
-   * name. It's however perfectly fine to have equal sign in value.
-   *
-   * @param io Shell's IO object
-   * @param input Input that we should read or edit
-   * @param reader Associated console reader
-   * @param bundle Resource bundle
-   * @return True if user wish to continue with loading additional inputs
-   * @throws IOException
-   */
-  private static boolean fillInputMap(IO io,
-                                      MMapInput input,
-                                      ConsoleReader reader,
-                                      ResourceBundle bundle)
-                                      throws IOException {
-    // Special prompt in Map case
-    io.out.println(bundle.getString(input.getLabelKey()) + ": ");
-
-    // Internal loading map
-    Map<String, String> values = input.getValue();
-    if(values == null) {
-      values = new HashMap<String, String>();
-    }
-
-    String userTyped;
-
-    while(true) {
-      // Print all current items in each iteration
-      io.out.println("There are currently " + values.size()
-        + " values in the map:");
-      for(Map.Entry<String, String> entry : values.entrySet()) {
-        io.out.println(entry.getKey() + " = " + entry.getValue());
-      }
-
-      // Special prompt for Map entry
-      reader.printString("entry# ");
-      reader.flushConsole();
-
-      userTyped = reader.readLine();
-
-      if(userTyped == null) {
-        // Finish loading and return back to Sqoop shell
-        return false;
-      } else if(userTyped.isEmpty()) {
-        // User has finished loading data to Map input, either set input empty
-        // if there are no entries or propagate entries to the input
-        if(values.size() == 0) {
-          input.setEmpty();
-        } else {
-          input.setValue(values);
-        }
-        return true;
-      } else {
-        // User has specified regular input, let's check if it contains equals
-        // sign. Save new entry (or update existing one) if it does. Otherwise
-        // try to remove entry that user specified.
-        if(userTyped.contains("=")) {
-          String []keyValue = userTyped.split("=", 2);
-          values.put(handleUserInput(keyValue[0]), handleUserInput(keyValue[1]));
-        } else {
-          String key = handleUserInput(userTyped);
-          if(values.containsKey(key)) {
-            values.remove(key);
-          } else {
-            errorMessage(io, "Don't know what to do with " + userTyped);
-          }
-        }
-      }
-
-    }
-  }
-
-  /**
-   * Handle special cases in user input.
-   *
-   * Preserve null and empty values, remove whitespace characters before and
-   * after loaded string and de-quote the string if it's quoted (to preserve
-   * spaces for example).
-   *
-   * @param input String loaded from user
-   * @return Unquoted transformed string
-   */
-  private static String handleUserInput(String input) {
-    // Preserve null and empty values
-    if(input == null) {
-      return null;
-    }
-    if(input.isEmpty()) {
-      return input;
-    }
-
-    // Removes empty characters at the begging and end of loaded string
-    input = input.trim();
-
-    int lastIndex = input.length() - 1;
-    char first = input.charAt(0);
-    char last = input.charAt(lastIndex);
-
-    // Remove quoting if present
-    if(first == '\'' && last == '\'') {
-      input = input.substring(1, lastIndex);
-    } else if(first == '"' && last == '"') {
-      input =  input.substring(1, lastIndex);
-    }
-
-    // Return final string
-    return input;
-  }
-
-  private static boolean fillInputInteger(IO io,
-                                          MIntegerInput input,
-                                          ConsoleReader reader,
-                                          ResourceBundle bundle)
-                                          throws IOException {
-    generatePrompt(reader, bundle, input);
-
-    // Fill already filled data when available
-    if(!input.isEmpty()) {
-      reader.putString(input.getValue().toString());
-    }
-
-    String userTyped = reader.readLine();
-
-    if (userTyped == null) {
-      return false;
-    } else if (userTyped.isEmpty()) {
-      input.setEmpty();
-    } else {
-      Integer value;
-      try {
-        value = Integer.valueOf(userTyped);
-        input.setValue(value);
-      } catch (NumberFormatException ex) {
-        errorMessage(io, "Input is not valid integer number");
-        return fillInputInteger(io, input, reader, bundle);
-      }
-
-      input.setValue(Integer.valueOf(userTyped));
-    }
-
-    return true;
-  }
-
-  /**
-   * Load string input from the user.
-   *
-   * @param io Shell's IO object
-   * @param input Input that we should load in
-   * @param reader Associated console reader
-   * @param bundle Resource bundle for this input
-   * @return
-   * @throws IOException
-   */
-  public static boolean fillInputString(IO io,
-                                        MStringInput input,
-                                        ConsoleReader reader,
-                                        ResourceBundle bundle)
-                                        throws IOException {
-    generatePrompt(reader, bundle, input);
-
-    // Fill already filled data when available
-    // However do not printout if this input contains sensitive information.
-    if(!input.isEmpty() && !input.isMasked()) {
-      reader.putString(input.getValue());
-    }
-
-    // Get the data
-    String userTyped;
-    if(input.isMasked()) {
-       userTyped = reader.readLine('*');
-    } else {
-      userTyped = reader.readLine();
-    }
-
-    if (userTyped == null) {
-      // Propagate end of loading process
-      return false;
-    } else if (userTyped.isEmpty()) {
-      // Empty input in case that nothing was given
-      input.setEmpty();
-    } else {
-      // Set value that user has entered
-      input.setValue(userTyped);
-
-      // Check that it did not exceeds maximal allowance for given input
-      if(userTyped.length() > input.getMaxLength()) {
-        errorMessage(io, "Size of input exceeds allowance for this input"
-          + " field. Maximal allowed size is " + input.getMaxLength());
-        return fillInputString(io, input, reader, bundle);
-      }
-    }
-
-    return true;
-  }
-
-  public static void generatePrompt(ConsoleReader reader,
-                                    ResourceBundle bundle,
-                                    MInput input)
-                                    throws IOException {
-    reader.printString(bundle.getString(input.getLabelKey()) + ": ");
-    reader.flushConsole();
-  }
-
-  public static String getName(IO io, ConsoleReader reader,
-                               String name) throws IOException {
-    if(name == null) {
-      nameInput.setEmpty();
-    } else {
-      nameInput.setValue(name);
-    }
-
-    fillInputString(io, nameInput, reader, Environment.getResourceBundle());
-
-    return nameInput.getValue();
-  }
-
-  public static void errorMessage(IO io, String message) {
-    io.out.println("Error message: @|red " + message + " |@");
-  }
-
-  public static void warningMessage(IO io, String message) {
-    io.out.println("Warning message: @|yellow " + message + " |@");
-  }
-
-  public static void errorIntroduction(IO io) {
-    io.out.println();
-    io.out.println("@|red There are issues with entered data, please"
-      + " revise your input:|@");
-  }
-
-  private FormFiller() {
-    // Do not instantiate
-  }
-}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/utils/SubmissionDisplayer.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/utils/SubmissionDisplayer.java b/client/src/main/java/org/apache/sqoop/client/utils/SubmissionDisplayer.java
new file mode 100644
index 0000000..d4d6825
--- /dev/null
+++ b/client/src/main/java/org/apache/sqoop/client/utils/SubmissionDisplayer.java
@@ -0,0 +1,76 @@
+/**
+ * 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.MSubmission;
+import org.apache.sqoop.submission.counter.Counter;
+import org.apache.sqoop.submission.counter.CounterGroup;
+import org.apache.sqoop.submission.counter.Counters;
+import org.codehaus.groovy.tools.shell.IO;
+
+/**
+ *
+ */
+public final class SubmissionDisplayer {
+
+  public static void display(IO io, MSubmission submission) {
+    io.out.println("@|bold Submission details|@");
+
+    io.out.print("Job id: ");
+    io.out.println(submission.getJobId());
+
+    io.out.print("Status: ");
+    io.out.println(submission.getStatus());
+
+    String externalId = submission.getExternalId();
+    if(externalId != null) {
+      io.out.print("External Id: ");
+      io.out.println(externalId);
+
+      String externalLink = submission.getExternalLink();
+      if(externalLink != null) {
+        io.out.println("\t" + externalLink);
+      }
+    }
+
+    if(submission.getStatus().isRunning()) {
+      double progress = submission.getProgress();
+      io.out.print("Progress: ");
+      if(progress == -1) {
+        io.out.println("Progress is not available");
+      } else {
+        io.out.println(String.format("%.2f %%", progress));
+      }
+    }
+
+    Counters counters = submission.getCounters();
+    if(counters != null) {
+      io.out.println("Counters:");
+      for(CounterGroup group : counters) {
+        io.out.print("\t");
+        io.out.println(group.getName());
+        for(Counter counter : group) {
+          io.out.print("\t\t");
+          io.out.print(counter.getName());
+          io.out.print(": ");
+          io.out.println(counter.getValue());
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/client/src/main/java/org/apache/sqoop/client/utils/ThrowableDisplayer.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/sqoop/client/utils/ThrowableDisplayer.java b/client/src/main/java/org/apache/sqoop/client/utils/ThrowableDisplayer.java
index 8d6e9b4..c8f8223 100644
--- a/client/src/main/java/org/apache/sqoop/client/utils/ThrowableDisplayer.java
+++ b/client/src/main/java/org/apache/sqoop/client/utils/ThrowableDisplayer.java
@@ -17,6 +17,7 @@
  */
 package org.apache.sqoop.client.utils;
 
+import groovy.lang.MissingPropertyException;
 import org.apache.sqoop.client.core.ClientError;
 import org.apache.sqoop.common.SqoopException;
 import org.codehaus.groovy.tools.shell.IO;
@@ -57,6 +58,9 @@ public class ThrowableDisplayer {
       && ((SqoopException)t).getErrorCode() == ClientError.CLIENT_0006) {
       io.out.print("@|red Server has returned exception: |@");
       printThrowable(io, t.getCause());
+    } else if(t.getClass() == MissingPropertyException.class) {
+      io.out.print("@|red Unknown command: |@");
+      io.out.println(t.getMessage());
     } else {
       printThrowable(io, t);
     }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/common/ImmutableContext.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/common/ImmutableContext.java b/common/src/main/java/org/apache/sqoop/common/ImmutableContext.java
new file mode 100644
index 0000000..69f3a03
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/common/ImmutableContext.java
@@ -0,0 +1,70 @@
+/**
+ * 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;
+
+/**
+ * Immutable context interface for key value pairs.
+ *
+ * Useful for configuration objects that are not allowed to change.
+ */
+public interface ImmutableContext {
+
+  /**
+   * Return string for given key or null by default.
+   *
+   * @param key Key
+   * @return Value for given key or null in case of unknown key
+   */
+  String getString(String key);
+
+  /**
+   * Return string for given key or default value.
+   *
+   * @param key Key
+   * @param defaultValue Default value
+   * @return Value for given key or default value in case of unknown key
+   */
+  String getString(String key, String defaultValue);
+
+  /**
+   * Return long for given key or default value.
+   *
+   * @param key Key
+   * @param defaultValue Default value
+   * @return Value for given key or default value in case of unknown key
+   */
+  public long getLong(String key, long defaultValue);
+
+  /**
+   * Return int for given key or default value.
+   *
+   * @param key Key
+   * @param defaultValue Default value
+   * @return Value for given key or default value in case of unknown key
+   */
+  public int getInt(String key, int defaultValue);
+
+  /**
+   * Return boolean for given key or default value.
+   *
+   * @param key Key
+   * @param defaultValue Default value
+   * @return Value for given key or default value in case of unknown key
+   */
+  public boolean getBoolean(String key, boolean defaultValue);
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/common/MapContext.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/common/MapContext.java b/common/src/main/java/org/apache/sqoop/common/MapContext.java
new file mode 100644
index 0000000..c1d24ad
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/common/MapContext.java
@@ -0,0 +1,114 @@
+/**
+ * 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.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * ImmutableContext implementation based on (Hash)Map.
+ */
+public class MapContext implements ImmutableContext {
+
+  private final Map<String, String> options;
+
+  public MapContext(Map<String, String> options) {
+    this.options = options;
+  }
+
+  protected Map<String, String> getOptions() {
+    return options;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String getString(String key) {
+    return options.get(key);
+  }
+
+  @Override
+  public String getString(String key, String defaultValue) {
+    String value = getString(key);
+    if (value == null || value.trim().length() == 0) {
+      value = defaultValue;
+    }
+    return value;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean getBoolean(String key, boolean defaultValue) {
+    String value = getString(key);
+    boolean result = defaultValue;
+    if (value != null) {
+      result = Boolean.valueOf(value);
+    }
+
+    return result;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public long getLong(String key, long defaultValue) {
+    if(!options.containsKey(key)) {
+      return defaultValue;
+    }
+
+    String value = options.get(key);
+
+    return Long.getLong(value);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int getInt(String key, int defaultValue) {
+    if(!options.containsKey(key)) {
+      return defaultValue;
+    }
+
+    String value = options.get(key);
+
+    return Integer.getInteger(value);
+  }
+
+  /**
+   * Return all properties starting with given prefix (without the prefix itself)
+   *
+   * @param prefix Prefix that we need to search and remove
+   * @return ImmutableContext with new sub properties
+   */
+  public Map<String, String> getNestedProperties(String prefix) {
+    Map<String, String> subProps = new HashMap<String, String>();
+    for (String key : options.keySet()) {
+      if (key.startsWith(prefix)) {
+        subProps.put(key.substring(prefix.length()), options.get(key));
+      }
+    }
+
+    return subProps;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/common/MutableContext.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/common/MutableContext.java b/common/src/main/java/org/apache/sqoop/common/MutableContext.java
new file mode 100644
index 0000000..238bbfd
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/common/MutableContext.java
@@ -0,0 +1,33 @@
+/**
+ * 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;
+
+/**
+ * Mutable addition to immutable context.
+ */
+public interface MutableContext extends ImmutableContext {
+
+  /**
+   * Set string value for given key.
+   *
+   * @param key Key
+   * @param value New value
+   */
+  public void setString(String key, String value);
+
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/common/MutableMapContext.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/common/MutableMapContext.java b/common/src/main/java/org/apache/sqoop/common/MutableMapContext.java
new file mode 100644
index 0000000..cd9d3e3
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/common/MutableMapContext.java
@@ -0,0 +1,49 @@
+/**
+ * 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.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Mutable variant of context class for "special" usage
+ */
+public class MutableMapContext extends MapContext implements Iterable<Map.Entry<String, String>>, MutableContext {
+
+  public MutableMapContext(Map<String, String> options) {
+    super(options);
+  }
+
+  public MutableMapContext() {
+    this(new HashMap<String, String>());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void setString(String key, String value) {
+    getOptions().put(key, value);
+  }
+
+  @Override
+  public Iterator<Map.Entry<String, String>> iterator() {
+    return getOptions().entrySet().iterator();
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/json/JobBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/JobBean.java b/common/src/main/java/org/apache/sqoop/json/JobBean.java
index 7fd2ce5..539efe0 100644
--- a/common/src/main/java/org/apache/sqoop/json/JobBean.java
+++ b/common/src/main/java/org/apache/sqoop/json/JobBean.java
@@ -36,7 +36,7 @@ import static org.apache.sqoop.json.util.ResourceBundleSerialization.*;
 /**
  *
  */
-public class JobBean implements  JsonBean {
+public class JobBean implements JsonBean {
 
   private static final String ALL = "all";
   private static final String ID = "id";
@@ -61,9 +61,9 @@ public class JobBean implements  JsonBean {
     this.jobs.add(job);
   }
 
-  public JobBean(List<MJob> connections) {
+  public JobBean(List<MJob> jobs) {
     this();
-    this.jobs = connections;
+    this.jobs = jobs;
   }
 
   // For "restore"

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/json/SubmissionBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/SubmissionBean.java b/common/src/main/java/org/apache/sqoop/json/SubmissionBean.java
new file mode 100644
index 0000000..d7188f4
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/json/SubmissionBean.java
@@ -0,0 +1,141 @@
+/**
+ * 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.MSubmission;
+import org.apache.sqoop.submission.SubmissionStatus;
+import org.apache.sqoop.submission.counter.Counter;
+import org.apache.sqoop.submission.counter.CounterGroup;
+import org.apache.sqoop.submission.counter.Counters;
+import org.json.simple.JSONObject;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ */
+public class SubmissionBean implements JsonBean {
+
+  private static final String JOB = "job";
+  private static final String DATE = "date";
+  private static final String STATUS = "status";
+  private static final String EXTERNAL_ID = "external-id";
+  private static final String EXTERNAL_LINK = "external-link";
+  private static final String PROGRESS = "progress";
+  private static final String COUNTERS = "counters";
+
+  private MSubmission submission;
+
+  public MSubmission getSubmission() {
+    return submission;
+  }
+
+  // For "extract"
+  public SubmissionBean(MSubmission submission) {
+    this.submission = submission;
+  }
+
+  // For "restore"
+  public SubmissionBean() {
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public JSONObject extract() {
+    JSONObject ret = new JSONObject();
+
+    ret.put(JOB, submission.getJobId());
+    ret.put(STATUS, submission.getStatus().name());
+    ret.put(PROGRESS, submission.getProgress());
+
+    if(submission.getDate() != null) {
+      ret.put(DATE, submission.getDate().getTime());
+    }
+    if(submission.getExternalId() != null) {
+      ret.put(EXTERNAL_ID, submission.getExternalId());
+    }
+    if(submission.getExternalLink() != null) {
+      ret.put(EXTERNAL_LINK, submission.getExternalLink());
+    }
+    if(submission.getCounters() != null) {
+      ret.put(COUNTERS, extractCounters(submission.getCounters()));
+    }
+
+    return ret;
+  }
+
+  @SuppressWarnings("unchecked")
+  public JSONObject extractCounters(Counters counters) {
+    JSONObject ret = new JSONObject();
+    for(CounterGroup group : counters) {
+      JSONObject counterGroup = new JSONObject();
+
+      for(Counter counter : group) {
+        counterGroup.put(counter.getName(), counter.getValue());
+      }
+
+      ret.put(group.getName(), counterGroup);
+    }
+    return ret;
+  }
+
+  @Override
+  public void restore(JSONObject json) {
+
+    submission = new MSubmission();
+    submission.setJobId((Long) json.get(JOB));
+    submission.setStatus(SubmissionStatus.valueOf((String) json.get(STATUS)));
+    submission.setProgress((Double) json.get(PROGRESS));
+
+    if(json.containsKey(DATE)) {
+      submission.setDate(new Date((Long) json.get(DATE)));
+    }
+    if(json.containsKey(EXTERNAL_ID)) {
+      submission.setExternalId((String) json.get(EXTERNAL_ID));
+    }
+    if(json.containsKey(EXTERNAL_LINK)) {
+      submission.setExternalLink((String) json.get(EXTERNAL_LINK));
+    }
+    if(json.containsKey(COUNTERS)) {
+      submission.setCounters(restoreCounters((JSONObject) json.get(COUNTERS)));
+    }
+  }
+
+  public Counters restoreCounters(JSONObject object) {
+    Set<Map.Entry<String, JSONObject>> groupSet = object.entrySet();
+    Counters counters = new Counters();
+
+    for(Map.Entry<String, JSONObject> groupEntry: groupSet) {
+
+      CounterGroup group = new CounterGroup(groupEntry.getKey());
+
+      Set<Map.Entry<String, Long>> counterSet = groupEntry.getValue().entrySet();
+
+      for(Map.Entry<String, Long> counterEntry: counterSet) {
+        Counter counter = new Counter(counterEntry.getKey(), counterEntry.getValue());
+        group.addCounter(counter);
+      }
+
+      counters.addCounterGroup(group);
+    }
+
+    return counters;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/json/ThrowableBean.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/json/ThrowableBean.java b/common/src/main/java/org/apache/sqoop/json/ThrowableBean.java
index 91914e8..4bb0ffc 100644
--- a/common/src/main/java/org/apache/sqoop/json/ThrowableBean.java
+++ b/common/src/main/java/org/apache/sqoop/json/ThrowableBean.java
@@ -17,6 +17,7 @@
  */
 package org.apache.sqoop.json;
 
+import org.apache.sqoop.utils.ClassUtils;
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 
@@ -24,10 +25,7 @@ import java.util.LinkedList;
 import java.util.List;
 
 /**
- * Transfer throwable.
- *
- * TODO(jarcec): After SQOOP-627 will get committed, change the throwable
- * creation to same class as was on the server instead of Throwable.
+ * Transfer throwable instance.
  */
 public class ThrowableBean implements JsonBean {
 
@@ -87,7 +85,20 @@ public class ThrowableBean implements JsonBean {
 
   @Override
   public void restore(JSONObject jsonObject) {
-    throwable = new Throwable((String) jsonObject.get(MESSAGE));
+    String exceptionClass = (String) jsonObject.get(CLASS);
+    String message = (String) jsonObject.get(MESSAGE);
+    if(message == null) {
+      message = "";
+    }
+
+    // Let's firstly try to instantiate same class that was originally on remote
+    // side. Fallback to generic Throwable in case that this particular
+    // exception is not known to this JVM (for example during  server-client
+    // exchange).
+    throwable = (Throwable) ClassUtils.instantiate(exceptionClass, message);
+    if(throwable == null) {
+      throwable = new Throwable(message);
+    }
 
     List<StackTraceElement> st = new LinkedList<StackTraceElement>();
     for(Object object : (JSONArray)jsonObject.get(STACK_TRACE)) {

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/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
index 424810c..ee92718 100644
--- a/common/src/main/java/org/apache/sqoop/model/FormUtils.java
+++ b/common/src/main/java/org/apache/sqoop/model/FormUtils.java
@@ -17,10 +17,14 @@
  */
 package org.apache.sqoop.model;
 
+import org.apache.log4j.Logger;
 import org.apache.sqoop.common.SqoopException;
 import org.apache.sqoop.validation.Status;
 import org.apache.sqoop.validation.Validation;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -52,6 +56,7 @@ public class FormUtils {
     return toForms(klass, null);
   }
 
+  @SuppressWarnings("unchecked")
   public static List<MForm> toForms(Class klass, Object configuration) {
     Configuration global =
       (Configuration)klass.getAnnotation(Configuration.class);
@@ -198,4 +203,112 @@ public class FormUtils {
       }
     }
   }
+
+  @SuppressWarnings("unchecked")
+  public static String toJson(Object configuration) {
+    Class klass = configuration.getClass();
+
+    Configuration global =
+      (Configuration)klass.getAnnotation(Configuration.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 jsonObject = new JSONObject();
+
+    // Iterate over all declared fields
+    for (Field field : klass.getDeclaredFields()) {
+      field.setAccessible(true);
+      String fieldName = field.getName();
+
+      // Each field that should be part of user input should have Input
+      // annotation.
+      Input inputAnnotation = field.getAnnotation(Input.class);
+
+      Object value;
+      try {
+        value = field.get(configuration);
+      } catch (IllegalAccessException e) {
+        throw new SqoopException(ModelError.MODEL_005,
+          "Issue with field " + field.getName(), e);
+      }
+
+      // Do not serialize all values
+      if(inputAnnotation != null && value != null) {
+        Class type = field.getType();
+
+        // We need to support NULL, so we do not support primitive types
+        if(type.isPrimitive()) {
+          throw new SqoopException(ModelError.MODEL_007,
+            "Detected primitive type " + type + " for field " + fieldName);
+        }
+
+        if(type == String.class) {
+          jsonObject.put(fieldName, value);
+        } else if (type.isAssignableFrom(Map.class)) {
+          JSONObject map = new JSONObject();
+          for(Object key : ((Map)value).keySet()) {
+            map.put(key, map.get(key));
+          }
+          jsonObject.put(fieldName, map);
+        } else if(type == Integer.class) {
+          jsonObject.put(fieldName, value);
+        } else {
+          throw new SqoopException(ModelError.MODEL_004,
+            "Unsupported type " + type.getName() + " for input " + fieldName);
+        }
+      }
+    }
+
+    return jsonObject.toJSONString();
+  }
+
+  // TODO(jarcec): This method currently do not iterate over all fields and
+  // therefore some fields might have original values when original object will
+  // be reused. This is unfortunately not acceptable.
+  public static void fillValues(String json, Object configuration) {
+    Class klass = configuration.getClass();
+
+    JSONObject jsonObject = (JSONObject) JSONValue.parse(json);
+
+    for(Object k : jsonObject.keySet()) {
+      String key = (String)k;
+
+      Field field;
+      try {
+        field = klass.getDeclaredField(key);
+      } catch (NoSuchFieldException e) {
+        throw new SqoopException(ModelError.MODEL_006,
+          "Missing field " + key, e);
+      }
+
+      // We need to access this field even if it would be declared as private
+      field.setAccessible(true);
+      Class type = field.getType();
+
+      try {
+        if(type == String.class) {
+          field.set(configuration, jsonObject.get(key));
+        } else if (type.isAssignableFrom(Map.class)) {
+          Map<String, String> map = new HashMap<String, String>();
+          for(Object kk : jsonObject.keySet()) {
+            map.put((String)kk, (String)jsonObject.get(kk));
+          }
+          field.set(key, map);
+        } else if(type == Integer.class) {
+          field.set(configuration, jsonObject.get(key));
+        } else {
+          throw new SqoopException(ModelError.MODEL_004,
+            "Unsupported type " + type.getName() + " for input " + key);
+        }
+      } catch (IllegalAccessException e) {
+        throw new SqoopException(ModelError.MODEL_005,
+          "Issue with field " + field.getName(), e);
+      }
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/model/MSubmission.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/model/MSubmission.java b/common/src/main/java/org/apache/sqoop/model/MSubmission.java
new file mode 100644
index 0000000..f8ca32e
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/model/MSubmission.java
@@ -0,0 +1,181 @@
+/**
+ * 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.submission.SubmissionStatus;
+import org.apache.sqoop.submission.counter.Counters;
+
+import java.util.Date;
+
+/**
+ * Metadata object for submission (executed job).
+ *
+ * Please note that not all properties are persisted in repository at the
+ * moment.
+ */
+public class MSubmission extends MPersistableEntity {
+
+  /**
+   * Job id that this submission object belongs.
+   *
+   * By transitivity of metadata structure you can get also connection and
+   * connector ids.
+   *
+   * This property is required and will be always present.
+   */
+  private long jobId;
+
+  /**
+   * Point in time when we submitted this submission.
+   *
+   * This property is required and will be always present.
+   */
+  private Date date;
+
+  /**
+   * Last known submission status.
+   *
+   * This property is required and will be always present.
+   */
+  SubmissionStatus status;
+
+  /**
+   * Any valid external id associated with this submission.
+   *
+   * This property might be missing, but it's value will be recorded in metastore.
+   */
+  String externalId;
+
+  /**
+   * Progress in the job.
+   *
+   * This is optional property that is not serialized in metastore.
+   */
+  double progress;
+
+  /**
+   * Counters associated with the job if it's already in finished state
+   *
+   * This is optional property that is not serialized in metastore.
+   */
+  Counters counters;
+
+  /**
+   * Link to external UI if available
+   *
+   * This is optional property that is not serialized in metastore.
+   */
+  String externalLink;
+
+  public MSubmission() {
+    status = SubmissionStatus.UNKNOWN;
+    progress = -1;
+    date = new Date();
+  }
+
+  public MSubmission(long jobId, Date date, SubmissionStatus status) {
+    this();
+    this.jobId = jobId;
+    this.date = date;
+    this.status = status;
+  }
+
+  public MSubmission(long jobId) {
+    this(jobId, new Date(), SubmissionStatus.BOOTING);
+  }
+
+  public MSubmission(long jobId, Date date, SubmissionStatus status,
+                     String externalId) {
+    this(jobId, date, status);
+    this.externalId = externalId;
+  }
+
+  public MSubmission(long jobId, Date date, SubmissionStatus status,
+                     String externalId, String externalLink, Counters counters){
+    this(jobId, date, status, externalId);
+    this.externalLink = externalLink;
+    this.counters = counters;
+  }
+
+  public void setJobId(long jobId) {
+    this.jobId = jobId;
+  }
+
+  public long getJobId() {
+    return jobId;
+  }
+
+  public void setDate(Date submissionDate) {
+    this.date = submissionDate;
+  }
+
+  public Date getDate() {
+    return date;
+  }
+
+  public void setStatus(SubmissionStatus status) {
+    this.status = status;
+  }
+
+  public SubmissionStatus getStatus() {
+    return status;
+  }
+
+  public void setExternalId(String externalId) {
+    this.externalId = externalId;
+  }
+
+  public String getExternalId() {
+    return externalId;
+  }
+
+  public void setProgress(double progress) {
+    this.progress = progress;
+  }
+
+  public double getProgress() {
+    return progress;
+  }
+
+  public void setCounters(Counters counters) {
+    this.counters = counters;
+  }
+
+  public Counters getCounters() {
+    return counters;
+  }
+
+  public void setExternalLink(String externalLink) {
+    this.externalLink = externalLink;
+  }
+
+  public String getExternalLink() {
+    return externalLink;
+  }
+
+  @Override
+  public String toString() {
+    return "MSubmission{" +
+      "jobId=" + jobId +
+      ", date=" + date +
+      ", status=" + status +
+      ", externalId=" + externalId + "}";
+  }
+
+  public static MSubmission UNKNOWN = new MSubmission();
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/submission/SubmissionStatus.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/submission/SubmissionStatus.java b/common/src/main/java/org/apache/sqoop/submission/SubmissionStatus.java
new file mode 100644
index 0000000..025690a
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/submission/SubmissionStatus.java
@@ -0,0 +1,75 @@
+/**
+ * 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.submission;
+
+/**
+ * List of states where the submission might be.
+ */
+public enum SubmissionStatus {
+
+  /**
+   * In the middle of creating new submission. This might be creation step
+   * on our side on remote cluster side.
+   */
+  BOOTING,
+
+  /**
+   * We weren't able to submit this submission to remote cluster
+   */
+  FAILURE_ON_SUBMIT,
+
+  /**
+   * Submission is running.
+   */
+  RUNNING,
+
+  /**
+   * Submission has finished gracefully
+   */
+  SUCCEEDED,
+
+  /**
+   * Submission has not finished gracefully, there were issues.
+   */
+  FAILED,
+
+  /**
+   * We have no idea in what state the submission actually is.
+   */
+  UNKNOWN,
+
+  /**
+   * Special submission type for job that was never executed.
+   */
+  NEVER_EXECUTED,
+
+  ;
+
+  /**
+   * Return array of submission status that are considered as unfinished.
+   *
+   * @return Array of unfinished submission statuses
+   */
+  public static SubmissionStatus[] unfinished() {
+    return new SubmissionStatus[] { RUNNING, BOOTING };
+  }
+
+  public boolean isRunning() {
+    return this == RUNNING || this == BOOTING;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/submission/counter/Counter.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/submission/counter/Counter.java b/common/src/main/java/org/apache/sqoop/submission/counter/Counter.java
new file mode 100644
index 0000000..6b9cfc0
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/submission/counter/Counter.java
@@ -0,0 +1,47 @@
+/**
+ * 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.submission.counter;
+
+/**
+ *
+ */
+public class Counter {
+  private final String name;
+  private long value;
+
+  public Counter(String name) {
+    this.name = name;
+  }
+
+  public Counter(String name, long value) {
+    this(name);
+    this.value = value;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public long getValue() {
+    return value;
+  }
+
+  public void setValue(long value) {
+    this.value = value;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/submission/counter/CounterGroup.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/submission/counter/CounterGroup.java b/common/src/main/java/org/apache/sqoop/submission/counter/CounterGroup.java
new file mode 100644
index 0000000..62a64c4
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/submission/counter/CounterGroup.java
@@ -0,0 +1,54 @@
+/**
+ * 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.submission.counter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ *
+ */
+public class CounterGroup implements Iterable<Counter> {
+
+  private final String name;
+  private Map<String, Counter> counters;
+
+  public CounterGroup(String name) {
+    this.name = name;
+    this.counters = new HashMap<String, Counter>();
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public CounterGroup addCounter(Counter counter) {
+    counters.put(counter.getName(), counter);
+    return this;
+  }
+
+  public Counter getCounter(String name) {
+    return counters.get(name);
+  }
+
+  @Override
+  public Iterator<Counter> iterator() {
+    return counters.values().iterator();
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/submission/counter/Counters.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/submission/counter/Counters.java b/common/src/main/java/org/apache/sqoop/submission/counter/Counters.java
new file mode 100644
index 0000000..12c9464
--- /dev/null
+++ b/common/src/main/java/org/apache/sqoop/submission/counter/Counters.java
@@ -0,0 +1,47 @@
+/**
+ * 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.submission.counter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ *
+ */
+public class Counters implements Iterable<CounterGroup> {
+  Map<String, CounterGroup> groups;
+
+  public Counters() {
+    this.groups = new HashMap<String, CounterGroup>();
+  }
+
+  public Counters addCounterGroup(CounterGroup group) {
+    groups.put(group.getName(), group);
+    return this;
+  }
+
+  public CounterGroup getCounterGroup(String name) {
+    return groups.get(name);
+  }
+
+  @Override
+  public Iterator<CounterGroup> iterator() {
+    return groups.values().iterator();
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/1cd3c373/common/src/main/java/org/apache/sqoop/utils/ClassLoadingUtils.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/sqoop/utils/ClassLoadingUtils.java b/common/src/main/java/org/apache/sqoop/utils/ClassLoadingUtils.java
deleted file mode 100644
index 0c7d09f..0000000
--- a/common/src/main/java/org/apache/sqoop/utils/ClassLoadingUtils.java
+++ /dev/null
@@ -1,86 +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.utils;
-
-import org.apache.log4j.Logger;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-public final class ClassLoadingUtils {
-
-  private static final Logger LOG = Logger.getLogger(ClassLoadingUtils.class);
-
-  public static Class<?> loadClass(String className) {
-    Class<?> klass = null;
-    try {
-      klass = Class.forName(className);
-    } catch (ClassNotFoundException ex) {
-      LOG.debug("Exception while loading class: " + className, ex);
-    }
-
-    if (klass == null) {
-      // Try the context class loader if one exists
-      ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
-      if (ctxLoader != null) {
-        try {
-          klass = ctxLoader.loadClass(className);
-        } catch (ClassNotFoundException ex) {
-          LOG.debug("Exception while load class: " + className, ex);
-        }
-      }
-    }
-
-    return klass;
-  }
-
-  public static Object instantiate(String className, Object ... args) {
-    return instantiate(loadClass(className), args);
-  }
-
-  public static Object instantiate(Class klass, Object ... args) {
-    if(klass == null) {
-      return null;
-    }
-
-    Class []argumentTypes = new Class[args.length];
-    for(int i = 0; i < args.length; i++) {
-      Class type = args[i].getClass();
-       argumentTypes[i] = type;
-    }
-
-    try {
-      Constructor constructor = klass.getConstructor(argumentTypes);
-      return constructor.newInstance(args);
-    } catch (NoSuchMethodException e) {
-      LOG.error("Can't find such constructor.", e);
-    } catch (InvocationTargetException e) {
-      LOG.error("Can't instantiate object.", e);
-    } catch (InstantiationException e) {
-      LOG.error("Can't instantiate object.", e);
-    } catch (IllegalAccessException e) {
-      LOG.error("Can't instantiate object.", e);
-    }
-
-    return null;
-  }
-
-  private ClassLoadingUtils() {
-    // Disable explicit object creation
-  }
-}


Mime
View raw message