package org.openflamingo.mapreduce.etl.accounting; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import org.mvel2.MVEL; import org.openflamingo.mapreduce.core.Delimiter; import org.openflamingo.mapreduce.parser.CsvRowParser; import org.openflamingo.mapreduce.util.ArrayUtils; import java.io.*; import java.util.HashMap; import java.util.Map; /** * 하나 이상의 입력 파일을 받아서 합치는 Aggregation ETL Mapper. * 이 Mapper는 입력을 그대로 다시 출력한다. * * @author Edward KIM * @author Seo Ji Hye * @since 0.1 */ public class AccountingMapper extends Mapper<LongWritable, Text, NullWritable, Text> { /** * */ private Configuration configuration; /** * */ private String inputDelimiter; /** * */ private String outputDelimiter; /** * user가 입력한 수식 */ private Path expressionPath; /** * */ private String expression; /** * compile된 수식 */ private Serializable compliedExpression; /** * */ private int columnSize; @Override protected void setup(Context context) throws IOException, InterruptedException { configuration = context.getConfiguration(); inputDelimiter = configuration.get("inputDelimiter", Delimiter.COMMA.getDelimiter()); outputDelimiter = configuration.get("outputDelimiter", Delimiter.COMMA.getDelimiter()); columnSize = configuration.getInt("columnSize", -1); if (columnSize == -1) { throw new IllegalArgumentException("column size를 입력해 주세요."); } expressionPath = new Path(configuration.get("expression", null)); validateInputFile(); readFromInputFile(); compliedExpression = MVEL.compileExpression(expression); // FIXME validation } /** * * * @param key * @param value * @param context * @throws IOException * @throws InterruptedException */ protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { CsvRowParser parser = new CsvRowParser(); parser.setInputDelimiter(inputDelimiter); parser.setOutputDelimiter(outputDelimiter); parser.parse(value.toString()); System.out.println(" INPUT FILE VALUE : " + value.toString()); // collect columns String[] columnsForKeys = extractColumnsAsKeys(); Integer[] columnsForValues = extractColumnsAsValues(); Map columnsToEvaluate = new HashMap(); for (int i = 0; i < columnsForValues.length; i++) { columnsToEvaluate.put(columnsForKeys[i], Double.parseDouble(parser.get(columnsForValues[i]))); } // evaluate Double result = (Double) MVEL.executeExpression(compliedExpression, columnsToEvaluate); context.write(NullWritable.get(), new Text(result.toString())); // TODO format? } /** * Map의 key에 넣을 인덱스값 파싱 */ private String[] extractColumnsAsKeys() { // FIXME embedded conditions String str = expression.replaceAll("[\\(|\\)|\u0009|\u0020|\\n||\u0004|\u0000]", ""); return str.split("[\\+|\\-|\\*|\\/]"); } /** * Map에 value에 넣을 인덱스값 파싱 */ private Integer[] extractColumnsAsValues() { // FIXME embedded conditions String str = expression.replaceAll("[\\(|\\)|\\$|\u0009|\u0020|\\n|\u0004|\u0000]", ""); String[] tempArray = str.split("[\\+|\\-|\\*|\\/]"); // convert string array to integer array return ArrayUtils.toIntegerArray(tempArray); } /** * 사용자가 입력한 수식 파일이 존재하는지 검사. * * @throws IOException */ private void validateInputFile() throws IOException { final FileSystem fs = getFileSystem(); if (!fs.exists(expressionPath)) { throw new IllegalArgumentException("expression의 경로가 올바르지 않습니다. 입력한 경로 [" + expressionPath + "]"); } else if (!fs.isFile(expressionPath)) { throw new IllegalArgumentException("expression이 File이 아닙니다."); } } /** * 사용자가 입력한 파일에서 bytes array를 읽어 string으로 변환하여 expression을 읽어와 저장. * * @throws IOException */ private void readFromInputFile() throws IOException { FileSystem fs = getFileSystem(); FSDataInputStream in = fs.open(expressionPath); StringBuilder builder = new StringBuilder(); byte[] buffer = new byte[1024]; // FIXME fixed memory size while (in.read(buffer) > 0) { builder.append(new String(buffer, "UTF-8")); } expression = builder.toString(); } /** * Hadoop File System을 반환. * * @return Hadoop FileSystem * @throws IOException */ private FileSystem getFileSystem() throws IOException { return FileSystem.get(configuration); } }