This is an automated email from the ASF dual-hosted git repository.

kmarton pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/oozie.git


The following commit(s) were added to refs/heads/master by this push:
     new 1a1a97a  OOZIE-2949 Escape quotes whitespaces in Sqoop <command> field 
(asalamon74 via kmarton)
1a1a97a is described below

commit 1a1a97acbab14161880c32651705e8aebe9ec8d0
Author: Julia Kinga Marton <[email protected]>
AuthorDate: Fri Feb 1 08:44:28 2019 +0100

    OOZIE-2949 Escape quotes whitespaces in Sqoop <command> field (asalamon74 
via kmarton)
---
 core/src/main/java/org/apache/oozie/ErrorCode.java |   1 -
 .../apache/oozie/action/hadoop/ShellSplitter.java  |  94 +++++++++++++++
 .../action/hadoop/ShellSplitterException.java      |  36 ++++++
 .../oozie/action/hadoop/SqoopActionExecutor.java   |  39 +++++-
 core/src/main/resources/oozie-default.xml          |   8 ++
 .../oozie/action/hadoop/TestShellSplitter.java     | 134 +++++++++++++++++++++
 docs/src/site/markdown/DG_SqoopActionExtension.md  |  14 ++-
 release-log.txt                                    |   1 +
 .../action/hadoop/TestSqoopActionExecutor.java     |  59 ++++++++-
 9 files changed, 373 insertions(+), 13 deletions(-)

diff --git a/core/src/main/java/org/apache/oozie/ErrorCode.java 
b/core/src/main/java/org/apache/oozie/ErrorCode.java
index e274b9d..6b0ce47 100644
--- a/core/src/main/java/org/apache/oozie/ErrorCode.java
+++ b/core/src/main/java/org/apache/oozie/ErrorCode.java
@@ -67,7 +67,6 @@ public enum ErrorCode {
     E0307(XLog.STD, "Runtime error [{0}]"),
     E0308(XLog.STD, "Could not parse date range parameter [{0}]"),
 
-
     E0401(XLog.STD, "Missing configuration property [{0}]"),
     E0402(XLog.STD, "Invalid callback ID [{0}]"),
     E0403(XLog.STD, "Invalid callback data, {0}"),
diff --git 
a/core/src/main/java/org/apache/oozie/action/hadoop/ShellSplitter.java 
b/core/src/main/java/org/apache/oozie/action/hadoop/ShellSplitter.java
new file mode 100644
index 0000000..69d39cf
--- /dev/null
+++ b/core/src/main/java/org/apache/oozie/action/hadoop/ShellSplitter.java
@@ -0,0 +1,94 @@
+/**
+ * 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.oozie.action.hadoop;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ShellSplitter {
+    private static final char BACKSPACE = '\\';
+    private static final char SINGLE_QUOTE = '\'';
+    private static final char DOUBLE_QUOTE = '"';
+    private static final char SPACE = ' ';
+    private boolean escaping;
+    private char quoteChar;
+    private boolean quoting;
+    private boolean addTokenEvenIfEmpty;
+    private StringBuilder current;
+    private List<String> tokens;
+
+    List<String> split(final String commandLine) throws ShellSplitterException 
{
+        if (commandLine == null) {
+            return null;
+        }
+        ensureFields();
+        for (int i = 0; i < commandLine.length(); i++) {
+            char c = commandLine.charAt(i);
+            processCharacter(c);
+        }
+        addLastToken();
+        if (quoting || escaping) {
+            final String errorMessage = String.format("Unable to parse command 
%s", commandLine);
+            throw new ShellSplitterException(errorMessage);
+        }
+        return tokens;
+    }
+
+    private void ensureFields() {
+        tokens = new ArrayList<>();
+        escaping = false;
+        quoteChar = SPACE;
+        quoting = false;
+        addTokenEvenIfEmpty = false;
+        current = new StringBuilder();
+    }
+
+    private void processCharacter(char c) {
+        if (escaping) {
+            current.append(c);
+            escaping = false;
+        } else if (c == BACKSPACE && !(quoting && quoteChar == SINGLE_QUOTE)) {
+            escaping = true;
+        } else if (quoting && c == quoteChar) {
+            quoting = false;
+            addTokenEvenIfEmpty = true;
+        } else if (!quoting && (c == SINGLE_QUOTE || c == DOUBLE_QUOTE)) {
+            quoting = true;
+            quoteChar = c;
+        } else if (!quoting && Character.isWhitespace(c)) {
+            if (current.length() > 0 || addTokenEvenIfEmpty ) {
+                addNewToken();
+            }
+        } else {
+            current.append(c);
+        }
+    }
+
+    private void addLastToken() {
+        if (current.length() > 0) {
+            addNewToken();
+        }
+    }
+
+    private void addNewToken() {
+        tokens.add(current.toString());
+        current = new StringBuilder();
+        addTokenEvenIfEmpty = false;
+    }
+}
diff --git 
a/core/src/main/java/org/apache/oozie/action/hadoop/ShellSplitterException.java 
b/core/src/main/java/org/apache/oozie/action/hadoop/ShellSplitterException.java
new file mode 100644
index 0000000..ef18d51
--- /dev/null
+++ 
b/core/src/main/java/org/apache/oozie/action/hadoop/ShellSplitterException.java
@@ -0,0 +1,36 @@
+/**
+ * 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.oozie.action.hadoop;
+
+import org.apache.oozie.ErrorCode;
+
+/**
+ * Exception thrown by {@link ShellSplitter}.
+ */
+public class ShellSplitterException extends Exception {
+
+    public ShellSplitterException(String msg) {
+        super(msg);
+    }
+
+    public ShellSplitterException(String msg, Throwable throwable) {
+        super(msg, throwable);
+    }
+
+}
diff --git 
a/core/src/main/java/org/apache/oozie/action/hadoop/SqoopActionExecutor.java 
b/core/src/main/java/org/apache/oozie/action/hadoop/SqoopActionExecutor.java
index 556f2cf..ffe27e3 100644
--- a/core/src/main/java/org/apache/oozie/action/hadoop/SqoopActionExecutor.java
+++ b/core/src/main/java/org/apache/oozie/action/hadoop/SqoopActionExecutor.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Charsets;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
@@ -42,9 +43,13 @@ import org.jdom.Namespace;
 public class SqoopActionExecutor extends JavaActionExecutor {
 
   public static final String OOZIE_ACTION_EXTERNAL_STATS_WRITE = 
"oozie.action.external.stats.write";
+  @VisibleForTesting
+  static final String OOZIE_ACTION_SQOOP_SHELLSPLITTER = 
"oozie.action.sqoop.shellsplitter";
+  private static final boolean SHELLSPLITTER_DEFAULT = false;
   private static final String SQOOP_MAIN_CLASS_NAME = 
"org.apache.oozie.action.hadoop.SqoopMain";
   static final String SQOOP_ARGS = "oozie.sqoop.args";
   private static final String SQOOP = "sqoop";
+  private ShellSplitter shellSplitter = new ShellSplitter();
 
     public SqoopActionExecutor() {
         super(SQOOP);
@@ -85,14 +90,11 @@ public class SqoopActionExecutor extends JavaActionExecutor 
{
             throw convertException(ex);
         }
 
-        final List<String> argList = new ArrayList<>();
+        List<String> argList = new ArrayList<>();
         // Build a list of arguments from either a tokenized <command> string 
or a list of <arg>
         if (actionXml.getChild("command", ns) != null) {
             String command = actionXml.getChild("command", ns).getTextTrim();
-            StringTokenizer st = new StringTokenizer(command, " ");
-            while (st.hasMoreTokens()) {
-                argList.add(st.nextToken());
-            }
+            argList = splitCommand(actionConf, command);
         }
         else {
             @SuppressWarnings("unchecked")
@@ -113,11 +115,36 @@ public class SqoopActionExecutor extends 
JavaActionExecutor {
                     "Found a redundant 'sqoop' prefixing the command. Removing 
it.");
             argList.remove(0);
         }
-
         setSqoopCommand(actionConf, argList.toArray(new 
String[argList.size()]));
         return actionConf;
     }
 
+    private List<String> splitCommand(Configuration actionConf, String 
command) throws ActionExecutorException {
+        List<String> argList;
+        boolean useShellSplitter = 
actionConf.getBoolean(OOZIE_ACTION_SQOOP_SHELLSPLITTER, SHELLSPLITTER_DEFAULT);
+        if (useShellSplitter) {
+            try {
+                argList = shellSplitter.split(command);
+            } catch (ShellSplitterException e) {
+                throw new 
ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "SQOOP002",
+                        "Cannot parse sqoop command: [{0}]", command, e);
+            }
+        }
+        else {
+            argList = splitBySpace(command);
+        }
+        return argList;
+    }
+
+    private List<String> splitBySpace(final String command) {
+        List<String> tokens = new ArrayList<>();
+        StringTokenizer st = new StringTokenizer(command, " ");
+        while (st.hasMoreTokens()) {
+            tokens.add(st.nextToken());
+        }
+        return tokens;
+    }
+
     private void setSqoopCommand(Configuration conf, String[] args) {
         ActionUtils.setStrings(conf, SQOOP_ARGS, args);
     }
diff --git a/core/src/main/resources/oozie-default.xml 
b/core/src/main/resources/oozie-default.xml
index 6c7fc9d..c7f2bec 100644
--- a/core/src/main/resources/oozie-default.xml
+++ b/core/src/main/resources/oozie-default.xml
@@ -3419,6 +3419,14 @@ will be the requeue interval for the actions which are 
waiting for a long time w
     </property>
 
     <property>
+        <name>oozie.action.sqoop.shellsplitter</name>
+        <value>false</value>
+        <description>
+            Whether to use shell splitter instead of the space-based tokenizer 
during sqoop command splitting.
+        </description>
+    </property>
+
+    <property>
         <name>oozie.fluent-job-api.generated.path</name>
         <value>/user/${user.name}/oozie-fluent-job-api-generated</value>
         <description>
diff --git 
a/core/src/test/java/org/apache/oozie/action/hadoop/TestShellSplitter.java 
b/core/src/test/java/org/apache/oozie/action/hadoop/TestShellSplitter.java
new file mode 100644
index 0000000..e93e3fd
--- /dev/null
+++ b/core/src/test/java/org/apache/oozie/action/hadoop/TestShellSplitter.java
@@ -0,0 +1,134 @@
+/**
+ * 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.oozie.action.hadoop;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
+public class TestShellSplitter {
+    private ShellSplitter shellSplitter;
+    private String input;
+    private List<String> expectedOutput;
+    private Class<? extends Exception> expectedExceptionClass;
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> params() {
+        return Arrays.asList(new Object[][]{
+                {null,
+                        null,
+                        null},
+                {"",
+                        Collections.<String>emptyList(),
+                        null},
+                {"  \t \n",
+                        Collections.<String>emptyList(),
+                        null},
+                {"a\tbee  cee",
+                        Arrays.asList("a", "bee", "cee"),
+                        null},
+                {" hello   world\t",
+                        Arrays.asList("hello", "world"),
+                        null},
+                {"\"hello world\"",
+                        Collections.singletonList("hello world"),
+                        null},
+                {"'hello world'",
+                        Collections.singletonList("hello world"),
+                        null},
+                {"\"\\\"hello world\\\"\"",
+                        Collections.singletonList("\"hello world\""),
+                        null},
+                {"'hello \\\" world'",
+                        Collections.singletonList("hello \\\" world"),
+                        null},
+                {"\"foo\"'bar'baz",
+                        Collections.singletonList("foobarbaz"),
+                        null},
+                {"\"three\"' 'four",
+                        Collections.singletonList("three four"),
+                        null},
+                {"three\\ four",
+                        Collections.singletonList("three four"),
+                        null},
+                {" '' one",
+                        Arrays.asList("", "one"),
+                        null},
+                {"command -a aa -b -c",
+                        Arrays.asList("command", "-a", "aa", "-b", "-c"),
+                        null},
+                {"command --longopt \"this is a single token\" --otherlongopt",
+                        Arrays.asList("command", "--longopt", "this is a 
single token", "--otherlongopt"),
+                        null},
+                {"command --longopt 'this is a single token' --otherlongopt",
+                        Arrays.asList("command", "--longopt", "this is a 
single token", "--otherlongopt"),
+                        null},
+                {"'",
+                        null,
+                        ShellSplitterException.class},
+                {"'Hello world",
+                        null,
+                        ShellSplitterException.class},
+                {"\"Hello world",
+                        null,
+                        ShellSplitterException.class},
+                {"Hellow world\\",
+                        null,
+                        ShellSplitterException.class},
+                {"\"Hello world'",
+                        null,
+                        ShellSplitterException.class},
+        });
+    }
+
+    public TestShellSplitter(String input, List<String> expectedOutput, 
Class<? extends Exception> expectedException) {
+        this.input = input;
+        this.expectedOutput = expectedOutput;
+        this.expectedExceptionClass = expectedException;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        shellSplitter = new ShellSplitter();
+    }
+
+    @Test
+    public void test() throws ShellSplitterException {
+        if (expectedExceptionClass != null) {
+            expectedException.expect(expectedExceptionClass);
+            shellSplitter.split(input);
+        }
+        else {
+            assertEquals("Invalid splitting", expectedOutput, 
shellSplitter.split(input));
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/src/site/markdown/DG_SqoopActionExtension.md 
b/docs/src/site/markdown/DG_SqoopActionExtension.md
index b186c5a..fa0674a 100644
--- a/docs/src/site/markdown/DG_SqoopActionExtension.md
+++ b/docs/src/site/markdown/DG_SqoopActionExtension.md
@@ -91,12 +91,18 @@ properties that are passed to the Sqoop job.
 The Sqoop command can be specified either using the `command` element or 
multiple `arg`
 elements.
 
-When using the `command` element, Oozie will split the command on every space
-into multiple arguments.
+When using the `command` element, Oozie will split the command into multiple 
arguments. There are two command splitting algorithms
+in Oozie.
 
-When using the `arg` elements, Oozie will pass each argument value as an 
argument to Sqoop.
+If `oozie.action.sqoop.shellsplitter` property is set to `false` Oozie will 
split the command on every space.
+
+If `oozie.action.sqoop.shellsplitter` property is set to `true` Oozie will 
split the command like `bash` splits the commands.
+In this case it's possible to group strings together using quotes. For 
instance oozie will split `--query "select * from employee"`
+into two tokens: `--query` and `select * from employee`.
 
-The `arg` variant should be used when there are spaces within a single 
argument.
+The default value of the `oozie.action.sqoop.shellsplitter` property is 
`false`.
+
+When using the `arg` elements, Oozie will pass each argument value as an 
argument to Sqoop.
 
 Consult the Sqoop documentation for a complete list of valid Sqoop commands.
 
diff --git a/release-log.txt b/release-log.txt
index 9e49d2b..339b930 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
 -- Oozie 5.2.0 release (trunk - unreleased)
 
+OOZIE-2949 Escape quotes whitespaces in Sqoop <command> field (asalamon74 via 
kmarton) 
 OOZIE-3426 [core] V1JobsServlet should log HDFS related error when trying to 
save workflow definition (asalamon74 via andras.piros)
 OOZIE-3417 [FS Action] Refactor and optimize FsActionExecutor.java decision 
making part (nobigo via asalamon74, kmarton)
 OOZIE-3243 [tests] Flaky test 
TestCoordActionsKillXCommand#testActionKillCommandDate (asalamon74 via kmarton)
diff --git 
a/sharelib/sqoop/src/test/java/org/apache/oozie/action/hadoop/TestSqoopActionExecutor.java
 
b/sharelib/sqoop/src/test/java/org/apache/oozie/action/hadoop/TestSqoopActionExecutor.java
index edfe0c7..e230f39 100644
--- 
a/sharelib/sqoop/src/test/java/org/apache/oozie/action/hadoop/TestSqoopActionExecutor.java
+++ 
b/sharelib/sqoop/src/test/java/org/apache/oozie/action/hadoop/TestSqoopActionExecutor.java
@@ -24,6 +24,7 @@ import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.PathFilter;
 import org.apache.oozie.WorkflowActionBean;
 import org.apache.oozie.WorkflowJobBean;
+import org.apache.oozie.action.ActionExecutorException;
 import org.apache.oozie.client.WorkflowAction;
 import org.apache.oozie.service.WorkflowAppService;
 import org.apache.oozie.util.IOUtils;
@@ -48,6 +49,9 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
     private static final String SQOOP_IMPORT_COMMAND =
             "import --connect {0} --table TT --target-dir {1} -m 1";
 
+    private static final String SQOOP_IMPORT_COMMAND_WITH_QUERY =
+            "import --connect {0} --username sa --password \"\" --verbose 
--query {1} --target-dir {2} --split-by I";
+
     private static final String SQOOP_ACTION_COMMAND_XML =
             "<sqoop xmlns=\"uri:oozie:sqoop-action:0.1\">" +
             "<job-tracker>{0}</job-tracker>" +
@@ -155,6 +159,20 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
                 "dummy", "dummyValue", command);
     }
 
+    private String getImportWithQueryActionXml(boolean useNewShellSplitter) {
+        String command = MessageFormat.format(SQOOP_IMPORT_COMMAND_WITH_QUERY, 
getActionJdbcUri(),
+                "\"select TT.I, TT.S from TT where $CONDITIONS\"", 
getSqoopOutputDir());
+        return MessageFormat.format(SQOOP_ACTION_COMMAND_XML, 
getJobTrackerUri(), getNameNodeUri(),
+                SqoopActionExecutor.OOZIE_ACTION_SQOOP_SHELLSPLITTER, 
useNewShellSplitter, command);
+    }
+
+    private String getInvalidImportWithQueryActionXml(boolean 
useNewShellSplitter) {
+        String command = MessageFormat.format(SQOOP_IMPORT_COMMAND_WITH_QUERY, 
getActionJdbcUri(),
+                "\"select TT.I, TT.S from TT where $CONDITIONS'", 
getSqoopOutputDir());
+        return MessageFormat.format(SQOOP_ACTION_COMMAND_XML, 
getJobTrackerUri(), getNameNodeUri(),
+                SqoopActionExecutor.OOZIE_ACTION_SQOOP_SHELLSPLITTER, 
useNewShellSplitter, command);
+    }
+
     private String getActionXmlEval() {
       String query = "select TT.I, TT.S from TT";
       return MessageFormat.format(SQOOP_ACTION_EVAL_XML, getJobTrackerUri(), 
getNameNodeUri(),
@@ -168,6 +186,12 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
                                     getActionJdbcUri(), query, 
getSqoopOutputDir());
     }
 
+    private String getArgsActionXmlFreeFromQueryInQuotes(String quote) {
+        String query = "select TT.I, TT.S from TT where $CONDITIONS";
+        return MessageFormat.format(SQOOP_ACTION_ARGS_XML, getJobTrackerUri(), 
getNameNodeUri(),
+               "<arg>import</arg>", getActionJdbcUri(), quote + query + quote, 
getSqoopOutputDir());
+    }
+
     private String getBadArgsActionXml() {
         String query = "select TT.I, TT.S from TT where $CONDITIONS";
         return MessageFormat.format(SQOOP_ACTION_ARGS_XML, getJobTrackerUri(), 
getNameNodeUri(),
@@ -190,6 +214,7 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
     /**
      * Tests a bad command of 'sqoop --username ...' style.
      * Test asserts that the job will fail.
+     * @throws java.lang.Exception
      */
     public void testSqoopActionWithBadCommand() throws Exception {
         runSqoopActionWithBadCommand(getBadCommandActionXml());
@@ -216,6 +241,7 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
 
     /**
      * Tests a normal command of 'import --username ...'.
+     * @throws java.lang.Exception
      */
     public void testSqoopAction() throws Exception {
         runSqoopAction(getActionXml());
@@ -224,6 +250,7 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
     /**
      * Tests a redundant command of 'sqoop import --username ...'.
      * The test guarantees a success, since the redundant 'sqoop' must get 
removed.
+     * @throws java.lang.Exception
      */
     public void testSqoopActionWithRedundantPrefix() throws Exception {
         runSqoopAction(getRedundantCommandActionXml());
@@ -290,6 +317,7 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
     /**
      * Runs a job with arg-style command of 'sqoop --username ...' form that's 
invalid.
      * The test ensures it fails.
+     * @throws java.lang.Exception
      */
     public void testSqoopActionWithBadRedundantArgsAndFreeFormQuery() throws 
Exception {
         runSqoopActionWithBadCommand(getBadArgsActionXml());
@@ -298,6 +326,7 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
     /**
      * Runs a job with the arg-style command of 'sqoop import --username ...'.
      * The test guarantees that the redundant 'sqoop' is auto-removed (job 
passes).
+     * @throws java.lang.Exception
      */
     public void testSqoopActionWithRedundantArgsAndFreeFormQuery() throws 
Exception {
         runSqoopActionFreeFormQuery(getArgsActionXmlFreeFromQuery(true));
@@ -305,11 +334,38 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
 
     /**
      * Runs a job with the normal arg-style command of 'import --username ...'.
+     * @throws java.lang.Exception
      */
     public void testSqoopActionWithArgsAndFreeFormQuery() throws Exception {
         runSqoopActionFreeFormQuery(getArgsActionXmlFreeFromQuery(false));
     }
 
+    /**
+     * Runs a job with the normal arg-style command of 'import --username ...'.
+     * This is meant to be a sanity check for when --query argument has
+     * double quotes around it.
+     * @throws java.lang.Exception
+     */
+    public void testSqoopActionWithArgsAndFreeFormQueryInDoubleQuotes() throws 
Exception {
+        
runSqoopActionFreeFormQuery(getArgsActionXmlFreeFromQueryInQuotes("\""));
+    }
+
+    public void testSqoopActionWithCommandAndFreeFormQuery() throws Exception {
+        boolean useNewShellSplitter = true;
+        
runSqoopActionFreeFormQuery(getImportWithQueryActionXml(useNewShellSplitter));
+    }
+
+    public void testInvalidSqoopActionWithCommandAndFreeFormQuery() throws 
Exception {
+        try {
+            boolean useNewShellSplitter = true;
+            
runSqoopActionFreeFormQuery(getInvalidImportWithQueryActionXml(useNewShellSplitter));
+            fail("Expected ActionExecutorException");
+        }
+        catch (ActionExecutorException e) {
+            assertTrue(String.format("Invalid error message: [%s]", 
e.getMessage()), e.getMessage().startsWith("SQOOP002"));
+        }
+    }
+
     private void runSqoopActionFreeFormQuery(String actionXml) throws 
Exception {
         createDB();
 
@@ -353,7 +409,6 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
         assertEquals(3, count);
     }
 
-
     private String submitAction(Context context) throws Exception {
         SqoopActionExecutor ae = new SqoopActionExecutor();
 
@@ -392,7 +447,7 @@ public class TestSqoopActionExecutor extends 
ActionExecutorTestCase {
     }
 
     private String[] copyDbToHdfs() throws Exception {
-        List<String> files = new ArrayList<String>();
+        List<String> files = new ArrayList<>();
         String[] exts = {".script", ".properties"};
         for (String ext : exts) {
             String file = getDbPath() + ext;

Reply via email to