| /** |
| * 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.util; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import com.cloudera.sqoop.Sqoop; |
| |
| /** |
| * Provides utility functions to read in options file. An options file is a |
| * regular text file with each line specifying a separate option. An option |
| * may continue into a following line by using a back-slash separator character |
| * at the end of the non-terminating line. Options file also allow empty lines |
| * and comment lines which are disregarded. Comment lines must begin with the |
| * hash character as the first character. Leading and trailing white-spaces are |
| * ignored for any options read from the Options file. |
| */ |
| public final class OptionsFileUtil { |
| |
| public static final Log LOG = LogFactory.getLog( |
| OptionsFileUtil.class.getName()); |
| |
| private OptionsFileUtil() { } |
| |
| /** |
| * Expands any options file that may be present in the given set of arguments. |
| * |
| * @param args the given arguments |
| * @return a new string array that contains the expanded arguments. |
| * @throws Exception |
| */ |
| public static String[] expandArguments(String[] args) throws Exception { |
| List<String> options = new ArrayList<String>(); |
| |
| for (int i = 0; i < args.length; i++) { |
| if (args[i].equals(Sqoop.SQOOP_OPTIONS_FILE_SPECIFIER)) { |
| if (i == args.length - 1) { |
| throw new Exception("Missing options file"); |
| } |
| |
| String fileName = args[++i]; |
| File optionsFile = new File(fileName); |
| BufferedReader reader = null; |
| StringBuilder buffer = new StringBuilder(); |
| try { |
| reader = new BufferedReader(new FileReader(optionsFile)); |
| String nextLine = null; |
| while ((nextLine = reader.readLine()) != null) { |
| nextLine = nextLine.trim(); |
| if (nextLine.length() == 0 || nextLine.startsWith("#")) { |
| // empty line or comment |
| continue; |
| } |
| |
| buffer.append(nextLine); |
| if (nextLine.endsWith("\\")) { |
| if (buffer.charAt(0) == '\'' || buffer.charAt(0) == '"') { |
| throw new Exception( |
| "Multiline quoted strings not supported in file(" |
| + fileName + "): " + buffer.toString()); |
| } |
| // Remove the trailing back-slash and continue |
| buffer.deleteCharAt(buffer.length() - 1); |
| } else { |
| // The buffer contains a full option |
| options.add( |
| removeQuotesEncolosingOption(fileName, buffer.toString())); |
| buffer.delete(0, buffer.length()); |
| } |
| } |
| |
| // Assert that the buffer is empty |
| if (buffer.length() != 0) { |
| throw new Exception("Malformed option in options file(" |
| + fileName + "): " + buffer.toString()); |
| } |
| } catch (IOException ex) { |
| throw new Exception("Unable to read options file: " + fileName, ex); |
| } finally { |
| if (reader != null) { |
| try { |
| reader.close(); |
| } catch (IOException ex) { |
| LOG.info("Exception while closing reader", ex); |
| } |
| } |
| } |
| } else { |
| // Regular option. Parse it and put it on the appropriate list |
| options.add(args[i]); |
| } |
| } |
| |
| return options.toArray(new String[options.size()]); |
| } |
| |
| /** |
| * Removes the surrounding quote characters as needed. It first attempts to |
| * remove surrounding double quotes. If successful, the resultant string is |
| * returned. If no surrounding double quotes are found, it attempts to remove |
| * surrounding single quote characters. If successful, the resultant string |
| * is returned. If not the original string is returnred. |
| * @param fileName |
| * @param option |
| * @return |
| * @throws Exception |
| */ |
| private static String removeQuotesEncolosingOption( |
| String fileName, String option) throws Exception { |
| |
| // Attempt to remove double quotes. If successful, return. |
| String option1 = removeQuoteCharactersIfNecessary(fileName, option, '"'); |
| if (!option1.equals(option)) { |
| // Quotes were successfully removed |
| return option1; |
| } |
| |
| // Attempt to remove single quotes. |
| return removeQuoteCharactersIfNecessary(fileName, option, '\''); |
| } |
| |
| /** |
| * Removes the surrounding quote characters from the given string. The quotes |
| * are identified by the quote parameter, the given string by option. The |
| * fileName parameter is used for raising exceptions with relevant message. |
| * @param fileName String The name of the file that contains sqoop options |
| * @param option String the actual options in the options file |
| * @param quote char The quote that we need to validate on, either single or double quotes |
| * @return String The validated options string |
| * @throws Exception |
| */ |
| private static String removeQuoteCharactersIfNecessary(String fileName, |
| String option, char quote) throws Exception { |
| boolean startingQuote = (option.charAt(0) == quote); |
| boolean endingQuote = (option.charAt(option.length() - 1) == quote); |
| |
| if (startingQuote && endingQuote) { |
| if (option.length() == 1) { |
| throw new Exception("Malformed option in options file(" |
| + fileName + "): " + option); |
| } |
| return option.substring(1, option.length() - 1); |
| } |
| |
| if (startingQuote || endingQuote) { |
| // Regular expression looks like below: |
| // .*=\s*".*"$ OR .*=\s*'.*'$ |
| // it tries to match the following: |
| // .... column_name = "values" OR .... column_name = 'values' |
| // so that the query like: |
| // SELECT * FROM table WHERE column = "values" |
| // is valid even though it ends with double quote but no starting double quote |
| if (!Pattern.matches(".*=\\s*"+quote+".*"+quote+"$", option)) { |
| throw new Exception("Malformed option in options file(" |
| + fileName + "): " + option); |
| } |
| } |
| |
| return option; |
| } |
| } |