mirror of
https://github.com/apache/sqoop.git
synced 2025-05-04 03:11:00 +08:00
SQOOP-179. Adding setField() method to SqoopRecord
This change introduces a setField(fieldName, fieldVal) method for SqoopRecord instances which would allow an arbitrary programmatic "setter" function without requiring reflection. From: Arvind Prabhakar <arvind@cloudera.com> git-svn-id: https://svn.apache.org/repos/asf/incubator/sqoop/trunk@1150021 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5ee493ee0c
commit
cf117e6500
@ -96,5 +96,17 @@ public Map<String, Object> getFieldMap() {
|
|||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Got null field map from record. Regenerate your record class.");
|
"Got null field map from record. Regenerate your record class.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows an arbitrary field to be set programmatically to the
|
||||||
|
* specified value object. The value object must match the
|
||||||
|
* type expected for the particular field or a RuntimeException
|
||||||
|
* will result.
|
||||||
|
* @throws RuntimeException if the specified field name does not exist.
|
||||||
|
*/
|
||||||
|
public void setField(String fieldName, Object fieldVal) {
|
||||||
|
throw new RuntimeException("This SqoopRecord does not support setField(). "
|
||||||
|
+ "Regenerate your record class.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,6 +649,41 @@ private void generateCloneMethod(Map<String, Integer> columnTypes,
|
|||||||
sb.append(" }\n\n");
|
sb.append(" }\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the setField() method.
|
||||||
|
* @param columnTypes - mapping from column names to sql types
|
||||||
|
* @param colNames - ordered list of column names for table.
|
||||||
|
* @param sb - StringBuilder to append code to
|
||||||
|
*/
|
||||||
|
private void generateSetField(Map<String, Integer> columnTypes,
|
||||||
|
String [] colNames, StringBuilder sb) {
|
||||||
|
sb.append(" public void setField(String __fieldName, Object __fieldVal) "
|
||||||
|
+ "{\n");
|
||||||
|
boolean first = true;
|
||||||
|
for (String colName : colNames) {
|
||||||
|
int sqlType = columnTypes.get(colName);
|
||||||
|
String javaType = connManager.toJavaType(sqlType);
|
||||||
|
if (null == javaType) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (!first) {
|
||||||
|
sb.append(" else");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(" if (\"" + colName + "\".equals(__fieldName)) {\n");
|
||||||
|
sb.append(" this." + colName + " = (" + javaType
|
||||||
|
+ ") __fieldVal;\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" else {\n");
|
||||||
|
sb.append(" throw new RuntimeException(");
|
||||||
|
sb.append("\"No such field: \" + __fieldName);\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the getFieldMap() method.
|
* Generate the getFieldMap() method.
|
||||||
* @param columnTypes - mapping from column names to sql types
|
* @param columnTypes - mapping from column names to sql types
|
||||||
@ -1131,6 +1166,7 @@ private StringBuilder generateClassForColumns(
|
|||||||
generateParser(columnTypes, colNames, sb);
|
generateParser(columnTypes, colNames, sb);
|
||||||
generateCloneMethod(columnTypes, colNames, sb);
|
generateCloneMethod(columnTypes, colNames, sb);
|
||||||
generateGetFieldMap(columnTypes, colNames, sb);
|
generateGetFieldMap(columnTypes, colNames, sb);
|
||||||
|
generateSetField(columnTypes, colNames, sb);
|
||||||
|
|
||||||
// TODO(aaron): Generate hashCode(), compareTo(), equals() so it can be a
|
// TODO(aaron): Generate hashCode(), compareTo(), equals() so it can be a
|
||||||
// WritableComparable
|
// WritableComparable
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
import com.cloudera.sqoop.SqoopOptions;
|
import com.cloudera.sqoop.SqoopOptions;
|
||||||
import com.cloudera.sqoop.SqoopOptions.InvalidOptionsException;
|
import com.cloudera.sqoop.SqoopOptions.InvalidOptionsException;
|
||||||
import com.cloudera.sqoop.config.ConfigurationHelper;
|
import com.cloudera.sqoop.config.ConfigurationHelper;
|
||||||
|
|
||||||
|
import com.cloudera.sqoop.testutil.ExplicitSetMapper;
|
||||||
import com.cloudera.sqoop.tool.ImportTool;
|
import com.cloudera.sqoop.tool.ImportTool;
|
||||||
import com.cloudera.sqoop.util.ClassLoaderStack;
|
import com.cloudera.sqoop.util.ClassLoaderStack;
|
||||||
|
|
||||||
@ -205,5 +207,66 @@ public void testNumericTypes() throws IOException {
|
|||||||
createTableWithColTypes(types, vals);
|
createTableWithColTypes(types, vals);
|
||||||
runParseTest(",", "\\n", "\\\'", "\\", false);
|
runParseTest(",", "\\n", "\\\'", "\\", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testFieldSetter() throws IOException {
|
||||||
|
ClassLoader prevClassLoader = null;
|
||||||
|
|
||||||
|
String [] types = { "VARCHAR(32)", "VARCHAR(32)" };
|
||||||
|
String [] vals = { "'meep'", "'foo'" };
|
||||||
|
createTableWithColTypes(types, vals);
|
||||||
|
|
||||||
|
String [] argv = getArgv(true, ",", "\\n", "\\\'", "\\", false);
|
||||||
|
runImport(argv);
|
||||||
|
try {
|
||||||
|
String tableClassName = getTableName();
|
||||||
|
|
||||||
|
argv = getArgv(false, ",", "\\n", "\\\'", "\\", false);
|
||||||
|
SqoopOptions opts = new ImportTool().parseArguments(argv, null, null,
|
||||||
|
true);
|
||||||
|
|
||||||
|
CompilationManager compileMgr = new CompilationManager(opts);
|
||||||
|
String jarFileName = compileMgr.getJarFilename();
|
||||||
|
|
||||||
|
// Make sure the user's class is loaded into our address space.
|
||||||
|
prevClassLoader = ClassLoaderStack.addJarFile(jarFileName,
|
||||||
|
tableClassName);
|
||||||
|
|
||||||
|
JobConf job = new JobConf();
|
||||||
|
job.setJar(jarFileName);
|
||||||
|
|
||||||
|
// Tell the job what class we're testing.
|
||||||
|
job.set(ExplicitSetMapper.USER_TYPE_NAME_KEY, tableClassName);
|
||||||
|
job.set(ExplicitSetMapper.SET_COL_KEY, BASE_COL_NAME + "0");
|
||||||
|
job.set(ExplicitSetMapper.SET_VAL_KEY, "this-is-a-test");
|
||||||
|
|
||||||
|
// use local mode in the same JVM.
|
||||||
|
ConfigurationHelper.setJobtrackerAddr(job, "local");
|
||||||
|
if (!BaseSqoopTestCase.isOnPhysicalCluster()) {
|
||||||
|
job.set(CommonArgs.FS_DEFAULT_NAME, CommonArgs.LOCAL_FS);
|
||||||
|
}
|
||||||
|
String warehouseDir = getWarehouseDir();
|
||||||
|
Path warehousePath = new Path(warehouseDir);
|
||||||
|
Path inputPath = new Path(warehousePath, getTableName());
|
||||||
|
Path outputPath = new Path(warehousePath, getTableName() + "-out");
|
||||||
|
|
||||||
|
job.setMapperClass(ExplicitSetMapper.class);
|
||||||
|
job.setNumReduceTasks(0);
|
||||||
|
FileInputFormat.addInputPath(job, inputPath);
|
||||||
|
FileOutputFormat.setOutputPath(job, outputPath);
|
||||||
|
|
||||||
|
job.setOutputKeyClass(Text.class);
|
||||||
|
job.setOutputValueClass(NullWritable.class);
|
||||||
|
|
||||||
|
JobClient.runJob(job);
|
||||||
|
} catch (InvalidOptionsException ioe) {
|
||||||
|
fail(ioe.toString());
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
fail(pe.toString());
|
||||||
|
} finally {
|
||||||
|
if (null != prevClassLoader) {
|
||||||
|
ClassLoaderStack.setCurrentClassLoader(prevClassLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ public void tearDown() {
|
|||||||
guaranteeCleanWarehouse();
|
guaranteeCleanWarehouse();
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String BASE_COL_NAME = "DATA_COL";
|
public static final String BASE_COL_NAME = "DATA_COL";
|
||||||
|
|
||||||
protected String getColName(int i) {
|
protected String getColName(int i) {
|
||||||
return BASE_COL_NAME + i;
|
return BASE_COL_NAME + i;
|
||||||
|
109
src/test/com/cloudera/sqoop/testutil/ExplicitSetMapper.java
Normal file
109
src/test/com/cloudera/sqoop/testutil/ExplicitSetMapper.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to Cloudera, Inc. under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. Cloudera, Inc. 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 com.cloudera.sqoop.testutil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.io.LongWritable;
|
||||||
|
import org.apache.hadoop.io.Text;
|
||||||
|
import org.apache.hadoop.io.NullWritable;
|
||||||
|
import org.apache.hadoop.mapred.JobConf;
|
||||||
|
import org.apache.hadoop.mapred.MapReduceBase;
|
||||||
|
import org.apache.hadoop.mapred.Mapper;
|
||||||
|
import org.apache.hadoop.mapred.OutputCollector;
|
||||||
|
import org.apache.hadoop.mapred.Reporter;
|
||||||
|
import com.cloudera.sqoop.lib.SqoopRecord;
|
||||||
|
import org.apache.hadoop.util.ReflectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test harness mapper. Instantiate the user's specific type, explicitly
|
||||||
|
* set the value of a field with setField(), and read the field value
|
||||||
|
* back via the field map. Throw an IOException if it doesn't get set
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
public class ExplicitSetMapper extends MapReduceBase
|
||||||
|
implements Mapper<LongWritable, Text, Text, NullWritable> {
|
||||||
|
|
||||||
|
public static final Log LOG = LogFactory.getLog(
|
||||||
|
ExplicitSetMapper.class.getName());
|
||||||
|
|
||||||
|
public static final String USER_TYPE_NAME_KEY = "sqoop.user.class";
|
||||||
|
public static final String SET_COL_KEY = "sqoop.explicit.set.col";
|
||||||
|
public static final String SET_VAL_KEY = "sqoop.explicit.set.val";
|
||||||
|
|
||||||
|
private SqoopRecord userRecord;
|
||||||
|
private String setCol;
|
||||||
|
private String setVal;
|
||||||
|
|
||||||
|
public void configure(JobConf job) {
|
||||||
|
String userTypeName = job.get(USER_TYPE_NAME_KEY);
|
||||||
|
if (null == userTypeName) {
|
||||||
|
throw new RuntimeException("Unconfigured parameter: "
|
||||||
|
+ USER_TYPE_NAME_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCol = job.get(SET_COL_KEY);
|
||||||
|
setVal = job.get(SET_VAL_KEY);
|
||||||
|
|
||||||
|
LOG.info("User type name set to " + userTypeName);
|
||||||
|
LOG.info("Will try to set col " + setCol + " to " + setVal);
|
||||||
|
|
||||||
|
this.userRecord = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Configuration conf = new Configuration();
|
||||||
|
Class userClass = Class.forName(userTypeName, true,
|
||||||
|
Thread.currentThread().getContextClassLoader());
|
||||||
|
this.userRecord =
|
||||||
|
(SqoopRecord) ReflectionUtils.newInstance(userClass, conf);
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
// handled by the next block.
|
||||||
|
LOG.error("ClassNotFound exception: " + cnfe.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Got an exception reflecting user class: " + e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == this.userRecord) {
|
||||||
|
LOG.error("Could not instantiate user record of type " + userTypeName);
|
||||||
|
throw new RuntimeException("Could not instantiate user record of type "
|
||||||
|
+ userTypeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map(LongWritable key, Text val,
|
||||||
|
OutputCollector<Text, NullWritable> out, Reporter r) throws IOException {
|
||||||
|
|
||||||
|
// Try to set the field.
|
||||||
|
userRecord.setField(setCol, setVal);
|
||||||
|
Map<String, Object> fieldVals = userRecord.getFieldMap();
|
||||||
|
if (!fieldVals.get(setCol).equals(setVal)) {
|
||||||
|
throw new IOException("Could not set column value! Got back "
|
||||||
|
+ fieldVals.get(setCol));
|
||||||
|
} else {
|
||||||
|
LOG.info("Correctly changed value for col " + setCol + " to " + setVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user