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(
|
||||
"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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param columnTypes - mapping from column names to sql types
|
||||
@ -1131,6 +1166,7 @@ private StringBuilder generateClassForColumns(
|
||||
generateParser(columnTypes, colNames, sb);
|
||||
generateCloneMethod(columnTypes, colNames, sb);
|
||||
generateGetFieldMap(columnTypes, colNames, sb);
|
||||
generateSetField(columnTypes, colNames, sb);
|
||||
|
||||
// TODO(aaron): Generate hashCode(), compareTo(), equals() so it can be a
|
||||
// WritableComparable
|
||||
|
@ -34,6 +34,8 @@
|
||||
import com.cloudera.sqoop.SqoopOptions;
|
||||
import com.cloudera.sqoop.SqoopOptions.InvalidOptionsException;
|
||||
import com.cloudera.sqoop.config.ConfigurationHelper;
|
||||
|
||||
import com.cloudera.sqoop.testutil.ExplicitSetMapper;
|
||||
import com.cloudera.sqoop.tool.ImportTool;
|
||||
import com.cloudera.sqoop.util.ClassLoaderStack;
|
||||
|
||||
@ -205,5 +207,66 @@ public void testNumericTypes() throws IOException {
|
||||
createTableWithColTypes(types, vals);
|
||||
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();
|
||||
}
|
||||
|
||||
static final String BASE_COL_NAME = "DATA_COL";
|
||||
public static final String BASE_COL_NAME = "DATA_COL";
|
||||
|
||||
protected String getColName(int 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