blob: ac90099f98ef8cc903051255e3a5856372071e19 [file] [log] [blame]
/**
* 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.mapreduce.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrTokenizer;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.JobConf;
import org.apache.sqoop.mapreduce.DBWritable;
/**
* A container for configuration property names for jobs with DB input/output.
*
* The job can be configured using the static methods in this class,
* {@link DBInputFormat}, and {@link DBOutputFormat}.
* Alternatively, the properties can be set in the configuration with proper
* values.
*
* @see DBConfiguration#configureDB(Configuration, String, String, String,
* String)
* @see DBInputFormat#setInput(Job, Class, String, String)
* @see DBInputFormat#setInput(Job, Class, String, String, String, String...)
* @see DBOutputFormat#setOutput(Job, String, String...)
*/
public class DBConfiguration {
public static final Log LOG =
LogFactory.getLog(DBConfiguration.class.getName());
/** The JDBC Driver class name. */
public static final String DRIVER_CLASS_PROPERTY =
"mapreduce.jdbc.driver.class";
/** JDBC Database access URL. */
public static final String URL_PROPERTY = "mapreduce.jdbc.url";
/** User name to access the database. */
public static final String USERNAME_PROPERTY = "mapreduce.jdbc.username";
/** Password to access the database. */
public static final String PASSWORD_PROPERTY = "mapreduce.jdbc.password";
private static final Text PASSWORD_SECRET_KEY =
new Text(DBConfiguration.PASSWORD_PROPERTY);
/** JDBC connection parameters. */
public static final String CONNECTION_PARAMS_PROPERTY =
"mapreduce.jdbc.params";
/** Fetch size. */
public static final String FETCH_SIZE = "mapreduce.jdbc.fetchsize";
/** Input table name. */
public static final String INPUT_TABLE_NAME_PROPERTY =
"mapreduce.jdbc.input.table.name";
/** Field names in the Input table. */
public static final String INPUT_FIELD_NAMES_PROPERTY =
"mapreduce.jdbc.input.field.names";
/** WHERE clause in the input SELECT statement. */
public static final String INPUT_CONDITIONS_PROPERTY =
"mapreduce.jdbc.input.conditions";
/** ORDER BY clause in the input SELECT statement. */
public static final String INPUT_ORDER_BY_PROPERTY =
"mapreduce.jdbc.input.orderby";
/** Whole input query, exluding LIMIT...OFFSET. */
public static final String INPUT_QUERY = "mapreduce.jdbc.input.query";
/** Input query to get the count of records. */
public static final String INPUT_COUNT_QUERY =
"mapreduce.jdbc.input.count.query";
/** Input query to get the max and min values of the jdbc.input.query. */
public static final String INPUT_BOUNDING_QUERY =
"mapred.jdbc.input.bounding.query";
/** Class name implementing DBWritable which will hold input tuples. */
public static final String INPUT_CLASS_PROPERTY =
"mapreduce.jdbc.input.class";
/** Output table name. */
public static final String OUTPUT_TABLE_NAME_PROPERTY =
"mapreduce.jdbc.output.table.name";
/** Field names in the Output table. */
public static final String OUTPUT_FIELD_NAMES_PROPERTY =
"mapreduce.jdbc.output.field.names";
/** Number of fields in the Output table. */
public static final String OUTPUT_FIELD_COUNT_PROPERTY =
"mapreduce.jdbc.output.field.count";
/**
* The name of the parameter to use for making Isolation level to be
* read uncommitted by default for connections.
*/
public static final String PROP_RELAXED_ISOLATION =
"org.apache.sqoop.db.relaxedisolation";
/**
* Sets the DB access related fields in the {@link Configuration}.
* @param conf the configuration
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL
* @param userName DB access username
* @param passwd DB access passwd
* @param fetchSize DB fetch size
* @param connectionParams JDBC connection parameters
*/
public static void configureDB(Configuration conf, String driverClass,
String dbUrl, String userName, String passwd, Integer fetchSize,
Properties connectionParams) {
conf.set(DRIVER_CLASS_PROPERTY, driverClass);
conf.set(URL_PROPERTY, dbUrl);
if (userName != null) {
conf.set(USERNAME_PROPERTY, userName);
}
if (passwd != null) {
setPassword((JobConf) conf, passwd);
}
if (fetchSize != null) {
conf.setInt(FETCH_SIZE, fetchSize);
}
if (connectionParams != null) {
conf.set(CONNECTION_PARAMS_PROPERTY,
propertiesToString(connectionParams));
}
}
// set the password in the secure credentials object
private static void setPassword(JobConf configuration, String password) {
LOG.debug("Securing password into job credentials store");
configuration.getCredentials().addSecretKey(
PASSWORD_SECRET_KEY, password.getBytes());
}
/**
* Sets the DB access related fields in the JobConf.
* @param job the job
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL
* @param fetchSize DB fetch size
* @param connectionParams JDBC connection parameters
*/
public static void configureDB(Configuration job, String driverClass,
String dbUrl, Integer fetchSize, Properties connectionParams) {
configureDB(job, driverClass, dbUrl, null, null, fetchSize,
connectionParams);
}
/**
* Sets the DB access related fields in the {@link Configuration}.
* @param conf the configuration
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL
* @param userName DB access username
* @param passwd DB access passwd
* @param connectionParams JDBC connection parameters
*/
public static void configureDB(Configuration conf, String driverClass,
String dbUrl, String userName, String passwd,
Properties connectionParams) {
configureDB(conf, driverClass, dbUrl, userName, passwd, null,
connectionParams);
}
/**
* Sets the DB access related fields in the JobConf.
* @param job the job
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL.
* @param connectionParams JDBC connection parameters
*/
public static void configureDB(Configuration job, String driverClass,
String dbUrl, Properties connectionParams) {
configureDB(job, driverClass, dbUrl, null, connectionParams);
}
/**
* Sets the DB access related fields in the {@link Configuration}.
* @param conf the configuration
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL
* @param userName DB access username
* @param passwd DB access passwd
* @param fetchSize DB fetch size
*/
public static void configureDB(Configuration conf, String driverClass,
String dbUrl, String userName, String passwd, Integer fetchSize) {
configureDB(conf, driverClass, dbUrl, userName, passwd, fetchSize,
(Properties) null);
}
/**
* Sets the DB access related fields in the JobConf.
* @param job the job
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL
* @param fetchSize DB fetch size
*/
public static void configureDB(Configuration job, String driverClass,
String dbUrl, Integer fetchSize) {
configureDB(job, driverClass, dbUrl, fetchSize, (Properties) null);
}
/**
* Sets the DB access related fields in the {@link Configuration}.
* @param conf the configuration
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL
* @param userName DB access username
* @param passwd DB access passwd
*/
public static void configureDB(Configuration conf, String driverClass,
String dbUrl, String userName, String passwd) {
configureDB(conf, driverClass, dbUrl, userName, passwd, (Properties) null);
}
/**
* Sets the DB access related fields in the JobConf.
* @param job the job
* @param driverClass JDBC Driver class name
* @param dbUrl JDBC DB access URL.
*/
public static void configureDB(Configuration job, String driverClass,
String dbUrl) {
configureDB(job, driverClass, dbUrl, (Properties) null);
}
private Configuration conf;
public DBConfiguration(Configuration job) {
this.conf = job;
}
/** Returns a connection object to the DB.
* @throws ClassNotFoundException
* @throws SQLException */
public Connection getConnection()
throws ClassNotFoundException, SQLException {
Connection connection;
Class.forName(conf.get(DBConfiguration.DRIVER_CLASS_PROPERTY));
String username = conf.get(DBConfiguration.USERNAME_PROPERTY);
String password = getPassword((JobConf) conf);
String connectString = conf.get(DBConfiguration.URL_PROPERTY);
String connectionParamsStr =
conf.get(DBConfiguration.CONNECTION_PARAMS_PROPERTY);
Properties connectionParams = propertiesFromString(connectionParamsStr);
if (connectionParams != null && connectionParams.size() > 0) {
Properties props = new Properties();
if (username != null) {
props.put("user", username);
}
if (password != null) {
props.put("password", password);
}
props.putAll(connectionParams);
connection = DriverManager.getConnection(connectString, props);
} else {
if (username == null) {
connection = DriverManager.getConnection(connectString);
} else {
connection = DriverManager.getConnection(
connectString, username, password);
}
}
return connection;
}
// retrieve the password from the credentials object
public static String getPassword(JobConf configuration) {
LOG.debug("Fetching password from job credentials store");
byte[] secret = configuration.getCredentials().getSecretKey(
PASSWORD_SECRET_KEY);
return secret != null ? new String(secret) : null;
}
public Configuration getConf() {
return conf;
}
public Integer getFetchSize() {
if (conf.get(DBConfiguration.FETCH_SIZE) == null) {
return null;
}
return conf.getInt(DBConfiguration.FETCH_SIZE, 0);
}
public void setFetchSize(Integer fetchSize) {
if (fetchSize != null) {
conf.setInt(DBConfiguration.FETCH_SIZE, fetchSize);
} else {
conf.set(FETCH_SIZE, null);
}
}
public String getInputTableName() {
return conf.get(DBConfiguration.INPUT_TABLE_NAME_PROPERTY);
}
public void setInputTableName(String tableName) {
conf.set(DBConfiguration.INPUT_TABLE_NAME_PROPERTY, tableName);
}
public String[] getInputFieldNames() {
return conf.getStrings(DBConfiguration.INPUT_FIELD_NAMES_PROPERTY);
}
public void setInputFieldNames(String... fieldNames) {
conf.setStrings(DBConfiguration.INPUT_FIELD_NAMES_PROPERTY, fieldNames);
}
public String getInputConditions() {
return conf.get(DBConfiguration.INPUT_CONDITIONS_PROPERTY);
}
public void setInputConditions(String conditions) {
if (conditions != null && conditions.length() > 0) {
conf.set(DBConfiguration.INPUT_CONDITIONS_PROPERTY, conditions);
}
}
public String getInputOrderBy() {
return conf.get(DBConfiguration.INPUT_ORDER_BY_PROPERTY);
}
public void setInputOrderBy(String orderby) {
if (orderby != null && orderby.length() >0) {
conf.set(DBConfiguration.INPUT_ORDER_BY_PROPERTY, orderby);
}
}
public String getInputQuery() {
return conf.get(DBConfiguration.INPUT_QUERY);
}
public void setInputQuery(String query) {
if (query != null && query.length() >0) {
conf.set(DBConfiguration.INPUT_QUERY, query);
}
}
public String getInputCountQuery() {
return conf.get(DBConfiguration.INPUT_COUNT_QUERY);
}
public void setInputCountQuery(String query) {
if (query != null && query.length() > 0) {
conf.set(DBConfiguration.INPUT_COUNT_QUERY, query);
}
}
public void setInputBoundingQuery(String query) {
if (query != null && query.length() > 0) {
conf.set(DBConfiguration.INPUT_BOUNDING_QUERY, query);
}
}
public String getInputBoundingQuery() {
return conf.get(DBConfiguration.INPUT_BOUNDING_QUERY);
}
public Class<?> getInputClass() {
return conf.getClass(DBConfiguration.INPUT_CLASS_PROPERTY,
DBInputFormat.NullDBWritable.class);
}
public void setInputClass(Class<? extends DBWritable> inputClass) {
conf.setClass(DBConfiguration.INPUT_CLASS_PROPERTY, inputClass,
DBWritable.class);
}
public String getOutputTableName() {
return conf.get(DBConfiguration.OUTPUT_TABLE_NAME_PROPERTY);
}
public void setOutputTableName(String tableName) {
conf.set(DBConfiguration.OUTPUT_TABLE_NAME_PROPERTY, tableName);
}
public String[] getOutputFieldNames() {
return conf.getStrings(DBConfiguration.OUTPUT_FIELD_NAMES_PROPERTY);
}
public void setOutputFieldNames(String... fieldNames) {
conf.setStrings(DBConfiguration.OUTPUT_FIELD_NAMES_PROPERTY, fieldNames);
}
public void setOutputFieldCount(int fieldCount) {
conf.setInt(DBConfiguration.OUTPUT_FIELD_COUNT_PROPERTY, fieldCount);
}
public int getOutputFieldCount() {
return conf.getInt(OUTPUT_FIELD_COUNT_PROPERTY, 0);
}
/**
* Converts connection properties to a String to be passed to the mappers.
* @param properties JDBC connection parameters
* @return String to be passed to configuration
*/
protected static String propertiesToString(Properties properties) {
List<String> propertiesList = new ArrayList<String>(properties.size());
for(Entry<Object, Object> property : properties.entrySet()) {
String key = StringEscapeUtils.escapeCsv(property.getKey().toString());
if (key.equals(property.getKey().toString()) && key.contains("=")) {
key = "\"" + key + "\"";
}
String val = StringEscapeUtils.escapeCsv(property.getValue().toString());
if (val.equals(property.getValue().toString()) && val.contains("=")) {
val = "\"" + val + "\"";
}
propertiesList.add(StringEscapeUtils.escapeCsv(key + "=" + val));
}
return StringUtils.join(propertiesList, ',');
}
/**
* Converts a String back to connection parameters.
* @param input String from configuration
* @return JDBC connection parameters
*/
protected static Properties propertiesFromString(String input) {
if (input != null && !input.isEmpty()) {
Properties result = new Properties();
StrTokenizer propertyTokenizer = StrTokenizer.getCSVInstance(input);
StrTokenizer valueTokenizer = StrTokenizer.getCSVInstance();
valueTokenizer.setDelimiterChar('=');
while (propertyTokenizer.hasNext()){
valueTokenizer.reset(propertyTokenizer.nextToken());
String[] values = valueTokenizer.getTokenArray();
if (values.length==2) {
result.put(values[0], values[1]);
}
}
return result;
} else {
return null;
}
}
}