/******************************************************************************* * Copyright © 2012-2015 eBay Software Foundation * This program is dual licensed under the MIT and Apache 2.0 licenses. * Please see LICENSE for more information. *******************************************************************************/ package com.ebay.jetstream.event.channel.file; import java.util.Collections; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ebay.jetstream.event.channel.ChannelAddress; /** * This class represents an "address" of the FileChannel (for both inbound and outbound), * which consists of a file-path pointing to a local file containing DB records or * JetStreamEvent, each record is a "line", i.e. * it is terminated by any one of a line feed ('\n'), a carriage return ('\r'), * or a carriage return followed immediately by a line feed. * Each column/attribute in a record (line) * is separated by a user-specified special delimiter which should NOT be embedded in * the original data. * For example: CSV (Comma Separated Values) file where newline is used for * separating the records and comma is used to separate the columns * the file looks like following: * * c11,c12,c13....\r\n * c21,c22,c23....\r\n * ... * The "address" also consists a list of column names. the number of columns in * the data file should match the number of column names. * In case there is no data for the column, the line can be like "c1,,c3...\r\n" * where c2 is missing because there is no value for this column. * * * For how MySQL does load the data into a tabel from the a text file * see http://dev.mysql.com/doc/refman/5.0/en/load-data.html * * @see CSVFileChannelAddress * * @author gjin */ public class FileChannelAddress extends ChannelAddress { private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.event.channel.file.FileChannelAddress"); private String m_pathname; /* either relative path or full pathname of the DB data file---readable */ private String[] m_columnNameArray= new String[0]; /* the fixed column names from callers */ private String m_columnDelimiter=","; /* delimiter between column. should not be embedded original data */ private Map<String, Object> m_columnNameToTypeMap; /*inbound only */ /*** for outbound only ***/ private long m_maxNumOfRecords = Long.MAX_VALUE; /* no max, application's own risk */ private boolean m_keepReservedKeys = false; /* should we remove those reserved keys from the events */ public FileChannelAddress() { } public String getPathname() { return m_pathname; } public void setPathname(String path) { m_pathname = path; if (m_pathname == null || m_pathname.length() == 0) { throw new IllegalArgumentException(" filename is null or empty unexpectedly"); } /* check readability */ /**#### File f = new File(m_pathname); if (!f.isFile() || !f.canRead()) { throw new IllegalArgumentException("Filename=" + m_pathname + " is not a file or not readable"); } ****/ } public String getColumnDelimiter() { return m_columnDelimiter; } public void setColumnDelimiter(String columnDelimiter) { if (columnDelimiter == null || columnDelimiter.length() ==0 ) { throw new IllegalArgumentException("Column Delimiter is null or empty unexpectedly"); } m_columnDelimiter = columnDelimiter; } public void setColumnNames(Map<String, Object> columnTypeMap) { /***###TODO: validation */ validateCloumns(columnTypeMap); m_columnNameToTypeMap = Collections.unmodifiableMap(columnTypeMap); Set<String> keys = m_columnNameToTypeMap.keySet(); m_columnNameArray = new String[keys.size()]; keys.toArray(m_columnNameArray); } /** * make sure the type is valid type and restore the value back with Class objects * @param columnNameToTypeMap */ private void validateCloumns(Map<String, Object> columnNameToTypeMap) { if (columnNameToTypeMap == null) { throw new IllegalArgumentException("columnNameToTypeMap should not be null"); } for (Map.Entry<String, Object> entry : columnNameToTypeMap.entrySet()) { Object objValue = entry.getValue(); if (objValue == null) { throw new IllegalArgumentException("columnName=" + entry.getKey() + " has null type"); } /* this should not happen */ if (objValue instanceof Class<?>) { continue; } /* "java.lang.Long" or "java.lang.String", ...*/ if (objValue instanceof String) { try { entry.setValue(Class.forName((String)objValue)); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("type field validation failed. e=", e); } } } } public Map<String, Object> getColumnNameToTypeMap() { return m_columnNameToTypeMap; } public String[] getColumnNameArray() { return m_columnNameArray; } /* for outbound only */ public void setColumnNameArray(String[] columnNameArray) { if (columnNameArray == null) { throw new IllegalArgumentException("column name aray should not be null"); } m_columnNameArray = new String[columnNameArray.length]; StringBuffer sb = new StringBuffer(); /* each column name should be non-null, non-empty string */ for (int i=0; i<columnNameArray.length; i++) { m_columnNameArray[i] = columnNameArray[i]; if (i != 0) { sb.append(","); } sb.append(columnNameArray[i]); if (m_columnNameArray[i]== null || m_columnNameArray[i].length() ==0) { throw new IllegalArgumentException("the "+ i + "th column is either null or empty unexpectedly"); } } LOGGER.warn( "filename=" + m_pathname + ", columnArray=" + sb.toString()); } /* for outbound only */ public void setMaxNumberOfRecords(long max) { if (max <=0) { return; } m_maxNumOfRecords = max; } public long getMaxNumberOfRecords() { return m_maxNumOfRecords; } public void setKeepReservedKeys(boolean keep) { m_keepReservedKeys = keep; } public boolean isKeepReservedKeys() { return m_keepReservedKeys; } }